Fixed thread safety for Terminal implementations. Changed Terminal implementation on windows to Flush System.out/System.err
This commit is contained in:
@@ -37,8 +37,8 @@ public class Main {
|
|||||||
System.out.print("[bold]");
|
System.out.print("[bold]");
|
||||||
terminal.normal();
|
terminal.normal();
|
||||||
System.out.println(" [normal]");
|
System.out.println(" [normal]");
|
||||||
|
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
|
||||||
System.out.println("COLORS");
|
System.out.println("COLORS");
|
||||||
for (Terminal.Color color : Terminal.Color.values()) {
|
for (Terminal.Color color : Terminal.Color.values()) {
|
||||||
terminal.foreground(color);
|
terminal.foreground(color);
|
||||||
@@ -48,8 +48,8 @@ public class Main {
|
|||||||
terminal.normal();
|
terminal.normal();
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
|
||||||
terminal.reset();
|
terminal.reset();
|
||||||
|
|
||||||
if (terminal.supportsCursorMotion()) {
|
if (terminal.supportsCursorMotion()) {
|
||||||
@@ -73,10 +73,10 @@ public class Main {
|
|||||||
terminal.cursorDown(1);
|
terminal.cursorDown(1);
|
||||||
terminal.cursorStartOfLine();
|
terminal.cursorStartOfLine();
|
||||||
terminal.clearToEndOfLine();
|
terminal.clearToEndOfLine();
|
||||||
|
terminal.foreground(Terminal.Color.Blue).bold();
|
||||||
System.out.println("done!");
|
System.out.println("done!");
|
||||||
|
System.out.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import java.io.File;
|
|||||||
/**
|
/**
|
||||||
* Provides access to the native integrations. Use {@link #get(Class)} to load a particular integration.
|
* Provides access to the native integrations. Use {@link #get(Class)} to load a particular integration.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public class Native {
|
public class Native {
|
||||||
private static final Object lock = new Object();
|
|
||||||
private static boolean loaded;
|
private static boolean loaded;
|
||||||
|
|
||||||
private Native() {
|
private Native() {
|
||||||
@@ -22,8 +22,9 @@ public class Native {
|
|||||||
* @param extractDir The directory to extract native resources into. May be null, in which case a default is
|
* @param extractDir The directory to extract native resources into. May be null, in which case a default is
|
||||||
* selected.
|
* selected.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
static public void init(File extractDir) {
|
static public void init(File extractDir) {
|
||||||
synchronized (lock) {
|
synchronized (Native.class) {
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
Platform platform = Platform.current();
|
Platform platform = Platform.current();
|
||||||
try {
|
try {
|
||||||
@@ -54,6 +55,7 @@ public class Native {
|
|||||||
* machine.
|
* machine.
|
||||||
* @throws NativeException On failure to load the native integration.
|
* @throws NativeException On failure to load the native integration.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public static <T extends NativeIntegration> T get(Class<T> type)
|
public static <T extends NativeIntegration> T get(Class<T> type)
|
||||||
throws NativeIntegrationUnavailableException, NativeException {
|
throws NativeIntegrationUnavailableException, NativeException {
|
||||||
init(null);
|
init(null);
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package net.rubygrapefruit.platform;
|
|||||||
/**
|
/**
|
||||||
* Provides access to the terminal/console.
|
* Provides access to the terminal/console.
|
||||||
*
|
*
|
||||||
* Supported on Linux, OS X, Windows.
|
* <p>On UNIX based platforms, this provides access to the terminal. On Windows platforms, this provides access to the
|
||||||
|
* console.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
public interface Terminals extends NativeIntegration {
|
public interface Terminals extends NativeIntegration {
|
||||||
/**
|
/**
|
||||||
* System outputs.
|
* System outputs.
|
||||||
@@ -16,6 +19,7 @@ public interface Terminals extends NativeIntegration {
|
|||||||
*
|
*
|
||||||
* @throws NativeException On failure.
|
* @throws NativeException On failure.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
boolean isTerminal(Output output) throws NativeException;
|
boolean isTerminal(Output output) throws NativeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,5 +27,6 @@ public interface Terminals extends NativeIntegration {
|
|||||||
*
|
*
|
||||||
* @throws NativeException When the output is not attached to a terminal.
|
* @throws NativeException When the output is not attached to a terminal.
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe
|
||||||
Terminal getTerminal(Output output) throws NativeException;
|
Terminal getTerminal(Output output) throws NativeException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package net.rubygrapefruit.platform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the given class or method is thread safe.
|
||||||
|
*/
|
||||||
|
public @interface ThreadSafe {
|
||||||
|
}
|
||||||
@@ -3,15 +3,5 @@ package net.rubygrapefruit.platform.internal;
|
|||||||
import net.rubygrapefruit.platform.Terminal;
|
import net.rubygrapefruit.platform.Terminal;
|
||||||
|
|
||||||
public abstract class AbstractTerminal implements Terminal {
|
public abstract class AbstractTerminal implements Terminal {
|
||||||
public final void init() {
|
protected abstract void init();
|
||||||
doInit();
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(){
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void doInit();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,14 @@ public abstract class AbstractTerminals implements Terminals {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (current == null) {
|
if (current == null) {
|
||||||
AbstractTerminal terminal = createTerminal(output);
|
final AbstractTerminal terminal = createTerminal(output);
|
||||||
terminal.init();
|
terminal.init();
|
||||||
|
Runtime.getRuntime().addShutdownHook(new Thread(){
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
terminal.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
currentlyOpen = output;
|
currentlyOpen = output;
|
||||||
current = terminal;
|
current = terminal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,18 @@ package net.rubygrapefruit.platform.internal;
|
|||||||
|
|
||||||
import net.rubygrapefruit.platform.NativeException;
|
import net.rubygrapefruit.platform.NativeException;
|
||||||
import net.rubygrapefruit.platform.Terminal;
|
import net.rubygrapefruit.platform.Terminal;
|
||||||
import net.rubygrapefruit.platform.Terminals;
|
|
||||||
import net.rubygrapefruit.platform.TerminalSize;
|
import net.rubygrapefruit.platform.TerminalSize;
|
||||||
|
import net.rubygrapefruit.platform.Terminals;
|
||||||
import net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions;
|
import net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions;
|
||||||
import net.rubygrapefruit.platform.internal.jni.TerminfoFunctions;
|
import net.rubygrapefruit.platform.internal.jni.TerminfoFunctions;
|
||||||
|
|
||||||
import java.io.PrintStream;
|
|
||||||
|
|
||||||
public class TerminfoTerminal extends AbstractTerminal {
|
public class TerminfoTerminal extends AbstractTerminal {
|
||||||
private final Terminals.Output output;
|
private final Terminals.Output output;
|
||||||
private final PrintStream stream;
|
|
||||||
private final TerminalCapabilities capabilities = new TerminalCapabilities();
|
private final TerminalCapabilities capabilities = new TerminalCapabilities();
|
||||||
private Color foreground;
|
private Color foreground;
|
||||||
|
|
||||||
public TerminfoTerminal(Terminals.Output output) {
|
public TerminfoTerminal(Terminals.Output output) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
stream = output == Terminals.Output.Stdout ? System.out : System.err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -26,8 +22,7 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doInit() {
|
protected void init() {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.initTerminal(output.ordinal(), capabilities, result);
|
TerminfoFunctions.initTerminal(output.ordinal(), capabilities, result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -62,7 +57,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.foreground(color.ordinal(), result);
|
TerminfoFunctions.foreground(color.ordinal(), result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -78,7 +72,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.bold(result);
|
TerminfoFunctions.bold(result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -97,7 +90,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Terminal reset() {
|
public Terminal reset() {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.reset(result);
|
TerminfoFunctions.reset(result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -107,7 +99,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Terminal cursorDown(int count) {
|
public Terminal cursorDown(int count) {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.down(count, result);
|
TerminfoFunctions.down(count, result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -117,7 +108,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Terminal cursorUp(int count) {
|
public Terminal cursorUp(int count) {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.up(count, result);
|
TerminfoFunctions.up(count, result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -127,7 +117,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Terminal cursorLeft(int count) {
|
public Terminal cursorLeft(int count) {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.left(count, result);
|
TerminfoFunctions.left(count, result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -137,7 +126,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Terminal cursorRight(int count) {
|
public Terminal cursorRight(int count) {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.right(count, result);
|
TerminfoFunctions.right(count, result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -147,7 +135,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Terminal cursorStartOfLine() throws NativeException {
|
public Terminal cursorStartOfLine() throws NativeException {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.startLine(result);
|
TerminfoFunctions.startLine(result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
@@ -157,7 +144,6 @@ public class TerminfoTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Terminal clearToEndOfLine() throws NativeException {
|
public Terminal clearToEndOfLine() throws NativeException {
|
||||||
stream.flush();
|
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
TerminfoFunctions.clearToEndOfLine(result);
|
TerminfoFunctions.clearToEndOfLine(result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package net.rubygrapefruit.platform.internal;
|
package net.rubygrapefruit.platform.internal;
|
||||||
|
|
||||||
|
import net.rubygrapefruit.platform.Terminals;
|
||||||
import net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions;
|
import net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
public class TerminfoTerminals extends AbstractTerminals {
|
public class TerminfoTerminals extends AbstractTerminals {
|
||||||
public boolean isTerminal(Output output) {
|
public boolean isTerminal(Output output) {
|
||||||
return PosixTerminalFunctions.isatty(output.ordinal());
|
return PosixTerminalFunctions.isatty(output.ordinal());
|
||||||
@@ -9,6 +12,7 @@ public class TerminfoTerminals extends AbstractTerminals {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractTerminal createTerminal(Output output) {
|
protected AbstractTerminal createTerminal(Output output) {
|
||||||
return new TerminfoTerminal(output);
|
PrintStream stream = output == Terminals.Output.Stdout ? System.out : System.err;
|
||||||
|
return new WrapperTerminal(stream, new TerminfoTerminal(output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class WindowsTerminal extends AbstractTerminal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doInit() {
|
protected void init() {
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
WindowsConsoleFunctions.initConsole(output.ordinal(), result);
|
WindowsConsoleFunctions.initConsole(output.ordinal(), result);
|
||||||
if (result.isFailed()) {
|
if (result.isFailed()) {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package net.rubygrapefruit.platform.internal;
|
package net.rubygrapefruit.platform.internal;
|
||||||
|
|
||||||
import net.rubygrapefruit.platform.NativeException;
|
import net.rubygrapefruit.platform.NativeException;
|
||||||
|
import net.rubygrapefruit.platform.Terminals;
|
||||||
import net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions;
|
import net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
public class WindowsTerminals extends AbstractTerminals {
|
public class WindowsTerminals extends AbstractTerminals {
|
||||||
public boolean isTerminal(Output output) {
|
public boolean isTerminal(Output output) {
|
||||||
FunctionResult result = new FunctionResult();
|
FunctionResult result = new FunctionResult();
|
||||||
@@ -16,6 +19,7 @@ public class WindowsTerminals extends AbstractTerminals {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractTerminal createTerminal(Output output) {
|
protected AbstractTerminal createTerminal(Output output) {
|
||||||
return new WindowsTerminal(output);
|
PrintStream stream = output == Terminals.Output.Stdout ? System.out : System.err;
|
||||||
|
return new WrapperTerminal(stream, new WindowsTerminal(output));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package net.rubygrapefruit.platform.internal;
|
||||||
|
|
||||||
|
import net.rubygrapefruit.platform.NativeException;
|
||||||
|
import net.rubygrapefruit.platform.Terminal;
|
||||||
|
import net.rubygrapefruit.platform.TerminalSize;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Terminal} implementation that wraps another to add thread safety.
|
||||||
|
*/
|
||||||
|
public class WrapperTerminal extends AbstractTerminal {
|
||||||
|
private final AbstractTerminal terminal;
|
||||||
|
private final PrintStream stream;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
public WrapperTerminal(PrintStream stream, AbstractTerminal terminal) {
|
||||||
|
this.stream = stream;
|
||||||
|
this.terminal = terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
stream.flush();
|
||||||
|
terminal.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminalSize getTerminalSize() throws NativeException {
|
||||||
|
return terminal.getTerminalSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsColor() {
|
||||||
|
return terminal.supportsColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCursorMotion() {
|
||||||
|
return terminal.supportsCursorMotion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTextAttributes() {
|
||||||
|
return terminal.supportsTextAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal normal() throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.normal();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal bold() throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.bold();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal reset() throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.reset();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal foreground(Color color) throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.foreground(color);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal cursorLeft(int count) throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.cursorLeft(count);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal cursorRight(int count) throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.cursorRight(count);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal cursorUp(int count) throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.cursorUp(count);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal cursorDown(int count) throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.cursorDown(count);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal cursorStartOfLine() throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.cursorStartOfLine();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Terminal clearToEndOfLine() throws NativeException {
|
||||||
|
stream.flush();
|
||||||
|
synchronized (lock) {
|
||||||
|
terminal.clearToEndOfLine();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user