- 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:
Adam Murdoch
2012-08-10 06:00:05 +10:00
parent 0180d2f035
commit ec2eb737a6
9 changed files with 282 additions and 186 deletions

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
package net.rubygrapefruit.platform.internal;
public class TerminalCapabilities {
String terminalName;
boolean textAttributes;
boolean colors;
boolean cursorMotion;
}

View File

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

View File

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

View File

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

View File

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