Tweaked input console setting for windows

This commit is contained in:
nathan 2016-05-28 21:44:10 +02:00
parent f190a31c39
commit aca4c493b0
2 changed files with 55 additions and 48 deletions

View File

@ -20,7 +20,7 @@ import java.io.IOException;
public abstract public abstract
class Terminal { class Terminal {
public static final String CONSOLE_ERROR_INIT = "Unable to get input console mode."; public static final String CONSOLE_ERROR_INIT = "Unable to initialize the input console.";
protected static final int DEFAULT_WIDTH = 80; protected static final int DEFAULT_WIDTH = 80;
protected static final int DEFAULT_HEIGHT = 24; protected static final int DEFAULT_HEIGHT = 24;
protected final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(getClass()); protected final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
@ -38,6 +38,7 @@ class Terminal {
public abstract public abstract
int getHeight(); int getHeight();
// NOT THREAD SAFE
public abstract public abstract
void setEchoEnabled(final boolean enabled); void setEchoEnabled(final boolean enabled);

View File

@ -11,16 +11,21 @@
*/ */
package dorkbox.console.input; package dorkbox.console.input;
import static dorkbox.console.util.windows.Kernel32.ASSERT;
import static dorkbox.console.util.windows.Kernel32.CloseHandle;
import static dorkbox.console.util.windows.Kernel32.GetConsoleMode;
import static dorkbox.console.util.windows.Kernel32.GetConsoleScreenBufferInfo; import static dorkbox.console.util.windows.Kernel32.GetConsoleScreenBufferInfo;
import static dorkbox.console.util.windows.Kernel32.GetStdHandle;
import static dorkbox.console.util.windows.Kernel32.STD_INPUT_HANDLE; import static dorkbox.console.util.windows.Kernel32.STD_INPUT_HANDLE;
import static dorkbox.console.util.windows.Kernel32.STD_OUTPUT_HANDLE; import static dorkbox.console.util.windows.Kernel32.STD_OUTPUT_HANDLE;
import static dorkbox.console.util.windows.Kernel32.SetConsoleMode;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream;
import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.IntByReference;
import dorkbox.console.util.windows.CONSOLE_SCREEN_BUFFER_INFO; import dorkbox.console.util.windows.CONSOLE_SCREEN_BUFFER_INFO;
import dorkbox.console.util.windows.ConsoleMode;
import dorkbox.console.util.windows.HANDLE; import dorkbox.console.util.windows.HANDLE;
import dorkbox.console.util.windows.INPUT_RECORD; import dorkbox.console.util.windows.INPUT_RECORD;
import dorkbox.console.util.windows.KEY_EVENT_RECORD; import dorkbox.console.util.windows.KEY_EVENT_RECORD;
@ -32,6 +37,24 @@ import dorkbox.console.util.windows.Kernel32;
public public
class WindowsTerminal extends Terminal { class WindowsTerminal extends Terminal {
// Console mode constants copied <tt>wincon.h</tt>.
// There are OTHER options, however they DO NOT work with unbuffered input or we just don't care about them.
/**
* CTRL+C is processed by the system and is not placed in the input buffer. If the input buffer is being read by ReadFile or
* ReadConsole, other control keys are processed by the system and are not returned in the ReadFile or ReadConsole buffer. If the
* ENABLE_LINE_INPUT mode is also enabled, backspace, carriage return, and linefeed characters are handled by the system.
*/
private static final int PROCESSED_INPUT = 1;
/**
* The ReadFile or ReadConsole function returns only when a carriage return character is read. If this mode is disable, the functions
* return when one or more characters are available.
*/
private static final int ENABLE_LINE_INPUT = 2;
// output stream for "echo" to goto
private static final PrintStream OUT = System.out;
private final HANDLE console; private final HANDLE console;
private final HANDLE outputConsole; private final HANDLE outputConsole;
@ -40,37 +63,30 @@ class WindowsTerminal extends Terminal {
private final IntByReference reference = new IntByReference(); private final IntByReference reference = new IntByReference();
private volatile int originalMode; private volatile int originalMode;
private boolean echoEnabled = false;
public public
WindowsTerminal() throws IOException { WindowsTerminal() throws IOException {
console = Kernel32.GetStdHandle(STD_INPUT_HANDLE); console = GetStdHandle(STD_INPUT_HANDLE);
if (console == HANDLE.INVALID_HANDLE_VALUE) { if (console == HANDLE.INVALID_HANDLE_VALUE) {
throw new IOException("Unable to get input console handle."); throw new IOException("Unable to get input console handle.");
} }
outputConsole = Kernel32.GetStdHandle(STD_OUTPUT_HANDLE); outputConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (outputConsole == HANDLE.INVALID_HANDLE_VALUE) { if (outputConsole == HANDLE.INVALID_HANDLE_VALUE) {
throw new IOException("Unable to get output console handle."); throw new IOException("Unable to get output console handle.");
} }
IntByReference mode = new IntByReference(); IntByReference mode = new IntByReference();
if (Kernel32.GetConsoleMode(console, mode) == 0) { if (GetConsoleMode(console, mode) == 0) {
throw new IOException(CONSOLE_ERROR_INIT); throw new IOException(CONSOLE_ERROR_INIT);
} }
this.originalMode = mode.getValue(); this.originalMode = mode.getValue();
int newMode = this.originalMode | int newMode = 0; // this is raw everything, not ignoring ctrl-c
ConsoleMode.ENABLE_LINE_INPUT.code |
ConsoleMode.ENABLE_ECHO_INPUT.code |
ConsoleMode.ENABLE_PROCESSED_INPUT.code |
ConsoleMode.ENABLE_WINDOW_INPUT.code;
// Disable input echo ASSERT(SetConsoleMode(console, newMode), Terminal.CONSOLE_ERROR_INIT);
newMode = newMode & ~ConsoleMode.ENABLE_ECHO_INPUT.code;
// Must set these four modes at the same time to make it work fine.
Kernel32.SetConsoleMode(console, newMode);
} }
/** /**
@ -80,11 +96,10 @@ class WindowsTerminal extends Terminal {
@Override @Override
public final public final
void restore() throws IOException { void restore() throws IOException {
// restore the old console mode ASSERT(SetConsoleMode(console, this.originalMode), Terminal.CONSOLE_ERROR_INIT);
Kernel32.SetConsoleMode(console, this.originalMode);
Kernel32.CloseHandle(console); CloseHandle(console);
Kernel32.CloseHandle(outputConsole); CloseHandle(outputConsole);
} }
@Override @Override
@ -106,37 +121,26 @@ class WindowsTerminal extends Terminal {
@Override @Override
public public
void setEchoEnabled(final boolean enabled) { void setEchoEnabled(final boolean enabled) {
IntByReference mode = new IntByReference(); // only way to do this, console modes DO NOT work
Kernel32.GetConsoleMode(console, mode); echoEnabled = enabled;
int newMode;
if (enabled) {
// Enable Ctrl+C
newMode = mode.getValue() | ConsoleMode.ENABLE_ECHO_INPUT.code;
} else {
// Disable Ctrl+C
newMode = mode.getValue() & ~ConsoleMode.ENABLE_ECHO_INPUT.code;
}
Kernel32.SetConsoleMode(console, newMode);
} }
@Override @Override
public public
void setInterruptEnabled(final boolean enabled) { void setInterruptEnabled(final boolean enabled) {
IntByReference mode = new IntByReference(); IntByReference mode = new IntByReference();
Kernel32.GetConsoleMode(console, mode); GetConsoleMode(console, mode);
int newMode; int newMode;
if (enabled) { if (enabled) {
// Enable Ctrl+C // Enable Ctrl+C
newMode = mode.getValue() | ConsoleMode.ENABLE_PROCESSED_INPUT.code; newMode = mode.getValue() | PROCESSED_INPUT;
} else { } else {
// Disable Ctrl+C // Disable Ctrl+C
newMode = mode.getValue() & ~ConsoleMode.ENABLE_PROCESSED_INPUT.code; newMode = mode.getValue() & ~PROCESSED_INPUT;
} }
Kernel32.SetConsoleMode(console, newMode); ASSERT(SetConsoleMode(console, newMode), Terminal.CONSOLE_ERROR_INIT);
} }
@Override @Override
@ -144,18 +148,18 @@ class WindowsTerminal extends Terminal {
int read() { int read() {
int input = readInput(); int input = readInput();
// if (Console.ENABLE_ECHO) { if (echoEnabled) {
// char asChar = (char) input; char asChar = (char) input;
// if (asChar == '\n') { if (asChar == '\n') {
// System.out.println(); OUT.println();
// } }
// else { else {
// System.out.print(asChar); OUT.print(asChar);
// } }
//
// // have to flush, otherwise we'll never see the chars on screen // have to flush, otherwise we'll never see the chars on screen
// System.out.flush(); OUT.flush();
// } }
return input; return input;
} }
@ -178,6 +182,8 @@ class WindowsTerminal extends Terminal {
if (uChar == '\r') { if (uChar == '\r') {
// we purposefully swallow input after \r, and substitute it with \n // we purposefully swallow input after \r, and substitute it with \n
return '\n'; return '\n';
} else if (uChar == '\n') {
continue;
} }
return uChar; return uChar;