-
Notifications
You must be signed in to change notification settings - Fork 176
Terminal: implement the alternate screen buffer (DEC mode 1049) #2899
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a3e108c
0924176
84b7af6
381accb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| package org.eclipse.terminal.internal.emulator; | ||
|
|
||
| import org.eclipse.terminal.model.ITerminalTextData; | ||
| import org.eclipse.terminal.internal.model.TerminalTextDataStore; | ||
| import org.eclipse.terminal.model.TerminalStyle; | ||
|
|
||
| /** | ||
|
|
@@ -92,13 +93,79 @@ int getHeight() { | |
| int fLines; | ||
| int fColumns; | ||
| final private ITerminalTextData fTerminal; | ||
|
|
||
| /** | ||
| * What the normal screen held while the alternate one is showing, and null the | ||
| * rest of the time, which is also how we know which screen we are on. | ||
| */ | ||
| private ITerminalTextData fNormalScreen; | ||
| private int fNormalMaxHeight; | ||
| /** in the normal buffer, not on the screen: the screen may be resized meanwhile */ | ||
| private int fNormalCursorLine; | ||
| private int fNormalCursorColumn; | ||
| private boolean fVT100LineWrapping; | ||
| private ScrollRegion fScrollRegion = ScrollRegion.FULL_WINDOW; | ||
|
|
||
| public VT100EmulatorBackend(ITerminalTextData terminal) { | ||
| fTerminal = terminal; | ||
| } | ||
|
|
||
| @Override | ||
| public void enableAlternateScreen(boolean enable) { | ||
| synchronized (fTerminal) { | ||
| if (enable == (fNormalScreen != null)) { | ||
| // Already on the screen being asked for. Programs do ask twice. | ||
| return; | ||
| } | ||
| if (enable) { | ||
| fNormalScreen = new TerminalTextDataStore(); | ||
| fNormalScreen.copy(fTerminal); | ||
| fNormalCursorLine = toAbsoluteLine(fCursorLine); | ||
| fNormalCursorColumn = fCursorColumn; | ||
| fNormalMaxHeight = fTerminal.getMaxHeight(); | ||
| // clearAll leaves the buffer the size of the screen, which is what the | ||
| // alternate screen is: no history to scroll back through. Capping the | ||
| // 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); | ||
|
insjang marked this conversation as resolved.
Comment on lines
+125
to
+131
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 381accb: VT100TerminalControl.setBufferLineLimit() now goes through the emulator to a new backend method. While the alternate screen is showing, the backend keeps the limit for the normal screen and applies it on return, so the cap stays and the new limit is not lost. Covered by testBufferLineLimitWhileAlternateScreen. |
||
| } else { | ||
| fTerminal.copy(fNormalScreen); | ||
| fNormalScreen = null; | ||
| // the margins were the full screen program's, not the shell's | ||
| fScrollRegion = ScrollRegion.FULL_WINDOW; | ||
| fTerminal.setMaxHeight(Math.max(fNormalMaxHeight, fTerminal.getHeight())); | ||
| // The window may have been resized while the program had the screen, and | ||
| // the buffer put back is the one from before. Narrower, and every write | ||
| // past its margin throws; shorter than the screen, and the top of the | ||
| // screen sits above its first line, so every line number comes out negative. | ||
| if (fTerminal.getHeight() < fLines || fTerminal.getWidth() != fColumns) { | ||
| fTerminal.setDimensions(Math.max(fTerminal.getHeight(), fLines), fColumns); | ||
| } | ||
| // back to the line the cursor was on, wherever it is on the screen now | ||
| setCursor(fNormalCursorLine - (fTerminal.getHeight() - fLines), fNormalCursorColumn); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void setBufferLineLimit(int bufferLineLimit) { | ||
| if (bufferLineLimit <= 0) { | ||
| return; | ||
| } | ||
| synchronized (fTerminal) { | ||
| if (fNormalScreen != null) { | ||
| // the alternate screen keeps its cap; the normal screen gets the limit back | ||
| fNormalMaxHeight = bufferLineLimit; | ||
| return; | ||
| } | ||
| if (fTerminal.getHeight() > bufferLineLimit) { | ||
| fTerminal.setDimensions(bufferLineLimit, fTerminal.getWidth()); | ||
| } | ||
| fTerminal.setMaxHeight(bufferLineLimit); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void clearAll() { | ||
| synchronized (fTerminal) { | ||
|
|
@@ -122,6 +189,22 @@ public void setDimensions(int lines, int cols) { | |
| // relative cursor line | ||
| int cl = getCursorLine(); | ||
| int cc = getCursorColumn(); | ||
| if (fNormalScreen != null) { | ||
| // The alternate screen has no history, so its buffer stays exactly as | ||
| // high as the screen. On a shorter screen the lines above the cursor go | ||
| // first, as many as it takes to keep the cursor on it; the cap follows | ||
| // once the buffer is no higher than it, which the buffer requires. | ||
| int drop = Math.max(0, cl + 1 - lines); | ||
| if (drop > 0) { | ||
| fTerminal.scroll(0, fTerminal.getHeight(), -drop); | ||
| } | ||
| fLines = lines; | ||
| fColumns = cols; | ||
| fTerminal.setDimensions(lines, cols); | ||
| fTerminal.setMaxHeight(lines); | ||
| setCursor(cl - drop, cc); | ||
| return; | ||
| } | ||
| int height = fTerminal.getHeight(); | ||
| // absolute cursor line | ||
| int acl = cl + height - fLines; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 381accb: resetState() now leaves the alternate screen, which puts the normal screen and its history back, so the next connection starts there and a late CSI ? 1049 l no longer brings back the old program's screen. Covered by testResetStateLeavesAlternateScreen, on the terminal's real buffer.