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 @@ -47,4 +47,54 @@ public interface ITerminalControlForText {
*/
void enableApplicationCursorKeys(boolean enable);

/**
* A program that asks for bracketed paste (CSI ?2004) wants pasted text marked
* as such, so that it can take it as text rather than as something typed.
*/
default void enableBracketedPaste(boolean enable) {
}

/**
* Shows or hides the cursor at the program's request (DEC mode 25).
*
* @param show whether the cursor is to be drawn
*/
default void showCursor(boolean show) {
}

/**
* Turns reporting of the terminal gaining and losing focus on or off (DEC mode 1004).
*
* @param enable whether the program wants to be told
*/
default void enableFocusReporting(boolean enable) {
}

/**
* A program that asks for mouse events wants them instead of the terminal's own
* handling of the mouse, scrolling included.
*
* @since 5.6
*/
default void enableMouseReporting(int mode) {
}

/**
* Says a program is drawing a screen and has not finished (DEC mode 2026), so
* that the view does not show it half drawn.
*
* @param redrawing whether a screen is being drawn
*/
default void enableSynchronizedOutput(boolean redrawing) {
}

/**
* SGR encoding (CSI ?1006) reports coordinates as decimal numbers instead of
* single bytes, which is what lets them go past column 223.
*
* @since 5.6
*/
default void enableSgrMouseEncoding(boolean enable) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1264,38 +1264,92 @@ private void processAnsiCommand_X() {
}

private void processDecPrivateCommand_h() {
int param = getAnsiParameter(0);
// A program may set several modes at once: ncurses turns the mouse on with CSI ? 1006 ; 1000 h.
for (int i = 0; i <= nextAnsiParameter; i++) {
setDecPrivateMode(getAnsiParameter(i));
}
}

private void setDecPrivateMode(int param) {
switch (param) {
case 1:
// Enable Application Cursor Keys (DECCKM)
terminal.enableApplicationCursorKeys(true);
break;
case 25:
// Show cursor (DECTCEM).
terminal.showCursor(true);
break;
case 47:
case 1047:
case 1048:
case 1049:
// Use Alternate Screen Buffer (ignored).
break;
case 2026:
terminal.enableSynchronizedOutput(true);
break;
case 2004:
// Bracketed paste: pasted text is wrapped so a program can tell it from typing.
terminal.enableBracketedPaste(true);
break;
case 1000: // report button press and release
case 1002: // also report drag
case 1003: // also report every move
terminal.enableMouseReporting(param);
break;
case 1006:
terminal.enableSgrMouseEncoding(true);
break;
case 1004:
terminal.enableFocusReporting(true);
break;
default:
Logger.log("Unsupported command parameter: CSI ?" + param + 'h'); //$NON-NLS-1$
break;
}
}

private void processDecPrivateCommand_l() {
int param = getAnsiParameter(0);
for (int i = 0; i <= nextAnsiParameter; i++) {
resetDecPrivateMode(getAnsiParameter(i));
}
}

private void resetDecPrivateMode(int param) {
switch (param) {
case 1:
// Enable Normal Cursor Keys (DECCKM)
terminal.enableApplicationCursorKeys(false);
break;
case 25:
// Hide cursor (DECTCEM).
terminal.showCursor(false);
break;
case 47:
case 1047:
case 1048:
case 1049:
// Use Normal Screen Buffer (ignored, but reset scroll region).
text.setScrollRegion(-1, -1);
break;
case 2026:
terminal.enableSynchronizedOutput(false);
break;
case 2004:
terminal.enableBracketedPaste(false);
break;
case 1000:
case 1002:
case 1003:
terminal.enableMouseReporting(0);
break;
case 1006:
terminal.enableSgrMouseEncoding(false);
break;
case 1004:
terminal.enableFocusReporting(false);
break;
default:
Logger.log("Unsupported command parameter: CSI ?" + param + 'l'); //$NON-NLS-1$
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,19 @@ public class VT100TerminalControl implements ITerminalControlForText, ITerminalC
private final EditActionAccelerators editActionAccelerators = new EditActionAccelerators();

private boolean fApplicationCursorKeys;
private int fMouseMode;

private int fLastReportedLine, fLastReportedColumn;
private boolean fSgrMouseEncoding;

/**
* Listens to changes in the preferences
*/
private final IPropertyChangeListener fPreferenceListener = this::updatePreferences;
private final IPropertyChangeListener fFontListener = this::updateFont;
private boolean fBracketedPaste;
private static final String PASTE_START = "\u001b[200~"; //$NON-NLS-1$
private static final String PASTE_END = "\u001b[201~"; //$NON-NLS-1$

/**
* Is protected by synchronize on this
Expand Down Expand Up @@ -308,7 +315,7 @@ public boolean pasteString(String strText) {
if (strText == null) {
return false;
}
sendString(strText);
sendString(fBracketedPaste ? bracketed(strText) : strText);
return true;
}

Expand Down Expand Up @@ -569,6 +576,16 @@ public Shell getShell() {
return getCtlText().getShell();
}

/**
* Marks text as pasted, so that a program takes the newlines in it as part of
* the text rather than as the user pressing return on each line. The end marker
* is taken out of the text itself, or the text could close the bracket early and
* the rest of it would arrive as if it had been typed.
*/
private static String bracketed(String text) {
return PASTE_START + text.replace(PASTE_END, "") + PASTE_END; //$NON-NLS-1$
}

protected void sendChar(char chKey, boolean altKeyPressed) {
try {
int byteToSend = chKey;
Expand Down Expand Up @@ -803,6 +820,8 @@ protected void setupHelp(Composite parent, String id) {
}

PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, id);
getCtlText().setMouseWheelHandler(this::reportMouseWheel);
getCtlText().setMouseButtonHandler(new MouseReporter());
}

@Override
Expand Down Expand Up @@ -873,6 +892,7 @@ public void focusGained(FocusEvent event) {
if (getState() == TerminalState.CONNECTED) {
captureKeyEvents(true);
}
reportFocus(true);

IContextService contextService = PlatformUI.getWorkbench().getAdapter(IContextService.class);
editContextActivation = contextService.activateContext("org.eclipse.terminal.EditContext"); //$NON-NLS-1$
Expand All @@ -882,6 +902,7 @@ public void focusGained(FocusEvent event) {
public void focusLost(FocusEvent event) {
// Enable all keybindings.
captureKeyEvents(false);
reportFocus(false);

// Restore the command context to its previous value.

Expand Down Expand Up @@ -1288,6 +1309,11 @@ public void setState(TerminalState state) {
});
}

@Override
public void enableBracketedPaste(boolean enable) {
fBracketedPaste = enable;
}

/**
* @param runnable run in display thread
*/
Expand Down Expand Up @@ -1397,6 +1423,103 @@ public void enableApplicationCursorKeys(boolean enable) {
fApplicationCursorKeys = enable;
}

@Override
public void enableMouseReporting(int mode) {
fMouseMode = mode;
fLastReportedLine = fLastReportedColumn = -1;
}

@Override
public void enableSgrMouseEncoding(boolean enable) {
fSgrMouseEncoding = enable;
}

/**
* A wheel notch is reported as a press of button 64 or 65, which is how a
* terminal has always told a program that the wheel moved. Without this the
* wheel only ever scrolled the terminal's own view, and a program drawing its
* own scrollable screen never heard about it.
*
* @return whether the program was told, in which case the canvas stays put
*/
private boolean reportMouseWheel(int count, int modifiers, int line, int column) {
if (fMouseMode == 0 || count == 0) {
return false;
}
int button = (count > 0 ? 64 : 65) + modifiers; // up, down
// SWT counts lines; a notch is three of them.
int notches = Math.max(1, Math.abs(count) / 3);
StringBuilder report = new StringBuilder();
for (int i = 0; i < notches; i++) {
report.append(mouseReport(button, line, column, true));
}
sendString(report.toString());
return true;
}

/**
* A press, a release and the pointer moving, told to the program the way a
* terminal has always told it, so that a program drawing its own screen can be
* clicked on. The terminal keeps the event when shift is held, which is how a
* selection is still made with the mouse while a program is listening for it.
*/
private class MouseReporter implements TextCanvas.IMouseButtonHandler {
@Override
public boolean mouseButton(int button, int modifiers, int line, int column, boolean pressed) {
if (fMouseMode == 0 || button < 1 || button > 3) {
return false;
}
sendString(mouseReport(button - 1 + modifiers, line, column, pressed));
return true;
}

@Override
public boolean mouseMoved(int button, int modifiers, int line, int column) {
// 1002 is only interested while a button is held, 1003 in every move.
if (fMouseMode < 1002 || (fMouseMode == 1002 && button == 0)) {
return false;
}
if (line == fLastReportedLine && column == fLastReportedColumn) {
return true; // still the same cell, and a cell is all the program is told
}
fLastReportedLine = line;
fLastReportedColumn = column;
sendString(mouseReport((button == 0 ? 3 : button - 1) + 32 + modifiers, line, column, true));
return true;
}
}

private boolean fFocusReporting;

@Override
public void enableFocusReporting(boolean enable) {
fFocusReporting = enable;
}

/**
* A program that asked for focus reporting stops its cursor blinking and holds
* off work while the terminal is not the window being typed into.
*/
private void reportFocus(boolean gained) {
if (fFocusReporting) {
sendString(gained ? "" : ""); //$NON-NLS-1$ //$NON-NLS-2$
}
}

private String mouseReport(int button, int line, int column, boolean pressed) {
return mouseReport(fSgrMouseEncoding, button, line, column, pressed);
}

public static String mouseReport(boolean sgr, int button, int line, int column, boolean pressed) {
if (sgr) {
return "\u001b[<" + button + ';' + column + ';' + line + (pressed ? 'M' : 'm'); //$NON-NLS-1$
}
// The older spelling has no room past column 223 and no way to say which
// button came up, so a release is reported as button 3, meaning "some button".
return "\u001b[M" + (char) (32 + (pressed ? button : 3)) + (char) (32 + column) //$NON-NLS-1$
+ (char) (32 + line);
}

@Override
public void addMouseListener(ITerminalMouseListener listener) {
getCtlText().addTerminalMouseListener(listener);
Expand All @@ -1412,6 +1535,20 @@ public String getHoverSelection() {
return fCtlText.getHoverSelection();
}

@Override
public void showCursor(boolean show) {
if (fPollingTextCanvasModel != null) {
fPollingTextCanvasModel.setCursorHidden(!show);
}
}

@Override
public void enableSynchronizedOutput(boolean redrawing) {
if (fPollingTextCanvasModel != null) {
fPollingTextCanvasModel.setSynchronizedOutput(redrawing);
}
}

@Override
public void updateTerminalDimensions() {
getTerminalText().adjustTerminalDimensions();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ abstract public class AbstractTextCanvasModel implements ITextCanvasModel {
private boolean fShowCursor;
private long fCursorTime;
private boolean fCursorIsEnabled;
private boolean fCursorHidden;
private final ITerminalTextDataSnapshot fSnapshot;
private int fLines;

Expand Down Expand Up @@ -149,7 +150,12 @@ public int getCursorLine() {

@Override
public boolean isCursorOn() {
return fShowCursor && fCursorIsEnabled;
return fShowCursor && fCursorIsEnabled && !fCursorHidden;
}

/** A program hides the cursor while it draws, or for good, with DEC mode 25. */
public void setCursorHidden(boolean hidden) {
fCursorHidden = hidden;
}

/**
Expand Down
Loading
Loading