Conversation
Full screen programs - vi, less, htop, and lately CLIs that draw their own UI - ask for the alternate screen with CSI ? 1049 h and give it back with CSI ? 1049 l. The emulator accepted the sequences and ignored them, so such a program drew over the shell's scrollback and left its last screen behind when it exited. Switching to the alternate screen now saves the normal buffer and the cursor, clears the screen and caps the buffer at the screen height, so that scrolling drops the top line instead of growing history the alternate screen is not supposed to have. Switching back restores the saved buffer and cursor, lifts the cap again, and brings the buffer to the current width and at least the screen height, since the window may have been resized in the meantime: a narrower buffer made every write past its old margin throw, a shorter one put the top of the screen above its first line. The restored buffer tells its snapshots so the view redraws instead of keeping the program's last screen. Modes 47 and 1047 are treated the same; 1048 (save/restore cursor alone) stays ignored. Asking for the screen one is already on is a no-op, as programs do ask twice.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Resizing on the alternate screen can recreate scrollback and restore the cursor onto the wrong history line.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 2
Open (2)
What changed in this PR
Implements DEC alternate-screen buffers so full-screen terminal applications preserve shell content and scrollback.
Changes:
- Handles DEC modes 47, 1047, and 1049.
- Saves/restores terminal content and cursor state.
- Adds snapshot notifications and alternate-screen tests.
| File | Description |
|---|---|
VT100EmulatorTest.java |
Tests escape-sequence switching. |
VT100EmulatorBackendTest.java |
Tests buffer restoration and resizing. |
TerminalTextData.java |
Notifies snapshots after copying. |
VT100EmulatorBackend.java |
Implements alternate-buffer lifecycle. |
VT100Emulator.java |
Dispatches DEC screen modes. |
VT100BackendTraceDecorator.java |
Traces screen switches. |
IVT100EmulatorBackend.java |
Adds the switching API. |
META-INF/MANIFEST.MF |
Increments the bundle version. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| // buffer there keeps it so: scrolling drops the top line instead of | ||
| // growing the buffer, as a program on this screen expects. | ||
| clearAll(); | ||
| fTerminal.setMaxHeight(fLines); |
| if (fTerminal.getHeight() < fLines || fTerminal.getWidth() != fColumns) { | ||
| fTerminal.setDimensions(Math.max(fTerminal.getHeight(), fLines), fColumns); | ||
| } | ||
| setCursor(fNormalCursorLine, fNormalCursorColumn); |
Test Results 33 files - 21 33 suites - 21 37m 5s ⏱️ - 21m 14s For more details on these errors, see this check. Results for commit 49aeb35. ± Comparison against base commit 470ac3d. This pull request skips 23 tests. |

Full screen programs - vi, less, htop, and lately CLIs that draw their own UI - ask for the alternate screen with CSI ? 1049 h and give it back with CSI ? 1049 l. The emulator accepted the sequences and ignored them, so such a program drew over the shell's scrollback and left its last screen behind when it exited.
Switching to the alternate screen now saves the normal buffer and the cursor, clears the screen and caps the buffer at the screen height, so that scrolling drops the top line instead of growing history the alternate screen is not supposed to have. Switching back restores the saved buffer and cursor, lifts the cap again, and brings the buffer to the current width and at least the screen height, since the window may have been resized in the meantime: a narrower buffer made every write past its old margin throw, a shorter one put the top of the screen above its first line. The restored buffer tells its snapshots so the view redraws instead of keeping the program's last screen.
Modes 47 and 1047 are treated the same; 1048 (save/restore cursor alone) stays ignored. Asking for the screen one is already on is a no-op, as programs do ask twice.