diff --git a/readline/src/main/java/org/aesh/readline/terminal/impl/AbstractWindowsTerminal.java b/readline/src/main/java/org/aesh/readline/terminal/impl/AbstractWindowsTerminal.java index 05f2b286..6ce55983 100644 --- a/readline/src/main/java/org/aesh/readline/terminal/impl/AbstractWindowsTerminal.java +++ b/readline/src/main/java/org/aesh/readline/terminal/impl/AbstractWindowsTerminal.java @@ -41,9 +41,27 @@ import java.util.logging.Level; import org.aesh.terminal.tty.Signal; import org.aesh.terminal.tty.Size; +import static org.fusesource.jansi.internal.Kernel32.GetStdHandle; +import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE; +import static org.fusesource.jansi.internal.Kernel32.WriteConsoleW; +import org.fusesource.jansi.internal.WindowsSupport; abstract class AbstractWindowsTerminal extends AbstractTerminal { + private static class ConsoleOutput extends OutputStream { + + private static final long console = GetStdHandle(STD_OUTPUT_HANDLE); + private final int[] writtenChars = new int[1]; + + @Override + public void write(int b) throws IOException { + char[] chars = new char[]{(char) b}; + if (WriteConsoleW(console, chars, 1, writtenChars, 0) == 0) { + throw new IOException("Failed to write to console: " + WindowsSupport.getLastErrorMessage()); + } + } + + } private static final int PIPE_SIZE = 1024; protected static final int ENABLE_PROCESSED_INPUT = 0x0001; @@ -65,17 +83,21 @@ abstract class AbstractWindowsTerminal extends AbstractTerminal { private volatile boolean closing; + public AbstractWindowsTerminal(String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException { + this(null, name, nativeSignals, signalHandler); + } + public AbstractWindowsTerminal(OutputStream output, String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException { super(name, "windows", signalHandler); PipedInputStream input = new PipedInputStream(PIPE_SIZE); this.slaveInputPipe = new PipedOutputStream(input); this.input = new FilterInputStream(input) {}; - this.output = output; + this.output = output == null ? new ConsoleOutput() : output; String encoding = getConsoleEncoding(); if (encoding == null) { encoding = Charset.defaultCharset().name(); } - this.writer = new PrintWriter(new OutputStreamWriter(output, encoding)); + this.writer = new PrintWriter(new OutputStreamWriter(this.output, encoding)); // Attributes attributes.setLocalFlag(Attributes.LocalFlag.ISIG, true); attributes.setControlChar(Attributes.ControlChar.VINTR, ctrl('C')); diff --git a/readline/src/main/java/org/aesh/readline/terminal/impl/WinSysTerminal.java b/readline/src/main/java/org/aesh/readline/terminal/impl/WinSysTerminal.java index ffe8587e..9c6d47a3 100644 --- a/readline/src/main/java/org/aesh/readline/terminal/impl/WinSysTerminal.java +++ b/readline/src/main/java/org/aesh/readline/terminal/impl/WinSysTerminal.java @@ -28,19 +28,22 @@ import org.aesh.terminal.tty.Size; import org.fusesource.jansi.WindowsAnsiOutputStream; import org.fusesource.jansi.internal.Kernel32; +import static org.fusesource.jansi.internal.Kernel32.GetStdHandle; import org.fusesource.jansi.internal.Kernel32.INPUT_RECORD; import org.fusesource.jansi.internal.Kernel32.KEY_EVENT_RECORD; +import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE; import org.fusesource.jansi.internal.WindowsSupport; public class WinSysTerminal extends AbstractWindowsTerminal { + private static final int VIRTUAL_TERMINAL_PROCESSING = 0x0004; + public WinSysTerminal(String name, boolean nativeSignals) throws IOException { this(name, nativeSignals, SignalHandlers.SIG_DFL); } public WinSysTerminal(String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException { - super(new WindowsAnsiOutputStream(new FileOutputStream(FileDescriptor.out)), - name, nativeSignals, signalHandler); + super(setVTMode() ? null : new WindowsAnsiOutputStream(new FileOutputStream(FileDescriptor.out)), name, nativeSignals, signalHandler); } protected int getConsoleOutputCP() { @@ -123,4 +126,23 @@ protected byte[] readConsoleInput() { return sb.toString().getBytes(); } + // This allows to take benefit from Windows 10+ new features. + private static boolean setVTMode() { + long console = GetStdHandle(STD_OUTPUT_HANDLE); + int[] mode = new int[1]; + if (Kernel32.GetConsoleMode(console, mode) == 0) { + // No need to go further, not supported. + return false; + } + if (Kernel32.SetConsoleMode(console, mode[0] | VIRTUAL_TERMINAL_PROCESSING) == 0) { + // No need to go further, not supported. + return false; + } + + return true; + } + + public static boolean isVTSupported() { + return setVTMode(); + } }