Added support for Terminal.up(), down(), left(), right() for the terminal.

This commit is contained in:
Adam Murdoch
2012-08-04 16:18:30 +10:00
parent 7ee843612a
commit ec9d8d7bf8
12 changed files with 686 additions and 467 deletions

View File

@@ -1,12 +1,23 @@
Provides Java bindings for various native APIs. Provides Java bindings for various native APIs.
# Available bindings
## Generic
* Get and set UNIX file mode. * Get and set UNIX file mode.
* Get PID of current process. * Get PID of current process.
## Terminal and console
These bindings work for both the UNIX terminal and Windows console:
* Determine if stdout/stderr are attached to a terminal. * Determine if stdout/stderr are attached to a terminal.
* Query the terminal size. * Query the terminal size.
* Switch between bold and normal mode on the terminal. * Switch between bold and normal mode on the terminal.
* Change foreground color on the terminal. * Change foreground color on the terminal.
* Move terminal cursor up, down, left, right.
Currently ported to OS X, Linux and Windows. Tested on: Currently ported to OS X, Linux and Windows. Tested on:
@@ -14,13 +25,17 @@ Currently ported to OS X, Linux and Windows. Tested on:
* Ubunutu 12.04 (amd64) * Ubunutu 12.04 (amd64)
* Windows 7 (amd64) * Windows 7 (amd64)
## Building # Building
### Ubuntu ## Ubuntu
You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too. You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too.
## TODO ## Windows
You need to install Visual studio, and build from a Visual studio command prompt.
# TODO
* Fix TERM=dumb on linux * Fix TERM=dumb on linux
* Split out separate native library for terminal handling. * Split out separate native library for terminal handling.

View File

