Skip to content

Commit a640fd2

Browse files
committed
Added documentation to describe the synchronized support
1 parent 2c7d1c6 commit a640fd2

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed

content/docs/readline/connection.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,25 @@ connection.setThemeChangeHandler(null);
452452
connection.close();
453453
```
454454

455+
## Synchronized Output (Mode 2026)
456+
457+
Synchronized output prevents screen tearing by telling the terminal to buffer all output until the frame is complete. See [Synchronized Output](synchronized-output) for full documentation.
458+
459+
```java
460+
// Check support (heuristic, no query sent)
461+
if (connection.supportsSynchronizedOutput()) {
462+
connection.enableSynchronizedOutput();
463+
// ... render frame ...
464+
connection.disableSynchronizedOutput();
465+
}
466+
467+
// Runtime query via DECRPM (authoritative)
468+
Boolean supported = connection.querySynchronizedOutput(500);
469+
// true = supported, false = not supported, null = timeout
470+
```
471+
472+
Synchronized output is automatically managed by `Readline` for supporting terminals. Use the `ReadlineFlag.NO_SYNCHRONIZED_OUTPUT` flag to opt out.
473+
455474
## Device Attributes (DA1/DA2)
456475

457476
Device Attributes queries allow you to detect terminal capabilities that cannot be determined from terminfo alone.
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
---
2+
date: '2026-02-23T15:00:00+01:00'
3+
draft: false
4+
title: 'Synchronized Output'
5+
weight: 13
6+
---
7+
8+
Synchronized output (Mode 2026) prevents screen tearing during rapid terminal redraws. When enabled, the terminal buffers all output until the mode is disabled, then paints the entire frame atomically. This is particularly useful for readline — completion lists, multi-line prompts, and screen repaints all cause multiple write calls that can tear without synchronization.
9+
10+
## How It Works
11+
12+
Mode 2026 uses two escape sequences:
13+
14+
| Sequence | Name | Purpose |
15+
|----------|------|---------|
16+
| `ESC[?2026h` | BSU (Begin Synchronized Update) | Start buffering output |
17+
| `ESC[?2026l` | ESU (End Synchronized Update) | Flush buffer and paint |
18+
19+
Everything written between BSU and ESU is held by the terminal and rendered in a single
20+
operation. On terminals that don't recognize Mode 2026, the sequences are silently ignored
21+
— so it's always safe to send them.
22+
23+
## Quick Start
24+
25+
```java
26+
import org.aesh.terminal.tty.TerminalConnection;
27+
import org.aesh.terminal.utils.ANSI;
28+
29+
TerminalConnection conn = new TerminalConnection();
30+
31+
// Check if terminal supports synchronized output
32+
if (conn.supportsSynchronizedOutput()) {
33+
conn.enableSynchronizedOutput();
34+
35+
// All writes here are buffered by the terminal
36+
conn.write("Line 1\n");
37+
conn.write("Line 2\n");
38+
conn.write("Line 3\n");
39+
40+
conn.disableSynchronizedOutput();
41+
// Terminal paints all three lines at once
42+
}
43+
```
44+
45+
## Terminal Support
46+
47+
Mode 2026 is supported by the following terminals:
48+
49+
| Terminal | Supported | Notes |
50+
|----------|-----------|-------|
51+
| Kitty | Yes | Since early versions |
52+
| Ghostty | Yes | Since 1.0.0 |
53+
| WezTerm | Yes | Full support |
54+
| foot | Yes | Full support |
55+
| Contour | Yes | Origin of the specification |
56+
| iTerm2 | Yes | Full support |
57+
| Mintty | Yes | Full support |
58+
| xterm | No | Sequences ignored safely |
59+
| GNOME Terminal | No | Sequences ignored safely |
60+
| Konsole | No | Sequences ignored safely |
61+
| Alacritty | No | Sequences ignored safely |
62+
63+
On unsupported terminals, the BSU/ESU sequences are silently ignored, so applications
64+
can always send them without feature detection.
65+
66+
## Connection API
67+
68+
The `Connection` interface provides four methods for synchronized output:
69+
70+
### Checking Support
71+
72+
```java
73+
// Heuristic check based on terminal type detection
74+
boolean supported = connection.supportsSynchronizedOutput();
75+
```
76+
77+
This uses environment-based terminal detection via `TerminalEnvironment` and the
78+
`Device.TerminalType` enum. No terminal query is sent.
79+
80+
### Runtime Query (DECRPM)
81+
82+
For authoritative detection, query the terminal directly using DECRQM/DECRPM:
83+
84+
```java
85+
// Send CSI ? 2026 $ p and parse the DECRPM response
86+
// Returns true (supported), false (not supported), or null (timeout)
87+
Boolean result = connection.querySynchronizedOutput(500);
88+
89+
if (Boolean.TRUE.equals(result)) {
90+
// Terminal definitively supports Mode 2026
91+
}
92+
```
93+
94+
The DECRPM response `CSI ? 2026 ; Ps $ y` uses these Ps values:
95+
96+
| Ps | Meaning | Result |
97+
|----|---------|--------|
98+
| 0 | Not recognized | `false` |
99+
| 1 | Set (enabled) | `true` |
100+
| 2 | Reset (recognized but disabled) | `true` |
101+
| 3 | Permanently set | `true` |
102+
| 4 | Permanently reset | `false` |
103+
104+
### Enable / Disable
105+
106+
```java
107+
connection.enableSynchronizedOutput(); // Send ESC[?2026h
108+
connection.disableSynchronizedOutput(); // Send ESC[?2026l
109+
```
110+
111+
Both methods return the `Connection` for chaining.
112+
113+
## Automatic Readline Integration
114+
115+
When using the `Readline` class, synchronized output is automatically applied. If the
116+
terminal supports Mode 2026, readline wraps prompt drawing, action execution, and resize
117+
redraws with BSU/ESU sequences. No application code is needed.
118+
119+
```java
120+
Readline readline = new Readline();
121+
122+
// Synchronized output is enabled automatically for supporting terminals
123+
readline.readline(connection, new Prompt("$ "), input -> {
124+
// handle input
125+
});
126+
```
127+
128+
### Opting Out
129+
130+
To disable automatic synchronized output, set the `NO_SYNCHRONIZED_OUTPUT` flag:
131+
132+
```java
133+
import org.aesh.readline.ReadlineFlag;
134+
135+
EnumMap<ReadlineFlag, Integer> flags = new EnumMap<>(ReadlineFlag.class);
136+
flags.put(ReadlineFlag.NO_SYNCHRONIZED_OUTPUT, 0);
137+
138+
readline.readline(connection, new Prompt("$ "), input -> {
139+
// handle input
140+
}, null, null, null, null, flags);
141+
```
142+
143+
## Manual Usage
144+
145+
For applications that manage their own rendering loop (outside of `Readline`), use
146+
the `Connection` methods directly or the ANSI constants:
147+
148+
```java
149+
// Begin synchronized update — terminal starts buffering
150+
connection.enableSynchronizedOutput();
151+
152+
// Each write is buffered by the terminal, not painted yet
153+
connection.write("\u001B[2J"); // Clear screen
154+
connection.write("\u001B[1;1H"); // Move to top-left
155+
connection.write("Header line");
156+
connection.write("\u001B[2;1H");
157+
connection.write("Content line");
158+
// ... more rendering ...
159+
160+
// End synchronized update — terminal paints everything at once
161+
connection.disableSynchronizedOutput();
162+
```
163+
164+
There is no need to batch writes into a `StringBuilder` — the terminal itself
165+
buffers all output between BSU and ESU. Multiple `write()` calls are fine.
166+
167+
### ANSI Constants
168+
169+
| Constant | Value | Description |
170+
|----------|-------|-------------|
171+
| `ANSI.MODE_2026_QUERY` | `ESC[?2026$p` | DECRQM query |
172+
| `ANSI.MODE_2026_ENABLE` | `ESC[?2026h` | Begin synchronized update (BSU) |
173+
| `ANSI.MODE_2026_DISABLE` | `ESC[?2026l` | End synchronized update (ESU) |
174+
175+
### DECRPM Response Parser
176+
177+
Parse a raw terminal DECRPM response:
178+
179+
```java
180+
// Parse ESC[?2026;Ps$y response
181+
String response = "\u001B[?2026;2$y";
182+
int[] codePoints = response.codePoints().toArray();
183+
184+
Boolean supported = ANSI.parseMode2026Response(codePoints);
185+
// true for Ps=1,2,3; false for Ps=0,4; null for invalid
186+
```
187+
188+
The parser handles leading noise and trailing data, which is common in real terminal I/O.
189+
190+
## Example: Bouncing Image Demo
191+
192+
The `SyncImageDemoExample` demonstrates synchronized output with inline image display.
193+
An image bounces around the screen like a screensaver — tearing is immediately visible
194+
when synchronized output is toggled off.
195+
196+
```bash
197+
mvn exec:java -pl terminal-tty \
198+
-Dexec.mainClass="org.aesh.terminal.tty.example.SyncImageDemoExample" \
199+
-Dexec.args="/path/to/image.jpg"
200+
```
201+
202+
| Key | Action |
203+
|-----|--------|
204+
| `S` | Toggle synchronized output on/off |
205+
| `+` / `-` | Zoom image in/out |
206+
| `<` / `>` | Decrease/increase bounce speed |
207+
| `Q` | Quit |
208+
209+
Source: [`SyncImageDemoExample.java`](https://github.com/aeshell/aesh-readline/blob/master/terminal-tty/src/main/java/org/aesh/terminal/tty/example/SyncImageDemoExample.java)
210+
211+
There is also a simpler dashboard demo without images:
212+
213+
```bash
214+
mvn exec:java -pl terminal-tty \
215+
-Dexec.mainClass="org.aesh.terminal.tty.example.SynchronizedOutputExample"
216+
```
217+
218+
Source: [`SynchronizedOutputExample.java`](https://github.com/aeshell/aesh-readline/blob/master/terminal-tty/src/main/java/org/aesh/terminal/tty/example/SynchronizedOutputExample.java)
219+
220+
## Comparison with Mode 2027
221+
222+
Mode 2026 (synchronized output) and Mode 2027 (grapheme cluster segmentation) are
223+
independent features that address different problems:
224+
225+
| | Mode 2026 | Mode 2027 |
226+
|-|-----------|-----------|
227+
| **Purpose** | Prevent screen tearing | Correct cursor positioning for complex characters |
228+
| **Scope** | Per-frame toggle (BSU/ESU) | Persistent mode (enable once at startup) |
229+
| **Cleanup** | None needed — each ESU completes | Must disable before exit |
230+
| **Effect** | Terminal buffers output | Terminal uses UAX #29 segmentation |
231+
232+
Both modes are automatically managed by `Readline` when supported.
233+
234+
## Specification
235+
236+
The synchronized output protocol was originally specified by the Contour terminal:
237+
238+
[Contour VT Extension: Synchronized Output](https://contour-terminal.org/vt-extensions/synchronized-output/)
239+
240+
## See Also
241+
242+
- [Connection](connection) — Full `Connection` API reference
243+
- [Terminal Images](terminal-images) — Inline image display
244+
- [Terminal Environment](terminal-environment) — Terminal type detection

0 commit comments

Comments
 (0)