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
|
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;
|
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
|
public
|
||||||
class Console {
|
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 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 = '*';
|
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;
|
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
|
// OS types supported by the input console. Default is AUTO
|
||||||
public static final String AUTO = "auto";
|
public static final String AUTO = "auto";
|
||||||
public static final String UNIX = "unix";
|
public static final String UNIX = "unix";
|
||||||
public static final String WIN = "win";
|
|
||||||
public static final String WINDOWS = "windows";
|
public static final String WINDOWS = "windows";
|
||||||
|
|
||||||
public static final String NONE = "none"; // this is the same as unsupported
|
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
|
// valid are what's above
|
||||||
public static final String INPUT_CONSOLE_OSTYPE = "AUTO";
|
@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";
|
return "2.9";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Input in = new Input();
|
/**
|
||||||
public static String out;
|
* Reads single character input from the console.
|
||||||
public static String err;
|
*
|
||||||
|
* @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;
|
package dorkbox.console;
|
||||||
|
|
||||||
/**
|
import java.io.IOException;
|
||||||
*
|
import java.io.InputStream;
|
||||||
*/
|
import java.io.PrintStream;
|
||||||
public
|
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 {
|
class Input {
|
||||||
|
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
|
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() {
|
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
|
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||||
* subsidiaries or affiliates. All rights reserved.
|
* subsidiaries or affiliates. All rights reserved.
|
||||||
* <p>
|
* <p>
|
||||||
@ -14,7 +29,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dorkbox.console.output;
|
package dorkbox.console.output;
|
||||||
|
|
||||||
import java.io.FilterOutputStream;
|
import java.io.FilterOutputStream;
|
||||||
@ -24,21 +38,19 @@ import java.nio.charset.Charset;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ANSI output stream extracts ANSI escape codes written to
|
* A ANSI output stream extracts ANSI escape codes written to an output stream.
|
||||||
* an output stream.
|
|
||||||
*
|
*
|
||||||
* For more information about ANSI escape codes, see:
|
* For more information about ANSI escape codes, see:
|
||||||
* http://en.wikipedia.org/wiki/ANSI_escape_code
|
* http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||||
*
|
*
|
||||||
* This class just filters out the escape codes so that they are not
|
* This class just filters out the escape codes so that they are not sent out to the underlying OutputStream. Subclasses should
|
||||||
* sent out to the underlying OutputStream. Subclasses should
|
|
||||||
* actually perform the ANSI escape behaviors.
|
* actually perform the ANSI escape behaviors.
|
||||||
*
|
*
|
||||||
|
* @author dorkbox, llc
|
||||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
||||||
* @author Joris Kuipers
|
* @author Joris Kuipers
|
||||||
* @since 1.0
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("NumericCastThatLosesPrecision")
|
@SuppressWarnings({"NumericCastThatLosesPrecision", "WeakerAccess"})
|
||||||
public
|
public
|
||||||
class AnsiOutputStream extends FilterOutputStream {
|
class AnsiOutputStream extends FilterOutputStream {
|
||||||
private static final Charset CHARSET = Charset.forName("UTF-8");
|
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 CYAN = 6;
|
||||||
static final int WHITE = 7;
|
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_DOWN = 'B';
|
||||||
static final char CURSOR_RIGHT = 'C';
|
static final char CURSOR_FORWARD = 'C';
|
||||||
static final char CURSOR_LEFT = 'D';
|
static final char CURSOR_BACK = '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_ERASE_SCREEN = 'J';
|
static final char CURSOR_DOWN_LINE = 'E'; // Moves cursor to beginning of the line n (default 1) lines down.
|
||||||
static final char CURSOR_ERASE_LINE = 'K';
|
static final char CURSOR_UP_LINE = 'F'; // Moves cursor to beginning of the line n (default 1) lines up.
|
||||||
static final char PAGE_SCROLL_UP = 'S';
|
|
||||||
static final char PAGE_SCROLL_DOWN = 'T';
|
static final char CURSOR_TO_COL = 'G'; // Moves the cursor to column n (default 1).
|
||||||
static final char SAVE_CURSOR_POS = 's';
|
|
||||||
static final char RESTORE_CURSOR_POS = 'u';
|
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 TEXT_ATTRIBUTE = 'm';
|
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_RESET = 0; // Reset / Normal - all attributes off
|
||||||
static final int ATTRIBUTE_BOLD = 1; // Intensity: Bold
|
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_CONCEAL_OFF = 28; // Reveal conceal off
|
||||||
static final int ATTRIBUTE_STRIKETHROUGH_OFF = 29; // Not crossed out
|
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_FG = 39; // Default text color (foreground)
|
||||||
static final int ATTRIBUTE_DEFAULT_BG = 49; // Default background color
|
static final int ATTRIBUTE_DEFAULT_BG = 49; // Default background color
|
||||||
|
|
||||||
|
|
||||||
// for Erase Screen/Line
|
// for Erase Screen/Line
|
||||||
static final int ERASE_TO_END = 0;
|
static final int ERASE_TO_END = 0;
|
||||||
static final int ERASE_TO_BEGINNING = 1;
|
static final int ERASE_TO_BEGINNING = 1;
|
||||||
static final int ERASE_ALL = 2;
|
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 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_FIRST_ESC_CHAR = 0;
|
||||||
private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1;
|
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 BEL = 7;
|
||||||
private static final int SECOND_ST_CHAR = '\\';
|
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
|
public
|
||||||
void write(int data) throws IOException {
|
void write(int data) throws IOException {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@ -301,9 +329,6 @@ class AnsiOutputStream extends FilterOutputStream {
|
|||||||
state = LOOKING_FOR_FIRST_ESC_CHAR;
|
state = LOOKING_FOR_FIRST_ESC_CHAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the escape command was processed.
|
* @return true if the escape command was processed.
|
||||||
*/
|
*/
|
||||||
@ -317,10 +342,10 @@ class AnsiOutputStream extends FilterOutputStream {
|
|||||||
case CURSOR_DOWN:
|
case CURSOR_DOWN:
|
||||||
processCursorDown(optionInt(options, 0, 1));
|
processCursorDown(optionInt(options, 0, 1));
|
||||||
return true;
|
return true;
|
||||||
case CURSOR_RIGHT:
|
case CURSOR_FORWARD:
|
||||||
processCursorRight(optionInt(options, 0, 1));
|
processCursorRight(optionInt(options, 0, 1));
|
||||||
return true;
|
return true;
|
||||||
case CURSOR_LEFT:
|
case CURSOR_BACK:
|
||||||
processCursorLeft(optionInt(options, 0, 1));
|
processCursorLeft(optionInt(options, 0, 1));
|
||||||
return true;
|
return true;
|
||||||
case CURSOR_DOWN_LINE:
|
case CURSOR_DOWN_LINE:
|
||||||
@ -342,10 +367,10 @@ class AnsiOutputStream extends FilterOutputStream {
|
|||||||
case CURSOR_ERASE_LINE:
|
case CURSOR_ERASE_LINE:
|
||||||
processEraseLine(optionInt(options, 0, 0));
|
processEraseLine(optionInt(options, 0, 0));
|
||||||
return true;
|
return true;
|
||||||
case PAGE_SCROLL_UP:
|
case SCROLL_UP:
|
||||||
processScrollUp(optionInt(options, 0, 1));
|
processScrollUp(optionInt(options, 0, 1));
|
||||||
return true;
|
return true;
|
||||||
case PAGE_SCROLL_DOWN:
|
case SCROLL_DOWN:
|
||||||
processScrollDown(optionInt(options, 0, 1));
|
processScrollDown(optionInt(options, 0, 1));
|
||||||
return true;
|
return true;
|
||||||
case TEXT_ATTRIBUTE:
|
case TEXT_ATTRIBUTE:
|
||||||
@ -554,7 +579,6 @@ class AnsiOutputStream extends FilterOutputStream {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void close() throws IOException {
|
void close() throws IOException {
|
||||||
write(RESET_CODE);
|
|
||||||
flush();
|
flush();
|
||||||
super.close();
|
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
|
@Override
|
||||||
public
|
public
|
||||||
PrintWriter format(final String format, final Object... args) {
|
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));
|
print(String.format(format, args));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -78,6 +75,7 @@ class AnsiRenderWriter extends PrintWriter {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
PrintWriter format(final Locale l, final String format, final Object... args) {
|
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));
|
print(String.format(l, format, args));
|
||||||
return this;
|
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).
|
* Copyright (C) 2009 the original author(s).
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -13,7 +28,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dorkbox.console.output;
|
package dorkbox.console.output;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -39,6 +53,7 @@ import java.util.Map;
|
|||||||
* <pre>
|
* <pre>
|
||||||
* <tt>@|bold,red Warning!|@</tt>
|
* <tt>@|bold,red Warning!|@</tt>
|
||||||
* </pre>
|
* </pre>
|
||||||
|
* For Colors, FG_x and BG_x are supported, as are BRIGHT_x (and consequently, FG_BRIGHT_x)
|
||||||
*
|
*
|
||||||
* @author dorkbox, llc
|
* @author dorkbox, llc
|
||||||
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
|
* @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 BEGIN_TOKEN_LEN = 2;
|
||||||
private static final int END_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
|
static
|
||||||
void reg(Enum anEnum, String codeName) {
|
void reg(Enum anEnum, String codeName) {
|
||||||
@ -64,11 +85,11 @@ class AnsiRenderer {
|
|||||||
|
|
||||||
static
|
static
|
||||||
void reg(Enum anEnum, String codeName, boolean isBackgroundColor) {
|
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 ansi The Ansi to render upon
|
||||||
* @param codeNames The code names to render
|
* @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 ansi The Ansi to render upon
|
||||||
* @param codeName The code name to render
|
* @param codeName The code name to render
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
Ansi render(Ansi ansi, String codeName) {
|
Ansi render(Ansi ansi, String codeName) {
|
||||||
AnsiCode ansiCode = codeMap.get(codeName.toUpperCase(Locale.ENGLISH));
|
AnsiCodeMap ansiCodeMap = codeMap.get(codeName.toUpperCase(Locale.ENGLISH));
|
||||||
assert ansiCode != null : "Invalid ANSI code name: '" + codeName + "'";
|
assert ansiCodeMap != null : "Invalid ANSI code name: '" + codeName + "'";
|
||||||
|
|
||||||
if (ansiCode.isColor()) {
|
if (ansiCodeMap.isColor()) {
|
||||||
if (ansiCode.isBackgroundColor()) {
|
if (ansiCodeMap.isBackgroundColor()) {
|
||||||
ansi = ansi.bg(ansiCode.getColor());
|
ansi = ansi.bg(ansiCodeMap.getColor());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ansi = ansi.fg(ansiCode.getColor());
|
ansi = ansi.fg(ansiCodeMap.getColor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ansiCode.isAttribute()) {
|
else if (ansiCodeMap.isAttribute()) {
|
||||||
ansi = ansi.a(ansiCode.getAttribute());
|
ansi = ansi.a(ansiCodeMap.getAttribute());
|
||||||
} else {
|
} else {
|
||||||
assert false : "Undetermined ANSI code name: '" + codeName + "'";
|
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 text The text to render
|
||||||
* @param codeNames The code names 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
|
* @param codeNames The code names to render
|
||||||
*
|
*
|
||||||
|
@ -18,6 +18,7 @@ package dorkbox.console.output;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ANSI string which reports the size of rendered text correctly (ignoring any ANSI escapes).
|
* An ANSI string which reports the size of rendered text correctly (ignoring any ANSI escapes).
|
||||||
@ -27,6 +28,7 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class AnsiString implements CharSequence {
|
class AnsiString implements CharSequence {
|
||||||
|
private static final Charset CHARSET = Charset.forName("UTF-8");
|
||||||
private final CharSequence encoded;
|
private final CharSequence encoded;
|
||||||
|
|
||||||
private final CharSequence plain;
|
private final CharSequence plain;
|
||||||
@ -47,7 +49,7 @@ class AnsiString implements CharSequence {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
out.write(str.toString()
|
out.write(str.toString()
|
||||||
.getBytes());
|
.getBytes(CHARSET));
|
||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
} catch (IOException e) {
|
} 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;
|
package dorkbox.console.output;
|
||||||
|
|
||||||
import static dorkbox.console.output.AnsiOutputStream.ATTRIBUTE_BLINK_FAST;
|
import static dorkbox.console.output.AnsiOutputStream.ATTRIBUTE_BLINK_FAST;
|
||||||
|
@ -17,21 +17,45 @@ package dorkbox.console.output;
|
|||||||
|
|
||||||
public
|
public
|
||||||
enum Color {
|
enum Color {
|
||||||
BLACK (AnsiOutputStream.BLACK, "BLACK"),
|
BLACK (AnsiOutputStream.BLACK, true, "BLACK"),
|
||||||
RED (AnsiOutputStream.RED, "RED"),
|
RED (AnsiOutputStream.RED, true, "RED"),
|
||||||
GREEN (AnsiOutputStream.GREEN, "GREEN"),
|
GREEN (AnsiOutputStream.GREEN, true, "GREEN"),
|
||||||
YELLOW (AnsiOutputStream.YELLOW, "YELLOW"),
|
YELLOW (AnsiOutputStream.YELLOW, true, "YELLOW"),
|
||||||
BLUE (AnsiOutputStream.BLUE, "BLUE"),
|
BLUE (AnsiOutputStream.BLUE, true, "BLUE"),
|
||||||
MAGENTA(AnsiOutputStream.MAGENTA, "MAGENTA"),
|
MAGENTA(AnsiOutputStream.MAGENTA, true, "MAGENTA"),
|
||||||
CYAN (AnsiOutputStream.CYAN, "CYAN"),
|
CYAN (AnsiOutputStream.CYAN, true, "CYAN"),
|
||||||
WHITE (AnsiOutputStream.WHITE, "WHITE");
|
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 int value;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
private final boolean isNormal;
|
||||||
|
|
||||||
Color(int index, String name) {
|
Color(int index, boolean isNormal, String name) {
|
||||||
this.value = index;
|
this.value = index;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.isNormal = isNormal;
|
||||||
|
|
||||||
// register code names with the ANSI renderer
|
// register code names with the ANSI renderer
|
||||||
AnsiRenderer.reg(this, name, false);
|
AnsiRenderer.reg(this, name, false);
|
||||||
@ -45,22 +69,23 @@ enum Color {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
|
||||||
int fg() {
|
int fg() {
|
||||||
return value + 30;
|
return value + 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
|
||||||
int bg() {
|
int bg() {
|
||||||
return value + 40;
|
return value + 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
/** is this a BRIGHT color or NORMAL color? */
|
||||||
|
boolean isNormal() {
|
||||||
|
return isNormal;
|
||||||
|
}
|
||||||
|
|
||||||
int fgBright() {
|
int fgBright() {
|
||||||
return value + 90;
|
return value + 90;
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
|
||||||
int bgBright() {
|
int bgBright() {
|
||||||
return value + 100;
|
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
|
* Copyright (C) 2009, Progress Software Corporation and/or its
|
||||||
* subsidiaries or affiliates. All rights reserved.
|
* subsidiaries or affiliates. All rights reserved.
|
||||||
* <p>
|
* <p>
|
||||||
@ -23,6 +38,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @author dorkbox, llc
|
||||||
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
|
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2009, Progress Software Corporation and/or its
|
* Copyright 2016 dorkbox, llc
|
||||||
* subsidiaries or affiliates. All rights reserved.
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
@ -14,11 +13,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
@ -58,7 +59,7 @@ import dorkbox.console.util.windows.SMALL_RECT;
|
|||||||
* @author Joris Kuipers
|
* @author Joris Kuipers
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("NumericCastThatLosesPrecision")
|
@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_FOREGROUND_COLOR_MAP[];
|
||||||
private static final short ANSI_BACKGROUND_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 savedX = (short) -1;
|
||||||
private volatile short savedY = (short) -1;
|
private volatile short savedY = (short) -1;
|
||||||
|
|
||||||
|
public
|
||||||
WindowsAnsiOutputStream(final OutputStream os, int fileHandle) throws IOException {
|
WindowsAnsiOutputStream(final OutputStream os, int fileHandle) throws IOException {
|
||||||
super(os);
|
super(os);
|
||||||
|
|
||||||
if (fileHandle == AnsiConsole.STDOUT_FILENO) {
|
if (fileHandle == 1) { // STDOUT_FILENO
|
||||||
fileHandle = Kernel32.STD_OUTPUT_HANDLE;
|
fileHandle = Kernel32.STD_OUTPUT_HANDLE;
|
||||||
} else if (fileHandle == AnsiConsole.STDERR_FILENO) {
|
} else if (fileHandle == 2) { // STDERR_FILENO
|
||||||
fileHandle = Kernel32.STD_ERROR_HANDLE;
|
fileHandle = Kernel32.STD_ERROR_HANDLE;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Invalid file handle " + fileHandle);
|
throw new IllegalArgumentException("Invalid file handle " + fileHandle);
|
||||||
@ -321,7 +323,7 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
|||||||
void processCursorUpLine(final int count) throws IOException {
|
void processCursorUpLine(final int count) throws IOException {
|
||||||
getConsoleInfo();
|
getConsoleInfo();
|
||||||
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y - count);
|
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y - count);
|
||||||
info.cursorPosition.x = 0;
|
info.cursorPosition.x = (short) 0;
|
||||||
applyCursorPosition();
|
applyCursorPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +331,7 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
|
|||||||
void processCursorDownLine(final int count) throws IOException {
|
void processCursorDownLine(final int count) throws IOException {
|
||||||
getConsoleInfo();
|
getConsoleInfo();
|
||||||
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y + count);
|
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y + count);
|
||||||
info.cursorPosition.x = 0;
|
info.cursorPosition.x = (short) 0;
|
||||||
applyCursorPosition();
|
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.
|
* @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
|
* 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
|
// the content that will be scrolled
|
||||||
scrollRect.top = (short) (0);
|
scrollRect.top = (short) (0);
|
||||||
scrollRect.bottom = (short) (Short.MAX_VALUE);
|
scrollRect.bottom = Short.MAX_VALUE;
|
||||||
scrollRect.left = 0;
|
scrollRect.left = (short) 0;
|
||||||
scrollRect.right = (short) (info.size.x - 1);
|
scrollRect.right = (short) (info.size.x - 1);
|
||||||
|
|
||||||
// The destination for the scroll rectangle is xxx row up/down.
|
// The destination for the scroll rectangle is xxx row up/down.
|
||||||
coordDest.x = 0;
|
coordDest.x = (short) 0;
|
||||||
coordDest.y = (short) (-rowsToScroll);
|
coordDest.y = (short) (-rowsToScroll);
|
||||||
|
|
||||||
// fill the space with whatever color was already there with spaces
|
// 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package dorkbox.console;
|
package dorkbox.console.util;
|
||||||
|
|
||||||
public class TerminalType {
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public String toString() {
|
public
|
||||||
return "Size: " + size + " CursorPos: " + cursorPosition + " Attribs: " + attributes + " Window: " + window + " MaxWindowSize: " + maximumWindowSize;
|
String toString() {
|
||||||
|
return "Size: " + size + " CursorPos: " + cursorPosition + " Attribs: " + attributes + " Window: " + window + " MaxWindowSize: " +
|
||||||
|
maximumWindowSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,6 @@ import com.sun.jna.Structure;
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class COORD extends Structure {
|
class COORD extends Structure {
|
||||||
static public class ByValue extends COORD implements Structure.ByValue { }
|
|
||||||
|
|
||||||
public short x;
|
public short x;
|
||||||
public short y;
|
public short y;
|
||||||
|
|
||||||
@ -45,7 +43,12 @@ class COORD extends Structure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public
|
||||||
|
String toString() {
|
||||||
return x + ":" + y;
|
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;
|
package dorkbox.console.util.windows;
|
||||||
|
|
||||||
import com.sun.jna.Union;
|
import com.sun.jna.Union;
|
||||||
|
|
||||||
public class CharUnion extends Union {
|
public
|
||||||
|
class CharUnion extends Union {
|
||||||
public char unicodeChar;
|
public char unicodeChar;
|
||||||
public byte asciiChar;
|
public byte asciiChar;
|
||||||
|
|
||||||
public CharUnion() {
|
public
|
||||||
|
CharUnion() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharUnion(char c) {
|
public
|
||||||
|
CharUnion(char c) {
|
||||||
setType(char.class);
|
setType(char.class);
|
||||||
unicodeChar = c;
|
unicodeChar = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharUnion(byte c) {
|
public
|
||||||
|
CharUnion(byte c) {
|
||||||
setType(byte.class);
|
setType(byte.class);
|
||||||
asciiChar = c;
|
asciiChar = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(char c) {
|
public
|
||||||
|
void set(char c) {
|
||||||
setType(char.class);
|
setType(char.class);
|
||||||
unicodeChar = c;
|
unicodeChar = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(byte c) {
|
public
|
||||||
|
void set(byte c) {
|
||||||
setType(byte.class);
|
setType(byte.class);
|
||||||
asciiChar = c;
|
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
|
public
|
||||||
class HANDLE extends PointerType {
|
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));
|
public static final HANDLE INVALID_HANDLE_VALUE = new HANDLE(Pointer.createConstant(Pointer.SIZE == 8 ? -1 : 0xFFFFFFFFL));
|
||||||
|
|
||||||
private boolean immutable;
|
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;
|
package dorkbox.console.util.windows;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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
|
* https://msdn.microsoft.com/en-us/library/ms683499(v=VS.85).aspx
|
||||||
*/
|
*/
|
||||||
public class INPUT_RECORD extends Structure {
|
public
|
||||||
static public class ByReference extends INPUT_RECORD implements Structure.ByReference {}
|
class INPUT_RECORD extends Structure {
|
||||||
|
|
||||||
public static final short KEY_EVENT = 0x0001;
|
public static final short KEY_EVENT = 0x0001;
|
||||||
public static final short MOUSE_EVENT = 0x0002;
|
public static final short MOUSE_EVENT = 0x0002;
|
||||||
|
|
||||||
public short EventType;
|
public short EventType;
|
||||||
public EventUnion Event;
|
public EventUnion Event;
|
||||||
|
|
||||||
public static class EventUnion extends Union {
|
|
||||||
public KEY_EVENT_RECORD KeyEvent;
|
|
||||||
public MOUSE_EVENT_RECORD MouseEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read() {
|
public
|
||||||
|
void read() {
|
||||||
readField("EventType");
|
readField("EventType");
|
||||||
switch (EventType) {
|
switch (EventType) {
|
||||||
case KEY_EVENT:
|
case KEY_EVENT:
|
||||||
@ -38,7 +47,19 @@ public class INPUT_RECORD extends Structure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getFieldOrder() {
|
protected
|
||||||
|
List<String> getFieldOrder() {
|
||||||
return Arrays.asList("EventType", "Event");
|
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;
|
package dorkbox.console.util.windows;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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
|
* 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 boolean keyDown;
|
||||||
public short repeatCount;
|
public short repeatCount;
|
||||||
public short virtualKeyCode;
|
public short virtualKeyCode;
|
||||||
@ -17,7 +33,8 @@ public class KEY_EVENT_RECORD extends Structure {
|
|||||||
public int controlKeyState;
|
public int controlKeyState;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getFieldOrder() {
|
protected
|
||||||
|
List<String> getFieldOrder() {
|
||||||
return Arrays.asList("keyDown", "repeatCount", "virtualKeyCode", "virtualScanCode", "uChar", "controlKeyState");
|
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;
|
package dorkbox.console.util.windows;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -5,14 +20,16 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.sun.jna.Structure;
|
import com.sun.jna.Structure;
|
||||||
|
|
||||||
public class MOUSE_EVENT_RECORD extends Structure {
|
public
|
||||||
|
class MOUSE_EVENT_RECORD extends Structure {
|
||||||
public COORD mousePosition;
|
public COORD mousePosition;
|
||||||
public int buttonState;
|
public int buttonState;
|
||||||
public int controlKeyState;
|
public int controlKeyState;
|
||||||
public int eventFlags;
|
public int eventFlags;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getFieldOrder() {
|
protected
|
||||||
|
List<String> getFieldOrder() {
|
||||||
return Arrays.asList("mousePosition", "buttonState", "controlKeyState", "eventFlags");
|
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
|
* https://msdn.microsoft.com/en-us/library/ms686311%28VS.85%29.aspx
|
||||||
*/
|
*/
|
||||||
public class SMALL_RECT extends Structure {
|
@SuppressWarnings("NumericCastThatLosesPrecision")
|
||||||
static public class ByReference extends SMALL_RECT implements Structure.ByReference { }
|
public
|
||||||
|
class SMALL_RECT extends Structure {
|
||||||
public short left;
|
public short left;
|
||||||
public short top;
|
public short top;
|
||||||
public short right;
|
public short right;
|
||||||
public short bottom;
|
public short bottom;
|
||||||
|
|
||||||
public short width() {
|
public
|
||||||
return (short) (this.right-this.left);
|
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
|
@Override
|
||||||
protected List<String> getFieldOrder() {
|
protected
|
||||||
|
List<String> getFieldOrder() {
|
||||||
return Arrays.asList("left", "top", "right", "bottom");
|
return Arrays.asList("left", "top", "right", "bottom");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public
|
||||||
|
String toString() {
|
||||||
return "LTRB: " + left + "," + top + "," + right + "," + bottom;
|
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;
|
package com.dorkbox.console;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import dorkbox.console.Console;
|
||||||
import dorkbox.console.output.Ansi;
|
import dorkbox.console.output.Ansi;
|
||||||
|
import dorkbox.console.output.Color;
|
||||||
|
import dorkbox.console.output.Erase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* System output for visual confirmation of ANSI codes.
|
||||||
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
|
* <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 {
|
System.out.println(Ansi.ansi()
|
||||||
String file = "jansi.ans";
|
.fg(Color.BLACK).a("black").bg(Color.BLACK).a("black")
|
||||||
if( args.length>0 )
|
.reset()
|
||||||
file = args[0];
|
.fg(Color.BRIGHT_BLACK).a("b-black").bg(Color.BRIGHT_BLACK).a("b-black")
|
||||||
|
.reset());
|
||||||
|
|
||||||
// Allows us to disable ANSI processing.
|
System.out.println(Ansi.ansi()
|
||||||
if( "true".equals(System.getProperty("jansi", "true")) ) {
|
.fg(Color.BLUE).a("blue").bg(Color.BLUE).a("blue")
|
||||||
Ansi.systemInstall();
|
.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);
|
Console.systemUninstall();
|
||||||
int c;
|
|
||||||
while( (c=f.read())>=0 ) {
|
|
||||||
System.out.write(c);
|
|
||||||
}
|
}
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
package com.dorkbox.console;
|
||||||
|
|
||||||
import static dorkbox.console.output.AnsiRenderer.render;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import dorkbox.console.Console;
|
||||||
import dorkbox.console.output.Ansi;
|
import dorkbox.console.output.Ansi;
|
||||||
import dorkbox.console.output.AnsiRenderer;
|
import dorkbox.console.output.AnsiRenderer;
|
||||||
import dorkbox.console.output.Color;
|
import dorkbox.console.output.Color;
|
||||||
@ -43,7 +44,7 @@ public class AnsiRendererTest
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
Ansi.setEnabled(true);
|
Console.ENABLE_ANSI = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -66,7 +67,6 @@ public class AnsiRendererTest
|
|||||||
String str = render("@|bold,red foo|@");
|
String str = render("@|bold,red foo|@");
|
||||||
System.out.println(str);
|
System.out.println(str);
|
||||||
assertEquals(Ansi.ansi().a(BOLD).fg(Color.RED).a("foo").reset().toString(), 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
|
@Test
|
||||||
@ -74,7 +74,6 @@ public class AnsiRendererTest
|
|||||||
String str = render("@|bold,red foo bar baz|@");
|
String str = render("@|bold,red foo bar baz|@");
|
||||||
System.out.println(str);
|
System.out.println(str);
|
||||||
assertEquals(Ansi.ansi().a(BOLD).fg(Color.RED).a("foo bar baz").reset().toString(), 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
|
@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;
|
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 static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -30,32 +34,54 @@ import dorkbox.console.output.Color;
|
|||||||
*/
|
*/
|
||||||
public class AnsiTest
|
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
|
@Test
|
||||||
public void testClone() throws CloneNotSupportedException {
|
public void testClone() throws CloneNotSupportedException {
|
||||||
Ansi ansi = Ansi.ansi().a("Some text").bg(Color.BLACK).fg(Color.WHITE);
|
Ansi ansi = ansi().a("Some text").bg(Color.BLACK).fg(Color.WHITE);
|
||||||
Ansi clone = new Ansi(ansi);
|
Ansi clone = ansi(ansi);
|
||||||
|
|
||||||
assertEquals(ansi.a("test").reset().toString(), clone.a("test").reset().toString());
|
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.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ import dorkbox.console.output.HtmlAnsiOutputStream;
|
|||||||
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
|
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
|
||||||
*/
|
*/
|
||||||
public class HtmlAnsiOutputStreamTest {
|
public class HtmlAnsiOutputStreamTest {
|
||||||
|
private static final Charset charset = Charset.forName("UTF-8");
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoMarkup() throws IOException {
|
public void testNoMarkup() throws IOException {
|
||||||
@ -82,8 +84,9 @@ public class HtmlAnsiOutputStreamTest {
|
|||||||
private String colorize(String text) throws IOException {
|
private String colorize(String text) throws IOException {
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
HtmlAnsiOutputStream hos = new HtmlAnsiOutputStream(os);
|
HtmlAnsiOutputStream hos = new HtmlAnsiOutputStream(os);
|
||||||
hos.write(text.getBytes("UTF-8"));
|
|
||||||
|
hos.write(text.getBytes(charset));
|
||||||
hos.close();
|
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