Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.terminal.control; singleton:=true
Bundle-Version: 1.1.200.qualifier
Bundle-Version: 1.1.300.qualifier
Bundle-Activator: org.eclipse.terminal.internal.control.impl.TerminalPlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ public interface IVT100EmulatorBackend {
*/
void clearAll();

/**
* Switches between the screen a shell writes to, which keeps its scrollback, and
* the one a full screen program is given, which has none. Leaving the alternate
* screen puts back exactly what was on the normal one, so a program that takes
* over the display does not cost the user the history behind it.
*
* @param enable whether to show the alternate screen
*/
void enableAlternateScreen(boolean enable);

/**
* Sets the Dimensions of the addressable scroll space of the screen....
* Keeps the cursor position relative to the bottom of the screen!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public void clearAll() {
fBackend.clearAll();
}

@Override
public void enableAlternateScreen(boolean enable) {
fWriter.println("enableAlternateScreen(" + enable + ')'); //$NON-NLS-1$
fBackend.enableAlternateScreen(enable);
}

@Override
public void deleteCharacters(int n) {
fWriter.println("deleteCharacters(" + n + ")"); //$NON-NLS-1$ //$NON-NLS-2$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1272,9 +1272,12 @@ private void processDecPrivateCommand_h() {
break;
case 47:
case 1047:
case 1048:
case 1049:
// Use Alternate Screen Buffer (ignored).
// Use Alternate Screen Buffer.
text.enableAlternateScreen(true);
break;
case 1048:
// Save cursor position (ignored).
break;
default:
Logger.log("Unsupported command parameter: CSI ?" + param + 'h'); //$NON-NLS-1$
Expand All @@ -1291,10 +1294,13 @@ private void processDecPrivateCommand_l() {
break;
case 47:
case 1047:
case 1048:
case 1049:
// Use Normal Screen Buffer (ignored, but reset scroll region).
// Use Normal Screen Buffer, putting back what was on it.
text.setScrollRegion(-1, -1);
text.enableAlternateScreen(false);
break;
case 1048:
// Restore cursor position (ignored).
break;
default:
Logger.log("Unsupported command parameter: CSI ?" + param + 'l'); //$NON-NLS-1$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -92,13 +93,57 @@ 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;
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 = 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);
} else {
fTerminal.copy(fNormalScreen);
fNormalScreen = null;
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);
}
setCursor(fNormalCursorLine, fNormalCursorColumn);
}
}
}

@Override
public void clearAll() {
synchronized (fTerminal) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,16 @@ public void addLine() {

@Override
public void copy(ITerminalTextData source) {
int height = getHeight(), width = getWidth();
fData.copy(source);
fCursorLine = source.getCursorLine();
fCursorColumn = source.getCursorColumn();
// Every line and the size of the buffer with them. Without saying so the view
// keeps drawing what it last saw, which is the screen a program left behind.
sendLinesChangedToSnapshot(0, Math.max(height, getHeight()));
if (height != getHeight() || width != getWidth()) {
sendDimensionsChanged();
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1086,4 +1086,44 @@ public void testNewlineInTopAnchoredScrollRegionCurrentlyDiscardsTopLine() {
assertNull(term.getChars(3));
assertEquals("4444", new String(term.getChars(4))); // footer below the region is untouched
}

@Test
public void testAlternateScreenBuffer() {
ITerminalTextData term = makeITerminalTextData();
IVT100EmulatorBackend vt100 = makeBakend(term);
term.setMaxHeight(100);
vt100.setDimensions(3, 10);
vt100.setCursor(0, 0);
vt100.appendString("one\r\n");
for (int i = 0; i < 5; i++) {
vt100.appendString("more");
vt100.processNewline();
vt100.setCursorColumn(0);
}
vt100.appendString("last");
int historyHeight = term.getHeight();
assertTrue(historyHeight > 3);

vt100.enableAlternateScreen(true);
// no history to scroll back through, and none accumulates: scrolling drops the top line
assertEquals(3, term.getHeight());
assertEquals(3, term.getMaxHeight());
vt100.appendString("alt");
vt100.setCursor(2, 0);
vt100.processNewline();
vt100.processNewline();
assertEquals(3, term.getHeight());
assertNull(term.getChars(0));

// resized while the program had the screen
vt100.setDimensions(5, 7);
vt100.enableAlternateScreen(false);
// the buffer put back is as wide as the screen is now and at least as tall
assertEquals(7, term.getWidth());
assertTrue(term.getHeight() >= 5);
assertEquals(100, term.getMaxHeight());
assertEquals("last", new String(term.getChars(term.getHeight() - 1), 0, 4));
vt100.appendString("x"); // a write past the old margin used to throw
assertEquals(5, vt100.getCursorColumn());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ private static String SCROLL_REGION(int startRow, int endRow) {
* Set the cursor position to line/column. Note that this is the logical
* line and column, so 1, 1 is the top left.
*/
private static final String ALTERNATE_SCREEN_ON = "\u001b[?1049h";
private static final String ALTERNATE_SCREEN_OFF = "\u001b[?1049l";

private static String CURSOR_POSITION(int line, int column) {
return "\033[" + line + ";" + column + "H";
}
Expand Down Expand Up @@ -375,4 +378,23 @@ public void testMalformedStringTerminator() {
() -> assertEquals(List.of("TITLE1", "TITLE2"), control.getAllTitles()));
}

@Test
public void testAlternateScreen() {
data.setMaxHeight(1000);
run("Hello 1\r\nHello 2");
assertAll(() -> assertCursorLocation(1, 7), () -> assertTextEquals("Hello 1", "Hello 2"));
// a full screen program takes the screen: it starts blank and no history is kept
run(ALTERNATE_SCREEN_ON);
assertAll(() -> assertCursorLocation(0, 0), () -> assertTextEquals(""));
assertEquals(WINDOW_LINES, data.getHeight());
run("Full screen");
assertTextEquals("Full screen");
run(ALTERNATE_SCREEN_ON); // programs do ask twice
assertTextEquals("Full screen");
// and gives it back with what was there before, cursor included
run(ALTERNATE_SCREEN_OFF);
assertAll(() -> assertCursorLocation(1, 7), () -> assertTextEquals("Hello 1", "Hello 2"));
run(ALTERNATE_SCREEN_OFF); // harmless twice as well
assertAll(() -> assertCursorLocation(1, 7), () -> assertTextEquals("Hello 1", "Hello 2"));
}
}
Loading