package com.googlecode.lanterna.terminal; import com.googlecode.lanterna.TerminalPosition; import com.googlecode.lanterna.TerminalSize; import com.googlecode.lanterna.terminal.ansi.UnixLikeTerminal; import com.sun.jna.Native; import com.sun.jna.ptr.IntByReference; import com.sun.jna.win32.W32APIOptions; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; /** * Terminal implementation for the regular Windows cmd.exe terminal emulator, using native invocations through jna to * interact with it. */ public class WindowsTerminal extends UnixLikeTerminal { private static final Wincon WINDOWS_CONSOLE = (Wincon) Native.loadLibrary("kernel32", Wincon.class, W32APIOptions.UNICODE_OPTIONS); private static final WinDef.HANDLE CONSOLE_INPUT_HANDLE = WINDOWS_CONSOLE.GetStdHandle(Wincon.STD_INPUT_HANDLE); private static final WinDef.HANDLE CONSOLE_OUTPUT_HANDLE = WINDOWS_CONSOLE.GetStdHandle(Wincon.STD_OUTPUT_HANDLE); private Integer savedTerminalMode; public WindowsTerminal() throws IOException { this(System.in, System.out, Charset.defaultCharset(), CtrlCBehaviour.CTRL_C_KILLS_APPLICATION); } public WindowsTerminal( InputStream terminalInput, OutputStream terminalOutput, Charset terminalCharset, CtrlCBehaviour terminalCtrlCBehaviour) throws IOException { super(terminalInput, terminalOutput, terminalCharset, terminalCtrlCBehaviour); } @Override public synchronized void saveTerminalSettings() throws IOException { this.savedTerminalMode = getConsoleMode(); } @Override public synchronized void restoreTerminalSettings() throws IOException { if(savedTerminalMode != null) { WINDOWS_CONSOLE.SetConsoleMode(CONSOLE_INPUT_HANDLE, savedTerminalMode); } } @Override public synchronized void keyEchoEnabled(boolean enabled) throws IOException { int mode = getConsoleMode(); if(enabled) { mode |= Wincon.ENABLE_ECHO_INPUT; } else { mode &= ~Wincon.ENABLE_ECHO_INPUT; } WINDOWS_CONSOLE.SetConsoleMode(CONSOLE_INPUT_HANDLE, mode); } @Override public synchronized void canonicalMode(boolean enabled) throws IOException { int mode = getConsoleMode(); if(enabled) { mode |= Wincon.ENABLE_LINE_INPUT; } else { mode &= ~Wincon.ENABLE_LINE_INPUT; } WINDOWS_CONSOLE.SetConsoleMode(CONSOLE_INPUT_HANDLE, mode); } @Override public synchronized void keyStrokeSignalsEnabled(boolean enabled) throws IOException { int mode = getConsoleMode(); if(enabled) { mode |= Wincon.ENABLE_PROCESSED_INPUT; } else { mode &= ~Wincon.ENABLE_PROCESSED_INPUT; } WINDOWS_CONSOLE.SetConsoleMode(CONSOLE_INPUT_HANDLE, mode); } @Override protected TerminalSize findTerminalSize() throws IOException { WinDef.CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo = new WinDef.CONSOLE_SCREEN_BUFFER_INFO(); WINDOWS_CONSOLE.GetConsoleScreenBufferInfo(CONSOLE_OUTPUT_HANDLE, screenBufferInfo); int columns = screenBufferInfo.srWindow.Right - screenBufferInfo.srWindow.Left + 1; int rows = screenBufferInfo.srWindow.Bottom - screenBufferInfo.srWindow.Top + 1; return new TerminalSize(columns, rows); } @Override public void registerTerminalResizeListener(Runnable runnable) throws IOException { // Not implemented yet } public synchronized TerminalPosition getCursorPosition() { WinDef.CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo = new WinDef.CONSOLE_SCREEN_BUFFER_INFO(); WINDOWS_CONSOLE.GetConsoleScreenBufferInfo(CONSOLE_OUTPUT_HANDLE, screenBufferInfo); int column = screenBufferInfo.dwCursorPosition.X - screenBufferInfo.srWindow.Left; int row = screenBufferInfo.dwCursorPosition.Y - screenBufferInfo.srWindow.Top; return new TerminalPosition(column, row); } private int getConsoleMode() { IntByReference lpMode = new IntByReference(); WINDOWS_CONSOLE.GetConsoleMode(CONSOLE_INPUT_HANDLE, lpMode); return lpMode.getValue(); } }