- Added Terminal.supportsTextAttributes(), supportsColor() and supportCursorMotion().
- Changed semantics for Terminal.normal(), bold(), foreground() and reset() so that they are no-ops when not supported. - Fixed test app not to blow up on unsupported capability.
This commit is contained in:
@@ -4,6 +4,7 @@ public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println();
|
||||
System.out.println("* OS: " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + ' ' + System.getProperty("os.arch"));
|
||||
System.out.println("* JVM: " + System.getProperty("java.vm.vendor") + ' ' + System.getProperty("java.version"));
|
||||
|
||||
Process process = Native.get(Process.class);
|
||||
System.out.println("* PID: " + process.getProcessId());
|
||||
@@ -17,8 +18,11 @@ public class Main {
|
||||
Terminal terminal = terminalAccess.getTerminal(TerminalAccess.Output.Stdout);
|
||||
TerminalSize terminalSize = terminal.getTerminalSize();
|
||||
System.out.println("* terminal size: " + terminalSize.getCols() + " cols x " + terminalSize.getRows() + " rows");
|
||||
System.out.println("* text attributes: " + (terminal.supportsTextAttributes() ? "yes" : "no"));
|
||||
System.out.println("* color: " + (terminal.supportsColor() ? "yes" : "no"));
|
||||
System.out.println("* cursor motion: " + (terminal.supportsCursorMotion() ? "yes" : "no"));
|
||||
System.out.println();
|
||||
System.out.println("TERMINAL OUTPUT");
|
||||
System.out.println("TEXT ATTRIBUTES");
|
||||
System.out.print("[normal] ");
|
||||
terminal.bold();
|
||||
System.out.print("[bold]");
|
||||
@@ -39,28 +43,29 @@ public class Main {
|
||||
System.out.println();
|
||||
terminal.reset();
|
||||
|
||||
System.out.println("CURSOR MOVEMENT");
|
||||
System.out.println(" ");
|
||||
System.out.println(" ");
|
||||
System.out.println(" ");
|
||||
System.out.print("[delete me]");
|
||||
if (terminal.supportsCursorMotion()) {
|
||||
System.out.println("CURSOR MOVEMENT");
|
||||
System.out.println(" ");
|
||||
System.out.println(" ");
|
||||
System.out.print("[delete me]");
|
||||
|
||||
terminal.cursorLeft(11);
|
||||
terminal.cursorUp(1);
|
||||
terminal.cursorRight(10);
|
||||
System.out.print("[4]");
|
||||
terminal.cursorUp(1);
|
||||
terminal.cursorLeft(3);
|
||||
System.out.print("[2]");
|
||||
terminal.cursorLeft(13);
|
||||
System.out.print("[1]");
|
||||
terminal.cursorLeft(3);
|
||||
terminal.cursorDown(1);
|
||||
System.out.print("[3]");
|
||||
terminal.cursorDown(1);
|
||||
terminal.cursorStartOfLine();
|
||||
terminal.clearToEndOfLine();
|
||||
System.out.println("done!");
|
||||
terminal.cursorLeft(11);
|
||||
terminal.cursorUp(1);
|
||||
terminal.cursorRight(10);
|
||||
System.out.print("[4]");
|
||||
terminal.cursorUp(1);
|
||||
terminal.cursorLeft(3);
|
||||
System.out.print("[2]");
|
||||
terminal.cursorLeft(13);
|
||||
System.out.print("[1]");
|
||||
terminal.cursorLeft(3);
|
||||
terminal.cursorDown(1);
|
||||
System.out.print("[3]");
|
||||
terminal.cursorDown(1);
|
||||
terminal.cursorStartOfLine();
|
||||
terminal.clearToEndOfLine();
|
||||
System.out.println("done!");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
|
||||
@@ -11,35 +11,51 @@ public interface Terminal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the terminal.
|
||||
* Returns true if this terminal supports setting text attributes, such as bold.
|
||||
*/
|
||||
boolean supportsTextAttributes();
|
||||
|
||||
/**
|
||||
* Returns true if this terminal supports setting output colors.
|
||||
*/
|
||||
boolean supportsColor();
|
||||
|
||||
/**
|
||||
* Returns true if this terminal supports moving the cursor.
|
||||
*/
|
||||
boolean supportsCursorMotion();
|
||||
|
||||
/**
|
||||
* Returns the size of the terminal. Supported by all terminals.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
*/
|
||||
TerminalSize getTerminalSize() throws NativeException;
|
||||
|
||||
/**
|
||||
* Sets the terminal foreground color.
|
||||
* Sets the terminal foreground color, if supported. Does nothing if this terminal does not support setting the
|
||||
* foreground color.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
*/
|
||||
Terminal foreground(Color color) throws NativeException;
|
||||
|
||||
/**
|
||||
* Switches the terminal to bold mode.
|
||||
* Switches the terminal to bold mode, if supported. Does nothing if this terminal does not support bold mode.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
*/
|
||||
Terminal bold() throws NativeException;
|
||||
|
||||
/**
|
||||
* Switches the terminal to normal mode.
|
||||
* Switches the terminal to normal mode. Supported by all terminals.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
*/
|
||||
Terminal normal() throws NativeException;
|
||||
|
||||
/**
|
||||
* Switches the terminal to normal mode and restores default colors.
|
||||
* Switches the terminal to normal mode and restores default colors. Supported by all terminals.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
*/
|
||||
@@ -48,42 +64,42 @@ public interface Terminal {
|
||||
/**
|
||||
* Moves the cursor the given number of characters to the left.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
* @throws NativeException On failure, or if this terminal does not support cursor motion.
|
||||
*/
|
||||
Terminal cursorLeft(int count) throws NativeException;
|
||||
|
||||
/**
|
||||
* Moves the cursor the given number of characters to the right.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
* @throws NativeException On failure, or if this terminal does not support cursor motion.
|
||||
*/
|
||||
Terminal cursorRight(int count) throws NativeException;
|
||||
|
||||
/**
|
||||
* Moves the cursor the given number of characters up.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
* @throws NativeException On failure, or if this terminal does not support cursor motion.
|
||||
*/
|
||||
Terminal cursorUp(int count) throws NativeException;
|
||||
|
||||
/**
|
||||
* Moves the cursor the given number of characters down.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
* @throws NativeException On failure, or if this terminal does not support cursor motion.
|
||||
*/
|
||||
Terminal cursorDown(int count) throws NativeException;
|
||||
|
||||
/**
|
||||
* Moves the cursor to the start of the current line.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
* @throws NativeException On failure, or if this terminal does not support cursor motion.
|
||||
*/
|
||||
Terminal cursorStartOfLine() throws NativeException;
|
||||
|
||||
/**
|
||||
* Clears characters from the cursor position to the end of the current line.
|
||||
*
|
||||
* @throws NativeException On failure.
|
||||
* @throws NativeException On failure, or if this terminal does not support clearing.
|
||||
*/
|
||||
Terminal clearToEndOfLine() throws NativeException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.rubygrapefruit.platform.internal;
|
||||
|
||||
public class TerminalCapabilities {
|
||||
String terminalName;
|
||||
boolean textAttributes;
|
||||
boolean colors;
|
||||
boolean cursorMotion;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import java.io.PrintStream;
|
||||
public class TerminfoTerminal extends AbstractTerminal {
|
||||
private final TerminalAccess.Output output;
|
||||
private final PrintStream stream;
|
||||
private final TerminalCapabilities capabilities = new TerminalCapabilities();
|
||||
private Color foreground;
|
||||
|
||||
public TerminfoTerminal(TerminalAccess.Output output) {
|
||||
@@ -28,7 +29,7 @@ public class TerminfoTerminal extends AbstractTerminal {
|
||||
protected void doInit() {
|
||||
stream.flush();
|
||||
FunctionResult result = new FunctionResult();
|
||||
TerminfoFunctions.initTerminal(output.ordinal(), result);
|
||||
TerminfoFunctions.initTerminal(output.ordinal(), capabilities, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not open terminal for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
@@ -45,13 +46,33 @@ public class TerminfoTerminal extends AbstractTerminal {
|
||||
return terminalSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsColor() {
|
||||
return capabilities.colors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCursorMotion() {
|
||||
return capabilities.cursorMotion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTextAttributes() {
|
||||
return capabilities.textAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal foreground(Color color) {
|
||||
if (!capabilities.colors) {
|
||||
return this;
|
||||
}
|
||||
|
||||
stream.flush();
|
||||
FunctionResult result = new FunctionResult();
|
||||
TerminfoFunctions.foreground(color.ordinal(), result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not switch foreground color for %s: %s", this, result.getMessage()));
|
||||
throw new NativeException(String.format("Could not switch foreground color for %s: %s", this,
|
||||
result.getMessage()));
|
||||
}
|
||||
foreground = color;
|
||||
return this;
|
||||
@@ -59,11 +80,16 @@ public class TerminfoTerminal extends AbstractTerminal {
|
||||
|
||||
@Override
|
||||
public Terminal bold() {
|
||||
if (!capabilities.textAttributes) {
|
||||
return this;
|
||||
}
|
||||
|
||||
stream.flush();
|
||||
FunctionResult result = new FunctionResult();
|
||||
TerminfoFunctions.bold(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not switch to bold mode for %s: %s", this, result.getMessage()));
|
||||
throw new NativeException(String.format("Could not switch to bold mode for %s: %s", this,
|
||||
result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,141 +1,155 @@
|
||||
package net.rubygrapefruit.platform.internal;
|
||||
|
||||
import net.rubygrapefruit.platform.NativeException;
|
||||
import net.rubygrapefruit.platform.Terminal;
|
||||
import net.rubygrapefruit.platform.TerminalAccess;
|
||||
import net.rubygrapefruit.platform.TerminalSize;
|
||||
import net.rubygrapefruit.platform.internal.jni.TerminfoFunctions;
|
||||
import net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions;
|
||||
|
||||
public class WindowsTerminal extends AbstractTerminal {
|
||||
private final TerminalAccess.Output output;
|
||||
|
||||
public WindowsTerminal(TerminalAccess.Output output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return output.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.initConsole(output.ordinal(), result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not open console for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalSize getTerminalSize() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
MutableTerminalSize size = new MutableTerminalSize();
|
||||
WindowsConsoleFunctions.getConsoleSize(output.ordinal(), size, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not determine console size for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal bold() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.bold(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not switch console to bold mode for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal foreground(Color color) {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.foreground(color.ordinal(), result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not change console foreground color for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal normal() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.normal(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not switch console to normal mode for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal reset() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.reset(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not reset console for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorDown(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.down(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor down for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorUp(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.up(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor up for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorLeft(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.left(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor left for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorRight(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.right(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor right for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorStartOfLine() throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.startLine(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor to start of line for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal clearToEndOfLine() throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.clearToEndOfLine(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could clear to end of line for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
package net.rubygrapefruit.platform.internal;
|
||||
|
||||
import net.rubygrapefruit.platform.NativeException;
|
||||
import net.rubygrapefruit.platform.Terminal;
|
||||
import net.rubygrapefruit.platform.TerminalAccess;
|
||||
import net.rubygrapefruit.platform.TerminalSize;
|
||||
import net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions;
|
||||
|
||||
public class WindowsTerminal extends AbstractTerminal {
|
||||
private final TerminalAccess.Output output;
|
||||
|
||||
public WindowsTerminal(TerminalAccess.Output output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return output.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.initConsole(output.ordinal(), result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not open console for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsColor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTextAttributes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCursorMotion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalSize getTerminalSize() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
MutableTerminalSize size = new MutableTerminalSize();
|
||||
WindowsConsoleFunctions.getConsoleSize(output.ordinal(), size, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not determine console size for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal bold() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.bold(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not switch console to bold mode for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal foreground(Color color) {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.foreground(color.ordinal(), result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not change console foreground color for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal normal() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.normal(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not switch console to normal mode for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal reset() {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.reset(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not reset console for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorDown(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.down(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor down for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorUp(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.up(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor up for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorLeft(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.left(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor left for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorRight(int count) throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.right(count, result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor right for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal cursorStartOfLine() throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.startLine(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could not move cursor to start of line for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Terminal clearToEndOfLine() throws NativeException {
|
||||
FunctionResult result = new FunctionResult();
|
||||
WindowsConsoleFunctions.clearToEndOfLine(result);
|
||||
if (result.isFailed()) {
|
||||
throw new NativeException(String.format("Could clear to end of line for %s: %s", this, result.getMessage()));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.rubygrapefruit.platform.internal.jni;
|
||||
|
||||
public class NativeLibraryFunctions {
|
||||
public static final int VERSION = 3;
|
||||
public static final int VERSION = 4;
|
||||
|
||||
public static native int getVersion();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package net.rubygrapefruit.platform.internal.jni;
|
||||
|
||||
import net.rubygrapefruit.platform.internal.FunctionResult;
|
||||
import net.rubygrapefruit.platform.internal.TerminalCapabilities;
|
||||
|
||||
public class TerminfoFunctions {
|
||||
/**
|
||||
* Sets up terminal info and switches output to normal mode.
|
||||
*/
|
||||
public static native void initTerminal(int filedes, FunctionResult result);
|
||||
public static native void initTerminal(int filedes, TerminalCapabilities terminalCapabilities, FunctionResult result);
|
||||
|
||||
public static native void bold(FunctionResult result);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user