Skip to content

Commit dc247fe

Browse files
committed
Fix screen clearing documentation and add mouse support, non-blocking input, and terminal attributes documentation
1 parent 1903399 commit dc247fe

File tree

5 files changed

+900
-11
lines changed

5 files changed

+900
-11
lines changed

.idea/AugmentWebviewStateStore.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Non-Blocking Input
6+
7+
JLine provides support for non-blocking input, allowing you to read user input without blocking the execution of your application. This is particularly useful for applications that need to perform background tasks while still being responsive to user input.
8+
9+
## NonBlockingReader
10+
11+
The `NonBlockingReader` class is the key component for non-blocking input in JLine:
12+
13+
```java title="NonBlockingReaderExample.java" showLineNumbers
14+
import org.jline.terminal.Terminal;
15+
import org.jline.terminal.TerminalBuilder;
16+
import org.jline.utils.NonBlockingReader;
17+
18+
import java.io.IOException;
19+
import java.util.concurrent.TimeUnit;
20+
21+
public class NonBlockingReaderExample {
22+
public static void main(String[] args) throws IOException {
23+
Terminal terminal = TerminalBuilder.builder().build();
24+
25+
// highlight-start
26+
// Get a non-blocking reader
27+
NonBlockingReader reader = terminal.reader();
28+
// highlight-end
29+
30+
terminal.writer().println("Type something (program will exit after 10 seconds):");
31+
terminal.writer().flush();
32+
33+
// Track start time
34+
long startTime = System.currentTimeMillis();
35+
36+
// Run for 10 seconds
37+
while (System.currentTimeMillis() - startTime < 10000) {
38+
try {
39+
// highlight-start
40+
// Check if input is available
41+
if (reader.available() > 0) {
42+
// Read a character (non-blocking)
43+
int c = reader.read();
44+
terminal.writer().println("Read character: " + (char)c);
45+
terminal.writer().flush();
46+
}
47+
// highlight-end
48+
49+
// Simulate background work
50+
terminal.writer().print(".");
51+
terminal.writer().flush();
52+
TimeUnit.MILLISECONDS.sleep(500);
53+
} catch (InterruptedException e) {
54+
Thread.currentThread().interrupt();
55+
break;
56+
}
57+
}
58+
59+
terminal.writer().println("\nTime's up!");
60+
terminal.close();
61+
}
62+
}
63+
```
64+
65+
## Reading with Timeout
66+
67+
You can also read with a timeout, which will block for up to the specified time:
68+
69+
```java title="TimeoutReadExample.java"
70+
import org.jline.terminal.Terminal;
71+
import org.jline.terminal.TerminalBuilder;
72+
import org.jline.utils.NonBlockingReader;
73+
74+
import java.io.IOException;
75+
76+
public class TimeoutReadExample {
77+
public static void main(String[] args) throws IOException {
78+
Terminal terminal = TerminalBuilder.builder().build();
79+
NonBlockingReader reader = terminal.reader();
80+
81+
terminal.writer().println("Press any key within 5 seconds:");
82+
terminal.writer().flush();
83+
84+
try {
85+
// highlight-start
86+
// Read with a 5-second timeout
87+
int c = reader.read(5, TimeUnit.SECONDS);
88+
// highlight-end
89+
90+
if (c != -1) {
91+
terminal.writer().println("You pressed: " + (char)c);
92+
} else {
93+
terminal.writer().println("Timeout expired!");
94+
}
95+
} catch (IOException e) {
96+
terminal.writer().println("Error reading input: " + e.getMessage());
97+
}
98+
99+
terminal.close();
100+
}
101+
}
102+
```
103+
104+
## Combining with LineReader
105+
106+
You can combine non-blocking input with the `LineReader` for more sophisticated input handling:
107+
108+
```java title="NonBlockingLineReaderExample.java" showLineNumbers
109+
import org.jline.reader.LineReader;
110+
import org.jline.reader.LineReaderBuilder;
111+
import org.jline.reader.UserInterruptException;
112+
import org.jline.terminal.Terminal;
113+
import org.jline.terminal.TerminalBuilder;
114+
import org.jline.utils.NonBlockingReader;
115+
116+
import java.io.IOException;
117+
import java.util.concurrent.ExecutorService;
118+
import java.util.concurrent.Executors;
119+
import java.util.concurrent.TimeUnit;
120+
import java.util.concurrent.atomic.AtomicBoolean;
121+
122+
public class NonBlockingLineReaderExample {
123+
public static void main(String[] args) throws IOException {
124+
Terminal terminal = TerminalBuilder.builder().build();
125+
LineReader lineReader = LineReaderBuilder.builder()
126+
.terminal(terminal)
127+
.build();
128+
129+
// Flag to control background task
130+
AtomicBoolean running = new AtomicBoolean(true);
131+
132+
// Start background task
133+
ExecutorService executor = Executors.newSingleThreadExecutor();
134+
executor.submit(() -> {
135+
try {
136+
while (running.get()) {
137+
// Simulate background work
138+
terminal.writer().print(".");
139+
terminal.writer().flush();
140+
TimeUnit.SECONDS.sleep(1);
141+
}
142+
} catch (InterruptedException e) {
143+
Thread.currentThread().interrupt();
144+
} catch (Exception e) {
145+
terminal.writer().println("Error in background task: " + e.getMessage());
146+
terminal.writer().flush();
147+
}
148+
});
149+
150+
try {
151+
// Main input loop
152+
while (running.get()) {
153+
try {
154+
// Read a line (this will block)
155+
String line = lineReader.readLine("\nprompt> ");
156+
157+
if ("exit".equalsIgnoreCase(line)) {
158+
running.set(false);
159+
} else {
160+
terminal.writer().println("You entered: " + line);
161+
terminal.writer().flush();
162+
}
163+
} catch (UserInterruptException e) {
164+
// Ctrl+C pressed
165+
running.set(false);
166+
}
167+
}
168+
} finally {
169+
// Shutdown background task
170+
executor.shutdownNow();
171+
try {
172+
executor.awaitTermination(1, TimeUnit.SECONDS);
173+
} catch (InterruptedException e) {
174+
Thread.currentThread().interrupt();
175+
}
176+
177+
terminal.writer().println("\nExiting...");
178+
terminal.close();
179+
}
180+
}
181+
}
182+
```
183+
184+
## Asynchronous Input Handling
185+
186+
For more complex scenarios, you can set up asynchronous input handling:
187+
188+
```java title="AsyncInputExample.java"
189+
import org.jline.terminal.Terminal;
190+
import org.jline.terminal.TerminalBuilder;
191+
import org.jline.utils.NonBlockingReader;
192+
193+
import java.io.IOException;
194+
import java.util.concurrent.ExecutorService;
195+
import java.util.concurrent.Executors;
196+
import java.util.concurrent.TimeUnit;
197+
import java.util.concurrent.atomic.AtomicBoolean;
198+
199+
public class AsyncInputExample {
200+
public static void main(String[] args) throws IOException {
201+
Terminal terminal = TerminalBuilder.builder().build();
202+
NonBlockingReader reader = terminal.reader();
203+
204+
// Flag to control input handling
205+
AtomicBoolean running = new AtomicBoolean(true);
206+
207+
// Start input handling thread
208+
ExecutorService executor = Executors.newSingleThreadExecutor();
209+
executor.submit(() -> {
210+
try {
211+
// highlight-start
212+
// Continuously read input
213+
while (running.get()) {
214+
int c = reader.read(100);
215+
if (c != -1) {
216+
// Process the input
217+
terminal.writer().println("\nReceived input: " + (char)c);
218+
219+
if (c == 'q' || c == 'Q') {
220+
running.set(false);
221+
}
222+
223+
terminal.writer().flush();
224+
}
225+
}
226+
// highlight-end
227+
} catch (IOException e) {
228+
if (running.get()) {
229+
terminal.writer().println("Error reading input: " + e.getMessage());
230+
terminal.writer().flush();
231+
}
232+
}
233+
});
234+
235+
// Main application loop
236+
try {
237+
terminal.writer().println("Press keys (q to quit):");
238+
terminal.writer().flush();
239+
240+
int count = 0;
241+
while (running.get() && count < 30) {
242+
// Simulate application work
243+
terminal.writer().print(".");
244+
terminal.writer().flush();
245+
246+
TimeUnit.MILLISECONDS.sleep(500);
247+
count++;
248+
}
249+
} catch (InterruptedException e) {
250+
Thread.currentThread().interrupt();
251+
} finally {
252+
// Shutdown input handling
253+
running.set(false);
254+
executor.shutdownNow();
255+
try {
256+
executor.awaitTermination(1, TimeUnit.SECONDS);
257+
} catch (InterruptedException e) {
258+
Thread.currentThread().interrupt();
259+
}
260+
261+
terminal.writer().println("\nExiting...");
262+
terminal.close();
263+
}
264+
}
265+
}
266+
```
267+
268+
## Raw Mode vs. Cooked Mode
269+
270+
Understanding terminal modes is important for non-blocking input:
271+
272+
- **Cooked Mode (Default)**: Input is buffered until Enter is pressed
273+
- **Raw Mode**: Input is made available immediately, without buffering
274+
275+
```java title="RawModeExample.java"
276+
import org.jline.terminal.Attributes;
277+
import org.jline.terminal.Attributes.InputFlag;
278+
import org.jline.terminal.Terminal;
279+
import org.jline.terminal.TerminalBuilder;
280+
import org.jline.utils.NonBlockingReader;
281+
282+
import java.io.IOException;
283+
284+
public class RawModeExample {
285+
public static void main(String[] args) throws IOException {
286+
Terminal terminal = TerminalBuilder.builder().build();
287+
288+
try {
289+
// Save original terminal attributes
290+
Attributes originalAttributes = terminal.getAttributes();
291+
292+
// highlight-start
293+
// Configure raw mode
294+
Attributes rawAttributes = new Attributes(originalAttributes);
295+
rawAttributes.setInputFlag(InputFlag.ICANON, false); // Disable canonical mode
296+
rawAttributes.setInputFlag(InputFlag.ECHO, false); // Disable echo
297+
rawAttributes.setControlChar(Attributes.ControlChar.VMIN, 0); // Non-blocking
298+
rawAttributes.setControlChar(Attributes.ControlChar.VTIME, 0); // No timeout
299+
300+
// Enter raw mode
301+
terminal.setAttributes(rawAttributes);
302+
// highlight-end
303+
304+
terminal.writer().println("Terminal is in raw mode. Press keys (q to quit):");
305+
terminal.writer().flush();
306+
307+
NonBlockingReader reader = terminal.reader();
308+
309+
// Read characters until 'q' is pressed
310+
while (true) {
311+
int c = reader.read(100);
312+
if (c != -1) {
313+
terminal.writer().println("Read: " + (char)c + " (ASCII: " + c + ")");
314+
terminal.writer().flush();
315+
316+
if (c == 'q' || c == 'Q') {
317+
break;
318+
}
319+
}
320+
}
321+
322+
// Restore original terminal attributes
323+
terminal.setAttributes(originalAttributes);
324+
} finally {
325+
terminal.close();
326+
}
327+
}
328+
}
329+
```
330+
331+
## Best Practices
332+
333+
When working with non-blocking input in JLine, consider these best practices:
334+
335+
1. **Handle Interruptions**: Always handle interruptions properly to ensure clean shutdown.
336+
337+
2. **Use Separate Threads**: Keep input handling in a separate thread from your main application logic.
338+
339+
3. **Set Appropriate Timeouts**: Choose timeout values that balance responsiveness with CPU usage.
340+
341+
4. **Restore Terminal State**: Always restore the terminal to its original state before exiting.
342+
343+
5. **Check for EOF**: Check for end-of-file conditions (-1 return value) when reading.
344+
345+
6. **Use Atomic Flags**: Use atomic boolean flags for thread coordination.
346+
347+
7. **Provide User Feedback**: Keep users informed about what's happening, especially during long operations.
348+
349+
8. **Graceful Shutdown**: Implement graceful shutdown procedures for all threads.
350+
351+
9. **Error Handling**: Implement robust error handling for I/O exceptions.
352+
353+
10. **Test on Different Platforms**: Test your non-blocking input handling on different platforms and terminal types.

0 commit comments

Comments
 (0)