Code polish, added comments/javadocs. Added copyright/authors
This commit is contained in:
parent
6f367aec7e
commit
a46cbefb02
38
Console.iml
Normal file
38
Console.iml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="EclipseModuleManager">
|
||||
<libelement value="jar://$MODULE_DIR$/libs/jansi/jansi-1.11b.jar!/" />
|
||||
<libelement value="jar://$MODULE_DIR$/libs/jna/jna-4.1.0.jar!/" />
|
||||
<libelement value="jar://$MODULE_DIR$/libs/logging/slf4j-api-1.7.5.jar!/" />
|
||||
<libelement value="jar://$MODULE_DIR$/libs/slf4j-api-1.7.5.jar!/" />
|
||||
<libelement value="jar://$MODULE_DIR$/libs/ObjectPool_v1.1.jar!/" />
|
||||
<libelement value="jar://$MODULE_DIR$/libs/dorkboxUtil_v1.1.jar!/" />
|
||||
<src_description expected_position="0">
|
||||
<src_folder value="file://$MODULE_DIR$/src" expected_position="0" />
|
||||
</src_description>
|
||||
</component>
|
||||
<component name="FindBugs-IDEA">
|
||||
<detectors>
|
||||
<detector name="InefficientIndexOf" enabled="true" />
|
||||
<detector name="InefficientInitializationInsideLoop" enabled="true" />
|
||||
<detector name="InefficientToArray" enabled="true" />
|
||||
</detectors>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/dist" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/libs" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="jdk" jdkName="1.6" jdkType="JavaSDK" />
|
||||
<orderEntry type="library" name="logging slf4j-api" level="application" />
|
||||
<orderEntry type="module" module-name="Dorkbox-Util" />
|
||||
<orderEntry type="module" module-name="JavaLauncher-Util" />
|
||||
<orderEntry type="module" module-name="ObjectPool" />
|
||||
<orderEntry type="library" name="jna" level="application" />
|
||||
<orderEntry type="library" name="junit-4.12" level="application" />
|
||||
</component>
|
||||
</module>
|
@ -1,3 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: com.dorkbox.console.AnsiRendererTestExample
|
||||
Main-Class: com.dorkbox.console.AnsiConsoleExample
|
||||
|
||||
|
@ -1,36 +1,99 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import dorkbox.console.output.AnsiOutputStream;
|
||||
import dorkbox.console.output.WindowsAnsiOutputStream;
|
||||
import dorkbox.console.util.posix.CLibraryPosix;
|
||||
import dorkbox.console.util.windows.Kernel32;
|
||||
import dorkbox.util.Property;
|
||||
|
||||
/**
|
||||
* Provides a fluent API for generating ANSI escape sequences and providing access to streams that support it.
|
||||
* <p>
|
||||
* See: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
*
|
||||
* @author dorkbox, llc
|
||||
*/
|
||||
public
|
||||
class Console {
|
||||
|
||||
|
||||
/**
|
||||
* If true, allows an ANSI output stream to be created, otherwise a NO-OP stream is created instead
|
||||
*/
|
||||
@Property
|
||||
public static boolean ENABLE_ANSI = true;
|
||||
|
||||
/**
|
||||
* If true, then we always force the raw ANSI output stream to be enabled (even if the output stream is not aware of ANSI commands).
|
||||
* This can be used to obtain the raw ANSI escape codes for other color aware programs (ie: less -r)
|
||||
*/
|
||||
@Property
|
||||
public static boolean FORCE_ENABLE_ANSI = false;
|
||||
|
||||
/**
|
||||
* Enables or disables character echo to stdout in the console, should call {@link #setEchoEnabled(boolean)} after initialization
|
||||
*/
|
||||
@Property
|
||||
public static volatile boolean ENABLE_ECHO = true;
|
||||
|
||||
public static boolean PASSWORD_ECHO = true;
|
||||
/**
|
||||
* Enables or disables CTRL-C behavior in the console, should call {@link #setInterruptEnabled(boolean)} after initialization
|
||||
*/
|
||||
@Property
|
||||
public static volatile boolean ENABLE_INTERRUPT = false;
|
||||
|
||||
@Property
|
||||
public static char PASSWORD_ECHO_CHAR = '*';
|
||||
|
||||
// how many threads can read from this input at the same time
|
||||
public static int NUMBER_OF_READERS = 32;
|
||||
|
||||
|
||||
/**
|
||||
* Enables the backspace key to delete characters in the line buffer and (if ANSI is enabled) from the screen.
|
||||
*/
|
||||
@Property
|
||||
public final static boolean ENABLE_BACKSPACE = true;
|
||||
// enableBackspace = Boolean.parseBoolean(System.getProperty(Console.ENABLE_BACKSPACE, "true"));
|
||||
|
||||
|
||||
public static String ENABLE_BACKSPACEs = "input.enableBackspace";
|
||||
|
||||
|
||||
// OS types supported by the input console. Default is AUTO
|
||||
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"; // this is the same as unsupported
|
||||
public static final String OFF = "off"; // this is the same as unsupported
|
||||
public static final String FALSE = "false"; // this is the same as unsupported
|
||||
public static final String INPUT_CONSOLE_OSTYPE = "AUTO";
|
||||
|
||||
// valid are what's above
|
||||
@Property
|
||||
public static final String INPUT_CONSOLE_TYPE = "AUTO";
|
||||
|
||||
|
||||
|
||||
private static final PrintStream original_out = System.out;
|
||||
private static final PrintStream original_err = System.err;
|
||||
|
||||
// protected by synchronize
|
||||
private static int installed = 0;
|
||||
private static PrintStream out;
|
||||
private static PrintStream err;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@ -41,7 +104,217 @@ class Console {
|
||||
return "2.9";
|
||||
}
|
||||
|
||||
public static Input in = new Input();
|
||||
public static String out;
|
||||
public static String err;
|
||||
/**
|
||||
* Reads single character input from the console.
|
||||
*
|
||||
* @return -1 if no data or problems
|
||||
*/
|
||||
public static
|
||||
int read() {
|
||||
return Input.read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line of characters from the console, defined as everything before the 'ENTER' key is pressed
|
||||
*
|
||||
* @return null if no data
|
||||
*/
|
||||
public static
|
||||
String readLine() {
|
||||
return Input.readLine();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a line of characters from the console as a character array, defined as everything before the 'ENTER' key is pressed
|
||||
*
|
||||
* @return empty char[] if no data
|
||||
*/
|
||||
public static
|
||||
char[] readLineChars() {
|
||||
return Input.readLineChars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line of characters from the console as a character array, defined as everything before the 'ENTER' key is pressed
|
||||
*
|
||||
* @return empty char[] if no data
|
||||
*/
|
||||
public static
|
||||
char[] readPassword() {
|
||||
return Input.readLinePassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables CTRL-C behavior in the console
|
||||
*/
|
||||
public static
|
||||
void setInterruptEnabled(final boolean enabled) {
|
||||
Console.ENABLE_INTERRUPT = enabled;
|
||||
Input.setInterruptEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables character echo to stdout
|
||||
*/
|
||||
public static
|
||||
void setEchoEnabled(final boolean enabled) {
|
||||
Console.ENABLE_ECHO = enabled;
|
||||
Input.setEchoEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override System.err and System.out with an ANSI capable {@link java.io.PrintStream}.
|
||||
*/
|
||||
public static synchronized
|
||||
void systemInstall() {
|
||||
installed++;
|
||||
if (installed == 1) {
|
||||
out = out();
|
||||
err = err();
|
||||
|
||||
System.setOut(out);
|
||||
System.setErr(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* un-does a previous {@link #systemInstall()}.
|
||||
* <p>
|
||||
* If {@link #systemInstall()} was called multiple times, then {@link #systemUninstall()} must be called the same number of
|
||||
* times before it is uninstalled.
|
||||
*/
|
||||
public static synchronized
|
||||
void systemUninstall() {
|
||||
installed--;
|
||||
if (installed == 0) {
|
||||
if (out != null && out != original_out) {
|
||||
out.close();
|
||||
System.setOut(original_out);
|
||||
}
|
||||
|
||||
if (err != null && err != original_err) {
|
||||
err.close();
|
||||
System.setErr(original_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are installed to the system (IE: System.err/out, then reset those streams, otherwise there is nothing to do from a static
|
||||
* perspective (since creating a NEW ANSI stream will automatically reset the output
|
||||
*/
|
||||
public static synchronized
|
||||
void reset() {
|
||||
if (installed >= 1) {
|
||||
// TODO: make this reset readLine, etc as well?
|
||||
try {
|
||||
System.out.write(AnsiOutputStream.RESET_CODE);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the standard out natively supports ANSI escape codes, then this just returns System.out, otherwise it will provide an ANSI
|
||||
* aware PrintStream which strips out the ANSI escape sequences or which implement the escape sequences.
|
||||
*
|
||||
* @return a PrintStream which is ANSI aware.
|
||||
*/
|
||||
public static
|
||||
PrintStream out() {
|
||||
if (out == null) {
|
||||
out = createPrintStream(original_out, 1); // STDOUT_FILENO
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the standard out natively supports ANSI escape codes, then this just returns System.err, otherwise it will provide an ANSI aware
|
||||
* PrintStream which strips out the ANSI escape sequences or which implement the escape sequences.
|
||||
*
|
||||
* @return a PrintStream which is ANSI aware.
|
||||
*/
|
||||
public static
|
||||
PrintStream err() {
|
||||
if (err == null) {
|
||||
err = createPrintStream(original_err, 2); // STDERR_FILENO
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static boolean isXterm() {
|
||||
String term = System.getenv("TERM");
|
||||
return "xterm".equalsIgnoreCase(term);
|
||||
}
|
||||
|
||||
private static
|
||||
PrintStream createPrintStream(final OutputStream stream, int fileno) {
|
||||
if (!ENABLE_ANSI) {
|
||||
// Use the ANSIOutputStream to strip out the ANSI escape sequences.
|
||||
return new PrintStream(new AnsiOutputStream(stream));
|
||||
}
|
||||
|
||||
if (!isXterm()) {
|
||||
String os = System.getProperty("os.name");
|
||||
if (os.startsWith("Windows")) {
|
||||
// check if windows10+ (which natively supports ANSI)
|
||||
if (Kernel32.isWindows10OrGreater()) {
|
||||
// Just wrap it up so that when we get closed, we reset the attributes.
|
||||
return deafultPrintStream(stream);
|
||||
}
|
||||
|
||||
// On windows we know the console does not interpret ANSI codes..
|
||||
try {
|
||||
return new PrintStream(new WindowsAnsiOutputStream(stream, fileno));
|
||||
} catch (Throwable ignore) {
|
||||
// this happens when JNA is not in the path.. or
|
||||
// this happens when the stdout is being redirected to a file.
|
||||
// this happens when the stdout is being redirected to different console.
|
||||
}
|
||||
|
||||
// Use the ANSIOutputStream to strip out the ANSI escape sequences.
|
||||
if (!FORCE_ENABLE_ANSI) {
|
||||
return new PrintStream(new AnsiOutputStream(stream));
|
||||
}
|
||||
} else {
|
||||
// We must be on some unix variant..
|
||||
try {
|
||||
// If we can detect that stdout is not a tty.. then setup to strip the ANSI sequences..
|
||||
if (!FORCE_ENABLE_ANSI && CLibraryPosix.isatty(fileno) == 0) {
|
||||
return new PrintStream(new AnsiOutputStream(stream));
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
// These errors happen if the JNI lib is not available for your platform.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// By default we assume the terminal can handle ANSI codes.
|
||||
// Just wrap it up so that when we get closed, we reset the attributes.
|
||||
return deafultPrintStream(stream);
|
||||
}
|
||||
|
||||
private static
|
||||
PrintStream deafultPrintStream(final OutputStream stream) {
|
||||
return new PrintStream(new FilterOutputStream(stream) {
|
||||
@Override
|
||||
public
|
||||
void close() throws IOException {
|
||||
write(AnsiOutputStream.RESET_CODE);
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,472 @@
|
||||
/*
|
||||
* Copyright 2010 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.console.input.PosixTerminal;
|
||||
import dorkbox.console.input.Terminal;
|
||||
import dorkbox.console.input.UnsupportedTerminal;
|
||||
import dorkbox.console.input.WindowsTerminal;
|
||||
import dorkbox.console.util.CharHolder;
|
||||
import dorkbox.objectPool.ObjectPool;
|
||||
import dorkbox.objectPool.PoolableObject;
|
||||
import dorkbox.util.OS;
|
||||
import dorkbox.util.bytes.ByteBuffer2;
|
||||
import dorkbox.util.bytes.ByteBuffer2Poolable;
|
||||
|
||||
class Input {
|
||||
public
|
||||
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(Input.class);
|
||||
private static final char[] emptyLine = new char[0];
|
||||
|
||||
|
||||
private static final List<CharHolder> charInputBuffers = new ArrayList<CharHolder>();
|
||||
private final static ObjectPool<CharHolder> charInputPool = ObjectPool.NonBlocking(new PoolableObject<CharHolder>() {
|
||||
@Override
|
||||
public
|
||||
CharHolder create() {
|
||||
return new CharHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void onReturn(final CharHolder object) {
|
||||
// dump the chars in the buffer (safer for passwords, etc)
|
||||
object.character = (char) 0;
|
||||
charInputBuffers.remove(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void onTake(final CharHolder object) {
|
||||
charInputBuffers.add(object);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
private static final List<ByteBuffer2> lineInputBuffers = new ArrayList<ByteBuffer2>();
|
||||
private final static ObjectPool<ByteBuffer2> lineInputPool = ObjectPool.NonBlocking(new ByteBuffer2Poolable() {
|
||||
@Override
|
||||
public
|
||||
void onReturn(final ByteBuffer2 object) {
|
||||
// dump the chars in the buffer (safer for passwords, etc)
|
||||
object.clearSecure();
|
||||
lineInputBuffers.remove(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void onTake(final ByteBuffer2 object) {
|
||||
lineInputBuffers.add(object);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
private final static Terminal terminal;
|
||||
|
||||
private static final Object inputLock = new Object();
|
||||
private static final Object inputLockSingle = new Object();
|
||||
private static final Object inputLockLine = new Object();
|
||||
|
||||
static {
|
||||
String type = Console.INPUT_CONSOLE_TYPE.toUpperCase(Locale.ENGLISH);
|
||||
|
||||
Throwable didFallbackE = null;
|
||||
Terminal term;
|
||||
try {
|
||||
if (type.equals(Console.UNIX)) {
|
||||
term = new PosixTerminal();
|
||||
}
|
||||
else if (type.equals(Console.WINDOWS)) {
|
||||
term = new WindowsTerminal();
|
||||
}
|
||||
else if (type.equals(Console.NONE)) {
|
||||
term = new UnsupportedTerminal();
|
||||
}
|
||||
else {
|
||||
// if these cannot be created, because we are in an IDE, an error will be thrown
|
||||
if (OS.isWindows()) {
|
||||
term = new WindowsTerminal();
|
||||
}
|
||||
else {
|
||||
term = new PosixTerminal();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
didFallbackE = e;
|
||||
term = new UnsupportedTerminal();
|
||||
}
|
||||
|
||||
terminal = term;
|
||||
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Created Terminal: {} ({}w x {}h)", Input.terminal.getClass().getSimpleName(),
|
||||
Input.terminal.getWidth(), Input.terminal.getHeight());
|
||||
}
|
||||
|
||||
if (didFallbackE != null && !didFallbackE.getMessage().equals(Terminal.CONSOLE_ERROR_INIT)) {
|
||||
logger.error("Failed to construct terminal, falling back to unsupported.", didFallbackE);
|
||||
} else if (term instanceof UnsupportedTerminal) {
|
||||
logger.debug("Terminal is in UNSUPPORTED (best guess). Unable to support single key input. Only line input available.");
|
||||
}
|
||||
|
||||
// echo and backspace
|
||||
term.setEchoEnabled(Console.ENABLE_ECHO);
|
||||
term.setInterruptEnabled(Console.ENABLE_INTERRUPT);
|
||||
|
||||
Thread consoleThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
Input.run();
|
||||
}
|
||||
});
|
||||
consoleThread.setDaemon(true);
|
||||
consoleThread.setName("Console Input Reader");
|
||||
|
||||
consoleThread.start();
|
||||
|
||||
// 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() {
|
||||
// called when the JVM is shutting down.
|
||||
release0();
|
||||
|
||||
try {
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
shutdownThread.setName("Console Input Shutdown");
|
||||
Runtime.getRuntime().addShutdownHook(shutdownThread);
|
||||
}
|
||||
|
||||
static
|
||||
void setInterruptEnabled(final boolean enabled) {
|
||||
terminal.setInterruptEnabled(enabled);
|
||||
}
|
||||
|
||||
static
|
||||
void setEchoEnabled(final boolean enabled) {
|
||||
terminal.setEchoEnabled(enabled);
|
||||
}
|
||||
|
||||
private static InputStream wrappedInputStream = new InputStream() {
|
||||
@Override
|
||||
public
|
||||
int read() throws IOException {
|
||||
return Input.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void close() throws IOException {
|
||||
Input.release0();
|
||||
}
|
||||
};
|
||||
|
||||
static
|
||||
InputStream getInputStream() {
|
||||
return wrappedInputStream;
|
||||
}
|
||||
|
||||
private
|
||||
Input() {
|
||||
}
|
||||
|
||||
public int read() {
|
||||
return InputConsole.read();
|
||||
/**
|
||||
* Reads single character input from the console.
|
||||
*
|
||||
* @return -1 if no data or problems
|
||||
*/
|
||||
static
|
||||
int read() {
|
||||
CharHolder holder;
|
||||
|
||||
synchronized (inputLock) {
|
||||
// don't want to register a read() WHILE we are still processing the current input.
|
||||
// also adds it to the global list of char inputs
|
||||
holder = charInputPool.take();
|
||||
}
|
||||
|
||||
synchronized (inputLockSingle) {
|
||||
try {
|
||||
inputLockSingle.wait();
|
||||
} catch (InterruptedException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
char c = holder.character;
|
||||
|
||||
// also clears and removes from the global list of char inputs
|
||||
charInputPool.put(holder);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return empty char[] if no data or problems
|
||||
*/
|
||||
static
|
||||
char[] readLineChars() {
|
||||
ByteBuffer2 buffer;
|
||||
|
||||
synchronized (inputLock) {
|
||||
// don't want to register a readLine() WHILE we are still processing the current line info.
|
||||
// also adds it to the global list of line inputs
|
||||
buffer = lineInputPool.take();
|
||||
}
|
||||
|
||||
synchronized (inputLockLine) {
|
||||
try {
|
||||
inputLockLine.wait();
|
||||
} catch (InterruptedException e) {
|
||||
return emptyLine;
|
||||
}
|
||||
}
|
||||
|
||||
int len = buffer.position();
|
||||
if (len == 0) {
|
||||
return emptyLine;
|
||||
}
|
||||
|
||||
buffer.rewind();
|
||||
char[] readChars = buffer.readChars(len / 2); // java always stores chars in 2 bytes
|
||||
|
||||
// also clears and removes from the global list of line inputs
|
||||
lineInputPool.put(buffer);
|
||||
|
||||
return readChars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads line input from the console
|
||||
*
|
||||
* @return empty char[] if no data
|
||||
*/
|
||||
static
|
||||
char[] readLinePassword() {
|
||||
// don't bother in an IDE. it won't work.
|
||||
boolean echoEnabled = Console.ENABLE_ECHO;
|
||||
Console.ENABLE_ECHO = false;
|
||||
char[] readLine0 = readLineChars();
|
||||
Console.ENABLE_ECHO = echoEnabled;
|
||||
|
||||
return readLine0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single line of characters, defined as everything before the 'ENTER' key is pressed
|
||||
* @return null if no data
|
||||
*/
|
||||
static
|
||||
String readLine() {
|
||||
char[] line = Input.readLineChars();
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(line);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* releases any thread still waiting.
|
||||
*/
|
||||
private static
|
||||
void release0() {
|
||||
synchronized (inputLockSingle) {
|
||||
inputLockSingle.notifyAll();
|
||||
}
|
||||
|
||||
synchronized (inputLockLine) {
|
||||
inputLockLine.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private static
|
||||
void run() {
|
||||
Logger logger2 = logger;
|
||||
|
||||
final boolean ansiEnabled = Console.ENABLE_ANSI;
|
||||
// Ansi ansi = Ansi.ansi();
|
||||
// PrintStream out = AnsiConsole.out;
|
||||
PrintStream out = System.out;
|
||||
|
||||
int typedChar;
|
||||
char asChar;
|
||||
final char overWriteChar = ' ';
|
||||
boolean enableBackspace = Console.ENABLE_BACKSPACE;
|
||||
|
||||
|
||||
// don't type ; in a bash shell, it quits everything
|
||||
// \n is replaced by \r in unix terminal?
|
||||
while ((typedChar = terminal.read()) != -1) {
|
||||
synchronized (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 (inputLockSingle) {
|
||||
// have to do readChar first (readLine has to deal with \b and \n
|
||||
for (CharHolder holder : charInputBuffers) {
|
||||
holder.character = asChar; // copy by value
|
||||
}
|
||||
|
||||
inputLockSingle.notifyAll();
|
||||
}
|
||||
|
||||
// now to handle readLine stuff
|
||||
|
||||
// if we type a backspace key, swallow it + previous in READLINE. READCHAR will have it passed anyways.
|
||||
if (enableBackspace && asChar == '\b') {
|
||||
int position = 0;
|
||||
char[] overwrite = null;
|
||||
|
||||
// clear ourself + one extra.
|
||||
for (ByteBuffer2 buffer : lineInputBuffers) {
|
||||
// size of the buffer BEFORE our backspace was typed
|
||||
int length = buffer.position();
|
||||
int amtToOverwrite = 4; // 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++;
|
||||
}
|
||||
|
||||
overwrite = new char[amtToOverwrite];
|
||||
for (int i = 0; i < amtToOverwrite; i++) {
|
||||
overwrite[i] = overWriteChar;
|
||||
}
|
||||
}
|
||||
|
||||
if (ansiEnabled && overwrite != null) {
|
||||
// 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, which we changed)
|
||||
synchronized (inputLockLine) {
|
||||
inputLockLine.notifyAll();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 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 (ByteBuffer2 buffer : lineInputBuffers) {
|
||||
buffer.writeChar(asChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int PLUS_TWO_MAYBE = 128 + 32;
|
||||
private static final int PLUS_ONE = 128 + 127;
|
||||
|
||||
/**
|
||||
* Return the number of characters that will be printed when the specified character is echoed to the screen
|
||||
* <p/>
|
||||
* 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) {
|
||||
// sbuff.append((char) ch);
|
||||
return 1;
|
||||
}
|
||||
else if (ch == 127) {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append('?');
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
// sbuff.append('M');
|
||||
// sbuff.append('-');
|
||||
int count = 2;
|
||||
|
||||
if (ch >= PLUS_TWO_MAYBE) {
|
||||
if (ch < PLUS_ONE) {
|
||||
// sbuff.append((char) (ch - 128));
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append('?');
|
||||
count += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append((char) (ch - 128 + 64));
|
||||
count += 2;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append((char) (ch + 64));
|
||||
return 2;
|
||||
}
|
||||
|
||||
// return sbuff;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,478 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.console.input.PosixTerminal;
|
||||
import dorkbox.console.input.Terminal;
|
||||
import dorkbox.console.input.UnsupportedTerminal;
|
||||
import dorkbox.console.input.WindowsTerminal;
|
||||
import dorkbox.console.output.Ansi;
|
||||
import dorkbox.objectPool.ObjectPool;
|
||||
import dorkbox.util.FastThreadLocal;
|
||||
import dorkbox.util.OS;
|
||||
import dorkbox.util.bytes.ByteBuffer2;
|
||||
import dorkbox.util.bytes.ByteBuffer2Poolable;
|
||||
|
||||
public
|
||||
class InputConsole {
|
||||
|
||||
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(InputConsole.class);
|
||||
private static final char[] emptyLine = new char[0];
|
||||
|
||||
|
||||
private final static ObjectPool<ByteBuffer2> pool;
|
||||
private final static Terminal terminal;
|
||||
|
||||
private static final Object inputLock = new Object();
|
||||
private static final Object inputLockSingle = new Object();
|
||||
private static final Object inputLockLine = new Object();
|
||||
|
||||
private static final FastThreadLocal<ByteBuffer2> readBuff = new FastThreadLocal<ByteBuffer2>();
|
||||
private static final List<ByteBuffer2> readBuffers = new CopyOnWriteArrayList<ByteBuffer2>();
|
||||
private static final FastThreadLocal<Integer> threadBufferCounter = new FastThreadLocal<Integer>();
|
||||
|
||||
private static final FastThreadLocal<ByteBuffer2> readLineBuff = new FastThreadLocal<ByteBuffer2>();
|
||||
private static final List<ByteBuffer2> readLineBuffers = new CopyOnWriteArrayList<ByteBuffer2>();
|
||||
|
||||
|
||||
|
||||
static {
|
||||
pool = ObjectPool.Blocking(new ByteBuffer2Poolable(), Console.NUMBER_OF_READERS);
|
||||
|
||||
String type = Console.INPUT_CONSOLE_OSTYPE.toUpperCase(Locale.ENGLISH);
|
||||
|
||||
Throwable didFallbackE = null;
|
||||
Class<? extends Terminal> t;
|
||||
try {
|
||||
if (type.equals(Console.UNIX)) {
|
||||
t = PosixTerminal.class;
|
||||
}
|
||||
else if (type.equals(Console.WIN) || type.equals(Console.WINDOWS)) {
|
||||
t = WindowsTerminal.class;
|
||||
}
|
||||
else if (type.equals(Console.NONE) || type.equals(Console.OFF) || type.equals(Console.FALSE)) {
|
||||
t = UnsupportedTerminal.class;
|
||||
}
|
||||
else {
|
||||
// if these cannot be created, because we are in an IDE, an error will be thrown
|
||||
if (OS.isWindows()) {
|
||||
t = WindowsTerminal.class;
|
||||
}
|
||||
else {
|
||||
t = PosixTerminal.class;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
didFallbackE = e;
|
||||
t = UnsupportedTerminal.class;
|
||||
}
|
||||
|
||||
Terminal term = null;
|
||||
try {
|
||||
term = t.newInstance();
|
||||
} catch (Throwable e) {
|
||||
didFallbackE = e;
|
||||
t = UnsupportedTerminal.class;
|
||||
|
||||
try {
|
||||
term = t.newInstance();
|
||||
} catch (Exception e1) {
|
||||
// UnsupportedTerminal can't do this
|
||||
}
|
||||
}
|
||||
terminal = term;
|
||||
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating terminal based on type: " + type);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Created Terminal: {} ({}w x {}h)", InputConsole.terminal.getClass().getSimpleName(),
|
||||
InputConsole.terminal.getWidth(), InputConsole.terminal.getHeight());
|
||||
}
|
||||
|
||||
if (didFallbackE != null && !didFallbackE.getMessage().equals(Terminal.CONSOLE_ERROR_INIT)) {
|
||||
logger.error("Failed to construct terminal, falling back to unsupported.", didFallbackE);
|
||||
} else if (term instanceof UnsupportedTerminal) {
|
||||
logger.debug("Terminal is in UNSUPPORTED (best guess). Unable to support single key input. Only line input available.");
|
||||
}
|
||||
|
||||
// echo and backspace
|
||||
term.setEchoEnabled(Console.ENABLE_ECHO);
|
||||
term.setInterruptEnabled(Console.ENABLE_BACKSPACE);
|
||||
|
||||
Thread consoleThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
InputConsole.run();
|
||||
}
|
||||
});
|
||||
consoleThread.setDaemon(true);
|
||||
consoleThread.setName("Console Input Reader");
|
||||
|
||||
consoleThread.start();
|
||||
|
||||
// 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() {
|
||||
// called when the JVM is shutting down.
|
||||
release0();
|
||||
|
||||
try {
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
shutdownThread.setName("Console Input Shutdown");
|
||||
Runtime.getRuntime().addShutdownHook(shutdownThread);
|
||||
}
|
||||
|
||||
private static InputStream wrappedInputStream = new InputStream() {
|
||||
@Override
|
||||
public
|
||||
int read() throws IOException {
|
||||
return InputConsole.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void close() throws IOException {
|
||||
InputConsole.release0();
|
||||
}
|
||||
};
|
||||
|
||||
public static
|
||||
InputStream getInputStream() {
|
||||
return wrappedInputStream;
|
||||
}
|
||||
|
||||
private
|
||||
InputConsole() {
|
||||
}
|
||||
|
||||
/**
|
||||
* return -1 if no data or bunged-up
|
||||
*/
|
||||
public static
|
||||
int read() {
|
||||
Integer bufferCounter = threadBufferCounter.get();
|
||||
ByteBuffer2 buffer = readBuff.get();
|
||||
|
||||
if (buffer == null) {
|
||||
bufferCounter = 0;
|
||||
threadBufferCounter.set(bufferCounter);
|
||||
|
||||
try {
|
||||
buffer = pool.takeInterruptibly();
|
||||
buffer.clear();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Interrupted while receiving buffer from pool.");
|
||||
buffer = pool.newInstance();
|
||||
}
|
||||
|
||||
readBuff.set(buffer);
|
||||
readBuffers.add(buffer);
|
||||
}
|
||||
|
||||
if (bufferCounter == buffer.position()) {
|
||||
synchronized (inputLockSingle) {
|
||||
buffer.setPosition(0);
|
||||
threadBufferCounter.set(0);
|
||||
|
||||
try {
|
||||
inputLockSingle.wait();
|
||||
} catch (InterruptedException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bufferCounter = threadBufferCounter.get();
|
||||
char c = buffer.readChar(bufferCounter);
|
||||
bufferCounter += 2;
|
||||
|
||||
threadBufferCounter.set(bufferCounter);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* return empty char[] if no data
|
||||
*/
|
||||
public static
|
||||
char[] readLinePassword() {
|
||||
// don't bother in an IDE. it won't work.
|
||||
boolean echoEnabled = Console.ENABLE_ECHO;
|
||||
Console.ENABLE_ECHO = false;
|
||||
char[] readLine0 = readLineChars();
|
||||
Console.ENABLE_ECHO = echoEnabled;
|
||||
|
||||
return readLine0;
|
||||
}
|
||||
|
||||
/**
|
||||
* return null if no data
|
||||
*/
|
||||
public static
|
||||
String readLine() {
|
||||
char[] line = InputConsole.readLineChars();
|
||||
if (line == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(line);
|
||||
}
|
||||
|
||||
/**
|
||||
* return empty char[] if no data
|
||||
*/
|
||||
public static
|
||||
char[] readLineChars() {
|
||||
synchronized (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 (readLineBuff.get() == null) {
|
||||
ByteBuffer2 buffer;
|
||||
try {
|
||||
buffer = pool.takeInterruptibly();
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Interrupted while receiving buffer from pool.");
|
||||
buffer = pool.newInstance();
|
||||
}
|
||||
|
||||
readLineBuff.set(buffer);
|
||||
readLineBuffers.add(buffer);
|
||||
}
|
||||
else {
|
||||
readLineBuff.get().clear();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (inputLockLine) {
|
||||
try {
|
||||
inputLockLine.wait();
|
||||
} catch (InterruptedException e) {
|
||||
return emptyLine;
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuffer2 buffer = readLineBuff.get();
|
||||
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();
|
||||
|
||||
readLineBuffers.remove(buffer);
|
||||
pool.put(buffer);
|
||||
readLineBuff.set(null);
|
||||
|
||||
return readChars;
|
||||
}
|
||||
|
||||
/**
|
||||
* releases any thread still waiting.
|
||||
*/
|
||||
private static
|
||||
void release0() {
|
||||
synchronized (inputLockSingle) {
|
||||
inputLockSingle.notifyAll();
|
||||
}
|
||||
|
||||
synchronized (inputLockLine) {
|
||||
inputLockLine.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private static
|
||||
void run() {
|
||||
Logger logger2 = logger;
|
||||
|
||||
final boolean ansiEnabled = Ansi.isEnabled();
|
||||
Ansi ansi = Ansi.ansi();
|
||||
// PrintStream out = AnsiConsole.out;
|
||||
PrintStream out = System.out;
|
||||
|
||||
int typedChar;
|
||||
char asChar;
|
||||
final char overWriteChar = ' ';
|
||||
boolean enableBackspace = Console.ENABLE_BACKSPACE;
|
||||
|
||||
|
||||
// don't type ; in a bash shell, it quits everything
|
||||
// \n is replaced by \r in unix terminal?
|
||||
while ((typedChar = terminal.read()) != -1) {
|
||||
synchronized (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 (inputLockSingle) {
|
||||
// have to do readChar first (readLine has to deal with \b and \n
|
||||
for (ByteBuffer2 buffer : readBuffers) {
|
||||
buffer.writeChar(asChar);
|
||||
}
|
||||
|
||||
inputLockSingle.notifyAll();
|
||||
}
|
||||
|
||||
// now to handle readLine stuff
|
||||
|
||||
// if we type a backspace key, swallow it + previous in READLINE. READCHAR will have it passed anyways.
|
||||
if (enableBackspace && asChar == '\b') {
|
||||
int position = 0;
|
||||
char[] overwrite = null;
|
||||
|
||||
// clear ourself + one extra.
|
||||
for (ByteBuffer2 buffer : readLineBuffers) {
|
||||
// size of the buffer BEFORE our backspace was typed
|
||||
int length = buffer.position();
|
||||
int amtToOverwrite = 4; // 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++;
|
||||
}
|
||||
|
||||
overwrite = new char[amtToOverwrite];
|
||||
for (int i = 0; i < amtToOverwrite; i++) {
|
||||
overwrite[i] = overWriteChar;
|
||||
}
|
||||
}
|
||||
|
||||
if (ansiEnabled && overwrite != null) {
|
||||
// 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 (inputLockLine) {
|
||||
inputLockLine.notifyAll();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 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 (ByteBuffer2 buffer : readLineBuffers) {
|
||||
buffer.writeChar(asChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int PLUS_TWO_MAYBE = 128 + 32;
|
||||
private static final int PLUS_ONE = 128 + 127;
|
||||
|
||||
/**
|
||||
* Return the number of characters that will be printed when the specified character is echoed to the screen
|
||||
* <p/>
|
||||
* 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) {
|
||||
// sbuff.append((char) ch);
|
||||
return 1;
|
||||
}
|
||||
else if (ch == 127) {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append('?');
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
// sbuff.append('M');
|
||||
// sbuff.append('-');
|
||||
int count = 2;
|
||||
|
||||
if (ch >= PLUS_TWO_MAYBE) {
|
||||
if (ch < PLUS_ONE) {
|
||||
// sbuff.append((char) (ch - 128));
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append('?');
|
||||
count += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append((char) (ch - 128 + 64));
|
||||
count += 2;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sbuff.append('^');
|
||||
// sbuff.append((char) (ch + 64));
|
||||
return 2;
|
||||
}
|
||||
|
||||
// return sbuff;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +0,0 @@
|
||||
package dorkbox.console.output;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class AnsiCode {
|
||||
Enum anEnum;
|
||||
String formalName;
|
||||
boolean isColorForBackground;
|
||||
|
||||
public
|
||||
AnsiCode(final Enum anEnum, final String formalName, final boolean isColorForBackground) {
|
||||
this.anEnum = anEnum;
|
||||
this.formalName = formalName;
|
||||
this.isColorForBackground = isColorForBackground;
|
||||
}
|
||||
|
||||
public
|
||||
boolean isColor() {
|
||||
return anEnum instanceof Color;
|
||||
}
|
||||
|
||||
public
|
||||
boolean isBackgroundColor() {
|
||||
return isColorForBackground;
|
||||
}
|
||||
|
||||
public
|
||||
Color getColor() {
|
||||
return (Color) anEnum;
|
||||
}
|
||||
|
||||
public
|
||||
boolean isAttribute() {
|
||||
return anEnum instanceof Attribute;
|
||||
}
|
||||
|
||||
public
|
||||
Attribute getAttribute() {
|
||||
return (Attribute) anEnum;
|
||||
}
|
||||
}
|
50
src/dorkbox/console/output/AnsiCodeMap.java
Normal file
50
src/dorkbox/console/output/AnsiCodeMap.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
package dorkbox.console.output;
|
||||
|
||||
/**
|
||||
* Used for determining what ANSI attribute to use based on a formal name
|
||||
*/
|
||||
class AnsiCodeMap {
|
||||
private final Enum anEnum;
|
||||
private final boolean isColorForBackground;
|
||||
|
||||
AnsiCodeMap(final Enum anEnum, final boolean isColorForBackground) {
|
||||
this.anEnum = anEnum;
|
||||
this.isColorForBackground = isColorForBackground;
|
||||
}
|
||||
|
||||
boolean isColor() {
|
||||
return anEnum instanceof Color;
|
||||
}
|
||||
|
||||
boolean isBackgroundColor() {
|
||||
return isColorForBackground;
|
||||
}
|
||||
|
||||
Color getColor() {
|
||||
return (Color) anEnum;
|
||||
}
|
||||
|
||||
boolean isAttribute() {
|
||||
return anEnum instanceof Attribute;
|
||||
}
|
||||
|
||||
Attribute getAttribute() {
|
||||
return (Attribute) anEnum;
|
||||
}
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||
* subsidiaries or affiliates. All rights reserved.
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a asValue of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dorkbox.console.output;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import dorkbox.console.util.posix.CLibraryPosix;
|
||||
import dorkbox.console.util.windows.Kernel32;
|
||||
|
||||
/**
|
||||
* Provides consistent access to an ANSI aware console PrintStream.
|
||||
*
|
||||
* See: https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
*
|
||||
* @author Dorkbox, LLC
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
@SuppressWarnings("SpellCheckingInspection")
|
||||
class AnsiConsole {
|
||||
|
||||
static final int STDOUT_FILENO = 1;
|
||||
static final int STDERR_FILENO = 2;
|
||||
|
||||
public static final PrintStream system_out = System.out;
|
||||
public static final PrintStream out = new PrintStream(wrapOutputStream(system_out, STDOUT_FILENO));
|
||||
|
||||
public static final PrintStream system_err = System.err;
|
||||
public static final PrintStream err = new PrintStream(wrapOutputStream(system_err, STDERR_FILENO));
|
||||
|
||||
private static boolean isXterm() {
|
||||
String term = System.getenv("TERM");
|
||||
return term != null && term.equals("xterm");
|
||||
}
|
||||
|
||||
private static
|
||||
OutputStream wrapOutputStream(final OutputStream stream, int fileno) {
|
||||
|
||||
// If the jansi.passthrough property is set, then don't interpret
|
||||
// any of the ansi sequences.
|
||||
if (Boolean.getBoolean("jansi.passthrough")) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
// If the jansi.strip property is set, then we just strip the
|
||||
// the ansi escapes.
|
||||
if (Boolean.getBoolean("jansi.strip")) {
|
||||
return new AnsiOutputStream(stream);
|
||||
}
|
||||
|
||||
String os = System.getProperty("os.name");
|
||||
if (os.startsWith("Windows") && !isXterm()) {
|
||||
|
||||
// check if windows10+ (which natively supports ANSI)
|
||||
if (Kernel32.isWindows10OrGreater()) {
|
||||
// Just wrap it up so that when we get closed, we reset the attributes.
|
||||
return new FilterOutputStream(stream) {
|
||||
@Override
|
||||
public
|
||||
void close() throws IOException {
|
||||
write(AnsiOutputStream.RESET_CODE);
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// On windows we know the console does not interpret ANSI codes..
|
||||
try {
|
||||
return new WindowsAnsiOutputStream(stream, fileno);
|
||||
} catch (Throwable ignore) {
|
||||
ignore.printStackTrace();
|
||||
// this happens when JNA is not in the path.. or
|
||||
// this happens when the stdout is being redirected to a file.
|
||||
// this happens when the stdout is being redirected to different console.
|
||||
}
|
||||
|
||||
// Use the ANSIOutputStream to strip out the ANSI escape sequences.
|
||||
return new AnsiOutputStream(stream);
|
||||
}
|
||||
|
||||
// We must be on some unix variant..
|
||||
try {
|
||||
// If the jansi.force property is set, then we force to output
|
||||
// the ansi escapes for piping it into ansi color aware commands (e.g. less -r)
|
||||
boolean forceColored = Boolean.getBoolean("jansi.force");
|
||||
|
||||
// If we can detect that stdout is not a tty.. then setup to strip the ANSI sequences..
|
||||
int rc = CLibraryPosix.isatty(fileno);
|
||||
if (!isXterm() && !forceColored && rc == 0) {
|
||||
return new AnsiOutputStream(stream);
|
||||
}
|
||||
|
||||
// These errors happen if the JNI lib is not available for your platform.
|
||||
} catch (NoClassDefFoundError ignore) {
|
||||
} catch (UnsatisfiedLinkError ignore) {
|
||||
}
|
||||
|
||||
// By default we assume your Unix tty can handle ANSI codes.
|
||||
// Just wrap it up so that when we get closed, we reset the attributes.
|
||||
return new FilterOutputStream(stream) {
|
||||
@Override
|
||||
public
|
||||
void close() throws IOException {
|
||||
write(AnsiOutputStream.RESET_CODE);
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If the standard out natively supports ANSI escape codes, then this just
|
||||
* returns System.out, otherwise it will provide an ANSI aware PrintStream
|
||||
* which strips out the ANSI escape sequences or which implement the escape
|
||||
* sequences.
|
||||
*
|
||||
* @return a PrintStream which is ANSI aware.
|
||||
*/
|
||||
public static
|
||||
PrintStream out() {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the standard out natively supports ANSI escape codes, then this just
|
||||
* returns System.err, otherwise it will provide an ANSI aware PrintStream
|
||||
* which strips out the ANSI escape sequences or which implement the escape
|
||||
* sequences.
|
||||
*
|
||||
* @return a PrintStream which is ANSI aware.
|
||||
*/
|
||||
public static
|
||||
PrintStream err() {
|
||||
return err;
|
||||
}
|
||||
}
|
@ -1,4 +1,19 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||
* subsidiaries or affiliates. All rights reserved.
|
||||
* <p>
|
||||
@ -14,7 +29,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dorkbox.console.output;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
@ -24,21 +38,19 @@ import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A ANSI output stream extracts ANSI escape codes written to
|
||||
* an output stream.
|
||||
* A ANSI output stream extracts ANSI escape codes written to an output stream.
|
||||
*
|
||||
* For more information about ANSI escape codes, see:
|
||||
* http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
*
|
||||
* This class just filters out the escape codes so that they are not
|
||||
* sent out to the underlying OutputStream. Subclasses should
|
||||
* This class just filters out the escape codes so that they are not sent out to the underlying OutputStream. Subclasses should
|
||||
* actually perform the ANSI escape behaviors.
|
||||
*
|
||||
* @author dorkbox, llc
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
* @author Joris Kuipers
|
||||
* @since 1.0
|
||||
*/
|
||||
@SuppressWarnings("NumericCastThatLosesPrecision")
|
||||
@SuppressWarnings({"NumericCastThatLosesPrecision", "WeakerAccess"})
|
||||
public
|
||||
class AnsiOutputStream extends FilterOutputStream {
|
||||
private static final Charset CHARSET = Charset.forName("UTF-8");
|
||||
@ -52,23 +64,29 @@ class AnsiOutputStream extends FilterOutputStream {
|
||||
static final int CYAN = 6;
|
||||
static final int WHITE = 7;
|
||||
|
||||
static final char CURSOR_UP = 'A';
|
||||
static final char CURSOR_UP = 'A'; // Moves the cursor n (default 1) cells in the given direction. If the cursor is already at the edge of the screen, this has no effect.
|
||||
static final char CURSOR_DOWN = 'B';
|
||||
static final char CURSOR_RIGHT = 'C';
|
||||
static final char CURSOR_LEFT = 'D';
|
||||
static final char CURSOR_DOWN_LINE = 'E';
|
||||
static final char CURSOR_UP_LINE = 'F';
|
||||
static final char CURSOR_TO_COL = 'G';
|
||||
static final char CURSOR_POS = 'H';
|
||||
static final char CURSOR_POS_ALT = 'f';
|
||||
static final char CURSOR_FORWARD = 'C';
|
||||
static final char CURSOR_BACK = 'D';
|
||||
|
||||
static final char CURSOR_ERASE_SCREEN = 'J';
|
||||
static final char CURSOR_ERASE_LINE = 'K';
|
||||
static final char PAGE_SCROLL_UP = 'S';
|
||||
static final char PAGE_SCROLL_DOWN = 'T';
|
||||
static final char SAVE_CURSOR_POS = 's';
|
||||
static final char RESTORE_CURSOR_POS = 'u';
|
||||
static final char TEXT_ATTRIBUTE = 'm';
|
||||
static final char CURSOR_DOWN_LINE = 'E'; // Moves cursor to beginning of the line n (default 1) lines down.
|
||||
static final char CURSOR_UP_LINE = 'F'; // Moves cursor to beginning of the line n (default 1) lines up.
|
||||
|
||||
static final char CURSOR_TO_COL = 'G'; // Moves the cursor to column n (default 1).
|
||||
|
||||
static final char CURSOR_POS = 'H'; // Moves the cursor to row n, column m. The values are 1-based, and default to 1 (top left corner) if omitted.
|
||||
static final char CURSOR_POS_ALT = 'f'; // Moves the cursor to row n, column m. Both default to 1 if omitted. Same as CUP
|
||||
|
||||
static final char CURSOR_ERASE_SCREEN = 'J'; // Clears part of the screen. If n is 0 (or missing), clear from cursor to end of screen. If n is 1, clear from cursor to beginning of the screen. If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS).
|
||||
static final char CURSOR_ERASE_LINE = 'K'; // Erases part of the line. If n is zero (or missing), clear from cursor to the end of the line. If n is one, clear from cursor to beginning of the line. If n is two, clear entire line. Cursor position does not change.
|
||||
|
||||
|
||||
static final char SCROLL_UP = 'S'; // Scroll whole page up by n (default 1) lines. New lines are added at the bottom. (not ANSI.SYS)
|
||||
static final char SCROLL_DOWN = 'T'; // Scroll whole page down by n (default 1) lines. New lines are added at the top. (not ANSI.SYS)
|
||||
|
||||
static final char SAVE_CURSOR_POS = 's'; // Saves the cursor position.
|
||||
static final char RESTORE_CURSOR_POS = 'u'; // Restores the cursor position.
|
||||
static final char TEXT_ATTRIBUTE = 'm'; // Sets SGR parameters, including text color. After CSI can be zero or more parameters separated with ;. With no parameters, CSI m is treated as CSI 0 m (reset / normal), which is typical of most of the ANSI escape sequences.
|
||||
|
||||
static final int ATTRIBUTE_RESET = 0; // Reset / Normal - all attributes off
|
||||
static final int ATTRIBUTE_BOLD = 1; // Intensity: Bold
|
||||
@ -90,27 +108,19 @@ class AnsiOutputStream extends FilterOutputStream {
|
||||
static final int ATTRIBUTE_CONCEAL_OFF = 28; // Reveal conceal off
|
||||
static final int ATTRIBUTE_STRIKETHROUGH_OFF = 29; // Not crossed out
|
||||
|
||||
|
||||
static final int ATTRIBUTE_DEFAULT_FG = 39; // Default text color (foreground)
|
||||
static final int ATTRIBUTE_DEFAULT_BG = 49; // Default background color
|
||||
|
||||
|
||||
// for Erase Screen/Line
|
||||
static final int ERASE_TO_END = 0;
|
||||
static final int ERASE_TO_BEGINNING = 1;
|
||||
static final int ERASE_ALL = 2;
|
||||
|
||||
static final byte[] RESET_CODE = new Ansi().reset()
|
||||
.toString()
|
||||
.getBytes(CHARSET);
|
||||
|
||||
AnsiOutputStream(OutputStream os) {
|
||||
super(os);
|
||||
}
|
||||
|
||||
private final static int MAX_ESCAPE_SEQUENCE_LENGTH = 100;
|
||||
private byte buffer[] = new byte[MAX_ESCAPE_SEQUENCE_LENGTH];
|
||||
private int pos = 0;
|
||||
private int startOfValue;
|
||||
private final ArrayList<Object> options = new ArrayList<Object>();
|
||||
|
||||
private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0;
|
||||
private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1;
|
||||
@ -131,8 +141,26 @@ class AnsiOutputStream extends FilterOutputStream {
|
||||
private static final int BEL = 7;
|
||||
private static final int SECOND_ST_CHAR = '\\';
|
||||
|
||||
// TODO: implement to get perf boost: public void write(byte[] b, int off, int len)
|
||||
|
||||
public static final byte[] RESET_CODE = new StringBuilder(3).append((char)FIRST_ESC_CHAR)
|
||||
.append((char)SECOND_ESC_CHAR)
|
||||
.append(AnsiOutputStream.TEXT_ATTRIBUTE)
|
||||
.toString()
|
||||
.getBytes(CHARSET);
|
||||
|
||||
public
|
||||
AnsiOutputStream(OutputStream os) {
|
||||
super(os);
|
||||
}
|
||||
|
||||
|
||||
private byte buffer[] = new byte[MAX_ESCAPE_SEQUENCE_LENGTH];
|
||||
private int pos = 0;
|
||||
private int startOfValue;
|
||||
private final ArrayList<Object> options = new ArrayList<Object>();
|
||||
|
||||
|
||||
@Override
|
||||
public
|
||||
void write(int data) throws IOException {
|
||||
switch (state) {
|
||||
@ -301,9 +329,6 @@ class AnsiOutputStream extends FilterOutputStream {
|
||||
state = LOOKING_FOR_FIRST_ESC_CHAR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return true if the escape command was processed.
|
||||
*/
|
||||
@ -317,10 +342,10 @@ class AnsiOutputStream extends FilterOutputStream {
|
||||
case CURSOR_DOWN:
|
||||
processCursorDown(optionInt(options, 0, 1));
|
||||
return true;
|
||||
case CURSOR_RIGHT:
|
||||
case CURSOR_FORWARD:
|
||||
processCursorRight(optionInt(options, 0, 1));
|
||||
return true;
|
||||
case CURSOR_LEFT:
|
||||
case CURSOR_BACK:
|
||||
processCursorLeft(optionInt(options, 0, 1));
|
||||
return true;
|
||||
case CURSOR_DOWN_LINE:
|
||||
@ -342,10 +367,10 @@ class AnsiOutputStream extends FilterOutputStream {
|
||||
case CURSOR_ERASE_LINE:
|
||||
processEraseLine(optionInt(options, 0, 0));
|
||||
return true;
|
||||
case PAGE_SCROLL_UP:
|
||||
case SCROLL_UP:
|
||||
processScrollUp(optionInt(options, 0, 1));
|
||||
return true;
|
||||
case PAGE_SCROLL_DOWN:
|
||||
case SCROLL_DOWN:
|
||||
processScrollDown(optionInt(options, 0, 1));
|
||||
return true;
|
||||
case TEXT_ATTRIBUTE:
|
||||
@ -554,7 +579,6 @@ class AnsiOutputStream extends FilterOutputStream {
|
||||
@Override
|
||||
public
|
||||
void close() throws IOException {
|
||||
write(RESET_CODE);
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
|
@ -64,13 +64,10 @@ class AnsiRenderWriter extends PrintWriter {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Need to prevent partial output from being written while formatting or we will get rendering exceptions
|
||||
//
|
||||
|
||||
@Override
|
||||
public
|
||||
PrintWriter format(final String format, final Object... args) {
|
||||
flush(); // prevents partial output from being written while formatting or we will get rendering exceptions
|
||||
print(String.format(format, args));
|
||||
return this;
|
||||
}
|
||||
@ -78,6 +75,7 @@ class AnsiRenderWriter extends PrintWriter {
|
||||
@Override
|
||||
public
|
||||
PrintWriter format(final Locale l, final String format, final Object... args) {
|
||||
flush(); // prevents partial output from being written while formatting or we will get rendering exceptions
|
||||
print(String.format(l, format, args));
|
||||
return this;
|
||||
}
|
||||
|
@ -1,4 +1,19 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2009 the original author(s).
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -13,7 +28,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dorkbox.console.output;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -39,6 +53,7 @@ import java.util.Map;
|
||||
* <pre>
|
||||
* <tt>@|bold,red Warning!|@</tt>
|
||||
* </pre>
|
||||
* For Colors, FG_x and BG_x are supported, as are BRIGHT_x (and consequently, FG_BRIGHT_x)
|
||||
*
|
||||
* @author dorkbox, llc
|
||||
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
|
||||
@ -55,7 +70,13 @@ class AnsiRenderer {
|
||||
private static final int BEGIN_TOKEN_LEN = 2;
|
||||
private static final int END_TOKEN_LEN = 2;
|
||||
|
||||
private static Map<String, AnsiCode> codeMap = new HashMap<String, AnsiCode>(32);
|
||||
private static Map<String, AnsiCodeMap> codeMap = new HashMap<String, AnsiCodeMap>(32);
|
||||
|
||||
static {
|
||||
// have to make sure that all the different categories are added to our map.
|
||||
Color red = Color.RED;
|
||||
Attribute bold = Attribute.BOLD;
|
||||
}
|
||||
|
||||
static
|
||||
void reg(Enum anEnum, String codeName) {
|
||||
@ -64,11 +85,11 @@ class AnsiRenderer {
|
||||
|
||||
static
|
||||
void reg(Enum anEnum, String codeName, boolean isBackgroundColor) {
|
||||
codeMap.put(codeName, new AnsiCode(anEnum, codeName, isBackgroundColor));
|
||||
codeMap.put(codeName, new AnsiCodeMap(anEnum, isBackgroundColor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders {@link AnsiCode} names on the given Ansi.
|
||||
* Renders {@link AnsiCodeMap} names on the given Ansi.
|
||||
*
|
||||
* @param ansi The Ansi to render upon
|
||||
* @param codeNames The code names to render
|
||||
@ -82,26 +103,26 @@ class AnsiRenderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a {@link AnsiCode} name on the given Ansi.
|
||||
* Renders a {@link AnsiCodeMap} name on the given Ansi.
|
||||
*
|
||||
* @param ansi The Ansi to render upon
|
||||
* @param codeName The code name to render
|
||||
*/
|
||||
public static
|
||||
Ansi render(Ansi ansi, String codeName) {
|
||||
AnsiCode ansiCode = codeMap.get(codeName.toUpperCase(Locale.ENGLISH));
|
||||
assert ansiCode != null : "Invalid ANSI code name: '" + codeName + "'";
|
||||
AnsiCodeMap ansiCodeMap = codeMap.get(codeName.toUpperCase(Locale.ENGLISH));
|
||||
assert ansiCodeMap != null : "Invalid ANSI code name: '" + codeName + "'";
|
||||
|
||||
if (ansiCode.isColor()) {
|
||||
if (ansiCode.isBackgroundColor()) {
|
||||
ansi = ansi.bg(ansiCode.getColor());
|
||||
if (ansiCodeMap.isColor()) {
|
||||
if (ansiCodeMap.isBackgroundColor()) {
|
||||
ansi = ansi.bg(ansiCodeMap.getColor());
|
||||
}
|
||||
else {
|
||||
ansi = ansi.fg(ansiCode.getColor());
|
||||
ansi = ansi.fg(ansiCodeMap.getColor());
|
||||
}
|
||||
}
|
||||
else if (ansiCode.isAttribute()) {
|
||||
ansi = ansi.a(ansiCode.getAttribute());
|
||||
else if (ansiCodeMap.isAttribute()) {
|
||||
ansi = ansi.a(ansiCodeMap.getAttribute());
|
||||
} else {
|
||||
assert false : "Undetermined ANSI code name: '" + codeName + "'";
|
||||
}
|
||||
@ -110,7 +131,7 @@ class AnsiRenderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders text using the {@link AnsiCode} names.
|
||||
* Renders text using the {@link AnsiCodeMap} names.
|
||||
*
|
||||
* @param text The text to render
|
||||
* @param codeNames The code names to render
|
||||
@ -167,7 +188,7 @@ class AnsiRenderer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders {@link AnsiCode} names as an ANSI escape string.
|
||||
* Renders {@link AnsiCodeMap} names as an ANSI escape string.
|
||||
*
|
||||
* @param codeNames The code names to render
|
||||
*
|
||||
|
@ -18,6 +18,7 @@ package dorkbox.console.output;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* An ANSI string which reports the size of rendered text correctly (ignoring any ANSI escapes).
|
||||
@ -27,6 +28,7 @@ import java.io.IOException;
|
||||
*/
|
||||
public
|
||||
class AnsiString implements CharSequence {
|
||||
private static final Charset CHARSET = Charset.forName("UTF-8");
|
||||
private final CharSequence encoded;
|
||||
|
||||
private final CharSequence plain;
|
||||
@ -47,7 +49,7 @@ class AnsiString implements CharSequence {
|
||||
|
||||
try {
|
||||
out.write(str.toString()
|
||||
.getBytes());
|
||||
.getBytes(CHARSET));
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console.output;
|
||||
|
||||
import static dorkbox.console.output.AnsiOutputStream.ATTRIBUTE_BLINK_FAST;
|
||||
|
@ -17,21 +17,45 @@ package dorkbox.console.output;
|
||||
|
||||
public
|
||||
enum Color {
|
||||
BLACK (AnsiOutputStream.BLACK, "BLACK"),
|
||||
RED (AnsiOutputStream.RED, "RED"),
|
||||
GREEN (AnsiOutputStream.GREEN, "GREEN"),
|
||||
YELLOW (AnsiOutputStream.YELLOW, "YELLOW"),
|
||||
BLUE (AnsiOutputStream.BLUE, "BLUE"),
|
||||
MAGENTA(AnsiOutputStream.MAGENTA, "MAGENTA"),
|
||||
CYAN (AnsiOutputStream.CYAN, "CYAN"),
|
||||
WHITE (AnsiOutputStream.WHITE, "WHITE");
|
||||
BLACK (AnsiOutputStream.BLACK, true, "BLACK"),
|
||||
RED (AnsiOutputStream.RED, true, "RED"),
|
||||
GREEN (AnsiOutputStream.GREEN, true, "GREEN"),
|
||||
YELLOW (AnsiOutputStream.YELLOW, true, "YELLOW"),
|
||||
BLUE (AnsiOutputStream.BLUE, true, "BLUE"),
|
||||
MAGENTA(AnsiOutputStream.MAGENTA, true, "MAGENTA"),
|
||||
CYAN (AnsiOutputStream.CYAN, true, "CYAN"),
|
||||
WHITE (AnsiOutputStream.WHITE, true, "WHITE"),
|
||||
|
||||
|
||||
// Brighter versions of those colors, ie: BRIGHT_BLACK is gray.
|
||||
BRIGHT_BLACK (AnsiOutputStream.BLACK, false, "BRIGHT_BLACK"),
|
||||
BRIGHT_RED (AnsiOutputStream.RED, false, "BRIGHT_RED"),
|
||||
BRIGHT_GREEN (AnsiOutputStream.GREEN, false, "BRIGHT_GREEN"),
|
||||
BRIGHT_YELLOW (AnsiOutputStream.YELLOW, false, "BRIGHT_YELLOW"),
|
||||
BRIGHT_BLUE (AnsiOutputStream.BLUE, false, "BRIGHT_BLUE"),
|
||||
BRIGHT_MAGENTA(AnsiOutputStream.MAGENTA, false, "BRIGHT_MAGENTA"),
|
||||
BRIGHT_CYAN (AnsiOutputStream.CYAN, false, "BRIGHT_CYAN"),
|
||||
BRIGHT_WHITE (AnsiOutputStream.WHITE, false, "BRIGHT_WHITE"),
|
||||
|
||||
// SPECIAL use case here. This is intercepted (so the color doesn't matter)
|
||||
/**
|
||||
* DEFAULT is the color of console BEFORE any colors/settings are applied
|
||||
*/
|
||||
DEFAULT (AnsiOutputStream.WHITE, true, "DEFAULT"),
|
||||
|
||||
/**
|
||||
* DEFAULT is the color of console BEFORE any colors/settings are applied
|
||||
*/
|
||||
BRIGHT_DEFAULT (AnsiOutputStream.WHITE, false, "BRIGHT_DEFAULT");
|
||||
|
||||
private final int value;
|
||||
private final String name;
|
||||
private final boolean isNormal;
|
||||
|
||||
Color(int index, String name) {
|
||||
Color(int index, boolean isNormal, String name) {
|
||||
this.value = index;
|
||||
this.name = name;
|
||||
this.isNormal = isNormal;
|
||||
|
||||
// register code names with the ANSI renderer
|
||||
AnsiRenderer.reg(this, name, false);
|
||||
@ -45,22 +69,23 @@ enum Color {
|
||||
return name;
|
||||
}
|
||||
|
||||
public
|
||||
int fg() {
|
||||
return value + 30;
|
||||
}
|
||||
|
||||
public
|
||||
int bg() {
|
||||
return value + 40;
|
||||
}
|
||||
|
||||
public
|
||||
/** is this a BRIGHT color or NORMAL color? */
|
||||
boolean isNormal() {
|
||||
return isNormal;
|
||||
}
|
||||
|
||||
int fgBright() {
|
||||
return value + 90;
|
||||
}
|
||||
|
||||
public
|
||||
int bgBright() {
|
||||
return value + 100;
|
||||
}
|
||||
|
@ -1,4 +1,19 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||
* subsidiaries or affiliates. All rights reserved.
|
||||
* <p>
|
||||
@ -23,6 +38,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dorkbox, llc
|
||||
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
|
||||
*/
|
||||
public
|
||||
|
@ -1,10 +1,9 @@
|
||||
/*
|
||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||
* subsidiaries or affiliates. All rights reserved.
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a asValue of the License at
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
@ -14,11 +13,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||
* subsidiaries or affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* You may obtain a asValue of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
@ -58,7 +59,7 @@ import dorkbox.console.util.windows.SMALL_RECT;
|
||||
* @author Joris Kuipers
|
||||
*/
|
||||
@SuppressWarnings("NumericCastThatLosesPrecision")
|
||||
final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
||||
public final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
||||
private static final short ANSI_FOREGROUND_COLOR_MAP[];
|
||||
private static final short ANSI_BACKGROUND_COLOR_MAP[];
|
||||
|
||||
@ -92,12 +93,13 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
||||
private volatile short savedX = (short) -1;
|
||||
private volatile short savedY = (short) -1;
|
||||
|
||||
public
|
||||
WindowsAnsiOutputStream(final OutputStream os, int fileHandle) throws IOException {
|
||||
super(os);
|
||||
|
||||
if (fileHandle == AnsiConsole.STDOUT_FILENO) {
|
||||
if (fileHandle == 1) { // STDOUT_FILENO
|
||||
fileHandle = Kernel32.STD_OUTPUT_HANDLE;
|
||||
} else if (fileHandle == AnsiConsole.STDERR_FILENO) {
|
||||
} else if (fileHandle == 2) { // STDERR_FILENO
|
||||
fileHandle = Kernel32.STD_ERROR_HANDLE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid file handle " + fileHandle);
|
||||
@ -321,7 +323,7 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
||||
void processCursorUpLine(final int count) throws IOException {
|
||||
getConsoleInfo();
|
||||
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y - count);
|
||||
info.cursorPosition.x = 0;
|
||||
info.cursorPosition.x = (short) 0;
|
||||
applyCursorPosition();
|
||||
}
|
||||
|
||||
@ -329,7 +331,7 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
||||
void processCursorDownLine(final int count) throws IOException {
|
||||
getConsoleInfo();
|
||||
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y + count);
|
||||
info.cursorPosition.x = 0;
|
||||
info.cursorPosition.x = (short) 0;
|
||||
applyCursorPosition();
|
||||
}
|
||||
|
||||
@ -383,6 +385,8 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrolls the contents of the buffer either UP or DOWN
|
||||
*
|
||||
* @param rowsToScroll negative to go down, positive to go up.
|
||||
*
|
||||
* Scroll up and new lines are added at the bottom, scroll down and new lines are added at the
|
||||
@ -405,12 +409,12 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
||||
|
||||
// the content that will be scrolled
|
||||
scrollRect.top = (short) (0);
|
||||
scrollRect.bottom = (short) (Short.MAX_VALUE);
|
||||
scrollRect.left = 0;
|
||||
scrollRect.bottom = Short.MAX_VALUE;
|
||||
scrollRect.left = (short) 0;
|
||||
scrollRect.right = (short) (info.size.x - 1);
|
||||
|
||||
// The destination for the scroll rectangle is xxx row up/down.
|
||||
coordDest.x = 0;
|
||||
coordDest.x = (short) 0;
|
||||
coordDest.y = (short) (-rowsToScroll);
|
||||
|
||||
// fill the space with whatever color was already there with spaces
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010 dorkbox, llc
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -13,10 +13,17 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console;
|
||||
|
||||
public class TerminalType {
|
||||
|
||||
package dorkbox.console.util;
|
||||
|
||||
/**
|
||||
* Used for single char input
|
||||
*/
|
||||
public
|
||||
class CharHolder {
|
||||
// default is nothing (0)
|
||||
public char character = (char) 0;
|
||||
|
||||
public
|
||||
CharHolder() {
|
||||
}
|
||||
}
|
@ -39,7 +39,9 @@ class CONSOLE_SCREEN_BUFFER_INFO extends Structure {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Size: " + size + " CursorPos: " + cursorPosition + " Attribs: " + attributes + " Window: " + window + " MaxWindowSize: " + maximumWindowSize;
|
||||
public
|
||||
String toString() {
|
||||
return "Size: " + size + " CursorPos: " + cursorPosition + " Attribs: " + attributes + " Window: " + window + " MaxWindowSize: " +
|
||||
maximumWindowSize;
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,6 @@ import com.sun.jna.Structure;
|
||||
*/
|
||||
public
|
||||
class COORD extends Structure {
|
||||
static public class ByValue extends COORD implements Structure.ByValue { }
|
||||
|
||||
public short x;
|
||||
public short y;
|
||||
|
||||
@ -45,7 +43,12 @@ class COORD extends Structure {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public
|
||||
String toString() {
|
||||
return x + ":" + y;
|
||||
}
|
||||
|
||||
|
||||
static public
|
||||
class ByValue extends COORD implements Structure.ByValue {}
|
||||
}
|
||||
|
@ -1,30 +1,51 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console.util.windows;
|
||||
|
||||
import com.sun.jna.Union;
|
||||
|
||||
public class CharUnion extends Union {
|
||||
public
|
||||
class CharUnion extends Union {
|
||||
public char unicodeChar;
|
||||
public byte asciiChar;
|
||||
|
||||
public CharUnion() {
|
||||
public
|
||||
CharUnion() {
|
||||
}
|
||||
|
||||
public CharUnion(char c) {
|
||||
public
|
||||
CharUnion(char c) {
|
||||
setType(char.class);
|
||||
unicodeChar = c;
|
||||
}
|
||||
|
||||
public CharUnion(byte c) {
|
||||
public
|
||||
CharUnion(byte c) {
|
||||
setType(byte.class);
|
||||
asciiChar = c;
|
||||
}
|
||||
|
||||
public void set(char c) {
|
||||
public
|
||||
void set(char c) {
|
||||
setType(char.class);
|
||||
unicodeChar = c;
|
||||
}
|
||||
|
||||
public void set(byte c) {
|
||||
public
|
||||
void set(byte c) {
|
||||
setType(byte.class);
|
||||
asciiChar = c;
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2012, the original author or authors.
|
||||
*
|
||||
* This software is distributable under the BSD license. See the terms of the
|
||||
* BSD license in the documentation provided with this software.
|
||||
*
|
||||
* http://www.opensource.org/licenses/bsd-license.php
|
||||
*
|
||||
* @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
|
||||
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
|
||||
*/
|
||||
package dorkbox.console.util.windows;
|
||||
|
||||
/**
|
||||
* Console mode
|
||||
* <p/>
|
||||
* Constants copied <tt>wincon.h</tt>.
|
||||
*/
|
||||
public enum ConsoleMode {
|
||||
/**
|
||||
* 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),
|
||||
|
||||
/**
|
||||
* 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),
|
||||
|
||||
/**
|
||||
* User interactions that change the size of the console screen buffer are reported in the console's input buffer. 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),
|
||||
;
|
||||
|
||||
|
||||
public final int code;
|
||||
|
||||
ConsoleMode(final int code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
@ -27,7 +27,9 @@ import com.sun.jna.PointerType;
|
||||
*/
|
||||
public
|
||||
class HANDLE extends PointerType {
|
||||
/** Constant value representing an invalid HANDLE. */
|
||||
/**
|
||||
* Constant value representing an invalid HANDLE.
|
||||
*/
|
||||
public static final HANDLE INVALID_HANDLE_VALUE = new HANDLE(Pointer.createConstant(Pointer.SIZE == 8 ? -1 : 0xFFFFFFFFL));
|
||||
|
||||
private boolean immutable;
|
||||
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console.util.windows;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -9,22 +24,16 @@ import com.sun.jna.Union;
|
||||
/**
|
||||
* https://msdn.microsoft.com/en-us/library/ms683499(v=VS.85).aspx
|
||||
*/
|
||||
public class INPUT_RECORD extends Structure {
|
||||
static public class ByReference extends INPUT_RECORD implements Structure.ByReference {}
|
||||
|
||||
public
|
||||
class INPUT_RECORD extends Structure {
|
||||
public static final short KEY_EVENT = 0x0001;
|
||||
public static final short MOUSE_EVENT = 0x0002;
|
||||
|
||||
public short EventType;
|
||||
public EventUnion Event;
|
||||
|
||||
public static class EventUnion extends Union {
|
||||
public KEY_EVENT_RECORD KeyEvent;
|
||||
public MOUSE_EVENT_RECORD MouseEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read() {
|
||||
public
|
||||
void read() {
|
||||
readField("EventType");
|
||||
switch (EventType) {
|
||||
case KEY_EVENT:
|
||||
@ -38,7 +47,19 @@ public class INPUT_RECORD extends Structure {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
protected
|
||||
List<String> getFieldOrder() {
|
||||
return Arrays.asList("EventType", "Event");
|
||||
}
|
||||
|
||||
|
||||
static public
|
||||
class ByReference extends INPUT_RECORD implements Structure.ByReference {}
|
||||
|
||||
|
||||
public static
|
||||
class EventUnion extends Union {
|
||||
public KEY_EVENT_RECORD KeyEvent;
|
||||
public MOUSE_EVENT_RECORD MouseEvent;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console.util.windows;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -8,7 +23,8 @@ import com.sun.jna.Structure;
|
||||
/**
|
||||
* https://msdn.microsoft.com/en-us/library/ms684166(v=VS.85).aspx
|
||||
*/
|
||||
public class KEY_EVENT_RECORD extends Structure {
|
||||
public
|
||||
class KEY_EVENT_RECORD extends Structure {
|
||||
public boolean keyDown;
|
||||
public short repeatCount;
|
||||
public short virtualKeyCode;
|
||||
@ -17,7 +33,8 @@ public class KEY_EVENT_RECORD extends Structure {
|
||||
public int controlKeyState;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
protected
|
||||
List<String> getFieldOrder() {
|
||||
return Arrays.asList("keyDown", "repeatCount", "virtualKeyCode", "virtualScanCode", "uChar", "controlKeyState");
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2016 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.console.util.windows;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -5,14 +20,16 @@ import java.util.List;
|
||||
|
||||
import com.sun.jna.Structure;
|
||||
|
||||
public class MOUSE_EVENT_RECORD extends Structure {
|
||||
public
|
||||
class MOUSE_EVENT_RECORD extends Structure {
|
||||
public COORD mousePosition;
|
||||
public int buttonState;
|
||||
public int controlKeyState;
|
||||
public int eventFlags;
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
protected
|
||||
List<String> getFieldOrder() {
|
||||
return Arrays.asList("mousePosition", "buttonState", "controlKeyState", "eventFlags");
|
||||
}
|
||||
}
|
||||
|
@ -23,28 +23,37 @@ import com.sun.jna.Structure;
|
||||
/**
|
||||
* https://msdn.microsoft.com/en-us/library/ms686311%28VS.85%29.aspx
|
||||
*/
|
||||
public class SMALL_RECT extends Structure {
|
||||
static public class ByReference extends SMALL_RECT implements Structure.ByReference { }
|
||||
|
||||
@SuppressWarnings("NumericCastThatLosesPrecision")
|
||||
public
|
||||
class SMALL_RECT extends Structure {
|
||||
public short left;
|
||||
public short top;
|
||||
public short right;
|
||||
public short bottom;
|
||||
|
||||
public short width() {
|
||||
return (short) (this.right-this.left);
|
||||
public
|
||||
short width() {
|
||||
return (short) (this.right - this.left);
|
||||
}
|
||||
public short height() {
|
||||
return (short) (this.bottom-this.top);
|
||||
|
||||
public
|
||||
short height() {
|
||||
return (short) (this.bottom - this.top);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFieldOrder() {
|
||||
protected
|
||||
List<String> getFieldOrder() {
|
||||
return Arrays.asList("left", "top", "right", "bottom");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public
|
||||
String toString() {
|
||||
return "LTRB: " + left + "," + top + "," + right + "," + bottom;
|
||||
}
|
||||
|
||||
|
||||
static public
|
||||
class ByReference extends SMALL_RECT implements Structure.ByReference {}
|
||||
}
|
||||
|
@ -1,50 +1,140 @@
|
||||
/**
|
||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||
* subsidiaries or affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a asValue of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dorkbox.console;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import dorkbox.console.Console;
|
||||
import dorkbox.console.output.Ansi;
|
||||
import dorkbox.console.output.Color;
|
||||
import dorkbox.console.output.Erase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
* System output for visual confirmation of ANSI codes.
|
||||
* <p>
|
||||
* Must enable assertions to verify no errors!! (ie: java -ea -jar blah.jar)
|
||||
*/
|
||||
public class AnsiConsoleExample {
|
||||
public
|
||||
class AnsiConsoleExample {
|
||||
public static
|
||||
void main(String[] args) throws IOException {
|
||||
|
||||
private AnsiConsoleExample() {}
|
||||
Console.systemInstall();
|
||||
Console.ENABLE_ANSI = true;
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
String file = "jansi.ans";
|
||||
if( args.length>0 )
|
||||
file = args[0];
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.BLACK).a("black").bg(Color.BLACK).a("black")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_BLACK).a("b-black").bg(Color.BRIGHT_BLACK).a("b-black")
|
||||
.reset());
|
||||
|
||||
// Allows us to disable ANSI processing.
|
||||
if( "true".equals(System.getProperty("jansi", "true")) ) {
|
||||
Ansi.systemInstall();
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.BLUE).a("blue").bg(Color.BLUE).a("blue")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_BLUE).a("b-blue").bg(Color.BRIGHT_BLUE).a("b-blue")
|
||||
.reset());
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.CYAN).a("cyan").bg(Color.CYAN).a("cyan")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_CYAN).a("b-cyan").bg(Color.BRIGHT_CYAN).a("b-cyan")
|
||||
.reset());
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.GREEN).a("green").bg(Color.GREEN).a("green")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_GREEN).a("b-green").bg(Color.BRIGHT_GREEN).a("b-green")
|
||||
.reset());
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.MAGENTA).a("magenta").bg(Color.MAGENTA).a("magenta")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_MAGENTA).a("b-magenta").bg(Color.BRIGHT_MAGENTA).a("b-magenta")
|
||||
.reset());
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.RED).a("red").bg(Color.RED).a("red")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_RED).a("b-red").bg(Color.BRIGHT_RED).a("b-red")
|
||||
.reset());
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.YELLOW).a("yellow").bg(Color.YELLOW).a("yellow")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_YELLOW).a("b-yellow").bg(Color.BRIGHT_YELLOW).a("b-yellow")
|
||||
.reset());
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.fg(Color.WHITE).a("white").bg(Color.WHITE).a("white")
|
||||
.reset()
|
||||
.fg(Color.BRIGHT_WHITE).a("b-white").bg(Color.BRIGHT_WHITE).a("b-white")
|
||||
.reset());
|
||||
|
||||
|
||||
Console.reset(); // reset the ansi stream. Can ALSO have ansi().reset(), but that would be redundant
|
||||
|
||||
|
||||
System.out.println("The following line should be blank except for the first '>'");
|
||||
System.out.println(Ansi.ansi()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.cursorToColumn(2)
|
||||
.eraseLine());
|
||||
|
||||
System.out.println("The following line should be blank");
|
||||
System.out.println(Ansi.ansi()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.eraseLine(Erase.ALL));
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.eraseLine(Erase.BACKWARD)
|
||||
.a("Everything on this line before this should be blank"));
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.a("Everything on this line after this should be blank")
|
||||
.saveCursorPosition()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.restoreCursorPosition()
|
||||
.eraseLine());
|
||||
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
|
||||
System.out.println(Ansi.ansi()
|
||||
.a("Should have two blank spots in the above 0's")
|
||||
.saveCursorPosition()
|
||||
.cursorUp(4)
|
||||
.cursorLeft(30)
|
||||
.a(" ")
|
||||
.cursorDownLine()
|
||||
.cursorRight(5)
|
||||
.a(" ")
|
||||
.restoreCursorPosition());
|
||||
|
||||
System.err.println("ver : " + Console.getVersion());
|
||||
|
||||
|
||||
System.out.println("Now testing the input console. 'q' to quit");
|
||||
Console.ENABLE_ECHO = true;
|
||||
|
||||
int read;
|
||||
while ((read = Console.read()) != 'q') {
|
||||
if (Character.isDigit(read)) {
|
||||
int numericValue = Character.getNumericValue(read);
|
||||
// reverse if pressing 2
|
||||
if (numericValue == 2) {
|
||||
System.out.print(Ansi.ansi().scrollDown(1));
|
||||
System.out.flush(); // flush guarantees the terminal moves the way we want
|
||||
}
|
||||
else {
|
||||
System.out.print(Ansi.ansi().scrollUp(numericValue));
|
||||
System.out.flush(); // flush guarantees the terminal moves the way we want
|
||||
}
|
||||
}
|
||||
// System.err.println("READ :" + read + " (" + (char) read + ")");
|
||||
}
|
||||
|
||||
FileInputStream f = new FileInputStream(file);
|
||||
int c;
|
||||
while( (c=f.read())>=0 ) {
|
||||
System.out.write(c);
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
Console.systemUninstall();
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||
* subsidiaries or affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a asValue of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.dorkbox.console;
|
||||
|
||||
import static dorkbox.console.output.Ansi.ansi;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import dorkbox.console.output.Ansi;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||
*/
|
||||
public class AnsiConsoleExample2 {
|
||||
|
||||
private AnsiConsoleExample2() {}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
String file = "jansi.ans";
|
||||
if( args.length>0 )
|
||||
file = args[0];
|
||||
|
||||
// Allows us to disable ANSI processing.
|
||||
if( "true".equals(System.getProperty("jansi", "true")) ) {
|
||||
Ansi.systemInstall();
|
||||
}
|
||||
|
||||
System.out.print(ansi().reset().eraseScreen().cursor(1, 1));
|
||||
System.out.print("=======================================================================");
|
||||
FileInputStream f = new FileInputStream(file);
|
||||
int c;
|
||||
while( (c=f.read())>=0 ) {
|
||||
System.out.write(c);
|
||||
}
|
||||
f.close();
|
||||
System.out.println("=======================================================================");
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
package com.dorkbox.console;
|
||||
|
||||
import static dorkbox.console.output.AnsiRenderer.render;
|
||||
import static java.awt.Font.BOLD;
|
||||
import static dorkbox.console.output.Attribute.BOLD;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import dorkbox.console.Console;
|
||||
import dorkbox.console.output.Ansi;
|
||||
import dorkbox.console.output.AnsiRenderer;
|
||||
import dorkbox.console.output.Color;
|
||||
@ -43,7 +44,7 @@ public class AnsiRendererTest
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Ansi.setEnabled(true);
|
||||
Console.ENABLE_ANSI = true;
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -66,7 +67,6 @@ public class AnsiRendererTest
|
||||
String str = render("@|bold,red foo|@");
|
||||
System.out.println(str);
|
||||
assertEquals(Ansi.ansi().a(BOLD).fg(Color.RED).a("foo").reset().toString(), str);
|
||||
assertEquals(Ansi.ansi().bold().fgRed().a("foo").reset().toString(), str);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -74,7 +74,6 @@ public class AnsiRendererTest
|
||||
String str = render("@|bold,red foo bar baz|@");
|
||||
System.out.println(str);
|
||||
assertEquals(Ansi.ansi().a(BOLD).fg(Color.RED).a("foo bar baz").reset().toString(), str);
|
||||
assertEquals(Ansi.ansi().bold().fgRed().a("foo bar baz").reset().toString(), str);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -82,10 +81,10 @@ public class AnsiRendererTest
|
||||
String str = render("@|bold,red foo bar baz|@ ick @|bold,red foo bar baz|@");
|
||||
System.out.println(str);
|
||||
assertEquals(Ansi.ansi()
|
||||
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
|
||||
.a(" ick ")
|
||||
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
|
||||
.toString(), str);
|
||||
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
|
||||
.a(" ick ")
|
||||
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
|
||||
.toString(), str);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,172 +0,0 @@
|
||||
package com.dorkbox.console;
|
||||
|
||||
import static dorkbox.console.output.Ansi.ansi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import dorkbox.console.Console;
|
||||
import dorkbox.console.output.Ansi;
|
||||
import dorkbox.console.output.Erase;
|
||||
|
||||
/**
|
||||
* System output for visual confirmation of ANSI codes.
|
||||
* <p>
|
||||
* Must enable assertions to verify no errors!! (ie: java -ea -jar blah.jar)
|
||||
*/
|
||||
public
|
||||
class AnsiRendererTestExample {
|
||||
public static
|
||||
void main(String[] args) throws IOException {
|
||||
Ansi.systemInstall();
|
||||
Ansi.setEnabled(true);
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgBlack().a("black").bgBlack().a("black")
|
||||
.reset()
|
||||
.fgBrightBlack().a("b-black").bgBrightBlack().a("b-black")
|
||||
.reset());
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgBlue().a("blue").bgBlue().a("blue")
|
||||
.reset()
|
||||
.fgBrightBlue().a("b-blue").bgBrightBlue().a("b-blue")
|
||||
.reset());
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgCyan().a("cyan").bgCyan().a("cyan")
|
||||
.reset()
|
||||
.fgBrightCyan().a("b-cyan").bgBrightCyan().a("b-cyan")
|
||||
.reset());
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgGreen().a("green").bgGreen().a("green")
|
||||
.reset()
|
||||
.fgBrightGreen().a("b-green").bgBrightGreen().a("b-green")
|
||||
.reset());
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgMagenta().a("magenta").bgMagenta().a("magenta")
|
||||
.reset()
|
||||
.fgBrightMagenta().a("b-magenta").bgBrightMagenta().a("b-magenta")
|
||||
.reset());
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgRed().a("red").bgRed().a("red")
|
||||
.reset()
|
||||
.fgBrightRed().a("b-red").bgBrightRed().a("b-red")
|
||||
.reset());
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgYellow().a("yellow").bgYellow().a("yellow")
|
||||
.reset()
|
||||
.fgBrightYellow().a("b-yellow").bgBrightYellow().a("b-yellow")
|
||||
.reset());
|
||||
|
||||
System.out.println(ansi()
|
||||
.fgWhite().a("white").bgWhite().a("white")
|
||||
.reset()
|
||||
.fgBrightWhite().a("b-white").bgBrightWhite().a("b-white")
|
||||
.reset());
|
||||
|
||||
|
||||
System.out.println(ansi()); // reset the ansi stream. Can ALSO have ansi().reset(), but that would be redundant
|
||||
|
||||
System.out.println("The following line should be blank except for the first '>'");
|
||||
System.out.println(ansi()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.cursorToColumn(2)
|
||||
.eraseLine());
|
||||
|
||||
System.out.println("The following line should be blank");
|
||||
System.out.println(ansi()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.eraseLine(Erase.ALL));
|
||||
|
||||
System.out.println(ansi()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.eraseLine(Erase.BACKWARD)
|
||||
.a("Everything before this should be blank"));
|
||||
|
||||
System.out.println(ansi()
|
||||
.a("Everything after this should be blank")
|
||||
.saveCursorPosition()
|
||||
.a(">THIS SHOULD BE BLANK")
|
||||
.restoreCursorPosition()
|
||||
.eraseLine(Erase.FORWARD));
|
||||
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
System.out.println("00000000000000000000000000");
|
||||
|
||||
System.out.println(ansi()
|
||||
.a("Should have two blank spots in the above 0's")
|
||||
.saveCursorPosition()
|
||||
.cursorUp(4)
|
||||
.cursorLeft(30)
|
||||
.a(" ")
|
||||
.cursorDownLine()
|
||||
.cursorRight(5)
|
||||
.a(" ")
|
||||
.restoreCursorPosition());
|
||||
|
||||
|
||||
// verify the output renderer
|
||||
// String str = render("@|bold foo|@foo");
|
||||
// System.out.println(str);
|
||||
// assertEquals(ansi().a(BOLD).a("foo").reset().a("foo").toString(), str);
|
||||
// assertEquals(ansi().bold().a("foo").reset().a("foo").toString(), str);
|
||||
//
|
||||
//
|
||||
// str = render("@|bold,red foo|@");
|
||||
// System.out.println(str);
|
||||
// assertEquals(ansi().a(BOLD).fg(RED).a("foo").reset().toString(), str);
|
||||
// assertEquals(ansi().bold().fgRed().a("foo").reset().toString(), str);
|
||||
//
|
||||
// str = render("@|bold,red foo bar baz|@");
|
||||
// System.out.println(str);
|
||||
// assertEquals(ansi().a(BOLD).fg(RED).a("foo bar baz").reset().toString(), str);
|
||||
// assertEquals(ansi().bold().fgRed().a("foo bar baz").reset().toString(), str);
|
||||
//
|
||||
//
|
||||
// str = render("@|bold,red foo bar baz|@ ick @|bold,red foo bar baz|@");
|
||||
// System.out.println(str);
|
||||
// String expected = ansi().a(BOLD)
|
||||
// .fg(RED)
|
||||
// .a("foo bar baz")
|
||||
// .reset()
|
||||
// .a(" ick ")
|
||||
// .a(BOLD)
|
||||
// .fg(RED)
|
||||
// .a("foo bar baz")
|
||||
// .reset()
|
||||
// .toString();
|
||||
//
|
||||
// assertEquals(expected, str);
|
||||
//
|
||||
//
|
||||
// str = render("@|bold foo"); // shouldn't work
|
||||
// System.err.println(str + " <- shouldn't work");
|
||||
//
|
||||
// str = render("@|bold|@"); // shouldn't work
|
||||
// System.err.println(str + " <- shouldn't work");
|
||||
//
|
||||
// Ansi.systemUninstall();
|
||||
//
|
||||
// str = render("@|bold foo|@foo");
|
||||
// System.out.println(str + " <- shouldn't work");
|
||||
|
||||
|
||||
System.err.println("ver : " + Console.getVersion());
|
||||
|
||||
|
||||
System.out.println("Now testing the input console. '?' to quit");
|
||||
Console.ENABLE_ECHO = true;
|
||||
|
||||
int read;
|
||||
while ((read = Console.in.read()) != '?') {
|
||||
// System.err.println("READ :" + read + " (" + (char) read + ")");
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,10 @@
|
||||
|
||||
package com.dorkbox.console;
|
||||
|
||||
import static dorkbox.console.output.Ansi.ansi;
|
||||
import static dorkbox.console.output.AnsiRenderer.render;
|
||||
import static dorkbox.console.output.Attribute.BOLD;
|
||||
import static dorkbox.console.output.Color.RED;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
@ -30,32 +34,54 @@ import dorkbox.console.output.Color;
|
||||
*/
|
||||
public class AnsiTest
|
||||
{
|
||||
@Test
|
||||
public void testSetEnabled() throws Exception {
|
||||
Ansi.setEnabled(false);
|
||||
new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run() {
|
||||
assertEquals(false, Ansi.isEnabled());
|
||||
}
|
||||
}.run();
|
||||
|
||||
Ansi.setEnabled(true);
|
||||
new Thread()
|
||||
{
|
||||
@Override
|
||||
public void run() {
|
||||
assertEquals(true, Ansi.isEnabled());
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClone() throws CloneNotSupportedException {
|
||||
Ansi ansi = Ansi.ansi().a("Some text").bg(Color.BLACK).fg(Color.WHITE);
|
||||
Ansi clone = new Ansi(ansi);
|
||||
Ansi ansi = ansi().a("Some text").bg(Color.BLACK).fg(Color.WHITE);
|
||||
Ansi clone = ansi(ansi);
|
||||
|
||||
assertEquals(ansi.a("test").reset().toString(), clone.a("test").reset().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOutput() throws CloneNotSupportedException {
|
||||
|
||||
// verify the output renderer
|
||||
String str = render("@|bold foo|@foo");
|
||||
assertEquals(ansi().a(BOLD).a("foo").reset().a("foo").toString(), str);
|
||||
assertEquals(ansi().bold().a("foo").reset().a("foo").toString(), str);
|
||||
|
||||
|
||||
str = render("@|bold,red foo|@");
|
||||
assertEquals(ansi().a(BOLD).fg(RED).a("foo").reset().toString(), str);
|
||||
assertEquals(ansi().bold().fg(RED).a("foo").reset().toString(), str);
|
||||
|
||||
str = render("@|bold,red foo bar baz|@");
|
||||
assertEquals(ansi().a(BOLD).fg(RED).a("foo bar baz").reset().toString(), str);
|
||||
assertEquals(ansi().bold().fg(RED).a("foo bar baz").reset().toString(), str);
|
||||
|
||||
|
||||
str = render("@|bold,red foo bar baz|@ ick @|bold,red foo bar baz|@");
|
||||
String expected = ansi().a(BOLD)
|
||||
.fg(RED)
|
||||
.a("foo bar baz")
|
||||
.reset()
|
||||
.a(" ick ")
|
||||
.a(BOLD)
|
||||
.fg(RED)
|
||||
.a("foo bar baz")
|
||||
.reset()
|
||||
.toString();
|
||||
|
||||
assertEquals(expected, str);
|
||||
|
||||
|
||||
str = render("@|bold foo"); // shouldn't work
|
||||
System.err.println(str + " <- shouldn't work");
|
||||
|
||||
str = render("@|bold|@"); // shouldn't work
|
||||
System.err.println(str + " <- shouldn't work");
|
||||
|
||||
str = render("@|bold foo|@foo");
|
||||
System.out.println(str + " <- shouldn't work");
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@ -29,6 +30,7 @@ import dorkbox.console.output.HtmlAnsiOutputStream;
|
||||
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
|
||||
*/
|
||||
public class HtmlAnsiOutputStreamTest {
|
||||
private static final Charset charset = Charset.forName("UTF-8");
|
||||
|
||||
@Test
|
||||
public void testNoMarkup() throws IOException {
|
||||
@ -82,8 +84,9 @@ public class HtmlAnsiOutputStreamTest {
|
||||
private String colorize(String text) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
HtmlAnsiOutputStream hos = new HtmlAnsiOutputStream(os);
|
||||
hos.write(text.getBytes("UTF-8"));
|
||||
|
||||
hos.write(text.getBytes(charset));
|
||||
hos.close();
|
||||
return new String(os.toByteArray(), "UTF-8");
|
||||
return new String(os.toByteArray(), charset);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
[?7h[255D[40m
|
||||
[0;1;33m[22C[32mレトトソレトトトトトソ レトトトトトソ レトトトトトトツトトソ
|
||||
[22Cウ[37mロロ[32mテル[37mロロロロロ[32mタツル[37mロロロロロ[32mタツル[37mロロロロロロ[32mウ[37m゙ン[32mウ
|
||||
[17Cレトトソ ウ[37mロロ[32mウ[37mロロワワワロロ[32mウ[37mロロ[32mレトソ[37mロロ[32mウ[37mロロワワワワ [32mウ[37mワワ[32mウ
|
||||
[17Cウ[37mアア[32mタトル[37mアロ[32mウ[37mアロ[32mレトソ[37mアロ[32mウ[37mアロ[32mウ ウ[37mアロ[32mウ [37m゚゚゚゚アロ[32mウ[37mアロ[32mウ
|
||||
[17Cタソ[37mイイイイイ[32mレエ[37mイイ[32mウ ウ[37mイイ[32mウ[37mイイ[32mウ ウ[37mイイ[32mウ[37m゚イイイイイ゚[32mウ[37mイイ[32mウ
|
||||
[18Cタトトトトトルタトトル タトトチトトル タトトチトトトトトトトチトトル
|
||||
[0m
|
Loading…
Reference in New Issue
Block a user