Code polish, added comments/javadocs. Added copyright/authors

This commit is contained in:
nathan 2016-05-29 11:44:30 +02:00
parent 6f367aec7e
commit a46cbefb02
34 changed files with 1825 additions and 1751 deletions

38
Console.iml Normal file
View 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>

View File

@ -1,3 +1,3 @@
Manifest-Version: 1.0
Main-Class: com.dorkbox.console.AnsiRendererTestExample
Main-Class: com.dorkbox.console.AnsiConsoleExample

View File

@ -1,36 +1,99 @@
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import dorkbox.console.output.AnsiOutputStream;
import dorkbox.console.output.WindowsAnsiOutputStream;
import dorkbox.console.util.posix.CLibraryPosix;
import dorkbox.console.util.windows.Kernel32;
import dorkbox.util.Property;
/**
* Provides a fluent API for generating ANSI escape sequences and providing access to streams that support it.
* <p>
* See: https://en.wikipedia.org/wiki/ANSI_escape_code
*
* @author dorkbox, llc
*/
public
class Console {
/**
* If true, allows an ANSI output stream to be created, otherwise a NO-OP stream is created instead
*/
@Property
public static boolean ENABLE_ANSI = true;
/**
* If true, then we always force the raw ANSI output stream to be enabled (even if the output stream is not aware of ANSI commands).
* This can be used to obtain the raw ANSI escape codes for other color aware programs (ie: less -r)
*/
@Property
public static boolean FORCE_ENABLE_ANSI = false;
/**
* Enables or disables character echo to stdout in the console, should call {@link #setEchoEnabled(boolean)} after initialization
*/
@Property
public static volatile boolean ENABLE_ECHO = true;
public static boolean PASSWORD_ECHO = true;
/**
* Enables or disables CTRL-C behavior in the console, should call {@link #setInterruptEnabled(boolean)} after initialization
*/
@Property
public static volatile boolean ENABLE_INTERRUPT = false;
@Property
public static char PASSWORD_ECHO_CHAR = '*';
// how many threads can read from this input at the same time
public static int NUMBER_OF_READERS = 32;
/**
* Enables the backspace key to delete characters in the line buffer and (if ANSI is enabled) from the screen.
*/
@Property
public final static boolean ENABLE_BACKSPACE = true;
// enableBackspace = Boolean.parseBoolean(System.getProperty(Console.ENABLE_BACKSPACE, "true"));
public static String ENABLE_BACKSPACEs = "input.enableBackspace";
// OS types supported by the input console. Default is AUTO
public static final String AUTO = "auto";
public static final String UNIX = "unix";
public static final String WIN = "win";
public static final String WINDOWS = "windows";
public static final String NONE = "none"; // this is the same as unsupported
public static final String OFF = "off"; // this is the same as unsupported
public static final String FALSE = "false"; // this is the same as unsupported
public static final String INPUT_CONSOLE_OSTYPE = "AUTO";
// valid are what's above
@Property
public static final String INPUT_CONSOLE_TYPE = "AUTO";
private static final PrintStream original_out = System.out;
private static final PrintStream original_err = System.err;
// protected by synchronize
private static int installed = 0;
private static PrintStream out;
private static PrintStream err;
/**
@ -41,7 +104,217 @@ class Console {
return "2.9";
}
public static Input in = new Input();
public static String out;
public static String err;
/**
* Reads single character input from the console.
*
* @return -1 if no data or problems
*/
public static
int read() {
return Input.read();
}
/**
* Reads a line of characters from the console, defined as everything before the 'ENTER' key is pressed
*
* @return null if no data
*/
public static
String readLine() {
return Input.readLine();
}
/**
* Reads a line of characters from the console as a character array, defined as everything before the 'ENTER' key is pressed
*
* @return empty char[] if no data
*/
public static
char[] readLineChars() {
return Input.readLineChars();
}
/**
* Reads a line of characters from the console as a character array, defined as everything before the 'ENTER' key is pressed
*
* @return empty char[] if no data
*/
public static
char[] readPassword() {
return Input.readLinePassword();
}
/**
* Enables or disables CTRL-C behavior in the console
*/
public static
void setInterruptEnabled(final boolean enabled) {
Console.ENABLE_INTERRUPT = enabled;
Input.setInterruptEnabled(enabled);
}
/**
* Enables or disables character echo to stdout
*/
public static
void setEchoEnabled(final boolean enabled) {
Console.ENABLE_ECHO = enabled;
Input.setEchoEnabled(enabled);
}
/**
* Override System.err and System.out with an ANSI capable {@link java.io.PrintStream}.
*/
public static synchronized
void systemInstall() {
installed++;
if (installed == 1) {
out = out();
err = err();
System.setOut(out);
System.setErr(err);
}
}
/**
* un-does a previous {@link #systemInstall()}.
* <p>
* If {@link #systemInstall()} was called multiple times, then {@link #systemUninstall()} must be called the same number of
* times before it is uninstalled.
*/
public static synchronized
void systemUninstall() {
installed--;
if (installed == 0) {
if (out != null && out != original_out) {
out.close();
System.setOut(original_out);
}
if (err != null && err != original_err) {
err.close();
System.setErr(original_err);
}
}
}
/**
* If we are installed to the system (IE: System.err/out, then reset those streams, otherwise there is nothing to do from a static
* perspective (since creating a NEW ANSI stream will automatically reset the output
*/
public static synchronized
void reset() {
if (installed >= 1) {
// TODO: make this reset readLine, etc as well?
try {
System.out.write(AnsiOutputStream.RESET_CODE);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* If the standard out natively supports ANSI escape codes, then this just returns System.out, otherwise it will provide an ANSI
* aware PrintStream which strips out the ANSI escape sequences or which implement the escape sequences.
*
* @return a PrintStream which is ANSI aware.
*/
public static
PrintStream out() {
if (out == null) {
out = createPrintStream(original_out, 1); // STDOUT_FILENO
}
return out;
}
/**
* If the standard out natively supports ANSI escape codes, then this just returns System.err, otherwise it will provide an ANSI aware
* PrintStream which strips out the ANSI escape sequences or which implement the escape sequences.
*
* @return a PrintStream which is ANSI aware.
*/
public static
PrintStream err() {
if (err == null) {
err = createPrintStream(original_err, 2); // STDERR_FILENO
}
return err;
}
private static boolean isXterm() {
String term = System.getenv("TERM");
return "xterm".equalsIgnoreCase(term);
}
private static
PrintStream createPrintStream(final OutputStream stream, int fileno) {
if (!ENABLE_ANSI) {
// Use the ANSIOutputStream to strip out the ANSI escape sequences.
return new PrintStream(new AnsiOutputStream(stream));
}
if (!isXterm()) {
String os = System.getProperty("os.name");
if (os.startsWith("Windows")) {
// check if windows10+ (which natively supports ANSI)
if (Kernel32.isWindows10OrGreater()) {
// Just wrap it up so that when we get closed, we reset the attributes.
return deafultPrintStream(stream);
}
// On windows we know the console does not interpret ANSI codes..
try {
return new PrintStream(new WindowsAnsiOutputStream(stream, fileno));
} catch (Throwable ignore) {
// this happens when JNA is not in the path.. or
// this happens when the stdout is being redirected to a file.
// this happens when the stdout is being redirected to different console.
}
// Use the ANSIOutputStream to strip out the ANSI escape sequences.
if (!FORCE_ENABLE_ANSI) {
return new PrintStream(new AnsiOutputStream(stream));
}
} else {
// We must be on some unix variant..
try {
// If we can detect that stdout is not a tty.. then setup to strip the ANSI sequences..
if (!FORCE_ENABLE_ANSI && CLibraryPosix.isatty(fileno) == 0) {
return new PrintStream(new AnsiOutputStream(stream));
}
} catch (Throwable ignore) {
// These errors happen if the JNI lib is not available for your platform.
}
}
}
// By default we assume the terminal can handle ANSI codes.
// Just wrap it up so that when we get closed, we reset the attributes.
return deafultPrintStream(stream);
}
private static
PrintStream deafultPrintStream(final OutputStream stream) {
return new PrintStream(new FilterOutputStream(stream) {
@Override
public
void close() throws IOException {
write(AnsiOutputStream.RESET_CODE);
flush();
super.close();
}
});
}
}

View File

@ -1,15 +1,472 @@
/*
* Copyright 2010 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console;
/**
*
*/
public
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.slf4j.Logger;
import dorkbox.console.input.PosixTerminal;
import dorkbox.console.input.Terminal;
import dorkbox.console.input.UnsupportedTerminal;
import dorkbox.console.input.WindowsTerminal;
import dorkbox.console.util.CharHolder;
import dorkbox.objectPool.ObjectPool;
import dorkbox.objectPool.PoolableObject;
import dorkbox.util.OS;
import dorkbox.util.bytes.ByteBuffer2;
import dorkbox.util.bytes.ByteBuffer2Poolable;
class Input {
public
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(Input.class);
private static final char[] emptyLine = new char[0];
private static final List<CharHolder> charInputBuffers = new ArrayList<CharHolder>();
private final static ObjectPool<CharHolder> charInputPool = ObjectPool.NonBlocking(new PoolableObject<CharHolder>() {
@Override
public
CharHolder create() {
return new CharHolder();
}
@Override
public
void onReturn(final CharHolder object) {
// dump the chars in the buffer (safer for passwords, etc)
object.character = (char) 0;
charInputBuffers.remove(object);
}
@Override
public
void onTake(final CharHolder object) {
charInputBuffers.add(object);
}
});
private static final List<ByteBuffer2> lineInputBuffers = new ArrayList<ByteBuffer2>();
private final static ObjectPool<ByteBuffer2> lineInputPool = ObjectPool.NonBlocking(new ByteBuffer2Poolable() {
@Override
public
void onReturn(final ByteBuffer2 object) {
// dump the chars in the buffer (safer for passwords, etc)
object.clearSecure();
lineInputBuffers.remove(object);
}
@Override
public
void onTake(final ByteBuffer2 object) {
lineInputBuffers.add(object);
}
});
private final static Terminal terminal;
private static final Object inputLock = new Object();
private static final Object inputLockSingle = new Object();
private static final Object inputLockLine = new Object();
static {
String type = Console.INPUT_CONSOLE_TYPE.toUpperCase(Locale.ENGLISH);
Throwable didFallbackE = null;
Terminal term;
try {
if (type.equals(Console.UNIX)) {
term = new PosixTerminal();
}
else if (type.equals(Console.WINDOWS)) {
term = new WindowsTerminal();
}
else if (type.equals(Console.NONE)) {
term = new UnsupportedTerminal();
}
else {
// if these cannot be created, because we are in an IDE, an error will be thrown
if (OS.isWindows()) {
term = new WindowsTerminal();
}
else {
term = new PosixTerminal();
}
}
} catch (Exception e) {
didFallbackE = e;
term = new UnsupportedTerminal();
}
terminal = term;
if (logger.isDebugEnabled()) {
logger.debug("Created Terminal: {} ({}w x {}h)", Input.terminal.getClass().getSimpleName(),
Input.terminal.getWidth(), Input.terminal.getHeight());
}
if (didFallbackE != null && !didFallbackE.getMessage().equals(Terminal.CONSOLE_ERROR_INIT)) {
logger.error("Failed to construct terminal, falling back to unsupported.", didFallbackE);
} else if (term instanceof UnsupportedTerminal) {
logger.debug("Terminal is in UNSUPPORTED (best guess). Unable to support single key input. Only line input available.");
}
// echo and backspace
term.setEchoEnabled(Console.ENABLE_ECHO);
term.setInterruptEnabled(Console.ENABLE_INTERRUPT);
Thread consoleThread = new Thread(new Runnable() {
@Override
public
void run() {
Input.run();
}
});
consoleThread.setDaemon(true);
consoleThread.setName("Console Input Reader");
consoleThread.start();
// has to be NOT DAEMON thread, since it must run before the app closes.
// don't forget we have to shut down the ansi console as well
// alternatively, shut everything down when the JVM closes.
Thread shutdownThread = new Thread() {
@Override
public
void run() {
// called when the JVM is shutting down.
release0();
try {
terminal.restore();
// this will 'hang' our shutdown, and honestly, who cares? We're shutting down anyways.
// inputConsole.reader.close(); // hangs on shutdown
} catch (IOException ignored) {
ignored.printStackTrace();
}
}
};
shutdownThread.setName("Console Input Shutdown");
Runtime.getRuntime().addShutdownHook(shutdownThread);
}
static
void setInterruptEnabled(final boolean enabled) {
terminal.setInterruptEnabled(enabled);
}
static
void setEchoEnabled(final boolean enabled) {
terminal.setEchoEnabled(enabled);
}
private static InputStream wrappedInputStream = new InputStream() {
@Override
public
int read() throws IOException {
return Input.read();
}
@Override
public
void close() throws IOException {
Input.release0();
}
};
static
InputStream getInputStream() {
return wrappedInputStream;
}
private
Input() {
}
public int read() {
return InputConsole.read();
/**
* Reads single character input from the console.
*
* @return -1 if no data or problems
*/
static
int read() {
CharHolder holder;
synchronized (inputLock) {
// don't want to register a read() WHILE we are still processing the current input.
// also adds it to the global list of char inputs
holder = charInputPool.take();
}
synchronized (inputLockSingle) {
try {
inputLockSingle.wait();
} catch (InterruptedException e) {
return -1;
}
}
char c = holder.character;
// also clears and removes from the global list of char inputs
charInputPool.put(holder);
return c;
}
/**
* @return empty char[] if no data or problems
*/
static
char[] readLineChars() {
ByteBuffer2 buffer;
synchronized (inputLock) {
// don't want to register a readLine() WHILE we are still processing the current line info.
// also adds it to the global list of line inputs
buffer = lineInputPool.take();
}
synchronized (inputLockLine) {
try {
inputLockLine.wait();
} catch (InterruptedException e) {
return emptyLine;
}
}
int len = buffer.position();
if (len == 0) {
return emptyLine;
}
buffer.rewind();
char[] readChars = buffer.readChars(len / 2); // java always stores chars in 2 bytes
// also clears and removes from the global list of line inputs
lineInputPool.put(buffer);
return readChars;
}
/**
* Reads line input from the console
*
* @return empty char[] if no data
*/
static
char[] readLinePassword() {
// don't bother in an IDE. it won't work.
boolean echoEnabled = Console.ENABLE_ECHO;
Console.ENABLE_ECHO = false;
char[] readLine0 = readLineChars();
Console.ENABLE_ECHO = echoEnabled;
return readLine0;
}
/**
* Reads a single line of characters, defined as everything before the 'ENTER' key is pressed
* @return null if no data
*/
static
String readLine() {
char[] line = Input.readLineChars();
if (line == null) {
return null;
}
return new String(line);
}
/**
* releases any thread still waiting.
*/
private static
void release0() {
synchronized (inputLockSingle) {
inputLockSingle.notifyAll();
}
synchronized (inputLockLine) {
inputLockLine.notifyAll();
}
}
private static
void run() {
Logger logger2 = logger;
final boolean ansiEnabled = Console.ENABLE_ANSI;
// Ansi ansi = Ansi.ansi();
// PrintStream out = AnsiConsole.out;
PrintStream out = System.out;
int typedChar;
char asChar;
final char overWriteChar = ' ';
boolean enableBackspace = Console.ENABLE_BACKSPACE;
// don't type ; in a bash shell, it quits everything
// \n is replaced by \r in unix terminal?
while ((typedChar = terminal.read()) != -1) {
synchronized (inputLock) {
// don't let anyone add a new reader while we are still processing the current actions
asChar = (char) typedChar;
if (logger2.isTraceEnabled()) {
logger2.trace("READ: {} ({})", asChar, typedChar);
}
// notify everyone waiting for a character.
synchronized (inputLockSingle) {
// have to do readChar first (readLine has to deal with \b and \n
for (CharHolder holder : charInputBuffers) {
holder.character = asChar; // copy by value
}
inputLockSingle.notifyAll();
}
// now to handle readLine stuff
// if we type a backspace key, swallow it + previous in READLINE. READCHAR will have it passed anyways.
if (enableBackspace && asChar == '\b') {
int position = 0;
char[] overwrite = null;
// clear ourself + one extra.
for (ByteBuffer2 buffer : lineInputBuffers) {
// size of the buffer BEFORE our backspace was typed
int length = buffer.position();
int amtToOverwrite = 4; // 2*2 backspace is always 2 chars (^?) * 2 because it's bytes
if (length > 1) {
char charAt = buffer.readChar(length - 2);
amtToOverwrite += getPrintableCharacters(charAt);
// delete last item in our buffer
length -= 2;
buffer.setPosition(length);
// now figure out where the cursor is really at.
// this is more memory friendly than buf.toString.length
for (int i = 0; i < length; i += 2) {
charAt = buffer.readChar(i);
position += getPrintableCharacters(charAt);
}
position++;
}
overwrite = new char[amtToOverwrite];
for (int i = 0; i < amtToOverwrite; i++) {
overwrite[i] = overWriteChar;
}
}
if (ansiEnabled && overwrite != null) {
// move back however many, over write, then go back again
// out.print(ansi.cursorToColumn(position));
out.print(overwrite);
// out.print(ansi.cursorToColumn(position));
out.flush();
}
}
else if (asChar == '\n') {
// ignoring \r, because \n is ALWAYS the last character in a new line sequence. (even for windows, which we changed)
synchronized (inputLockLine) {
inputLockLine.notifyAll();
}
}
else {
// only append if we are not a new line.
// our windows console PREVENTS us from returning '\r' (it truncates '\r\n', and returns just '\n')
for (ByteBuffer2 buffer : lineInputBuffers) {
buffer.writeChar(asChar);
}
}
}
}
}
private static final int PLUS_TWO_MAYBE = 128 + 32;
private static final int PLUS_ONE = 128 + 127;
/**
* Return the number of characters that will be printed when the specified character is echoed to the screen
* <p/>
* Adapted from cat by Torbjorn Granlund, as repeated in stty by David MacKenzie.
*/
private static
int getPrintableCharacters(final int ch) {
// StringBuilder sbuff = new StringBuilder();
if (ch >= 32) {
if (ch < 127) {
// sbuff.append((char) ch);
return 1;
}
else if (ch == 127) {
// sbuff.append('^');
// sbuff.append('?');
return 2;
}
else {
// sbuff.append('M');
// sbuff.append('-');
int count = 2;
if (ch >= PLUS_TWO_MAYBE) {
if (ch < PLUS_ONE) {
// sbuff.append((char) (ch - 128));
count++;
}
else {
// sbuff.append('^');
// sbuff.append('?');
count += 2;
}
}
else {
// sbuff.append('^');
// sbuff.append((char) (ch - 128 + 64));
count += 2;
}
return count;
}
}
else {
// sbuff.append('^');
// sbuff.append((char) (ch + 64));
return 2;
}
// return sbuff;
}
}

View File

@ -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

View File

@ -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;
}
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -1,4 +1,19 @@
/**
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* Copyright (C) 2009, Progress Software Corporation and/or its
* subsidiaries or affiliates. All rights reserved.
* <p>
@ -14,7 +29,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console.output;
import java.io.FilterOutputStream;
@ -24,21 +38,19 @@ import java.nio.charset.Charset;
import java.util.ArrayList;
/**
* A ANSI output stream extracts ANSI escape codes written to
* an output stream.
* A ANSI output stream extracts ANSI escape codes written to an output stream.
*
* For more information about ANSI escape codes, see:
* http://en.wikipedia.org/wiki/ANSI_escape_code
*
* This class just filters out the escape codes so that they are not
* sent out to the underlying OutputStream. Subclasses should
* This class just filters out the escape codes so that they are not sent out to the underlying OutputStream. Subclasses should
* actually perform the ANSI escape behaviors.
*
* @author dorkbox, llc
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
* @author Joris Kuipers
* @since 1.0
*/
@SuppressWarnings("NumericCastThatLosesPrecision")
@SuppressWarnings({"NumericCastThatLosesPrecision", "WeakerAccess"})
public
class AnsiOutputStream extends FilterOutputStream {
private static final Charset CHARSET = Charset.forName("UTF-8");
@ -52,23 +64,29 @@ class AnsiOutputStream extends FilterOutputStream {
static final int CYAN = 6;
static final int WHITE = 7;
static final char CURSOR_UP = 'A';
static final char CURSOR_UP = 'A'; // Moves the cursor n (default 1) cells in the given direction. If the cursor is already at the edge of the screen, this has no effect.
static final char CURSOR_DOWN = 'B';
static final char CURSOR_RIGHT = 'C';
static final char CURSOR_LEFT = 'D';
static final char CURSOR_DOWN_LINE = 'E';
static final char CURSOR_UP_LINE = 'F';
static final char CURSOR_TO_COL = 'G';
static final char CURSOR_POS = 'H';
static final char CURSOR_POS_ALT = 'f';
static final char CURSOR_FORWARD = 'C';
static final char CURSOR_BACK = 'D';
static final char CURSOR_ERASE_SCREEN = 'J';
static final char CURSOR_ERASE_LINE = 'K';
static final char PAGE_SCROLL_UP = 'S';
static final char PAGE_SCROLL_DOWN = 'T';
static final char SAVE_CURSOR_POS = 's';
static final char RESTORE_CURSOR_POS = 'u';
static final char TEXT_ATTRIBUTE = 'm';
static final char CURSOR_DOWN_LINE = 'E'; // Moves cursor to beginning of the line n (default 1) lines down.
static final char CURSOR_UP_LINE = 'F'; // Moves cursor to beginning of the line n (default 1) lines up.
static final char CURSOR_TO_COL = 'G'; // Moves the cursor to column n (default 1).
static final char CURSOR_POS = 'H'; // Moves the cursor to row n, column m. The values are 1-based, and default to 1 (top left corner) if omitted.
static final char CURSOR_POS_ALT = 'f'; // Moves the cursor to row n, column m. Both default to 1 if omitted. Same as CUP
static final char CURSOR_ERASE_SCREEN = 'J'; // Clears part of the screen. If n is 0 (or missing), clear from cursor to end of screen. If n is 1, clear from cursor to beginning of the screen. If n is 2, clear entire screen (and moves cursor to upper left on DOS ANSI.SYS).
static final char CURSOR_ERASE_LINE = 'K'; // Erases part of the line. If n is zero (or missing), clear from cursor to the end of the line. If n is one, clear from cursor to beginning of the line. If n is two, clear entire line. Cursor position does not change.
static final char SCROLL_UP = 'S'; // Scroll whole page up by n (default 1) lines. New lines are added at the bottom. (not ANSI.SYS)
static final char SCROLL_DOWN = 'T'; // Scroll whole page down by n (default 1) lines. New lines are added at the top. (not ANSI.SYS)
static final char SAVE_CURSOR_POS = 's'; // Saves the cursor position.
static final char RESTORE_CURSOR_POS = 'u'; // Restores the cursor position.
static final char TEXT_ATTRIBUTE = 'm'; // Sets SGR parameters, including text color. After CSI can be zero or more parameters separated with ;. With no parameters, CSI m is treated as CSI 0 m (reset / normal), which is typical of most of the ANSI escape sequences.
static final int ATTRIBUTE_RESET = 0; // Reset / Normal - all attributes off
static final int ATTRIBUTE_BOLD = 1; // Intensity: Bold
@ -90,27 +108,19 @@ class AnsiOutputStream extends FilterOutputStream {
static final int ATTRIBUTE_CONCEAL_OFF = 28; // Reveal conceal off
static final int ATTRIBUTE_STRIKETHROUGH_OFF = 29; // Not crossed out
static final int ATTRIBUTE_DEFAULT_FG = 39; // Default text color (foreground)
static final int ATTRIBUTE_DEFAULT_BG = 49; // Default background color
// for Erase Screen/Line
static final int ERASE_TO_END = 0;
static final int ERASE_TO_BEGINNING = 1;
static final int ERASE_ALL = 2;
static final byte[] RESET_CODE = new Ansi().reset()
.toString()
.getBytes(CHARSET);
AnsiOutputStream(OutputStream os) {
super(os);
}
private final static int MAX_ESCAPE_SEQUENCE_LENGTH = 100;
private byte buffer[] = new byte[MAX_ESCAPE_SEQUENCE_LENGTH];
private int pos = 0;
private int startOfValue;
private final ArrayList<Object> options = new ArrayList<Object>();
private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0;
private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1;
@ -131,8 +141,26 @@ class AnsiOutputStream extends FilterOutputStream {
private static final int BEL = 7;
private static final int SECOND_ST_CHAR = '\\';
// TODO: implement to get perf boost: public void write(byte[] b, int off, int len)
public static final byte[] RESET_CODE = new StringBuilder(3).append((char)FIRST_ESC_CHAR)
.append((char)SECOND_ESC_CHAR)
.append(AnsiOutputStream.TEXT_ATTRIBUTE)
.toString()
.getBytes(CHARSET);
public
AnsiOutputStream(OutputStream os) {
super(os);
}
private byte buffer[] = new byte[MAX_ESCAPE_SEQUENCE_LENGTH];
private int pos = 0;
private int startOfValue;
private final ArrayList<Object> options = new ArrayList<Object>();
@Override
public
void write(int data) throws IOException {
switch (state) {
@ -301,9 +329,6 @@ class AnsiOutputStream extends FilterOutputStream {
state = LOOKING_FOR_FIRST_ESC_CHAR;
}
/**
* @return true if the escape command was processed.
*/
@ -317,10 +342,10 @@ class AnsiOutputStream extends FilterOutputStream {
case CURSOR_DOWN:
processCursorDown(optionInt(options, 0, 1));
return true;
case CURSOR_RIGHT:
case CURSOR_FORWARD:
processCursorRight(optionInt(options, 0, 1));
return true;
case CURSOR_LEFT:
case CURSOR_BACK:
processCursorLeft(optionInt(options, 0, 1));
return true;
case CURSOR_DOWN_LINE:
@ -342,10 +367,10 @@ class AnsiOutputStream extends FilterOutputStream {
case CURSOR_ERASE_LINE:
processEraseLine(optionInt(options, 0, 0));
return true;
case PAGE_SCROLL_UP:
case SCROLL_UP:
processScrollUp(optionInt(options, 0, 1));
return true;
case PAGE_SCROLL_DOWN:
case SCROLL_DOWN:
processScrollDown(optionInt(options, 0, 1));
return true;
case TEXT_ATTRIBUTE:
@ -554,7 +579,6 @@ class AnsiOutputStream extends FilterOutputStream {
@Override
public
void close() throws IOException {
write(RESET_CODE);
flush();
super.close();
}

View File

@ -64,13 +64,10 @@ class AnsiRenderWriter extends PrintWriter {
}
}
//
// Need to prevent partial output from being written while formatting or we will get rendering exceptions
//
@Override
public
PrintWriter format(final String format, final Object... args) {
flush(); // prevents partial output from being written while formatting or we will get rendering exceptions
print(String.format(format, args));
return this;
}
@ -78,6 +75,7 @@ class AnsiRenderWriter extends PrintWriter {
@Override
public
PrintWriter format(final Locale l, final String format, final Object... args) {
flush(); // prevents partial output from being written while formatting or we will get rendering exceptions
print(String.format(l, format, args));
return this;
}

View File

@ -1,4 +1,19 @@
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* Copyright (C) 2009 the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -13,7 +28,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console.output;
import java.util.HashMap;
@ -39,6 +53,7 @@ import java.util.Map;
* <pre>
* <tt>@|bold,red Warning!|@</tt>
* </pre>
* For Colors, FG_x and BG_x are supported, as are BRIGHT_x (and consequently, FG_BRIGHT_x)
*
* @author dorkbox, llc
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
@ -55,7 +70,13 @@ class AnsiRenderer {
private static final int BEGIN_TOKEN_LEN = 2;
private static final int END_TOKEN_LEN = 2;
private static Map<String, AnsiCode> codeMap = new HashMap<String, AnsiCode>(32);
private static Map<String, AnsiCodeMap> codeMap = new HashMap<String, AnsiCodeMap>(32);
static {
// have to make sure that all the different categories are added to our map.
Color red = Color.RED;
Attribute bold = Attribute.BOLD;
}
static
void reg(Enum anEnum, String codeName) {
@ -64,11 +85,11 @@ class AnsiRenderer {
static
void reg(Enum anEnum, String codeName, boolean isBackgroundColor) {
codeMap.put(codeName, new AnsiCode(anEnum, codeName, isBackgroundColor));
codeMap.put(codeName, new AnsiCodeMap(anEnum, isBackgroundColor));
}
/**
* Renders {@link AnsiCode} names on the given Ansi.
* Renders {@link AnsiCodeMap} names on the given Ansi.
*
* @param ansi The Ansi to render upon
* @param codeNames The code names to render
@ -82,26 +103,26 @@ class AnsiRenderer {
}
/**
* Renders a {@link AnsiCode} name on the given Ansi.
* Renders a {@link AnsiCodeMap} name on the given Ansi.
*
* @param ansi The Ansi to render upon
* @param codeName The code name to render
*/
public static
Ansi render(Ansi ansi, String codeName) {
AnsiCode ansiCode = codeMap.get(codeName.toUpperCase(Locale.ENGLISH));
assert ansiCode != null : "Invalid ANSI code name: '" + codeName + "'";
AnsiCodeMap ansiCodeMap = codeMap.get(codeName.toUpperCase(Locale.ENGLISH));
assert ansiCodeMap != null : "Invalid ANSI code name: '" + codeName + "'";
if (ansiCode.isColor()) {
if (ansiCode.isBackgroundColor()) {
ansi = ansi.bg(ansiCode.getColor());
if (ansiCodeMap.isColor()) {
if (ansiCodeMap.isBackgroundColor()) {
ansi = ansi.bg(ansiCodeMap.getColor());
}
else {
ansi = ansi.fg(ansiCode.getColor());
ansi = ansi.fg(ansiCodeMap.getColor());
}
}
else if (ansiCode.isAttribute()) {
ansi = ansi.a(ansiCode.getAttribute());
else if (ansiCodeMap.isAttribute()) {
ansi = ansi.a(ansiCodeMap.getAttribute());
} else {
assert false : "Undetermined ANSI code name: '" + codeName + "'";
}
@ -110,7 +131,7 @@ class AnsiRenderer {
}
/**
* Renders text using the {@link AnsiCode} names.
* Renders text using the {@link AnsiCodeMap} names.
*
* @param text The text to render
* @param codeNames The code names to render
@ -167,7 +188,7 @@ class AnsiRenderer {
}
/**
* Renders {@link AnsiCode} names as an ANSI escape string.
* Renders {@link AnsiCodeMap} names as an ANSI escape string.
*
* @param codeNames The code names to render
*

View File

@ -18,6 +18,7 @@ package dorkbox.console.output;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* An ANSI string which reports the size of rendered text correctly (ignoring any ANSI escapes).
@ -27,6 +28,7 @@ import java.io.IOException;
*/
public
class AnsiString implements CharSequence {
private static final Charset CHARSET = Charset.forName("UTF-8");
private final CharSequence encoded;
private final CharSequence plain;
@ -47,7 +49,7 @@ class AnsiString implements CharSequence {
try {
out.write(str.toString()
.getBytes());
.getBytes(CHARSET));
out.flush();
out.close();
} catch (IOException e) {

View File

@ -1,3 +1,18 @@
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console.output;
import static dorkbox.console.output.AnsiOutputStream.ATTRIBUTE_BLINK_FAST;

View File

@ -17,21 +17,45 @@ package dorkbox.console.output;
public
enum Color {
BLACK (AnsiOutputStream.BLACK, "BLACK"),
RED (AnsiOutputStream.RED, "RED"),
GREEN (AnsiOutputStream.GREEN, "GREEN"),
YELLOW (AnsiOutputStream.YELLOW, "YELLOW"),
BLUE (AnsiOutputStream.BLUE, "BLUE"),
MAGENTA(AnsiOutputStream.MAGENTA, "MAGENTA"),
CYAN (AnsiOutputStream.CYAN, "CYAN"),
WHITE (AnsiOutputStream.WHITE, "WHITE");
BLACK (AnsiOutputStream.BLACK, true, "BLACK"),
RED (AnsiOutputStream.RED, true, "RED"),
GREEN (AnsiOutputStream.GREEN, true, "GREEN"),
YELLOW (AnsiOutputStream.YELLOW, true, "YELLOW"),
BLUE (AnsiOutputStream.BLUE, true, "BLUE"),
MAGENTA(AnsiOutputStream.MAGENTA, true, "MAGENTA"),
CYAN (AnsiOutputStream.CYAN, true, "CYAN"),
WHITE (AnsiOutputStream.WHITE, true, "WHITE"),
// Brighter versions of those colors, ie: BRIGHT_BLACK is gray.
BRIGHT_BLACK (AnsiOutputStream.BLACK, false, "BRIGHT_BLACK"),
BRIGHT_RED (AnsiOutputStream.RED, false, "BRIGHT_RED"),
BRIGHT_GREEN (AnsiOutputStream.GREEN, false, "BRIGHT_GREEN"),
BRIGHT_YELLOW (AnsiOutputStream.YELLOW, false, "BRIGHT_YELLOW"),
BRIGHT_BLUE (AnsiOutputStream.BLUE, false, "BRIGHT_BLUE"),
BRIGHT_MAGENTA(AnsiOutputStream.MAGENTA, false, "BRIGHT_MAGENTA"),
BRIGHT_CYAN (AnsiOutputStream.CYAN, false, "BRIGHT_CYAN"),
BRIGHT_WHITE (AnsiOutputStream.WHITE, false, "BRIGHT_WHITE"),
// SPECIAL use case here. This is intercepted (so the color doesn't matter)
/**
* DEFAULT is the color of console BEFORE any colors/settings are applied
*/
DEFAULT (AnsiOutputStream.WHITE, true, "DEFAULT"),
/**
* DEFAULT is the color of console BEFORE any colors/settings are applied
*/
BRIGHT_DEFAULT (AnsiOutputStream.WHITE, false, "BRIGHT_DEFAULT");
private final int value;
private final String name;
private final boolean isNormal;
Color(int index, String name) {
Color(int index, boolean isNormal, String name) {
this.value = index;
this.name = name;
this.isNormal = isNormal;
// register code names with the ANSI renderer
AnsiRenderer.reg(this, name, false);
@ -45,22 +69,23 @@ enum Color {
return name;
}
public
int fg() {
return value + 30;
}
public
int bg() {
return value + 40;
}
public
/** is this a BRIGHT color or NORMAL color? */
boolean isNormal() {
return isNormal;
}
int fgBright() {
return value + 90;
}
public
int bgBright() {
return value + 100;
}

View File

@ -1,4 +1,19 @@
/**
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* Copyright (C) 2009, Progress Software Corporation and/or its
* subsidiaries or affiliates. All rights reserved.
* <p>
@ -23,6 +38,7 @@ import java.util.ArrayList;
import java.util.List;
/**
* @author dorkbox, llc
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
*/
public

View File

@ -1,10 +1,9 @@
/*
* Copyright (C) 2009, Progress Software Corporation and/or its
* subsidiaries or affiliates. All rights reserved.
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a asValue of the License at
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
@ -14,11 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2016 dorkbox, llc
*
* Copyright (C) 2009, Progress Software Corporation and/or its
* subsidiaries or affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* You may obtain a asValue of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
@ -58,7 +59,7 @@ import dorkbox.console.util.windows.SMALL_RECT;
* @author Joris Kuipers
*/
@SuppressWarnings("NumericCastThatLosesPrecision")
final class WindowsAnsiOutputStream extends AnsiOutputStream {
public final class WindowsAnsiOutputStream extends AnsiOutputStream {
private static final short ANSI_FOREGROUND_COLOR_MAP[];
private static final short ANSI_BACKGROUND_COLOR_MAP[];
@ -92,12 +93,13 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
private volatile short savedX = (short) -1;
private volatile short savedY = (short) -1;
public
WindowsAnsiOutputStream(final OutputStream os, int fileHandle) throws IOException {
super(os);
if (fileHandle == AnsiConsole.STDOUT_FILENO) {
if (fileHandle == 1) { // STDOUT_FILENO
fileHandle = Kernel32.STD_OUTPUT_HANDLE;
} else if (fileHandle == AnsiConsole.STDERR_FILENO) {
} else if (fileHandle == 2) { // STDERR_FILENO
fileHandle = Kernel32.STD_ERROR_HANDLE;
} else {
throw new IllegalArgumentException("Invalid file handle " + fileHandle);
@ -321,7 +323,7 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
void processCursorUpLine(final int count) throws IOException {
getConsoleInfo();
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y - count);
info.cursorPosition.x = 0;
info.cursorPosition.x = (short) 0;
applyCursorPosition();
}
@ -329,7 +331,7 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
void processCursorDownLine(final int count) throws IOException {
getConsoleInfo();
info.cursorPosition.y = (short) Math.max(info.window.top, info.cursorPosition.y + count);
info.cursorPosition.x = 0;
info.cursorPosition.x = (short) 0;
applyCursorPosition();
}
@ -383,6 +385,8 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
}
/**
* Scrolls the contents of the buffer either UP or DOWN
*
* @param rowsToScroll negative to go down, positive to go up.
*
* Scroll up and new lines are added at the bottom, scroll down and new lines are added at the
@ -405,12 +409,12 @@ final class WindowsAnsiOutputStream extends AnsiOutputStream {
// the content that will be scrolled
scrollRect.top = (short) (0);
scrollRect.bottom = (short) (Short.MAX_VALUE);
scrollRect.left = 0;
scrollRect.bottom = Short.MAX_VALUE;
scrollRect.left = (short) 0;
scrollRect.right = (short) (info.size.x - 1);
// The destination for the scroll rectangle is xxx row up/down.
coordDest.x = 0;
coordDest.x = (short) 0;
coordDest.y = (short) (-rowsToScroll);
// fill the space with whatever color was already there with spaces

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010 dorkbox, llc
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,10 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console;
public class TerminalType {
package dorkbox.console.util;
/**
* Used for single char input
*/
public
class CharHolder {
// default is nothing (0)
public char character = (char) 0;
public
CharHolder() {
}
}

View File

@ -39,7 +39,9 @@ class CONSOLE_SCREEN_BUFFER_INFO extends Structure {
}
@Override
public String toString() {
return "Size: " + size + " CursorPos: " + cursorPosition + " Attribs: " + attributes + " Window: " + window + " MaxWindowSize: " + maximumWindowSize;
public
String toString() {
return "Size: " + size + " CursorPos: " + cursorPosition + " Attribs: " + attributes + " Window: " + window + " MaxWindowSize: " +
maximumWindowSize;
}
}

View File

@ -25,8 +25,6 @@ import com.sun.jna.Structure;
*/
public
class COORD extends Structure {
static public class ByValue extends COORD implements Structure.ByValue { }
public short x;
public short y;
@ -45,7 +43,12 @@ class COORD extends Structure {
}
@Override
public String toString() {
public
String toString() {
return x + ":" + y;
}
static public
class ByValue extends COORD implements Structure.ByValue {}
}

View File

@ -1,30 +1,51 @@
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console.util.windows;
import com.sun.jna.Union;
public class CharUnion extends Union {
public
class CharUnion extends Union {
public char unicodeChar;
public byte asciiChar;
public CharUnion() {
public
CharUnion() {
}
public CharUnion(char c) {
public
CharUnion(char c) {
setType(char.class);
unicodeChar = c;
}
public CharUnion(byte c) {
public
CharUnion(byte c) {
setType(byte.class);
asciiChar = c;
}
public void set(char c) {
public
void set(char c) {
setType(char.class);
unicodeChar = c;
}
public void set(byte c) {
public
void set(byte c) {
setType(byte.class);
asciiChar = c;
}

View File

@ -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;
}
}

View File

@ -27,7 +27,9 @@ import com.sun.jna.PointerType;
*/
public
class HANDLE extends PointerType {
/** Constant value representing an invalid HANDLE. */
/**
* Constant value representing an invalid HANDLE.
*/
public static final HANDLE INVALID_HANDLE_VALUE = new HANDLE(Pointer.createConstant(Pointer.SIZE == 8 ? -1 : 0xFFFFFFFFL));
private boolean immutable;

View File

@ -1,3 +1,18 @@
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console.util.windows;
import java.util.Arrays;
@ -9,22 +24,16 @@ import com.sun.jna.Union;
/**
* https://msdn.microsoft.com/en-us/library/ms683499(v=VS.85).aspx
*/
public class INPUT_RECORD extends Structure {
static public class ByReference extends INPUT_RECORD implements Structure.ByReference {}
public
class INPUT_RECORD extends Structure {
public static final short KEY_EVENT = 0x0001;
public static final short MOUSE_EVENT = 0x0002;
public short EventType;
public EventUnion Event;
public static class EventUnion extends Union {
public KEY_EVENT_RECORD KeyEvent;
public MOUSE_EVENT_RECORD MouseEvent;
}
@Override
public void read() {
public
void read() {
readField("EventType");
switch (EventType) {
case KEY_EVENT:
@ -38,7 +47,19 @@ public class INPUT_RECORD extends Structure {
}
@Override
protected List<String> getFieldOrder() {
protected
List<String> getFieldOrder() {
return Arrays.asList("EventType", "Event");
}
static public
class ByReference extends INPUT_RECORD implements Structure.ByReference {}
public static
class EventUnion extends Union {
public KEY_EVENT_RECORD KeyEvent;
public MOUSE_EVENT_RECORD MouseEvent;
}
}

View File

@ -1,3 +1,18 @@
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console.util.windows;
import java.util.Arrays;
@ -8,7 +23,8 @@ import com.sun.jna.Structure;
/**
* https://msdn.microsoft.com/en-us/library/ms684166(v=VS.85).aspx
*/
public class KEY_EVENT_RECORD extends Structure {
public
class KEY_EVENT_RECORD extends Structure {
public boolean keyDown;
public short repeatCount;
public short virtualKeyCode;
@ -17,7 +33,8 @@ public class KEY_EVENT_RECORD extends Structure {
public int controlKeyState;
@Override
protected List<String> getFieldOrder() {
protected
List<String> getFieldOrder() {
return Arrays.asList("keyDown", "repeatCount", "virtualKeyCode", "virtualScanCode", "uChar", "controlKeyState");
}
}

View File

@ -1,3 +1,18 @@
/*
* Copyright 2016 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.console.util.windows;
import java.util.Arrays;
@ -5,14 +20,16 @@ import java.util.List;
import com.sun.jna.Structure;
public class MOUSE_EVENT_RECORD extends Structure {
public
class MOUSE_EVENT_RECORD extends Structure {
public COORD mousePosition;
public int buttonState;
public int controlKeyState;
public int eventFlags;
@Override
protected List<String> getFieldOrder() {
protected
List<String> getFieldOrder() {
return Arrays.asList("mousePosition", "buttonState", "controlKeyState", "eventFlags");
}
}

View File

@ -23,28 +23,37 @@ import com.sun.jna.Structure;
/**
* https://msdn.microsoft.com/en-us/library/ms686311%28VS.85%29.aspx
*/
public class SMALL_RECT extends Structure {
static public class ByReference extends SMALL_RECT implements Structure.ByReference { }
@SuppressWarnings("NumericCastThatLosesPrecision")
public
class SMALL_RECT extends Structure {
public short left;
public short top;
public short right;
public short bottom;
public short width() {
return (short) (this.right-this.left);
public
short width() {
return (short) (this.right - this.left);
}
public short height() {
return (short) (this.bottom-this.top);
public
short height() {
return (short) (this.bottom - this.top);
}
@Override
protected List<String> getFieldOrder() {
protected
List<String> getFieldOrder() {
return Arrays.asList("left", "top", "right", "bottom");
}
@Override
public String toString() {
public
String toString() {
return "LTRB: " + left + "," + top + "," + right + "," + bottom;
}
static public
class ByReference extends SMALL_RECT implements Structure.ByReference {}
}

View File

@ -1,50 +1,140 @@
/**
* Copyright (C) 2009, Progress Software Corporation and/or its
* subsidiaries or affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a asValue of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.dorkbox.console;
import java.io.FileInputStream;
import java.io.IOException;
import dorkbox.console.Console;
import dorkbox.console.output.Ansi;
import dorkbox.console.output.Color;
import dorkbox.console.output.Erase;
/**
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
* System output for visual confirmation of ANSI codes.
* <p>
* Must enable assertions to verify no errors!! (ie: java -ea -jar blah.jar)
*/
public class AnsiConsoleExample {
public
class AnsiConsoleExample {
public static
void main(String[] args) throws IOException {
private AnsiConsoleExample() {}
Console.systemInstall();
Console.ENABLE_ANSI = true;
public static void main(String[] args) throws IOException {
String file = "jansi.ans";
if( args.length>0 )
file = args[0];
System.out.println(Ansi.ansi()
.fg(Color.BLACK).a("black").bg(Color.BLACK).a("black")
.reset()
.fg(Color.BRIGHT_BLACK).a("b-black").bg(Color.BRIGHT_BLACK).a("b-black")
.reset());
// Allows us to disable ANSI processing.
if( "true".equals(System.getProperty("jansi", "true")) ) {
Ansi.systemInstall();
System.out.println(Ansi.ansi()
.fg(Color.BLUE).a("blue").bg(Color.BLUE).a("blue")
.reset()
.fg(Color.BRIGHT_BLUE).a("b-blue").bg(Color.BRIGHT_BLUE).a("b-blue")
.reset());
System.out.println(Ansi.ansi()
.fg(Color.CYAN).a("cyan").bg(Color.CYAN).a("cyan")
.reset()
.fg(Color.BRIGHT_CYAN).a("b-cyan").bg(Color.BRIGHT_CYAN).a("b-cyan")
.reset());
System.out.println(Ansi.ansi()
.fg(Color.GREEN).a("green").bg(Color.GREEN).a("green")
.reset()
.fg(Color.BRIGHT_GREEN).a("b-green").bg(Color.BRIGHT_GREEN).a("b-green")
.reset());
System.out.println(Ansi.ansi()
.fg(Color.MAGENTA).a("magenta").bg(Color.MAGENTA).a("magenta")
.reset()
.fg(Color.BRIGHT_MAGENTA).a("b-magenta").bg(Color.BRIGHT_MAGENTA).a("b-magenta")
.reset());
System.out.println(Ansi.ansi()
.fg(Color.RED).a("red").bg(Color.RED).a("red")
.reset()
.fg(Color.BRIGHT_RED).a("b-red").bg(Color.BRIGHT_RED).a("b-red")
.reset());
System.out.println(Ansi.ansi()
.fg(Color.YELLOW).a("yellow").bg(Color.YELLOW).a("yellow")
.reset()
.fg(Color.BRIGHT_YELLOW).a("b-yellow").bg(Color.BRIGHT_YELLOW).a("b-yellow")
.reset());
System.out.println(Ansi.ansi()
.fg(Color.WHITE).a("white").bg(Color.WHITE).a("white")
.reset()
.fg(Color.BRIGHT_WHITE).a("b-white").bg(Color.BRIGHT_WHITE).a("b-white")
.reset());
Console.reset(); // reset the ansi stream. Can ALSO have ansi().reset(), but that would be redundant
System.out.println("The following line should be blank except for the first '>'");
System.out.println(Ansi.ansi()
.a(">THIS SHOULD BE BLANK")
.cursorToColumn(2)
.eraseLine());
System.out.println("The following line should be blank");
System.out.println(Ansi.ansi()
.a(">THIS SHOULD BE BLANK")
.eraseLine(Erase.ALL));
System.out.println(Ansi.ansi()
.a(">THIS SHOULD BE BLANK")
.eraseLine(Erase.BACKWARD)
.a("Everything on this line before this should be blank"));
System.out.println(Ansi.ansi()
.a("Everything on this line after this should be blank")
.saveCursorPosition()
.a(">THIS SHOULD BE BLANK")
.restoreCursorPosition()
.eraseLine());
System.out.println("00000000000000000000000000");
System.out.println("00000000000000000000000000");
System.out.println("00000000000000000000000000");
System.out.println("00000000000000000000000000");
System.out.println("00000000000000000000000000");
System.out.println(Ansi.ansi()
.a("Should have two blank spots in the above 0's")
.saveCursorPosition()
.cursorUp(4)
.cursorLeft(30)
.a(" ")
.cursorDownLine()
.cursorRight(5)
.a(" ")
.restoreCursorPosition());
System.err.println("ver : " + Console.getVersion());
System.out.println("Now testing the input console. 'q' to quit");
Console.ENABLE_ECHO = true;
int read;
while ((read = Console.read()) != 'q') {
if (Character.isDigit(read)) {
int numericValue = Character.getNumericValue(read);
// reverse if pressing 2
if (numericValue == 2) {
System.out.print(Ansi.ansi().scrollDown(1));
System.out.flush(); // flush guarantees the terminal moves the way we want
}
else {
System.out.print(Ansi.ansi().scrollUp(numericValue));
System.out.flush(); // flush guarantees the terminal moves the way we want
}
}
// System.err.println("READ :" + read + " (" + (char) read + ")");
}
FileInputStream f = new FileInputStream(file);
int c;
while( (c=f.read())>=0 ) {
System.out.write(c);
}
f.close();
}
Console.systemUninstall();
}
}

View File

@ -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("=======================================================================");
}
}

View File

@ -17,7 +17,7 @@
package com.dorkbox.console;
import static dorkbox.console.output.AnsiRenderer.render;
import static java.awt.Font.BOLD;
import static dorkbox.console.output.Attribute.BOLD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import dorkbox.console.Console;
import dorkbox.console.output.Ansi;
import dorkbox.console.output.AnsiRenderer;
import dorkbox.console.output.Color;
@ -43,7 +44,7 @@ public class AnsiRendererTest
@Before
public void setUp() {
Ansi.setEnabled(true);
Console.ENABLE_ANSI = true;
}
@Test
@ -66,7 +67,6 @@ public class AnsiRendererTest
String str = render("@|bold,red foo|@");
System.out.println(str);
assertEquals(Ansi.ansi().a(BOLD).fg(Color.RED).a("foo").reset().toString(), str);
assertEquals(Ansi.ansi().bold().fgRed().a("foo").reset().toString(), str);
}
@Test
@ -74,7 +74,6 @@ public class AnsiRendererTest
String str = render("@|bold,red foo bar baz|@");
System.out.println(str);
assertEquals(Ansi.ansi().a(BOLD).fg(Color.RED).a("foo bar baz").reset().toString(), str);
assertEquals(Ansi.ansi().bold().fgRed().a("foo bar baz").reset().toString(), str);
}
@Test
@ -82,10 +81,10 @@ public class AnsiRendererTest
String str = render("@|bold,red foo bar baz|@ ick @|bold,red foo bar baz|@");
System.out.println(str);
assertEquals(Ansi.ansi()
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
.a(" ick ")
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
.toString(), str);
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
.a(" ick ")
.a(BOLD).fg(Color.RED).a("foo bar baz").reset()
.toString(), str);
}
@Test

View File

@ -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 + ")");
}
}
}

View File

@ -16,6 +16,10 @@
package com.dorkbox.console;
import static dorkbox.console.output.Ansi.ansi;
import static dorkbox.console.output.AnsiRenderer.render;
import static dorkbox.console.output.Attribute.BOLD;
import static dorkbox.console.output.Color.RED;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
@ -30,32 +34,54 @@ import dorkbox.console.output.Color;
*/
public class AnsiTest
{
@Test
public void testSetEnabled() throws Exception {
Ansi.setEnabled(false);
new Thread()
{
@Override
public void run() {
assertEquals(false, Ansi.isEnabled());
}
}.run();
Ansi.setEnabled(true);
new Thread()
{
@Override
public void run() {
assertEquals(true, Ansi.isEnabled());
}
}.run();
}
@Test
public void testClone() throws CloneNotSupportedException {
Ansi ansi = Ansi.ansi().a("Some text").bg(Color.BLACK).fg(Color.WHITE);
Ansi clone = new Ansi(ansi);
Ansi ansi = ansi().a("Some text").bg(Color.BLACK).fg(Color.WHITE);
Ansi clone = ansi(ansi);
assertEquals(ansi.a("test").reset().toString(), clone.a("test").reset().toString());
}
@Test
public void testOutput() throws CloneNotSupportedException {
// verify the output renderer
String str = render("@|bold foo|@foo");
assertEquals(ansi().a(BOLD).a("foo").reset().a("foo").toString(), str);
assertEquals(ansi().bold().a("foo").reset().a("foo").toString(), str);
str = render("@|bold,red foo|@");
assertEquals(ansi().a(BOLD).fg(RED).a("foo").reset().toString(), str);
assertEquals(ansi().bold().fg(RED).a("foo").reset().toString(), str);
str = render("@|bold,red foo bar baz|@");
assertEquals(ansi().a(BOLD).fg(RED).a("foo bar baz").reset().toString(), str);
assertEquals(ansi().bold().fg(RED).a("foo bar baz").reset().toString(), str);
str = render("@|bold,red foo bar baz|@ ick @|bold,red foo bar baz|@");
String expected = ansi().a(BOLD)
.fg(RED)
.a("foo bar baz")
.reset()
.a(" ick ")
.a(BOLD)
.fg(RED)
.a("foo bar baz")
.reset()
.toString();
assertEquals(expected, str);
str = render("@|bold foo"); // shouldn't work
System.err.println(str + " <- shouldn't work");
str = render("@|bold|@"); // shouldn't work
System.err.println(str + " <- shouldn't work");
str = render("@|bold foo|@foo");
System.out.println(str + " <- shouldn't work");
}
}

View File

@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import org.junit.Test;
@ -29,6 +30,7 @@ import dorkbox.console.output.HtmlAnsiOutputStream;
* @author <a href="http://code.dblock.org">Daniel Doubrovkine</a>
*/
public class HtmlAnsiOutputStreamTest {
private static final Charset charset = Charset.forName("UTF-8");
@Test
public void testNoMarkup() throws IOException {
@ -82,8 +84,9 @@ public class HtmlAnsiOutputStreamTest {
private String colorize(String text) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
HtmlAnsiOutputStream hos = new HtmlAnsiOutputStream(os);
hos.write(text.getBytes("UTF-8"));
hos.write(text.getBytes(charset));
hos.close();
return new String(os.toByteArray(), "UTF-8");
return new String(os.toByteArray(), charset);
}
}

View File

@ -1,8 +0,0 @@
[?7h
レトトソレトトトトトソ レトトトトトソ レトトトトトトツトトソ
ウロロテルロロロロロタツルロロロロロタツルロロロロロロヴンウ
レトトソ ウロロウロロワワワロロウロロレトソロロウロロワワワワ ウワワウ
ウアアタトルアロウアロレトソアロウアロウ ウアロウ ゚゚゚゚アロウアロウ
タソイイイイイレエイイウ ウイイウイイウ ウイイウ゚イイイイイ゚ウイイウ
タトトトトトルタトトル タトトチトトル タトトチトトトトトトトチトトル