@@ -17,5 +17,5 @@ void mark_failed_with_code(JNIEnv *env, const char* message, int error_code, job
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getVersion(JNIEnv *env, jclass target) { Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getVersion(JNIEnv *env, jclass target) {
return 2; return 3;
} }

View File

@@ -108,6 +108,25 @@ void write_capability(JNIEnv *env, const char* capability, jobject result) {
} }
} }
void write_param_capability(JNIEnv *env, const char* capability, int count, jobject result) {
char* cap = tgetstr((char*)capability, NULL);
if (cap == NULL) {
mark_failed_with_message(env, "unknown terminal capability", result);
return;
}
cap = tparm(cap, count, 0, 0, 0, 0, 0, 0, 0, 0);
if (cap == NULL) {
mark_failed_with_message(env, "could not format terminal capability string", result);
return;
}
if (tputs(cap, 1, write_to_terminal) == ERR) {
mark_failed_with_message(env, "could not write to terminal", result);
return;
}
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_initTerminal(JNIEnv *env, jclass target, jint output, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_initTerminal(JNIEnv *env, jclass target, jint output, jobject result) {
if (!isatty(output+1)) { if (!isatty(output+1)) {
@@ -140,22 +159,36 @@ Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_reset(JNIEnv *en
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) {
char* capability = tgetstr((char*)"AF", NULL); write_param_capability(env, "AF", color, result);
if (capability == NULL) { }
mark_failed_with_message(env, "unknown terminal capability", result);
return;
}
capability = tparm(capability, color, 0, 0, 0, 0, 0, 0, 0, 0); JNIEXPORT void JNICALL
if (capability == NULL) { Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) {
mark_failed_with_message(env, "could not format terminal capability string", result); for (jint i = 0; i < count; i++) {
return; write_capability(env, "up", result);
}
if (tputs(capability, 1, write_to_terminal) == ERR) {
mark_failed_with_message(env, "could not write to terminal", result);
return;
} }
} }
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) {
for (jint i = 0; i < count; i++) {
write_capability(env, "do", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) {
for (jint i = 0; i < count; i++) {
write_capability(env, "le", result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) {
for (jint i = 0; i < count; i++) {
write_capability(env, "nd", result);
}
}
#endif #endif

View File

@@ -34,6 +34,31 @@ public class Main {
terminal.normal(); terminal.normal();
System.out.println(); System.out.println();
} }
System.out.println();
terminal.reset();
System.out.println("CURSOR MOVEMENT");
System.out.println(" ");
System.out.println(" ");
System.out.println(" ");
System.out.print("draw ");
terminal.cursorLeft(10);
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.cursorRight(10);
System.out.println("done!");
} }
System.out.println(); System.out.println();

View File

@@ -4,9 +4,21 @@ import java.io.File;
/** /**
* Functions to query and modify a file's POSIX meta-data. * Functions to query and modify a file's POSIX meta-data.
*
* Supported on Linux, OS X
*/ */
public interface PosixFile extends NativeIntegration { public interface PosixFile extends NativeIntegration {
/**
* Sets the mode for the given file.
*
* @throws NativeException On failure.
*/
void setMode(File path, int perms) throws NativeException; void setMode(File path, int perms) throws NativeException;
/**
* Gets the mode for the given file.
*
* @throws NativeException On failure.
*/
int getMode(File path) throws NativeException; int getMode(File path) throws NativeException;
} }

View File

@@ -2,7 +2,14 @@ package net.rubygrapefruit.platform;
/** /**
* Functions to query and modify a process' meta-data * Functions to query and modify a process' meta-data
*
* Supported on Linux, OS X, Windows.
*/ */
public interface Process extends NativeIntegration { public interface Process extends NativeIntegration {
/**
* Returns the process identifier.
*
* @throws NativeException On failure.
*/
int getProcessId() throws NativeException; int getProcessId() throws NativeException;
} }

View File

@@ -1,5 +1,10 @@
package net.rubygrapefruit.platform; package net.rubygrapefruit.platform;
/**
* Allows the terminal/console to be manipulated.
*
* Supported on Linux, OS X, Windows.
*/
public interface Terminal { public interface Terminal {
enum Color { enum Color {
Black, Red, Green, Yellow, Blue, Magenta, Cyan, White Black, Red, Green, Yellow, Blue, Magenta, Cyan, White
@@ -7,26 +12,64 @@ public interface Terminal {
/** /**
* Returns the size of the terminal. * Returns the size of the terminal.
*
* @throws NativeException On failure.
*/ */
TerminalSize getTerminalSize(); TerminalSize getTerminalSize() throws NativeException;
/** /**
* Sets the terminal foreground color. * Sets the terminal foreground color.
*
* @throws NativeException On failure.
*/ */
Terminal foreground(Color color); Terminal foreground(Color color) throws NativeException;
/** /**
* Switches the terminal to bold mode. * Switches the terminal to bold mode.
*
* @throws NativeException On failure.
*/ */
Terminal bold(); Terminal bold() throws NativeException;
/** /**
* Switches the terminal to normal mode. * Switches the terminal to normal mode.
*
* @throws NativeException On failure.
*/ */
Terminal normal(); Terminal normal() throws NativeException;
/** /**
* Switches the terminal to normal mode and restores default colors. * Switches the terminal to normal mode and restores default colors.
*
* @throws NativeException On failure.
*/ */
Terminal reset(); Terminal reset() throws NativeException;
/**
* Moves the cursor the given number of characters to the left.
*
* @throws NativeException On failure.
*/
Terminal cursorLeft(int count) throws NativeException;
/**
* Moves the cursor the given number of characters to the right.
*
* @throws NativeException On failure.
*/
Terminal cursorRight(int count) throws NativeException;
/**
* Moves the cursor the given number of characters up.
*
* @throws NativeException On failure.
*/
Terminal cursorUp(int count) throws NativeException;
/**
* Moves the cursor the given number of characters down.
*
* @throws NativeException On failure.
*/
Terminal cursorDown(int count) throws NativeException;
} }

View File

@@ -1,9 +1,24 @@
package net.rubygrapefruit.platform; package net.rubygrapefruit.platform;
/**
* Provides access to the terminal/console.
*
* Supported on Linux, OS X, Windows.
*/
public interface TerminalAccess extends NativeIntegration { public interface TerminalAccess extends NativeIntegration {
enum Output {Stdout, Stderr} enum Output {Stdout, Stderr}
boolean isTerminal(Output output); /**
* Returns true if the given output is attached to a terminal.
*
* @throws NativeException On failure.
*/
boolean isTerminal(Output output) throws NativeException;
Terminal getTerminal(Output output); /**
* Returns the terminal attached to the given output.
*
* @throws NativeException When the output is not attached to a terminal.
*/
Terminal getTerminal(Output output) throws NativeException;
} }

View File

@@ -87,4 +87,48 @@ public class TerminfoTerminal extends AbstractTerminal {
} }
return this; return this;
} }
@Override
public Terminal cursorDown(int count) {
stream.flush();
FunctionResult result = new FunctionResult();
TerminfoFunctions.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) {
stream.flush();
FunctionResult result = new FunctionResult();
TerminfoFunctions.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) {
stream.flush();
FunctionResult result = new FunctionResult();
TerminfoFunctions.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) {
stream.flush();
FunctionResult result = new FunctionResult();
TerminfoFunctions.right(count, result);
if (result.isFailed()) {
throw new NativeException(String.format("Could not move cursor right for %s: %s", this, result.getMessage()));
}
return this;
}
} }

View File

@@ -77,4 +77,24 @@ public class WindowsTerminal extends AbstractTerminal {
} }
return this; return this;
} }
@Override
public Terminal cursorDown(int count) throws NativeException {
throw new UnsupportedOperationException();
}
@Override
public Terminal cursorUp(int count) throws NativeException {
throw new UnsupportedOperationException();
}
@Override
public Terminal cursorLeft(int count) throws NativeException {
throw new UnsupportedOperationException();
}
@Override
public Terminal cursorRight(int count) throws NativeException {
throw new UnsupportedOperationException();
}
} }

View File

@@ -1,7 +1,7 @@
package net.rubygrapefruit.platform.internal.jni; package net.rubygrapefruit.platform.internal.jni;
public class NativeLibraryFunctions { public class NativeLibraryFunctions {
public static final int VERSION = 2; public static final int VERSION = 3;
public static native int getVersion(); public static native int getVersion();
} }

View File

@@ -12,8 +12,13 @@ public class TerminfoFunctions {
public static native void reset(FunctionResult result); public static native void reset(FunctionResult result);
/**
* Set the foreground color to the given ansi color.
*/
public static native void foreground(int ansiColor, FunctionResult result); public static native void foreground(int ansiColor, FunctionResult result);
public static native void left(int count, FunctionResult result);
public static native void right(int count, FunctionResult result);
public static native void up(int count, FunctionResult result);
public static native void down(int count, FunctionResult result);
} }