From d2cc0e3bdea025283af11d98f5aa6fcc9504beea Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Thu, 7 Jun 2018 20:53:28 +0200 Subject: [PATCH] Support advanced escape sequences on Windows 10, fixes #279 --- .../main/java/org/jline/builtins/Nano.java | 24 +++++---- .../terminal/impl/jansi/JansiSupportImpl.java | 2 +- .../impl/jansi/win/JansiWinSysTerminal.java | 53 ++++++++++++------- .../terminal/impl/jna/JnaSupportImpl.java | 4 +- .../impl/jna/win/JnaWinSysTerminal.java | 51 ++++++++++++------ .../org/jline/terminal/TerminalBuilder.java | 3 -- .../impl/AbstractWindowsTerminal.java | 3 ++ .../main/java/org/jline/utils/InfoCmp.java | 3 +- .../org/jline/utils/windows-vtp.caps | 33 ++++++++++++ 9 files changed, 123 insertions(+), 53 deletions(-) create mode 100644 terminal/src/main/resources/org/jline/utils/windows-vtp.caps diff --git a/builtins/src/main/java/org/jline/builtins/Nano.java b/builtins/src/main/java/org/jline/builtins/Nano.java index 3440e69c5..19ab185ce 100644 --- a/builtins/src/main/java/org/jline/builtins/Nano.java +++ b/builtins/src/main/java/org/jline/builtins/Nano.java @@ -973,25 +973,27 @@ public void run() throws IOException { newAttr.setControlChar(ControlChar.VTIME, 0); newAttr.setControlChar(ControlChar.VINTR, 0); terminal.setAttributes(newAttr); - SignalHandler prevHandler = terminal.handle(Signal.WINCH, this::handle); terminal.puts(Capability.enter_ca_mode); terminal.puts(Capability.keypad_xmit); - size.copy(terminal.getSize()); - display.clear(); - display.reset(); - display.resize(size.getRows(), size.getColumns()); if (mouseSupport) { terminal.trackMouse(Terminal.MouseTracking.Normal); } this.shortcuts = standardShortcuts(); + SignalHandler prevHandler = null; try { buffer.open(); if (buffer.file != null) { setMessage("Read " + buffer.lines.size() + " lines"); } + size.copy(terminal.getSize()); + display.clear(); + display.reset(); + display.resize(size.getRows(), size.getColumns()); + prevHandler = terminal.handle(Signal.WINCH, this::handle); + display(); while (true) { @@ -1923,11 +1925,13 @@ protected List computeFooter() { } protected void handle(Signal signal) { - size.copy(terminal.getSize()); - buffer.computeAllOffsets(); - buffer.moveToChar(buffer.offsetInLine + buffer.column); - resetDisplay(); - display(); + if (buffer != null) { + size.copy(terminal.getSize()); + buffer.computeAllOffsets(); + buffer.moveToChar(buffer.offsetInLine + buffer.column); + resetDisplay(); + display(); + } } protected void bindKeys() { diff --git a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiSupportImpl.java b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiSupportImpl.java index ec3839817..093a1cb17 100644 --- a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiSupportImpl.java +++ b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/JansiSupportImpl.java @@ -100,7 +100,7 @@ public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough @Override public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException { if (JANSI_MAJOR_VERSION > 1 || JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION >= 12) { - JansiWinSysTerminal terminal = new JansiWinSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, paused); + JansiWinSysTerminal terminal = JansiWinSysTerminal.createTerminal(name, type, encoding, codepage, nativeSignals, signalHandler, paused); if (JANSI_MAJOR_VERSION == 1 && JANSI_MINOR_VERSION < 16) { terminal.disableScrolling(); } diff --git a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java index 739533fab..b0a573ce9 100644 --- a/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java +++ b/terminal-jansi/src/main/java/org/jline/terminal/impl/jansi/win/JansiWinSysTerminal.java @@ -11,6 +11,7 @@ import java.io.BufferedWriter; import java.io.IOError; import java.io.IOException; +import java.io.Writer; import java.nio.charset.Charset; import java.util.function.IntConsumer; @@ -21,38 +22,50 @@ import org.fusesource.jansi.internal.WindowsSupport; import org.jline.terminal.Cursor; import org.jline.terminal.Size; +import org.jline.terminal.Terminal; import org.jline.terminal.impl.AbstractWindowsTerminal; import org.jline.utils.InfoCmp; import static org.fusesource.jansi.internal.Kernel32.GetConsoleScreenBufferInfo; import static org.fusesource.jansi.internal.Kernel32.GetStdHandle; import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE; +import org.jline.utils.OSUtils; public class JansiWinSysTerminal extends AbstractWindowsTerminal { - public JansiWinSysTerminal(String name, boolean nativeSignals) throws IOException { - this(name, TYPE_WINDOWS, false, null, 0, nativeSignals, SignalHandler.SIG_DFL); - } - - public JansiWinSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler) throws IOException { - this(name, TYPE_WINDOWS, false, null, 0, nativeSignals, signalHandler, false); - } - - public JansiWinSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, boolean paused) throws IOException { - super(ansiPassThrough - ? new JansiWinConsoleWriter() - : new WindowsAnsiWriter(new BufferedWriter(new JansiWinConsoleWriter())), - name, - type, - encoding, - codepage, - nativeSignals, - signalHandler); - + public static JansiWinSysTerminal createTerminal(String name, String type, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException { + Writer writer; + long console = GetStdHandle(STD_OUTPUT_HANDLE); + int[] mode = new int[1]; + if (Kernel32.GetConsoleMode(console, mode) == 0) { + throw new IOException("Failed to get console mode: " + WindowsSupport.getLastErrorMessage()); + } + if (Kernel32.SetConsoleMode(console, mode[0] | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0) { + if (type == null) { + type = TYPE_WINDOWS_VTP; + } + writer = new JansiWinConsoleWriter(); + } else if (OSUtils.IS_CONEMU) { + if (type == null) { + type = TYPE_WINDOWS_256_COLOR; + } + writer = new JansiWinConsoleWriter(); + } else { + if (type == null) { + type = TYPE_WINDOWS; + } + writer = new WindowsAnsiWriter(new BufferedWriter(new JansiWinConsoleWriter())); + } + JansiWinSysTerminal terminal = new JansiWinSysTerminal(writer, name, type, encoding, codepage, nativeSignals, signalHandler); // Start input pump thread if (!paused) { - resume(); + terminal.resume(); } + return terminal; + } + + JansiWinSysTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler) throws IOException { + super(writer, name, type, encoding, codepage, nativeSignals, signalHandler); } @Override diff --git a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaSupportImpl.java b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaSupportImpl.java index cbc8288f8..cfe041d24 100644 --- a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaSupportImpl.java +++ b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/JnaSupportImpl.java @@ -23,11 +23,11 @@ public Pty open(Attributes attributes, Size size) throws IOException { @Override public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler) throws IOException { - return new JnaWinSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler); + return winSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, false); } @Override public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException { - return new JnaWinSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, paused); + return JnaWinSysTerminal.createTerminal(name, type, encoding, codepage, nativeSignals, signalHandler, paused); } } diff --git a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java index 8d0447b87..b27b8c0a6 100644 --- a/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java +++ b/terminal-jna/src/main/java/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java @@ -10,40 +10,59 @@ import java.io.BufferedWriter; import java.io.IOException; +import java.io.Writer; import java.nio.charset.Charset; import java.util.function.IntConsumer; +import com.sun.jna.LastErrorException; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import org.jline.terminal.Cursor; import org.jline.terminal.Size; +import org.jline.terminal.Terminal; import org.jline.terminal.impl.AbstractWindowsTerminal; import org.jline.utils.InfoCmp; +import org.jline.utils.OSUtils; public class JnaWinSysTerminal extends AbstractWindowsTerminal { private static final Pointer consoleIn = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_INPUT_HANDLE); private static final Pointer consoleOut = Kernel32.INSTANCE.GetStdHandle(Kernel32.STD_OUTPUT_HANDLE); - public JnaWinSysTerminal(String name, boolean nativeSignals) throws IOException { - this(name, TYPE_WINDOWS, false, null, 0, nativeSignals, SignalHandler.SIG_DFL); - } - - public JnaWinSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler) throws IOException { - this(name, TYPE_WINDOWS, false, null, 0, nativeSignals, signalHandler, false); - } - - public JnaWinSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, boolean paused) throws IOException { - super(ansiPassThrough - ? new JnaWinConsoleWriter(consoleOut) - : new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(consoleOut)), consoleOut), - name, type, encoding, codepage, nativeSignals, signalHandler); - strings.put(InfoCmp.Capability.key_mouse, "\\E[M"); - + public static JnaWinSysTerminal createTerminal(String name, String type, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException { + Writer writer; + IntByReference mode = new IntByReference(); + Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode); + try { + Kernel32.INSTANCE.SetConsoleMode(consoleOut, mode.getValue() | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING); + if (type == null) { + type = TYPE_WINDOWS_VTP; + } + writer = new JnaWinConsoleWriter(consoleOut); + } catch (LastErrorException e) { + if (OSUtils.IS_CONEMU) { + if (type == null) { + type = TYPE_WINDOWS_256_COLOR; + } + writer = new JnaWinConsoleWriter(consoleOut); + } else { + if (type == null) { + type = TYPE_WINDOWS; + } + writer = new WindowsAnsiWriter(new BufferedWriter(new JnaWinConsoleWriter(consoleOut)), consoleOut); + } + } + JnaWinSysTerminal terminal = new JnaWinSysTerminal(writer, name, type, encoding, codepage, nativeSignals, signalHandler); // Start input pump thread if (!paused) { - resume(); + terminal.resume(); } + return terminal; + } + + JnaWinSysTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler) throws IOException { + super(writer, name, type, encoding, codepage, nativeSignals, signalHandler); + strings.put(InfoCmp.Capability.key_mouse, "\\E[M"); } @Override diff --git a/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java b/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java index 41c2d4f24..a16f80bbf 100644 --- a/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java +++ b/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java @@ -317,9 +317,6 @@ private Terminal doBuild() throws IOException { if (OSUtils.IS_WINDOWS) { boolean cygwinTerm = "cygwin".equals(System.getenv("TERM")); boolean ansiPassThrough = OSUtils.IS_CONEMU; - if (type == null) { - type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_256_COLOR : TYPE_WINDOWS; - } // // Cygwin support // diff --git a/terminal/src/main/java/org/jline/terminal/impl/AbstractWindowsTerminal.java b/terminal/src/main/java/org/jline/terminal/impl/AbstractWindowsTerminal.java index 5a70170c5..00878b852 100644 --- a/terminal/src/main/java/org/jline/terminal/impl/AbstractWindowsTerminal.java +++ b/terminal/src/main/java/org/jline/terminal/impl/AbstractWindowsTerminal.java @@ -49,6 +49,9 @@ public abstract class AbstractWindowsTerminal extends AbstractTerminal { public static final String TYPE_WINDOWS = "windows"; public static final String TYPE_WINDOWS_256_COLOR = "windows-256color"; + public static final String TYPE_WINDOWS_VTP = "windows-vtp"; + + public static final int ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; private static final int UTF8_CODE_PAGE = 65001; diff --git a/terminal/src/main/java/org/jline/utils/InfoCmp.java b/terminal/src/main/java/org/jline/utils/InfoCmp.java index ffdf8c242..542aae5f8 100644 --- a/terminal/src/main/java/org/jline/utils/InfoCmp.java +++ b/terminal/src/main/java/org/jline/utils/InfoCmp.java @@ -615,7 +615,8 @@ static String loadDefaultInfoCmp(String name) { static { for (String s : Arrays.asList("dumb", "ansi", "xterm", "xterm-256color", - "windows", "windows-256color", "screen", "screen-256color")) { + "windows", "windows-256color", "windows-vtp", + "screen", "screen-256color")) { setDefaultInfoCmp(s, () -> loadDefaultInfoCmp(s)); } } diff --git a/terminal/src/main/resources/org/jline/utils/windows-vtp.caps b/terminal/src/main/resources/org/jline/utils/windows-vtp.caps new file mode 100644 index 000000000..5279dec36 --- /dev/null +++ b/terminal/src/main/resources/org/jline/utils/windows-vtp.caps @@ -0,0 +1,33 @@ +windows-vtp|windows with virtual terminal processing, + am, mc5i, mir, msgr, + colors#256, cols#80, it#8, lines#24, ncv#3, pairs#64, + bel=^G, blink=\E[5m, bold=\E[1m, cbt=\E[Z, clear=\E[H\E[J, + cr=^M, cub=\E[%p1%dD, cub1=\E[D, cud=\E[%p1%dB, cud1=\E[B, + cuf=\E[%p1%dC, cuf1=\E[C, cup=\E[%i%p1%d;%p2%dH, + cuu=\E[%p1%dA, cuu1=\E[A, + il=\E[%p1%dL, il1=\E[L, + dl=\E[%p1%dM, dl1=\E[M, + ech=\E[%p1%dX, + el=\E[K, ed=\E[2K, + el1=\E[1K, home=\E[H, hpa=\E[%i%p1%dG, + ind=^J, + invis=\E[8m, kbs=^H, kcbt=\E[Z, + kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, + khome=\E[H, + op=\E[39;49m, + rev=\E[7m, + rmacs=\E[10m, rmpch=\E[10m, rmso=\E[m, rmul=\E[m, + setab=\E[4%p1%dm, setaf=\E[3%p1%dm, + sgr=\E[0;10%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m, + sgr0=\E[0;10m, + smso=\E[7m, + smul=\E[4m, + kdch1=\E[3~, kich1=\E[2~, kend=\E[4~, knp=\E[6~, kpp=\E[5~, + kf1=\EOP, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\E[15~, kf6=\E[17~, + kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, + smcup=\E[?1049h, rmcup=\E[?1049l, indn=\E[%p1%dS, rin=\E[%p1%dT, + ich=\E[%p1%d@, dch=\E[%p1%dP, ech=\E[%p1%dX, il=\E[%p1%dL, dl=\E[%p1%dM, + sc=\E7, rc=\E8, cnorm=\E[?12l\E[?25h, civis=\E[?25l, cvvis=\E[?12h\E[?25h, + smkx=\E[?1h\E=, rmkx=\E[?1l\E>, u6=\E[%i%d;%dR, u7=\E[6n, + hts=\EH, smacs=\E(0, rmacs=\E(B, + csr=\E[%i%p1%d;%p2%dr,