diff --git a/src/dorkbox/util/input/Encoding.java b/src/dorkbox/util/input/Encoding.java
index fdce31e..2bed1d7 100644
--- a/src/dorkbox/util/input/Encoding.java
+++ b/src/dorkbox/util/input/Encoding.java
@@ -14,36 +14,37 @@ package dorkbox.util.input;
import java.nio.charset.Charset;
public class Encoding {
- /**
- * Get the default encoding. Will first look at the LC_CTYPE environment variable, then the input.encoding
- * system property, then the default charset according to the JVM.
- *
- * @return The default encoding to use when none is specified.
- */
- public static String get() {
- // LC_CTYPE is usually in the form en_US.UTF-8
- String envEncoding = extractEncodingFromCtype(System.getenv("LC_CTYPE"));
- if (envEncoding != null) {
- return envEncoding;
- }
- return System.getProperty("input.encoding", Charset.defaultCharset().name());
- }
- /**
- * Parses the LC_CTYPE value to extract the encoding according to the POSIX standard, which says that the LC_CTYPE
- * environment variable may be of the format [language[_territory][.codeset][@modifier]]
- *
- * @param ctype The ctype to parse, may be null
- * @return The encoding, if one was present, otherwise null
- */
- private static String extractEncodingFromCtype(String ctype) {
- if (ctype != null && ctype.indexOf('.') > 0) {
- String encodingAndModifier = ctype.substring(ctype.indexOf('.') + 1);
- if (encodingAndModifier.indexOf('@') > 0) {
- return encodingAndModifier.substring(0, encodingAndModifier.indexOf('@'));
- }
- return encodingAndModifier;
- }
- return null;
+ /**
+ * Get the default encoding. Will first look at the LC_CTYPE environment variable, then the input.encoding system
+ * property, then the default charset according to the JVM.
+ *
+ * @return The default encoding to use when none is specified.
+ */
+ public static String get() {
+ // LC_CTYPE is usually in the form en_US.UTF-8
+ String envEncoding = extractEncodingFromCtype(System.getenv("LC_CTYPE"));
+ if (envEncoding != null) {
+ return envEncoding;
}
+ return System.getProperty("input.encoding", Charset.defaultCharset().name());
+ }
+
+ /**
+ * Parses the LC_CTYPE value to extract the encoding according to the POSIX standard, which says that the LC_CTYPE
+ * environment variable may be of the format [language[_territory][.codeset][@modifier]]
+ *
+ * @param ctype The ctype to parse, may be null
+ * @return The encoding, if one was present, otherwise null
+ */
+ private static String extractEncodingFromCtype(String ctype) {
+ if (ctype != null && ctype.indexOf('.') > 0) {
+ String encodingAndModifier = ctype.substring(ctype.indexOf('.') + 1);
+ if (encodingAndModifier.indexOf('@') > 0) {
+ return encodingAndModifier.substring(0, encodingAndModifier.indexOf('@'));
+ }
+ return encodingAndModifier;
+ }
+ return null;
+ }
}
diff --git a/src/dorkbox/util/input/InputConsole.java b/src/dorkbox/util/input/InputConsole.java
index b6e6c82..44f478b 100644
--- a/src/dorkbox/util/input/InputConsole.java
+++ b/src/dorkbox/util/input/InputConsole.java
@@ -15,6 +15,10 @@
*/
package dorkbox.util.input;
+import org.fusesource.jansi.Ansi;
+import org.fusesource.jansi.AnsiConsole;
+import org.slf4j.Logger;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -25,10 +29,6 @@ import java.security.ProtectionDomain;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import org.fusesource.jansi.Ansi;
-import org.fusesource.jansi.AnsiConsole;
-import org.slf4j.Logger;
-
import dorkbox.util.OS;
import dorkbox.util.bytes.ByteBuffer2;
import dorkbox.util.bytes.ByteBuffer2Poolable;
@@ -40,485 +40,491 @@ import dorkbox.util.objectPool.ObjectPoolFactory;
import dorkbox.util.objectPool.ObjectPoolHolder;
public class InputConsole {
- private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InputConsole.class);
- private static final InputConsole consoleProxyReader = new InputConsole();
- private static final char[] emptyLine = new char[0];
- /**
- * empty method to allow code to initialize the input console.
- */
- public static void init() {
- }
+ private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InputConsole.class);
+ private static final InputConsole consoleProxyReader = new InputConsole();
+ private static final char[] emptyLine = new char[0];
- // this is run by our init...
- {
- AnsiConsole.systemInstall();
+ // this is run by our init...
+ static {
+ AnsiConsole.systemInstall();
- Thread consoleThread = new Thread(new Runnable() {
- @Override
- public void run() {
- consoleProxyReader.run();
- }
- });
- consoleThread.setDaemon(true);
- consoleThread.setName("Console Input Reader");
+ Thread consoleThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ consoleProxyReader.run();
+ }
+ });
+ consoleThread.setDaemon(true);
+ consoleThread.setName("Console Input Reader");
- consoleThread.start();
+ consoleThread.start();
- // has to be NOT DAEMON thread, since it must run before the app closes.
+ // has to be NOT DAEMON thread, since it must run before the app closes.
- // don't forget we have to shut down the ansi console as well
- // alternatively, shut everything down when the JVM closes.
- Thread shutdownThread = new Thread() {
- @Override
- public void run() {
- AnsiConsole.systemUninstall();
+ // don't forget we have to shut down the ansi console as well
+ // alternatively, shut everything down when the JVM closes.
+ Thread shutdownThread = new Thread() {
+ @Override
+ public void run() {
+ AnsiConsole.systemUninstall();
- consoleProxyReader.shutdown0();
- }
- };
- shutdownThread.setName("Console Input Shutdown");
- Runtime.getRuntime().addShutdownHook(shutdownThread);
- }
-
- /** return null if no data */
- public static final String readLine() {
- char[] line = consoleProxyReader.readLine0();
- return new String(line);
- }
-
- private static InputStream wrappedInputStream = new InputStream() {
- @Override
- public int read() throws IOException {
- return consoleProxyReader.read0();
- }
-
- @Override
- public void close() throws IOException {
- consoleProxyReader.release0();
- }
+ consoleProxyReader.shutdown0();
+ }
};
+ shutdownThread.setName("Console Input Shutdown");
+ Runtime.getRuntime().addShutdownHook(shutdownThread);
+ }
+ /**
+ * Permit our InputConsole to be initialized
+ */
+ public static void init() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Created Terminal: {} ({}w x {}h)", consoleProxyReader.terminal.getClass().getSimpleName(),
+ consoleProxyReader.terminal.getWidth(),
+ consoleProxyReader.terminal.getHeight());
+ }
+ }
- /** return -1 if no data */
- public static final int read() {
- return consoleProxyReader.read0();
+ /**
+ * return null if no data
+ */
+ public static String readLine() {
+ char[] line = consoleProxyReader.readLine0();
+ return new String(line);
+ }
+
+ private static InputStream wrappedInputStream = new InputStream() {
+ @Override
+ public int read() throws IOException {
+ return consoleProxyReader.read0();
}
- /** return null if no data */
- public static final char[] readLinePassword() {
- return consoleProxyReader.readLinePassword0();
+ @Override
+ public void close() throws IOException {
+ consoleProxyReader.release0();
+ }
+ };
+
+
+ /**
+ * return -1 if no data
+ */
+ public static int read() {
+ return consoleProxyReader.read0();
+ }
+
+ /**
+ * return null if no data
+ */
+ public static char[] readLinePassword() {
+ return consoleProxyReader.readLinePassword0();
+ }
+
+ public static InputStream getInputStream() {
+ return wrappedInputStream;
+ }
+
+ public static void echo(boolean enableEcho) {
+ consoleProxyReader.echo0(enableEcho);
+ }
+
+ public static boolean echo() {
+ return consoleProxyReader.echo0();
+ }
+
+ private final Object inputLock = new Object();
+ private final Object inputLockSingle = new Object();
+ private final Object inputLockLine = new Object();
+
+ private final ObjectPool pool;
+
+ private ThreadLocal> readBuff = new ThreadLocal>();
+ private List> readBuffers = new CopyOnWriteArrayList>();
+ private ThreadLocal threadBufferCounter = new ThreadLocal();
+
+ private ThreadLocal> readLineBuff = new ThreadLocal>();
+ private List>
+ readLineBuffers =
+ new CopyOnWriteArrayList>();
+
+ private final Terminal terminal;
+ private final Boolean enableBackspace;
+
+ private InputConsole() {
+ Logger logger = InputConsole.logger;
+
+ String readers = System.getProperty(TerminalType.READERS);
+ int readers2 = 32;
+ if (readers != null) {
+ try {
+ readers2 = Integer.parseInt(readers);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ this.pool = ObjectPoolFactory.create(new ByteBuffer2Poolable(), readers2);
+
+ String type = System.getProperty(TerminalType.TYPE, TerminalType.AUTO).toLowerCase();
+ if ("dumb".equals(System.getenv("TERM"))) {
+ type = TerminalType.NONE;
+ if (logger.isTraceEnabled()) {
+ logger.trace("System environment 'TERM'=dumb, creating type=" + type);
+ }
+ } else {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Creating terminal, type=" + type);
+ }
}
- public static InputStream getInputStream() {
- return wrappedInputStream;
- }
-
- public static void echo(boolean enableEcho) {
- consoleProxyReader.echo0(enableEcho);
- }
-
- public static boolean echo() {
- return consoleProxyReader.echo0();
- }
-
- private final Object inputLock = new Object();
- private final Object inputLockSingle = new Object();
- private final Object inputLockLine = new Object();
-
- private final ObjectPool pool;
-
- private ThreadLocal> readBuff = new ThreadLocal>();
- private List> readBuffers = new CopyOnWriteArrayList>();
- private ThreadLocal threadBufferCounter = new ThreadLocal();
-
- private ThreadLocal> readLineBuff = new ThreadLocal>();
- private List> readLineBuffers = new CopyOnWriteArrayList>();
-
- private final Terminal terminal;
- private final Boolean enableBackspace;
-
- private InputConsole() {
- Logger logger = InputConsole.logger;
-
- String readers = System.getProperty(TerminalType.READERS);
- int readers2 = 32;
- if (readers != null) {
- try {
- readers2 = Integer.parseInt(readers);
- } catch (Exception e) {
- }
- }
- this.pool = ObjectPoolFactory.create(new ByteBuffer2Poolable(), readers2);
-
-
- String type = System.getProperty(TerminalType.TYPE, TerminalType.AUTO).toLowerCase();
- if ("dumb".equals(System.getenv("TERM"))) {
- type = TerminalType.NONE;
- if (logger.isTraceEnabled()) {
- logger.trace("System environment 'TERM'=dumb, creating type=" + type);
- }
+ Terminal t;
+ try {
+ if (type.equals(TerminalType.UNIX)) {
+ t = new UnixTerminal();
+ } else if (type.equals(TerminalType.WIN) || type.equals(TerminalType.WINDOWS)) {
+ t = new WindowsTerminal();
+ } else if (type.equals(TerminalType.NONE) || type.equals(TerminalType.OFF) || type.equals(TerminalType.FALSE)) {
+ t = new UnsupportedTerminal();
+ } else {
+ if (isIDEAutoDetect()) {
+ logger.debug(
+ "Terminal is in UNSUPPORTED (best guess). Unable to support single key input. Only line input available.");
+ t = new UnsupportedTerminal();
} else {
- if (logger.isTraceEnabled()) {
- logger.trace("Creating terminal, type=" + type);
- }
+ if (OS.isWindows()) {
+ t = new WindowsTerminal();
+ } else {
+ t = new UnixTerminal();
+ }
}
-
-
- Terminal t;
- try {
- if (type.equals(TerminalType.UNIX)) {
- t = new UnixTerminal();
- }
- else if (type.equals(TerminalType.WIN) || type.equals(TerminalType.WINDOWS)) {
- t = new WindowsTerminal();
- }
- else if (type.equals(TerminalType.NONE) || type.equals(TerminalType.OFF) || type.equals(TerminalType.FALSE)) {
- t = new UnsupportedTerminal();
- } else {
- if (isIDEAutoDetect()) {
- logger.debug("Terminal is in UNSUPPORTED (best guess). Unable to support single key input. Only line input available.");
- t = new UnsupportedTerminal();
- } else {
- if (OS.isWindows()) {
- t = new WindowsTerminal();
- } else {
- t = new UnixTerminal();
- }
- }
- }
- }
- catch (Exception e) {
- logger.error("Failed to construct terminal, falling back to unsupported");
- t = new UnsupportedTerminal();
- }
-
- try {
- t.init();
- }
- catch (Throwable e) {
- logger.error("Terminal initialization failed, falling back to unsupported");
- t = new UnsupportedTerminal();
-
- try {
- t.init();
- } catch (IOException e1) {
- // UnsupportedTerminal can't do this
- }
- }
-
- t.setEchoEnabled(true);
-
- this.terminal = t;
- this.enableBackspace = Boolean.parseBoolean(System.getProperty(TerminalType.ENABLE_BACKSPACE, "true"));
- logger.debug("Created Terminal: {} ({}x{})", t.getClass().getSimpleName(), t.getWidth(), t.getHeight());
+ }
+ } catch (Exception e) {
+ logger.error("Failed to construct terminal, falling back to unsupported");
+ t = new UnsupportedTerminal();
}
- // called when the JVM is shutting down.
- private void shutdown0() {
+ try {
+ t.init();
+ } catch (Throwable e) {
+ logger.error("Terminal initialization failed, falling back to unsupported");
+ t = new UnsupportedTerminal();
+
+ try {
+ t.init();
+ } catch (IOException e1) {
+ // UnsupportedTerminal can't do this
+ }
+ }
+
+ t.setEchoEnabled(true);
+
+ this.terminal = t;
+ this.enableBackspace = Boolean.parseBoolean(System.getProperty(TerminalType.ENABLE_BACKSPACE, "true"));
+ }
+
+ // called when the JVM is shutting down.
+ private void shutdown0() {
+ synchronized (this.inputLockSingle) {
+ this.inputLockSingle.notifyAll();
+ }
+
+ synchronized (this.inputLockLine) {
+ this.inputLockLine.notifyAll();
+ }
+
+ try {
+ InputConsole inputConsole = InputConsole.this;
+
+ inputConsole.terminal.restore();
+ // this will 'hang' our shutdown, and honestly, who cares? We're shutting down anyways.
+ // inputConsole.reader.close(); // hangs on shutdown
+ } catch (IOException ignored) {
+ ignored.printStackTrace();
+ }
+ }
+
+ private void echo0(boolean enableEcho) {
+ this.terminal.setEchoEnabled(enableEcho);
+ }
+
+ private boolean echo0() {
+ return this.terminal.isEchoEnabled();
+ }
+
+ /**
+ * return -1 if no data or bunged-up
+ */
+ private int read0() {
+ Integer bufferCounter = this.threadBufferCounter.get();
+ ObjectPoolHolder objectPoolHolder = this.readBuff.get();
+ ByteBuffer2 buffer;
+
+ if (objectPoolHolder == null) {
+ bufferCounter = 0;
+ this.threadBufferCounter.set(bufferCounter);
+
+ ObjectPoolHolder holder = this.pool.take();
+ buffer = holder.getValue();
+ buffer.clear();
+ this.readBuff.set(holder);
+ this.readBuffers.add(holder);
+ } else {
+ buffer = objectPoolHolder.getValue();
+ }
+
+ if (bufferCounter == buffer.position()) {
+ synchronized (this.inputLockSingle) {
+ buffer.setPosition(0);
+ this.threadBufferCounter.set(0);
+
+ try {
+ this.inputLockSingle.wait();
+ } catch (InterruptedException e) {
+ return -1;
+ }
+ }
+ }
+
+ bufferCounter = this.threadBufferCounter.get();
+ char c = buffer.readChar(bufferCounter);
+ bufferCounter += 2;
+
+ this.threadBufferCounter.set(bufferCounter);
+ return c;
+ }
+
+ /**
+ * return empty char[] if no data
+ */
+ private char[] readLinePassword0() {
+ // don't bother in an IDE. it won't work.
+ boolean echoEnabled = this.terminal.isEchoEnabled();
+ this.terminal.setEchoEnabled(false);
+ char[] readLine0 = readLine0();
+ this.terminal.setEchoEnabled(echoEnabled);
+
+ return readLine0;
+ }
+
+ /**
+ * return empty char[] if no data
+ */
+ private char[] readLine0() {
+ synchronized (this.inputLock) {
+ // empty here, because we don't want to register a readLine WHILE we are still processing
+ // the current line info.
+
+ // the threadBufferForRead getting added is the part that is important
+ if (this.readLineBuff.get() == null) {
+ ObjectPoolHolder holder = this.pool.take();
+ this.readLineBuff.set(holder);
+ this.readLineBuffers.add(holder);
+ } else {
+ this.readLineBuff.get().getValue().clear();
+ }
+ }
+
+ synchronized (this.inputLockLine) {
+ try {
+ this.inputLockLine.wait();
+ } catch (InterruptedException e) {
+ return emptyLine;
+ }
+ }
+
+ ObjectPoolHolder objectPoolHolder = this.readLineBuff.get();
+ ByteBuffer2 buffer = objectPoolHolder.getValue();
+ int len = buffer.position();
+ if (len == 0) {
+ return emptyLine;
+ }
+
+ buffer.rewind();
+ char[] readChars = buffer.readChars(len / 2); // java always stores chars in 2 bytes
+
+ // dump the chars in the buffer (safer for passwords, etc)
+ buffer.clearSecure();
+
+ this.readLineBuffers.remove(objectPoolHolder);
+ this.pool.release(objectPoolHolder);
+ this.readLineBuff.set(null);
+
+ return readChars;
+ }
+
+ /**
+ * releases any thread still waiting.
+ */
+ private void release0() {
+ synchronized (this.inputLockSingle) {
+ this.inputLockSingle.notifyAll();
+ }
+
+ synchronized (this.inputLockLine) {
+ this.inputLockLine.notifyAll();
+ }
+ }
+
+ private void run() {
+ Logger logger2 = logger;
+
+ final boolean ansiEnabled = Ansi.isEnabled();
+ Ansi ansi = Ansi.ansi();
+ PrintStream out = AnsiConsole.out;
+
+ int typedChar;
+ char asChar;
+
+ // don't type ; in a bash shell, it quits everything
+ // \n is replaced by \r in unix terminal?
+ while ((typedChar = this.terminal.read()) != -1) {
+ synchronized (this.inputLock) {
+ // don't let anyone add a new reader while we are still processing the current actions
+ asChar = (char) typedChar;
+
+ if (logger2.isTraceEnabled()) {
+ logger2.trace("READ: {} ({})", asChar, typedChar);
+ }
+
+ // notify everyone waiting for a character.
synchronized (this.inputLockSingle) {
- this.inputLockSingle.notifyAll();
+ // have to do readChar first (readLine has to deal with \b and \n
+ for (ObjectPoolHolder objectPoolHolder : this.readBuffers) {
+ ByteBuffer2 buffer = objectPoolHolder.getValue();
+ buffer.writeChar(asChar);
+ }
+
+ this.inputLockSingle.notifyAll();
}
- synchronized (this.inputLockLine) {
+ // now to handle readLine stuff
+
+ // if we type a backspace key, swallow it + previous in READLINE. READCHAR will have it passed.
+ if (this.enableBackspace && asChar == '\b') {
+ int position = 0;
+
+ // clear ourself + one extra.
+ if (ansiEnabled) {
+ for (ObjectPoolHolder objectPoolHolder : this.readLineBuffers) {
+ ByteBuffer2 buffer = objectPoolHolder.getValue();
+ // size of the buffer BEFORE our backspace was typed
+ int length = buffer.position();
+ int amtToOverwrite = 2 * 2; // backspace is always 2 chars (^?) * 2 because it's bytes
+
+ if (length > 1) {
+ char charAt = buffer.readChar(length - 2);
+ amtToOverwrite += getPrintableCharacters(charAt);
+
+ // delete last item in our buffer
+ length -= 2;
+ buffer.setPosition(length);
+
+ // now figure out where the cursor is really at.
+ // this is more memory friendly than buf.toString.length
+ for (int i = 0; i < length; i += 2) {
+ charAt = buffer.readChar(i);
+ position += getPrintableCharacters(charAt);
+ }
+
+ position++;
+ }
+
+ char[] overwrite = new char[amtToOverwrite];
+ char c = ' ';
+ for (int i = 0; i < amtToOverwrite; i++) {
+ overwrite[i] = c;
+ }
+
+ // move back however many, over write, then go back again
+ out.print(ansi.cursorToColumn(position));
+ out.print(overwrite);
+ out.print(ansi.cursorToColumn(position));
+ out.flush();
+ }
+ }
+ } else if (asChar == '\n') {
+ // ignoring \r, because \n is ALWAYS the last character in a new line sequence. (even for windows)
+ synchronized (this.inputLockLine) {
this.inputLockLine.notifyAll();
- }
-
- try {
- InputConsole inputConsole = InputConsole.this;
-
- inputConsole.terminal.restore();
- // this will 'hang' our shutdown, and honestly, who cares? We're shutting down anyways.
- // inputConsole.reader.close(); // hangs on shutdown
- } catch (IOException ignored) {
- ignored.printStackTrace();
- }
- }
-
- private void echo0(boolean enableEcho) {
- this.terminal.setEchoEnabled(enableEcho);
- }
-
- private boolean echo0() {
- return this.terminal.isEchoEnabled();
- }
-
- /** return -1 if no data or bunged-up */
- private final int read0() {
- Integer bufferCounter = this.threadBufferCounter.get();
- ObjectPoolHolder objectPoolHolder = this.readBuff.get();
- ByteBuffer2 buffer = null;
-
- if (objectPoolHolder == null) {
- bufferCounter = 0;
- this.threadBufferCounter.set(bufferCounter);
-
- ObjectPoolHolder holder = this.pool.take();
- buffer = holder.getValue();
- buffer.clear();
- this.readBuff.set(holder);
- this.readBuffers.add(holder);
+ }
} else {
- buffer = objectPoolHolder.getValue();
+ // only append if we are not a new line.
+ // our windows console PREVENTS us from returning '\r' (it truncates '\r\n', and returns just '\n')
+ for (ObjectPoolHolder objectPoolHolder : this.readLineBuffers) {
+ ByteBuffer2 buffer = objectPoolHolder.getValue();
+ buffer.writeChar(asChar);
+ }
}
+ }
+ }
+ }
- if (bufferCounter == buffer.position()) {
- synchronized (this.inputLockSingle) {
- buffer.setPosition(0);
- this.threadBufferCounter.set(0);
+ /**
+ * try to guess if we are running inside an IDE
+ */
+ private boolean isIDEAutoDetect() {
+ try {
+ // Get the location of this class
+ ProtectionDomain pDomain = getClass().getProtectionDomain();
+ CodeSource cSource = pDomain.getCodeSource();
+ URL loc = cSource.getLocation(); // file:/X:/workspace/xxxx/classes/ when it's in eclipse
- try {
- this.inputLockSingle.wait();
- } catch (InterruptedException e) {
- return -1;
- }
- }
- }
+ // if we are in eclipse, this won't be a jar -- it will be a class directory.
+ File locFile = new File(loc.getFile());
+ return locFile.isDirectory();
- bufferCounter = this.threadBufferCounter.get();
- char c = buffer.readChar(bufferCounter);
- bufferCounter += 2;
-
- this.threadBufferCounter.set(bufferCounter);
- return c;
+ } catch (Exception ignored) {
}
- /** return empty char[] if no data */
- private final char[] readLinePassword0() {
- // don't bother in an IDE. it won't work.
- boolean echoEnabled = this.terminal.isEchoEnabled();
- this.terminal.setEchoEnabled(false);
- char[] readLine0 = readLine0();
- this.terminal.setEchoEnabled(echoEnabled);
-
- return readLine0;
- }
-
- /** return empty char[] if no data */
- private final char[] readLine0() {
- synchronized (this.inputLock) {
- // empty here, because we don't want to register a readLine WHILE we are still processing
- // the current line info.
-
- // the threadBufferForRead getting added is the part that is important
- if (this.readLineBuff.get() == null) {
- ObjectPoolHolder holder = this.pool.take();
- this.readLineBuff.set(holder);
- this.readLineBuffers.add(holder);
- } else {
- this.readLineBuff.get().getValue().clear();
- }
- }
-
- synchronized (this.inputLockLine) {
- try {
- this.inputLockLine.wait();
- } catch (InterruptedException e) {
- return emptyLine;
- }
- }
-
- ObjectPoolHolder objectPoolHolder = this.readLineBuff.get();
- ByteBuffer2 buffer = objectPoolHolder.getValue();
- int len = buffer.position();
- if (len == 0) {
- return emptyLine;
- }
-
- buffer.rewind();
- char[] readChars = buffer.readChars(len/2); // java always stores chars in 2 bytes
-
- // dump the chars in the buffer (safer for passwords, etc)
- buffer.clearSecure();
-
- this.readLineBuffers.remove(objectPoolHolder);
- this.pool.release(objectPoolHolder);
- this.readLineBuff.set(null);
-
- return readChars;
- }
-
- /**
- * releases any thread still waiting.
- */
- private void release0() {
- synchronized (this.inputLockSingle) {
- this.inputLockSingle.notifyAll();
- }
-
- synchronized (this.inputLockLine) {
- this.inputLockLine.notifyAll();
- }
- }
-
- private final void run() {
- Logger logger2 = logger;
-
- final boolean ansiEnabled = Ansi.isEnabled();
- Ansi ansi = Ansi.ansi();
- PrintStream out = AnsiConsole.out;
-
- int typedChar;
- char asChar;
-
- // don't type ; in a bash shell, it quits everything
- // \n is replaced by \r in unix terminal?
- while ((typedChar = this.terminal.read()) != -1) {
- synchronized (this.inputLock) {
- // don't let anyone add a new reader while we are still processing the current actions
- asChar = (char) typedChar;
-
- if (logger2.isTraceEnabled()) {
- logger2.trace("READ: {} ({})", asChar, typedChar);
- }
-
- // notify everyone waiting for a character.
- synchronized (this.inputLockSingle) {
- // have to do readChar first (readLine has to deal with \b and \n
- for (ObjectPoolHolder objectPoolHolder : this.readBuffers) {
- ByteBuffer2 buffer = objectPoolHolder.getValue();
- buffer.writeChar(asChar);
- }
-
- this.inputLockSingle.notifyAll();
- }
+ // fall-back to unsupported
+ return true;
+ }
- // now to handle readLine stuff
-
- // if we type a backspace key, swallow it + previous in READLINE. READCHAR will have it passed.
- if (this.enableBackspace && asChar == '\b') {
- int position = 0;
-
- // clear ourself + one extra.
- if (ansiEnabled) {
- for (ObjectPoolHolder objectPoolHolder : this.readLineBuffers) {
- ByteBuffer2 buffer = objectPoolHolder.getValue();
- // size of the buffer BEFORE our backspace was typed
- int length = buffer.position();
- int amtToOverwrite = 2 * 2; // backspace is always 2 chars (^?) * 2 because it's bytes
-
- if (length > 1) {
- char charAt = buffer.readChar(length-2);
- amtToOverwrite += getPrintableCharacters(charAt);
-
- // delete last item in our buffer
- length -= 2;
- buffer.setPosition(length);
-
- // now figure out where the cursor is really at.
- // this is more memory friendly than buf.toString.length
- for (int i=0;i objectPoolHolder : this.readLineBuffers) {
- ByteBuffer2 buffer = objectPoolHolder.getValue();
- buffer.writeChar(asChar);
- }
- }
- }
- }
- }
-
- /**
- * try to guess if we are running inside an IDE
- */
- private boolean isIDEAutoDetect() {
- try {
- // Get the location of this class
- ProtectionDomain pDomain = getClass().getProtectionDomain();
- CodeSource cSource = pDomain.getCodeSource();
- URL loc = cSource.getLocation(); // file:/X:/workspace/xxxx/classes/ when it's in eclipse
-
- // if we are in eclipse, this won't be a jar -- it will be a class directory.
- File locFile = new File(loc.getFile());
- return locFile.isDirectory();
-
- } catch (Exception e) {
- }
-
- // fall-back to unsupported
- return true;
- }
-
-
- /**
- * Return the number of characters that will be printed when the specified
- * character is echoed to the screen
- *
- * Adapted from cat by Torbjorn Granlund, as repeated in stty by David MacKenzie.
- */
- public static int getPrintableCharacters(final int ch) {
+ /**
+ * Return the number of characters that will be printed when the specified character is echoed to the screen
+ *
+ * Adapted from cat by Torbjorn Granlund, as repeated in stty by David MacKenzie.
+ */
+ private static int getPrintableCharacters(final int ch) {
// StringBuilder sbuff = new StringBuilder();
- if (ch >= 32) {
- if (ch < 127) {
+ if (ch >= 32) {
+ if (ch < 127) {
// sbuff.append((char) ch);
- return 1;
- }
- else if (ch == 127) {
+ return 1;
+ } else if (ch == 127) {
// sbuff.append('^');
// sbuff.append('?');
- return 2;
- }
- else {
+ return 2;
+ } else {
// sbuff.append('M');
// sbuff.append('-');
- int count = 2;
+ int count = 2;
- if (ch >= 128 + 32) {
- if (ch < 128 + 127) {
+ if (ch >= 128 + 32) {
+ if (ch < 128 + 127) {
// sbuff.append((char) (ch - 128));
- count++;
- }
- else {
+ count++;
+ } else {
// sbuff.append('^');
// sbuff.append('?');
- count += 2;
- }
- }
- else {
+ count += 2;
+ }
+ } else {
// sbuff.append('^');
// sbuff.append((char) (ch - 128 + 64));
- count += 2;
- }
- return count;
- }
+ count += 2;
}
- else {
+ return count;
+ }
+ } else {
// sbuff.append('^');
// sbuff.append((char) (ch + 64));
- return 2;
- }
+ return 2;
+ }
// return sbuff;
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/src/dorkbox/util/input/Terminal.java b/src/dorkbox/util/input/Terminal.java
index eaaf549..92f057c 100644
--- a/src/dorkbox/util/input/Terminal.java
+++ b/src/dorkbox/util/input/Terminal.java
@@ -18,34 +18,36 @@ package dorkbox.util.input;
import java.io.IOException;
public abstract class Terminal {
- protected final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
+
+ protected final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
- public static final int DEFAULT_WIDTH = 80;
- public static final int DEFAULT_HEIGHT = 24;
+ protected static final int DEFAULT_WIDTH = 80;
+ protected static final int DEFAULT_HEIGHT = 24;
- private volatile boolean echoEnabled;
+ private volatile boolean echoEnabled;
- public Terminal() {
- }
+ protected Terminal() {
+ }
- public abstract void init() throws IOException;
+ public abstract void init() throws IOException;
- public abstract void restore() throws IOException;
+ public abstract void restore() throws IOException;
- public void setEchoEnabled(boolean enabled) {
- this.echoEnabled = enabled;
- }
+ public void setEchoEnabled(boolean enabled) {
+ this.echoEnabled = enabled;
+ }
- public boolean isEchoEnabled() {
- return this.echoEnabled;
- }
+ public boolean isEchoEnabled() {
+ return this.echoEnabled;
+ }
- public abstract int getWidth();
- public abstract int getHeight();
+ public abstract int getWidth();
- /**
- * @return a character from whatever underlying input method the terminal has available.
- */
- public abstract int read();
+ public abstract int getHeight();
+
+ /**
+ * @return a character from whatever underlying input method the terminal has available.
+ */
+ public abstract int read();
}
diff --git a/src/dorkbox/util/input/TerminalType.java b/src/dorkbox/util/input/TerminalType.java
index b6171be..6b5d7e0 100644
--- a/src/dorkbox/util/input/TerminalType.java
+++ b/src/dorkbox/util/input/TerminalType.java
@@ -16,16 +16,17 @@
package dorkbox.util.input;
public class TerminalType {
- public static final String TYPE = "input.terminal";
- public static final String READERS = "input.terminal.readers";
- public static final String ENABLE_BACKSPACE = "input.enableBackspace";
- public static final String AUTO = "auto";
- public static final String UNIX = "unix";
- public static final String WIN = "win";
- public static final String WINDOWS = "windows";
+ public static final String TYPE = "input.terminal";
+ public static final String READERS = "input.terminal.readers";
+ public static final String ENABLE_BACKSPACE = "input.enableBackspace";
- public static final String NONE = "none";
- public static final String OFF = "off";
- public static final String FALSE = "false";
+ public static final String AUTO = "auto";
+ public static final String UNIX = "unix";
+ public static final String WIN = "win";
+ public static final String WINDOWS = "windows";
+
+ public static final String NONE = "none";
+ public static final String OFF = "off";
+ public static final String FALSE = "false";
}
diff --git a/src/dorkbox/util/input/posix/InputStreamReader.java b/src/dorkbox/util/input/posix/InputStreamReader.java
index 593a8de..0285b76 100644
--- a/src/dorkbox/util/input/posix/InputStreamReader.java
+++ b/src/dorkbox/util/input/posix/InputStreamReader.java
@@ -32,319 +32,283 @@ import java.nio.charset.UnmappableCharacterException;
/**
- *
- * NOTE for JLine: the default InputStreamReader that comes from the JRE
- * usually read more bytes than needed from the input stream, which
- * is not usable in a character per character model used in the console.
- * We thus use the harmony code which only reads the minimal number of bytes,
- * with a modification to ensure we can read larger characters (UTF-16 has
- * up to 4 bytes, and UTF-32, rare as it is, may have up to 8).
+ * NOTE for JLine: the default InputStreamReader that comes from the JRE usually read more bytes than needed from the
+ * input stream, which is not usable in a character per character model used in the console. We thus use the harmony
+ * code which only reads the minimal number of bytes, with a modification to ensure we can read larger characters
+ * (UTF-16 has up to 4 bytes, and UTF-32, rare as it is, may have up to 8).
*
*
- * A class for turning a byte stream into a character stream. Data read from the
- * source input stream is converted into characters by either a default or a
- * provided character converter. The default encoding is taken from the
- * "file.encoding" system property. {@code InputStreamReader} contains a buffer
- * of bytes read from the source stream and converts these into characters as
- * needed. The buffer size is 8K.
+ * A class for turning a byte stream into a character stream. Data read from the source input stream is converted into
+ * characters by either a default or a provided character converter. The default encoding is taken from the
+ * "file.encoding" system property. {@code InputStreamReader} contains a buffer of bytes read from the source stream and
+ * converts these into characters as needed. The buffer size is 8K.
*
* @see OutputStreamWriter
*/
-public class InputStreamReader extends Reader {
- private InputStream in;
+@SuppressWarnings("ALL")
+class InputStreamReader extends Reader {
- private static final int BUFFER_SIZE = 8192;
+ private InputStream in;
- private boolean endOfInput = false;
+ private static final int BUFFER_SIZE = 8192;
- String encoding;
+ private boolean endOfInput = false;
- CharsetDecoder decoder;
+ String encoding;
- ByteBuffer bytes = ByteBuffer.allocate(BUFFER_SIZE);
+ CharsetDecoder decoder;
- /**
- * Constructs a new {@code InputStreamReader} on the {@link InputStream}
- * {@code in}. This constructor sets the character converter to the encoding
- * specified in the "file.encoding" property and falls back to ISO 8859_1
- * (ISO-Latin-1) if the property doesn't exist.
- *
- * @param in
- * the input stream from which to read characters.
- */
- public InputStreamReader(InputStream in) {
- super(in);
- this.in = in;
- // FIXME: This should probably use Configuration.getFileEncoding()
- this.encoding = System.getProperty("file.encoding", "ISO8859_1"); //$NON-NLS-1$//$NON-NLS-2$
- this.decoder = Charset.forName(this.encoding).newDecoder().onMalformedInput(
- CodingErrorAction.REPLACE).onUnmappableCharacter(
- CodingErrorAction.REPLACE);
- this.bytes.limit(0);
- }
+ ByteBuffer bytes = ByteBuffer.allocate(BUFFER_SIZE);
- /**
- * Constructs a new InputStreamReader on the InputStream {@code in}. The
- * character converter that is used to decode bytes into characters is
- * identified by name by {@code enc}. If the encoding cannot be found, an
- * UnsupportedEncodingException error is thrown.
- *
- * @param in
- * the InputStream from which to read characters.
- * @param enc
- * identifies the character converter to use.
- * @throws NullPointerException
- * if {@code enc} is {@code null}.
- * @throws UnsupportedEncodingException
- * if the encoding specified by {@code enc} cannot be found.
- */
- public InputStreamReader(InputStream in, final String enc)
+ /**
+ * Constructs a new {@code InputStreamReader} on the {@link InputStream} {@code in}. This constructor sets the
+ * character converter to the encoding specified in the "file.encoding" property and falls back to ISO 8859_1
+ * (ISO-Latin-1) if the property doesn't exist.
+ *
+ * @param in the input stream from which to read characters.
+ */
+ public InputStreamReader(InputStream in) {
+ super(in);
+ this.in = in;
+ // FIXME: This should probably use Configuration.getFileEncoding()
+ this.encoding = System.getProperty("file.encoding", "ISO8859_1"); //$NON-NLS-1$//$NON-NLS-2$
+ this.decoder = Charset.forName(this.encoding).newDecoder().onMalformedInput(
+ CodingErrorAction.REPLACE).onUnmappableCharacter(
+ CodingErrorAction.REPLACE);
+ this.bytes.limit(0);
+ }
+
+ /**
+ * Constructs a new InputStreamReader on the InputStream {@code in}. The character converter that is used to decode
+ * bytes into characters is identified by name by {@code enc}. If the encoding cannot be found, an
+ * UnsupportedEncodingException error is thrown.
+ *
+ * @param in the InputStream from which to read characters.
+ * @param enc identifies the character converter to use.
+ * @throws NullPointerException if {@code enc} is {@code null}.
+ * @throws UnsupportedEncodingException if the encoding specified by {@code enc} cannot be found.
+ */
+ public InputStreamReader(InputStream in, final String enc)
// throws UnsupportedEncodingException
- {
- super(in);
+ {
+ super(in);
- if (enc == null) {
- throw new NullPointerException();
- }
- this.in = in;
+ if (enc == null) {
+ throw new NullPointerException();
+ }
+ this.in = in;
- try {
- this.decoder = Charset.forName(enc).newDecoder().onMalformedInput(
- CodingErrorAction.REPLACE).onUnmappableCharacter(
- CodingErrorAction.REPLACE);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
+ try {
+ this.decoder = Charset.forName(enc).newDecoder().onMalformedInput(
+ CodingErrorAction.REPLACE).onUnmappableCharacter(
+ CodingErrorAction.REPLACE);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
// throw (UnsupportedEncodingException)
// new UnsupportedEncodingException(enc).initCause(e);
+ }
+ this.bytes.limit(0);
+ }
+
+ /**
+ * Constructs a new InputStreamReader on the InputStream {@code in} and CharsetDecoder {@code dec}.
+ *
+ * @param in the source InputStream from which to read characters.
+ * @param dec the CharsetDecoder used by the character conversion.
+ */
+ public InputStreamReader(InputStream in, CharsetDecoder dec) {
+ super(in);
+ dec.averageCharsPerByte();
+ this.in = in;
+ this.decoder = dec;
+ this.bytes.limit(0);
+ }
+
+ /**
+ * Constructs a new InputStreamReader on the InputStream {@code in} and Charset {@code charset}.
+ *
+ * @param in the source InputStream from which to read characters.
+ * @param charset the Charset that defines the character converter
+ */
+ public InputStreamReader(InputStream in, Charset charset) {
+ super(in);
+ this.in = in;
+ this.decoder = charset.newDecoder().onMalformedInput(
+ CodingErrorAction.REPLACE).onUnmappableCharacter(
+ CodingErrorAction.REPLACE);
+ this.bytes.limit(0);
+ }
+
+ /**
+ * Closes this reader. This implementation closes the source InputStream and releases all local storage.
+ *
+ * @throws IOException if an error occurs attempting to close this reader.
+ */
+ @Override
+ public void close() throws IOException {
+ synchronized (this.lock) {
+ this.decoder = null;
+ if (this.in != null) {
+ this.in.close();
+ this.in = null;
+ }
+ }
+ }
+
+ /**
+ * Returns the name of the encoding used to convert bytes into characters. The value {@code null} is returned if this
+ * reader has been closed.
+ *
+ * @return the name of the character converter or {@code null} if this reader is closed.
+ */
+ public String getEncoding() {
+ if (!isOpen()) {
+ return null;
+ }
+ return this.encoding;
+ }
+
+ /**
+ * Reads a single character from this reader and returns it as an integer with the two higher-order bytes set to 0.
+ * Returns -1 if the end of the reader has been reached. The byte value is either obtained from converting bytes in
+ * this reader's buffer or by first filling the buffer from the source InputStream and then reading from the buffer.
+ *
+ * @return the character read or -1 if the end of the reader has been reached.
+ * @throws IOException if this reader is closed or some other I/O error occurs.
+ */
+ @Override
+ public int read() throws IOException {
+ synchronized (this.lock) {
+ if (!isOpen()) {
+ throw new IOException("InputStreamReader is closed.");
+ }
+
+ char buf[] = new char[4];
+ return read(buf, 0, 4) != -1 ? Character.codePointAt(buf, 0) : -1;
+ }
+ }
+
+ /**
+ * Reads at most {@code length} characters from this reader and stores them at position {@code offset} in the
+ * character array {@code buf}. Returns the number of characters actually read or -1 if the end of the reader has been
+ * reached. The bytes are either obtained from converting bytes in this reader's buffer or by first filling the buffer
+ * from the source InputStream and then reading from the buffer.
+ *
+ * @param buf the array to store the characters read.
+ * @param offset the initial position in {@code buf} to store the characters read from this reader.
+ * @param length the maximum number of characters to read.
+ * @return the number of characters read or -1 if the end of the reader has been reached.
+ * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code length < 0}, or if {@code offset + length} is
+ * greater than the length of {@code buf}.
+ * @throws IOException if this reader is closed or some other I/O error occurs.
+ */
+ @Override
+ public int read(char[] buf, int offset, int length) throws IOException {
+ synchronized (this.lock) {
+ if (!isOpen()) {
+ throw new IOException("InputStreamReader is closed.");
+ }
+ if (offset < 0 || offset > buf.length - length || length < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (length == 0) {
+ return 0;
+ }
+
+ CharBuffer out = CharBuffer.wrap(buf, offset, length);
+ CoderResult result = CoderResult.UNDERFLOW;
+
+ // bytes.remaining() indicates number of bytes in buffer
+ // when 1-st time entered, it'll be equal to zero
+ boolean needInput = !this.bytes.hasRemaining();
+
+ while (out.hasRemaining()) {
+ // fill the buffer if needed
+ if (needInput) {
+ try {
+ if (this.in.available() == 0
+ && out.position() > offset) {
+ // we could return the result without blocking read
+ break;
+ }
+ } catch (IOException e) {
+ // available didn't work so just try the read
+ }
+
+ int to_read = this.bytes.capacity() - this.bytes.limit();
+ int off = this.bytes.arrayOffset() + this.bytes.limit();
+ int was_red = this.in.read(this.bytes.array(), off, to_read);
+
+ if (was_red == -1) {
+ this.endOfInput = true;
+ break;
+ } else if (was_red == 0) {
+ break;
+ }
+ this.bytes.limit(this.bytes.limit() + was_red);
+ needInput = false;
}
- this.bytes.limit(0);
- }
- /**
- * Constructs a new InputStreamReader on the InputStream {@code in} and
- * CharsetDecoder {@code dec}.
- *
- * @param in
- * the source InputStream from which to read characters.
- * @param dec
- * the CharsetDecoder used by the character conversion.
- */
- public InputStreamReader(InputStream in, CharsetDecoder dec) {
- super(in);
- dec.averageCharsPerByte();
- this.in = in;
- this.decoder = dec;
- this.bytes.limit(0);
- }
+ // decode bytes
+ result = this.decoder.decode(this.bytes, out, false);
- /**
- * Constructs a new InputStreamReader on the InputStream {@code in} and
- * Charset {@code charset}.
- *
- * @param in
- * the source InputStream from which to read characters.
- * @param charset
- * the Charset that defines the character converter
- */
- public InputStreamReader(InputStream in, Charset charset) {
- super(in);
- this.in = in;
- this.decoder = charset.newDecoder().onMalformedInput(
- CodingErrorAction.REPLACE).onUnmappableCharacter(
- CodingErrorAction.REPLACE);
- this.bytes.limit(0);
- }
-
- /**
- * Closes this reader. This implementation closes the source InputStream and
- * releases all local storage.
- *
- * @throws IOException
- * if an error occurs attempting to close this reader.
- */
- @Override
- public void close() throws IOException {
- synchronized (this.lock) {
- this.decoder = null;
- if (this.in != null) {
- this.in.close();
- this.in = null;
- }
+ if (result.isUnderflow()) {
+ // compact the buffer if no space left
+ if (this.bytes.limit() == this.bytes.capacity()) {
+ this.bytes.compact();
+ this.bytes.limit(this.bytes.position());
+ this.bytes.position(0);
+ }
+ needInput = true;
+ } else {
+ break;
}
+ }
+
+ if (result == CoderResult.UNDERFLOW && this.endOfInput) {
+ result = this.decoder.decode(this.bytes, out, true);
+ this.decoder.flush(out);
+ this.decoder.reset();
+ }
+ if (result.isMalformed()) {
+ throw new MalformedInputException(result.length());
+ } else if (result.isUnmappable()) {
+ throw new UnmappableCharacterException(result.length());
+ }
+
+ return out.position() - offset == 0 ? -1 : out.position() - offset;
}
+ }
- /**
- * Returns the name of the encoding used to convert bytes into characters.
- * The value {@code null} is returned if this reader has been closed.
- *
- * @return the name of the character converter or {@code null} if this
- * reader is closed.
- */
- public String getEncoding() {
- if (!isOpen()) {
- return null;
- }
- return this.encoding;
+ /*
+ * Answer a boolean indicating whether or not this InputStreamReader is
+ * open.
+ */
+ private boolean isOpen() {
+ return this.in != null;
+ }
+
+ /**
+ * Indicates whether this reader is ready to be read without blocking. If the result is {@code true}, the next {@code
+ * read()} will not block. If the result is {@code false} then this reader may or may not block when {@code read()} is
+ * called. This implementation returns {@code true} if there are bytes available in the buffer or the source stream
+ * has bytes available.
+ *
+ * @return {@code true} if the receiver will not block when {@code read()} is called, {@code false} if unknown or
+ * blocking will occur.
+ * @throws IOException if this reader is closed or some other I/O error occurs.
+ */
+ @Override
+ public boolean ready() throws IOException {
+ synchronized (this.lock) {
+ if (this.in == null) {
+ throw new IOException("InputStreamReader is closed.");
+ }
+ try {
+ return this.bytes.hasRemaining() || this.in.available() > 0;
+ } catch (IOException e) {
+ return false;
+ }
}
-
- /**
- * Reads a single character from this reader and returns it as an integer
- * with the two higher-order bytes set to 0. Returns -1 if the end of the
- * reader has been reached. The byte value is either obtained from
- * converting bytes in this reader's buffer or by first filling the buffer
- * from the source InputStream and then reading from the buffer.
- *
- * @return the character read or -1 if the end of the reader has been
- * reached.
- * @throws IOException
- * if this reader is closed or some other I/O error occurs.
- */
- @Override
- public int read() throws IOException {
- synchronized (this.lock) {
- if (!isOpen()) {
- throw new IOException("InputStreamReader is closed.");
- }
-
- char buf[] = new char[4];
- return read(buf, 0, 4) != -1 ? Character.codePointAt(buf, 0) : -1;
- }
- }
-
- /**
- * Reads at most {@code length} characters from this reader and stores them
- * at position {@code offset} in the character array {@code buf}. Returns
- * the number of characters actually read or -1 if the end of the reader has
- * been reached. The bytes are either obtained from converting bytes in this
- * reader's buffer or by first filling the buffer from the source
- * InputStream and then reading from the buffer.
- *
- * @param buf
- * the array to store the characters read.
- * @param offset
- * the initial position in {@code buf} to store the characters
- * read from this reader.
- * @param length
- * the maximum number of characters to read.
- * @return the number of characters read or -1 if the end of the reader has
- * been reached.
- * @throws IndexOutOfBoundsException
- * if {@code offset < 0} or {@code length < 0}, or if
- * {@code offset + length} is greater than the length of
- * {@code buf}.
- * @throws IOException
- * if this reader is closed or some other I/O error occurs.
- */
- @Override
- public int read(char[] buf, int offset, int length) throws IOException {
- synchronized (this.lock) {
- if (!isOpen()) {
- throw new IOException("InputStreamReader is closed.");
- }
- if (offset < 0 || offset > buf.length - length || length < 0) {
- throw new IndexOutOfBoundsException();
- }
- if (length == 0) {
- return 0;
- }
-
- CharBuffer out = CharBuffer.wrap(buf, offset, length);
- CoderResult result = CoderResult.UNDERFLOW;
-
- // bytes.remaining() indicates number of bytes in buffer
- // when 1-st time entered, it'll be equal to zero
- boolean needInput = !this.bytes.hasRemaining();
-
- while (out.hasRemaining()) {
- // fill the buffer if needed
- if (needInput) {
- try {
- if (this.in.available() == 0
- && out.position() > offset) {
- // we could return the result without blocking read
- break;
- }
- } catch (IOException e) {
- // available didn't work so just try the read
- }
-
- int to_read = this.bytes.capacity() - this.bytes.limit();
- int off = this.bytes.arrayOffset() + this.bytes.limit();
- int was_red = this.in.read(this.bytes.array(), off, to_read);
-
- if (was_red == -1) {
- this.endOfInput = true;
- break;
- } else if (was_red == 0) {
- break;
- }
- this.bytes.limit(this.bytes.limit() + was_red);
- needInput = false;
- }
-
- // decode bytes
- result = this.decoder.decode(this.bytes, out, false);
-
- if (result.isUnderflow()) {
- // compact the buffer if no space left
- if (this.bytes.limit() == this.bytes.capacity()) {
- this.bytes.compact();
- this.bytes.limit(this.bytes.position());
- this.bytes.position(0);
- }
- needInput = true;
- } else {
- break;
- }
- }
-
- if (result == CoderResult.UNDERFLOW && this.endOfInput) {
- result = this.decoder.decode(this.bytes, out, true);
- this.decoder.flush(out);
- this.decoder.reset();
- }
- if (result.isMalformed()) {
- throw new MalformedInputException(result.length());
- } else if (result.isUnmappable()) {
- throw new UnmappableCharacterException(result.length());
- }
-
- return out.position() - offset == 0 ? -1 : out.position() - offset;
- }
- }
-
- /*
- * Answer a boolean indicating whether or not this InputStreamReader is
- * open.
- */
- private boolean isOpen() {
- return this.in != null;
- }
-
- /**
- * Indicates whether this reader is ready to be read without blocking. If
- * the result is {@code true}, the next {@code read()} will not block. If
- * the result is {@code false} then this reader may or may not block when
- * {@code read()} is called. This implementation returns {@code true} if
- * there are bytes available in the buffer or the source stream has bytes
- * available.
- *
- * @return {@code true} if the receiver will not block when {@code read()}
- * is called, {@code false} if unknown or blocking will occur.
- * @throws IOException
- * if this reader is closed or some other I/O error occurs.
- */
- @Override
- public boolean ready() throws IOException {
- synchronized (this.lock) {
- if (this.in == null) {
- throw new IOException("InputStreamReader is closed.");
- }
- try {
- return this.bytes.hasRemaining() || this.in.available() > 0;
- } catch (IOException e) {
- return false;
- }
- }
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/src/dorkbox/util/input/posix/PosixTerminalControl.java b/src/dorkbox/util/input/posix/PosixTerminalControl.java
index a280851..cfb2148 100644
--- a/src/dorkbox/util/input/posix/PosixTerminalControl.java
+++ b/src/dorkbox/util/input/posix/PosixTerminalControl.java
@@ -15,69 +15,71 @@
*/
package dorkbox.util.input.posix;
-import java.nio.ByteBuffer;
-
import com.sun.jna.Library;
-public interface PosixTerminalControl extends Library {
- public static final int TCSANOW = 0;
- public static final int TBUFLEN = 124;
+import java.nio.ByteBuffer;
- // Definitions at: http://linux.die.net/man/3/termios
- // also: http://code.metager.de/source/xref/DragonFly-BSD/sys/sys/termios.h
- public static final int IGNBRK = 0x00000001; /* ignore BREAK condition */
- public static final int BRKINT = 0x00000002; /* map BREAK to SIGINTR */
+@SuppressWarnings("ALL")
+interface PosixTerminalControl extends Library {
+
+ public static final int TCSANOW = 0;
+ public static final int TBUFLEN = 124;
+
+ // Definitions at: http://linux.die.net/man/3/termios
+ // also: http://code.metager.de/source/xref/DragonFly-BSD/sys/sys/termios.h
+ public static final int IGNBRK = 0x00000001; /* ignore BREAK condition */
+ public static final int BRKINT = 0x00000002; /* map BREAK to SIGINTR */
- public static final int ISIG = 0000001;
- public static final int ICANON = 0000002;
- public static final int ECHO = 0000010;
- public static final int IXON = 0002000;
+ public static final int ISIG = 0000001;
+ public static final int ICANON = 0000002;
+ public static final int ECHO = 0000010;
+ public static final int IXON = 0002000;
- public static final int VINTR = 0;
- public static final int VQUIT = 1;
- public static final int VERASE = 2;
- public static final int VKILL = 3;
- public static final int VEOF = 4;
- public static final int VTIME = 5;
- public static final int VMIN = 6;
- public static final int VSWTC = 7;
- public static final int VSTART = 8;
- public static final int VSTOP = 9;
- public static final int VSUSP = 10;
- public static final int VEOL = 11;
- public static final int VREPRINT = 12;
- public static final int VDISCARD = 13;
- public static final int VWERASE = 14;
- public static final int VLNEXT = 15;
- public static final int VEOL2 = 16;
+ public static final int VINTR = 0;
+ public static final int VQUIT = 1;
+ public static final int VERASE = 2;
+ public static final int VKILL = 3;
+ public static final int VEOF = 4;
+ public static final int VTIME = 5;
+ public static final int VMIN = 6;
+ public static final int VSWTC = 7;
+ public static final int VSTART = 8;
+ public static final int VSTOP = 9;
+ public static final int VSUSP = 10;
+ public static final int VEOL = 11;
+ public static final int VREPRINT = 12;
+ public static final int VDISCARD = 13;
+ public static final int VWERASE = 14;
+ public static final int VLNEXT = 15;
+ public static final int VEOL2 = 16;
- // MAGIC!
- public static final int TIOCGWINSZ = System.getProperty("os.name").equalsIgnoreCase("linux") ? 0x5413 : 1074295912;
+ // MAGIC!
+ public static final int TIOCGWINSZ = System.getProperty("os.name").equalsIgnoreCase("linux") ? 0x5413 : 1074295912;
- public int open(String path, int flags);
+ public int open(String path, int flags);
- public int close(int fd);
+ public int close(int fd);
- /**
- * Original signature : int ioctl(int, int, char*)
- */
- public int ioctl(int d, int request, ByteBuffer data);
+ /**
+ * Original signature : int ioctl(int, int, char*)
+ */
+ public int ioctl(int d, int request, ByteBuffer data);
- /**
- * Put the state of FD into *TERMIOS_P.
- *
- * Original signature : int tcgetattr(int, char*)
- */
- public int tcgetattr(int fd, TermiosStruct termios_p);
+ /**
+ * Put the state of FD into *TERMIOS_P.
+ *
+ * Original signature : int tcgetattr(int, char*)
+ */
+ public int tcgetattr(int fd, TermiosStruct termios_p);
- /**
- * Set the state of FD to *TERMIOS_P.
- *
- * Values for OPTIONAL_ACTIONS (TCSA*) are in .
- *
- * Original signature : int tcsetattr(int, int, char*)
- */
- public int tcsetattr(int fd, int optional_actions, TermiosStruct termios_p);
+ /**
+ * Set the state of FD to *TERMIOS_P.
+ *
+ * Values for OPTIONAL_ACTIONS (TCSA*) are in .
+ *
+ * Original signature : int tcsetattr(int, int, char*)
+ */
+ public int tcsetattr(int fd, int optional_actions, TermiosStruct termios_p);
}
diff --git a/src/dorkbox/util/input/posix/TermiosStruct.java b/src/dorkbox/util/input/posix/TermiosStruct.java
index a5dc77f..05ae22f 100644
--- a/src/dorkbox/util/input/posix/TermiosStruct.java
+++ b/src/dorkbox/util/input/posix/TermiosStruct.java
@@ -15,45 +15,63 @@
*/
package dorkbox.util.input.posix;
+import com.sun.jna.Structure;
+
import java.util.Arrays;
import java.util.List;
-import com.sun.jna.Structure;
+@SuppressWarnings("ALL")
+class TermiosStruct extends Structure {
-public class TermiosStruct extends Structure {
- /** input mode flags */
- public int c_iflag;
- /** output mode flags */
- public int c_oflag;
- /** control mode flags */
- public int c_cflag;
- /** local mode flags */
- public int c_lflag;
- /** line discipline */
- public byte c_line;
+ /**
+ * input mode flags
+ */
+ public int c_iflag;
+ /**
+ * output mode flags
+ */
+ public int c_oflag;
+ /**
+ * control mode flags
+ */
+ public int c_cflag;
+ /**
+ * local mode flags
+ */
+ public int c_lflag;
+ /**
+ * line discipline
+ */
+ public byte c_line;
- /** control characters */
- public byte[] c_cc = new byte[32];
+ /**
+ * control characters
+ */
+ public byte[] c_cc = new byte[32];
- /** input speed */
- public int c_ispeed;
- /** output speed */
- public int c_ospeed;
+ /**
+ * input speed
+ */
+ public int c_ispeed;
+ /**
+ * output speed
+ */
+ public int c_ospeed;
- public TermiosStruct() {
- }
+ public TermiosStruct() {
+ }
- @Override
- protected List getFieldOrder() {
- return Arrays.asList(
- "c_iflag",
- "c_oflag",
- "c_cflag",
- "c_lflag",
- "c_line",
- "c_cc",
- "c_ispeed",
- "c_ospeed"
- );
- }
-}
\ No newline at end of file
+ @Override
+ protected List getFieldOrder() {
+ return Arrays.asList(
+ "c_iflag",
+ "c_oflag",
+ "c_cflag",
+ "c_lflag",
+ "c_line",
+ "c_cc",
+ "c_ispeed",
+ "c_ospeed"
+ );
+ }
+}
diff --git a/src/dorkbox/util/input/posix/UnixTerminal.java b/src/dorkbox/util/input/posix/UnixTerminal.java
index bc99f67..e6bad18 100644
--- a/src/dorkbox/util/input/posix/UnixTerminal.java
+++ b/src/dorkbox/util/input/posix/UnixTerminal.java
@@ -15,50 +15,50 @@
*/
package dorkbox.util.input.posix;
+import com.sun.jna.Native;
+
import java.io.IOException;
import java.io.Reader;
import java.nio.ByteBuffer;
-import com.sun.jna.Native;
-
import dorkbox.util.input.Encoding;
import dorkbox.util.input.Terminal;
/**
- * Terminal that is used for unix platforms. Terminal initialization
- * is handled via JNA and ioctl/tcgetattr/tcsetattr/cfmakeraw.
+ * Terminal that is used for unix platforms. Terminal initialization is handled via JNA and
+ * ioctl/tcgetattr/tcsetattr/cfmakeraw.
*
* This implementation should work for an reasonable POSIX system.
*/
public class UnixTerminal extends Terminal {
- private volatile TermiosStruct termInfoDefault = new TermiosStruct();
- private volatile TermiosStruct termInfo = new TermiosStruct();
+ private volatile TermiosStruct termInfoDefault = new TermiosStruct();
+ private volatile TermiosStruct termInfo = new TermiosStruct();
- private Reader reader;
+ private final Reader reader;
- private PosixTerminalControl term;
- private ByteBuffer windowSizeBuffer = ByteBuffer.allocate(8);
+ private final PosixTerminalControl term;
+ private ByteBuffer windowSizeBuffer = ByteBuffer.allocate(8);
- public UnixTerminal() throws Exception {
- String encoding = Encoding.get();
- this.reader = new InputStreamReader(System.in, encoding);
+ public UnixTerminal() throws Exception {
+ String encoding = Encoding.get();
+ this.reader = new InputStreamReader(System.in, encoding);
- this.term = (PosixTerminalControl) Native.loadLibrary("c", PosixTerminalControl.class);
+ this.term = (PosixTerminalControl) Native.loadLibrary("c", PosixTerminalControl.class);
- // save off the defaults
- if (this.term.tcgetattr(0, this.termInfoDefault) !=0) {
- throw new IOException("Failed to get terminal info");
- }
+ // save off the defaults
+ if (this.term.tcgetattr(0, this.termInfoDefault) != 0) {
+ throw new IOException("Failed to get terminal info");
}
+ }
- @Override
- public void init() throws IOException {
+ @Override
+ public void init() throws IOException {
- // COMPARABLE TO (from upstream)
- //settings.set("-icanon min 1 -ixon");
- //settings.set("dsusp undef");
+ // COMPARABLE TO (from upstream)
+ //settings.set("-icanon min 1 -ixon");
+ //settings.set("dsusp undef");
/*
* NOT done in constructor, since our unit test DOES NOT use this!
@@ -77,90 +77,86 @@ public class UnixTerminal extends Terminal {
// t->c_cc[VMIN] = 1;
// t->c_cc[VTIME] = 0;
- if (this.term.tcgetattr(0, this.termInfo) !=0) {
- throw new IOException("Failed to get terminal info");
- }
+ if (this.term.tcgetattr(0, this.termInfo) != 0) {
+ throw new IOException("Failed to get terminal info");
+ }
- this.termInfo.c_iflag &= ~PosixTerminalControl.IXON; // DISABLE - flow control mediated by ^S and ^Q
+ this.termInfo.c_iflag &= ~PosixTerminalControl.IXON; // DISABLE - flow control mediated by ^S and ^Q
// struct.c_iflag |= PosixTerminalControl.IUTF8; // DISABLE - flow control mediated by ^S and ^Q
- this.termInfo.c_lflag &= ~PosixTerminalControl.ICANON; // DISABLE - canonical mode (pass chars straight through to terminal)
+ this.termInfo.c_lflag &=
+ ~PosixTerminalControl.ICANON; // DISABLE - canonical mode (pass chars straight through to terminal)
// struct.c_lflag &= ~PosixTerminalControl.ISIG; // DISABLE - When any of the characters INTR, QUIT, SUSP, or DSUSP are received, generate the corresponding signal.
+ // If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before the read is satisfied. As TIME is zero, the timer is not used.
+ this.termInfo.c_cc[PosixTerminalControl.VMIN] = 1; // Minimum number of characters for noncanonical read (MIN).
+ this.termInfo.c_cc[PosixTerminalControl.VTIME] = 0; // Timeout in deciseconds for noncanonical read (TIME).
- // If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before the read is satisfied. As TIME is zero, the timer is not used.
- this.termInfo.c_cc[PosixTerminalControl.VMIN] = 1; // Minimum number of characters for noncanonical read (MIN).
- this.termInfo.c_cc[PosixTerminalControl.VTIME] = 0; // Timeout in deciseconds for noncanonical read (TIME).
+ this.termInfo.c_cc[PosixTerminalControl.VSUSP] = 0; // suspend disabled
+ this.termInfo.c_cc[PosixTerminalControl.VEOF] = 0; // eof disabled
+ this.termInfo.c_cc[PosixTerminalControl.VEOL] = 0; // eol disabled
- this.termInfo.c_cc[PosixTerminalControl.VSUSP] = 0; // suspend disabled
- this.termInfo.c_cc[PosixTerminalControl.VEOF] = 0; // eof disabled
- this.termInfo.c_cc[PosixTerminalControl.VEOL] = 0; // eol disabled
+ if (this.term.tcsetattr(0, PosixTerminalControl.TCSANOW, this.termInfo) != 0) {
+ throw new IOException("Can not set terminal flags");
+ }
+ }
+ /**
+ * Restore the original terminal configuration, which can be used when shutting down the console reader. The
+ * ConsoleReader cannot be used after calling this method.
+ */
+ @Override
+ public final void restore() throws IOException {
+ if (this.term.tcsetattr(0, PosixTerminalControl.TCSANOW, this.termInfoDefault) != 0) {
+ throw new IOException("Can not reset terminal to defaults");
+ }
+ }
- if (this.term.tcsetattr(0, PosixTerminalControl.TCSANOW, this.termInfo) != 0) {
- throw new IOException("Can not set terminal flags");
- }
+ /**
+ * Returns number of columns in the terminal.
+ */
+ @Override
+ public final int getWidth() {
+ if (this.term.ioctl(0, PosixTerminalControl.TIOCGWINSZ, this.windowSizeBuffer) != 0) {
+ return DEFAULT_WIDTH;
}
- /**
- * Restore the original terminal configuration, which can be used when
- * shutting down the console reader. The ConsoleReader cannot be
- * used after calling this method.
- */
- @Override
- public final void restore() throws IOException {
- if (this.term.tcsetattr(0, PosixTerminalControl.TCSANOW, this.termInfoDefault) != 0) {
- throw new IOException("Can not reset terminal to defaults");
- }
+ return (short) (0x000000FF & this.windowSizeBuffer.get(2) + (0x000000FF & this.windowSizeBuffer.get(3)) * 256);
+ }
+
+ /**
+ * Returns number of rows in the terminal.
+ */
+ @Override
+ public final int getHeight() {
+ if (this.term.ioctl(0, PosixTerminalControl.TIOCGWINSZ, this.windowSizeBuffer) != 0) {
+ return DEFAULT_HEIGHT;
}
- /**
- * Returns number of columns in the terminal.
- */
- @Override
- public final int getWidth() {
- if (this.term.ioctl(0, PosixTerminalControl.TIOCGWINSZ, this.windowSizeBuffer) != 0) {
- return DEFAULT_WIDTH;
- }
+ return
+ (short) (0x000000FF & this.windowSizeBuffer.get(0) + (0x000000FF & this.windowSizeBuffer.get(1)) * 256);
+ }
- short columns = (short)(0x000000FF &this.windowSizeBuffer.get(2) + (0x000000FF & this.windowSizeBuffer.get(3)) * 256);
- return columns;
+ @Override
+ public final synchronized void setEchoEnabled(final boolean enabled) {
+ // have to reget them, since flags change everything
+ if (this.term.tcgetattr(0, this.termInfo) != 0) {
+ this.logger.error("Failed to get terminal info");
}
- /**
- * Returns number of rows in the terminal.
- */
- @Override
- public final int getHeight() {
- if (this.term.ioctl(0, PosixTerminalControl.TIOCGWINSZ, this.windowSizeBuffer) != 0) {
- return DEFAULT_HEIGHT;
- }
-
- short rows = (short)(0x000000FF &this.windowSizeBuffer.get(0) + (0x000000FF & this.windowSizeBuffer.get(1)) * 256);
- return rows;
+ if (enabled) {
+ this.termInfo.c_lflag |= PosixTerminalControl.ECHO; // ENABLE Echo input characters.
+ } else {
+ this.termInfo.c_lflag &= ~PosixTerminalControl.ECHO; // DISABLE Echo input characters.
}
- @Override
- public final synchronized void setEchoEnabled(final boolean enabled) {
- // have to reget them, since flags change everything
- if (this.term.tcgetattr(0, this.termInfo) !=0) {
- this.logger.error("Failed to get terminal info");
- }
-
- if (enabled) {
- this.termInfo.c_lflag |= PosixTerminalControl.ECHO; // ENABLE Echo input characters.
- }
- else {
- this.termInfo.c_lflag &= ~PosixTerminalControl.ECHO; // DISABLE Echo input characters.
- }
-
- if (this.term.tcsetattr(0, PosixTerminalControl.TCSANOW, this.termInfo) != 0) {
- this.logger.error("Can not set terminal flags");
- }
-
- super.setEchoEnabled(enabled);
+ if (this.term.tcsetattr(0, PosixTerminalControl.TCSANOW, this.termInfo) != 0) {
+ this.logger.error("Can not set terminal flags");
}
+ super.setEchoEnabled(enabled);
+ }
+
// public final void disableInterruptCharacter() {
// // have to re-get them, since flags change everything
// if (this.term.tcgetattr(0, this.termInfo) !=0) {
@@ -187,12 +183,12 @@ public class UnixTerminal extends Terminal {
// }
// }
- @Override
- public final int read() {
- try {
- return this.reader.read();
- } catch (IOException ignored) {
- return -1;
- }
+ @Override
+ public final int read() {
+ try {
+ return this.reader.read();
+ } catch (IOException ignored) {
+ return -1;
}
+ }
}
diff --git a/src/dorkbox/util/input/unsupported/UnsupportedTerminal.java b/src/dorkbox/util/input/unsupported/UnsupportedTerminal.java
index 6cdcb47..3d1747b 100644
--- a/src/dorkbox/util/input/unsupported/UnsupportedTerminal.java
+++ b/src/dorkbox/util/input/unsupported/UnsupportedTerminal.java
@@ -23,68 +23,66 @@ import dorkbox.util.input.Terminal;
public class UnsupportedTerminal extends Terminal {
- private ByteBuffer2 buffer = new ByteBuffer2(8, -1);
+ private final ByteBuffer2 buffer = new ByteBuffer2(8, -1);
- private int readerCount = -1;
- private InputStream in;
+ private int readerCount = -1;
+ private final InputStream in;
- public UnsupportedTerminal() {
- this.in = System.in;
- }
+ public UnsupportedTerminal() {
+ this.in = System.in;
+ }
- @Override
- public final void init() throws IOException {
- }
+ @Override
+ public final void init() throws IOException {
+ }
- @Override
- public final void restore() {
- }
+ @Override
+ public final void restore() {
+ }
- @Override
- public final int getWidth() {
- return 0;
- }
+ @Override
+ public final int getWidth() {
+ return 0;
+ }
- @Override
- public final int getHeight() {
- return 0;
- }
+ @Override
+ public final int getHeight() {
+ return 0;
+ }
- @Override
- public final int read() {
- // if we are reading data (because we are in IDE mode), we want to return ALL the chars of the line!
+ @Override
+ public final int read() {
+ // if we are reading data (because we are in IDE mode), we want to return ALL the chars of the line!
- // so, 'readerCount' is REALLY the index at which we return letters (until the whole string is returned)
- if (this.readerCount == -1) {
- // we have to wait for more data.
- try {
- InputStream sysIn = this.in;
- int read;
- char asChar;
- this.buffer.clearSecure();
+ // so, 'readerCount' is REALLY the index at which we return letters (until the whole string is returned)
+ if (this.readerCount == -1) {
+ // we have to wait for more data.
+ try {
+ InputStream sysIn = this.in;
+ int read;
+ char asChar;
+ this.buffer.clearSecure();
- while ((read = sysIn.read()) != -1) {
- asChar = (char)read;
- if (asChar == '\n') {
- this.readerCount = this.buffer.position();
- this.buffer.rewind();
- break;
- } else {
- this.buffer.writeChar(asChar);
- }
- }
- } catch (IOException e1) {
- }
- }
-
-
- // EACH thread will have it's own count!
- if (this.readerCount == this.buffer.position()) {
- this.readerCount = -1;
- return '\n';
- } else {
- char c = this.buffer.readChar();
- return c;
+ while ((read = sysIn.read()) != -1) {
+ asChar = (char) read;
+ if (asChar == '\n') {
+ this.readerCount = this.buffer.position();
+ this.buffer.rewind();
+ break;
+ } else {
+ this.buffer.writeChar(asChar);
+ }
}
+ } catch (IOException ignored) {
+ }
}
-}
\ No newline at end of file
+
+ // EACH thread will have it's own count!
+ if (this.readerCount == this.buffer.position()) {
+ this.readerCount = -1;
+ return '\n';
+ } else {
+ return this.buffer.readChar();
+ }
+ }
+}
diff --git a/src/dorkbox/util/input/windows/ConsoleMode.java b/src/dorkbox/util/input/windows/ConsoleMode.java
index abd00a6..66e0bcb 100644
--- a/src/dorkbox/util/input/windows/ConsoleMode.java
+++ b/src/dorkbox/util/input/windows/ConsoleMode.java
@@ -12,57 +12,47 @@
package dorkbox.util.input.windows;
/**
- * Console mode
- *
- * Constants copied wincon.h.
+ * Console mode Constants copied wincon.h.
*/
public enum ConsoleMode {
- /**
- * 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.
- */
- ENABLE_LINE_INPUT(2),
+ /**
+ * 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.
+ */
+ ENABLE_LINE_INPUT(2),
- /**
- * Characters read by the ReadFile or ReadConsole function are written to
- * the active screen buffer as they are read. This mode can be used only if
- * the ENABLE_LINE_INPUT mode is also enabled.
- */
- ENABLE_ECHO_INPUT(4),
+ /**
+ * Characters read by the ReadFile or ReadConsole function are written to the active screen buffer as they are read.
+ * This mode can be used only if the ENABLE_LINE_INPUT mode is also enabled.
+ */
+ ENABLE_ECHO_INPUT(4),
- /**
- * 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.
- */
- ENABLE_PROCESSED_INPUT(1),
+ /**
+ * 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.
+ */
+ ENABLE_PROCESSED_INPUT(1),
- /**
- * User interactions that change the size of the console screen buffer are
- * reported in the console's input buffee. Information about these events
- * can be read from the input buffer by applications using
- * theReadConsoleInput function, but not by those using ReadFile
- * orReadConsole.
- */
- ENABLE_WINDOW_INPUT(8),
+ /**
+ * User interactions that change the size of the console screen buffer are reported in the console's input buffee.
+ * Information about these events can be read from the input buffer by applications using theReadConsoleInput
+ * function, but not by those using ReadFile orReadConsole.
+ */
+ ENABLE_WINDOW_INPUT(8),
- /**
- * If the mouse pointer is within the borders of the console window and the
- * window has the keyboard focus, mouse events generated by mouse movement
- * and button presses are placed in the input buffer. These events are
- * discarded by ReadFile or ReadConsole, even when this mode is enabled.
- */
- ENABLE_MOUSE_INPUT(16),
- ;
+ /**
+ * If the mouse pointer is within the borders of the console window and the window has the keyboard focus, mouse
+ * events generated by mouse movement and button presses are placed in the input buffer. These events are discarded by
+ * ReadFile or ReadConsole, even when this mode is enabled.
+ */
+ ENABLE_MOUSE_INPUT(16),;
- public final int code;
+ public final int code;
- ConsoleMode(final int code) {
- this.code = code;
- }
-}
\ No newline at end of file
+ ConsoleMode(final int code) {
+ this.code = code;
+ }
+}
diff --git a/src/dorkbox/util/input/windows/WindowsTerminal.java b/src/dorkbox/util/input/windows/WindowsTerminal.java
index 3c281f1..6e1a3ed 100644
--- a/src/dorkbox/util/input/windows/WindowsTerminal.java
+++ b/src/dorkbox/util/input/windows/WindowsTerminal.java
@@ -11,117 +11,114 @@
*/
package dorkbox.util.input.windows;
-import java.io.IOException;
-import java.io.PrintStream;
-
import org.fusesource.jansi.internal.Kernel32.INPUT_RECORD;
import org.fusesource.jansi.internal.Kernel32.KEY_EVENT_RECORD;
import org.fusesource.jansi.internal.WindowsSupport;
+import java.io.IOException;
+import java.io.PrintStream;
+
import dorkbox.util.input.Terminal;
/**
- * Terminal implementation for Microsoft Windows. Terminal initialization in
- * {@link #init} is accomplished by calling the Win32 APIs SetConsoleMode and
- * GetConsoleMode to
- * disable character echoing.
- *
+ * Terminal implementation for Microsoft Windows. Terminal initialization in {@link #init} is accomplished by calling
+ * the Win32 APIs SetConsoleMode
+ * and GetConsoleMode
+ * to disable character echoing.
+ *
* @since 2.0 (customized)
*/
public class WindowsTerminal extends Terminal {
- private volatile int originalMode;
- private final PrintStream out;
- public WindowsTerminal() {
- this.out = System.out;
+ private volatile int originalMode;
+ private final PrintStream out;
+
+ public WindowsTerminal() {
+ this.out = System.out;
+ }
+
+ @Override
+ public final void init() throws IOException {
+ this.originalMode = WindowsSupport.getConsoleMode();
+
+ // Must set these four modes at the same time to make it work fine.
+ WindowsSupport.setConsoleMode(this.originalMode |
+ ConsoleMode.ENABLE_LINE_INPUT.code |
+ ConsoleMode.ENABLE_ECHO_INPUT.code |
+ ConsoleMode.ENABLE_PROCESSED_INPUT.code |
+ ConsoleMode.ENABLE_WINDOW_INPUT.code);
+ }
+
+ /**
+ * Restore the original terminal configuration, which can be used when shutting down the console reader. The
+ * ConsoleReader cannot be used after calling this method.
+ */
+ @Override
+ public final void restore() throws IOException {
+ // restore the old console mode
+ WindowsSupport.setConsoleMode(this.originalMode);
+ }
+
+ @Override
+ public final int getWidth() {
+ int w = WindowsSupport.getWindowsTerminalWidth();
+ return w < 1 ? DEFAULT_WIDTH : w;
+ }
+
+ @Override
+ public final int getHeight() {
+ int h = WindowsSupport.getWindowsTerminalHeight();
+ return h < 1 ? DEFAULT_HEIGHT : h;
+ }
+
+ @Override
+ public final int read() {
+ int input = readInput();
+
+ if (isEchoEnabled()) {
+ char asChar = (char) input;
+ if (asChar == '\n') {
+ this.out.println();
+ } else {
+ this.out.print(asChar);
+ }
+ // have to flush, otherwise we'll never see the chars on screen
+ this.out.flush();
}
- @Override
- public final void init() throws IOException {
- this.originalMode = WindowsSupport.getConsoleMode();
+ return input;
+ }
- // Must set these four modes at the same time to make it work fine.
- WindowsSupport.setConsoleMode(this.originalMode |
- ConsoleMode.ENABLE_LINE_INPUT.code |
- ConsoleMode.ENABLE_ECHO_INPUT.code |
- ConsoleMode.ENABLE_PROCESSED_INPUT.code |
- ConsoleMode.ENABLE_WINDOW_INPUT.code);
- }
+ private int readInput() {
+ // this HOOKS the input event, and prevents it from going to the console "proper"
+ try {
+ INPUT_RECORD[] events;
+ while (true) {
+ // we ALWAYS read until we have an event we care about!
+ events = WindowsSupport.readConsoleInput(1);
- /**
- * Restore the original terminal configuration, which can be used when
- * shutting down the console reader. The ConsoleReader cannot be
- * used after calling this method.
- */
- @Override
- public final void restore() throws IOException {
- // restore the old console mode
- WindowsSupport.setConsoleMode(this.originalMode);
- }
-
- @Override
- public final int getWidth() {
- int w = WindowsSupport.getWindowsTerminalWidth();
- return w < 1 ? DEFAULT_WIDTH : w;
- }
-
- @Override
- public final int getHeight() {
- int h = WindowsSupport.getWindowsTerminalHeight();
- return h < 1 ? DEFAULT_HEIGHT : h;
- }
-
- @Override
- public final int read() {
- int input = readInput();
-
- if (isEchoEnabled()) {
- char asChar = (char) input;
- if (asChar == '\n') {
- this.out.println();
- } else {
- this.out.print(asChar);
- }
- // have to flush, otherwise we'll never see the chars on screen
- this.out.flush();
- }
-
- return input;
- }
-
- private final int readInput() {
- // this HOOKS the input event, and prevents it from going to the console "proper"
- try {
- INPUT_RECORD[] events = null;
- while (true) {
- // we ALWAYS read until we have an event we care about!
- events = WindowsSupport.readConsoleInput(1);
-
- if (events != null) {
- for (int i = 0; i < events.length; i++ ) {
- KEY_EVENT_RECORD keyEvent = events[i].keyEvent;
- //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar);
- if (keyEvent.keyDown) {
- if (keyEvent.uchar > 0) {
- char uchar = keyEvent.uchar;
- if (uchar == '\r') {
- // we purposefully swallow input after \r, and substitute it with \n
- return '\n';
- }
-
- return uchar;
- }
- }
- }
+ if (events != null) {
+ for (int i = 0; i < events.length; i++) {
+ KEY_EVENT_RECORD keyEvent = events[i].keyEvent;
+ //Log.trace(keyEvent.keyDown? "KEY_DOWN" : "KEY_UP", "key code:", keyEvent.keyCode, "char:", (long)keyEvent.uchar);
+ if (keyEvent.keyDown) {
+ if (keyEvent.uchar > 0) {
+ char uchar = keyEvent.uchar;
+ if (uchar == '\r') {
+ // we purposefully swallow input after \r, and substitute it with \n
+ return '\n';
}
- }
- } catch (IOException e) {
- this.logger.error("Windows console input error: ", e);
- }
- return -1;
+ return uchar;
+ }
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ this.logger.error("Windows console input error: ", e);
}
+
+ return -1;
+ }
}