/**
* Public constructor
*/
- public DemoApplication() {
+ public DemoApplication() throws Exception {
+ super(null, null);
+ /*
try {
ColorTheme theme = new ColorTheme();
TTYSessionInfo tty = new TTYSessionInfo();
} catch (Exception e) {
e.printStackTrace();
}
+ */
}
}
* @param args Command line arguments
*/
public static void main(String [] args) {
- DemoApplication app = new DemoApplication();
- app.run();
+ try {
+ DemoApplication app = new DemoApplication();
+ app.run();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
}
*/
package jexer;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.LinkedList;
+import java.util.List;
+
+import jexer.bits.CellAttributes;
+import jexer.bits.ColorTheme;
+import jexer.bits.GraphicsChars;
+import jexer.event.TCommandEvent;
+import jexer.event.TInputEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import jexer.event.TResizeEvent;
+import jexer.backend.Backend;
+import jexer.backend.ECMA48Backend;
+import static jexer.TCommand.*;
+import static jexer.TKeypress.*;
+
/**
* TApplication sets up a full Text User Interface application.
*/
public class TApplication {
/**
- * Run this application until it exits, using stdin and stdout
+ * Access to the physical screen, keyboard, and mouse.
+ */
+ public Backend backend;
+
+ /**
+ * Actual mouse coordinate X
+ */
+ private int mouseX;
+
+ /**
+ * Actual mouse coordinate Y
+ */
+ private int mouseY;
+
+ /**
+ * Event queue that will be drained by either primary or secondary Fiber
+ */
+ private List<TInputEvent> eventQueue;
+
+ /**
+ * Windows and widgets pull colors from this ColorTheme.
+ */
+ public ColorTheme theme;
+
+ /**
+ * When true, exit the application.
+ */
+ public boolean quit = false;
+
+ /**
+ * When true, repaint the entire screen.
+ */
+ public boolean repaint = true;
+
+ /**
+ * When true, just flush updates from the screen.
*/
- final public void run() {
+ public boolean flush = false;
+
+ /**
+ * Y coordinate of the top edge of the desktop.
+ */
+ static public final int desktopTop = 1;
+
+ /**
+ * Y coordinate of the bottom edge of the desktop.
+ */
+ public int desktopBottom;
+
+ /**
+ * Public constructor.
+ *
+ * @param input an InputStream connected to the remote user, or null for
+ * System.in. If System.in is used, then on non-Windows systems it will
+ * be put in raw mode; shutdown() will (blindly!) put System.in in cooked
+ * mode. input is always converted to a Reader with UTF-8 encoding.
+ * @param output an OutputStream connected to the remote user, or null
+ * for System.out. output is always converted to a Writer with UTF-8
+ * encoding.
+ */
+ public TApplication(InputStream input, OutputStream output) throws UnsupportedEncodingException {
+
+ backend = new ECMA48Backend(input, output);
+ theme = new ColorTheme();
+ desktopBottom = backend.screen.getHeight() - 1;
+ eventQueue = new LinkedList<TInputEvent>();
+ }
+
+ /**
+ * Invert the cell at the mouse pointer position.
+ */
+ private void drawMouse() {
+ CellAttributes attr = backend.screen.getAttrXY(mouseX, mouseY);
+ attr.foreColor = attr.foreColor.invert();
+ attr.backColor = attr.backColor.invert();
+ backend.screen.putAttrXY(mouseX, mouseY, attr, false);
+ flush = true;
/*
- while (quit == false) {
+ if (windows.length == 0) {
+ repaint = true;
+ }
+ */
+ // TODO: remove this repaint after the above if (windows.length == 0)
+ // can be used again.
+ repaint = true;
+ }
+
+ /**
+ * Draw everything.
+ */
+ final public void drawAll() {
+ if ((flush) && (!repaint)) {
+ backend.flushScreen();
+ flush = false;
+ return;
+ }
+
+ if (!repaint) {
+ return;
+ }
+
+ // If true, the cursor is not visible
+ boolean cursor = false;
+
+ // Start with a clean screen
+ backend.screen.clear();
+
+ // Draw the background
+ CellAttributes background = theme.getColor("tapplication.background");
+ backend.screen.putAll(GraphicsChars.HATCH, background);
+
+ /*
+ // Draw each window in reverse Z order
+ TWindow [] sorted = windows.dup;
+ sorted.sort.reverse;
+ foreach (w; sorted) {
+ w.drawChildren();
+ }
+
+ // Draw the blank menubar line - reset the screen clipping first so
+ // it won't trim it out.
+ backend.screen.resetClipping();
+ backend.screen.hLineXY(0, 0, backend.screen.getWidth(), ' ',
+ theme.getColor("tmenu"));
+ // Now draw the menus.
+ int x = 1;
+ foreach (m; menus) {
+ CellAttributes menuColor;
+ CellAttributes menuMnemonicColor;
+ if (m.active) {
+ menuColor = theme.getColor("tmenu.highlighted");
+ menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted");
+ } else {
+ menuColor = theme.getColor("tmenu");
+ menuMnemonicColor = theme.getColor("tmenu.mnemonic");
+ }
+ // Draw the menu title
+ backend.screen.hLineXY(x, 0, cast(int)m.title.length + 2, ' ',
+ menuColor);
+ backend.screen.putStrXY(x + 1, 0, m.title, menuColor);
+ // Draw the highlight character
+ backend.screen.putCharXY(x + 1 + m.mnemonic.shortcutIdx, 0,
+ m.mnemonic.shortcut, menuMnemonicColor);
+
+ if (m.active) {
+ m.drawChildren();
+ // Reset the screen clipping so we can draw the next title.
+ backend.screen.resetClipping();
+ }
+ x += m.title.length + 2;
+ }
+
+ foreach (m; subMenus) {
+ // Reset the screen clipping so we can draw the next sub-menu.
+ backend.screen.resetClipping();
+ m.drawChildren();
+ }
+ */
+
+ // Draw the mouse pointer
+ drawMouse();
- // Timeout is in milliseconds, so default timeout after 1
- // second of inactivity.
- uint timeout = getSleepTime(1000);
+ /*
+ // Place the cursor if it is visible
+ TWidget activeWidget = null;
+ if (sorted.length > 0) {
+ activeWidget = sorted[$ - 1].getActiveChild();
+ if (activeWidget.hasCursor) {
+ backend.screen.putCursor(true, activeWidget.getCursorAbsoluteX(),
+ activeWidget.getCursorAbsoluteY());
+ cursor = true;
+ }
+ }
+
+ // Kill the cursor
+ if (cursor == false) {
+ backend.screen.hideCursor();
+ }
+ */
+
+ // Flush the screen contents
+ backend.flushScreen();
+
+ repaint = false;
+ flush = false;
+ }
+
+ /**
+ * Run this application until it exits, using stdin and stdout
+ */
+ public final void run() {
+
+ while (quit == false) {
+ // Timeout is in milliseconds, so default timeout after 1 second
+ // of inactivity.
+ int timeout = getSleepTime(1000);
// std.stdio.stderr.writefln("poll() timeout: %d", timeout);
- if (eventQueue.length > 0) {
+ if (eventQueue.size() > 0) {
// Do not wait if there are definitely events waiting to be
// processed or a screen redraw to do.
timeout = 0;
}
// Pull any pending input events
- TInputEvent [] events = backend.getEvents(timeout);
+ List<TInputEvent> events = backend.getEvents(timeout);
metaHandleEvents(events);
// Process timers and call doIdle()'s
drawAll();
}
+ /*
+
// Shutdown the fibers
eventQueue.length = 0;
if (secondaryEventFiber !is null) {
// Wake up the primary handler so that it can exit.
primaryEventFiber.call();
}
+ */
backend.shutdown();
+ }
+
+ /**
+ * Peek at certain application-level events, add to eventQueue, and wake
+ * up the consuming Fiber.
+ *
+ * @param events the input events to consume
+ */
+ private void metaHandleEvents(List<TInputEvent> events) {
+
+ for (TInputEvent event: events) {
+
+ /*
+ System.err.printf(String.format("metaHandleEvents event: %s\n",
+ event)); System.err.flush();
+ */
+
+ if (quit == true) {
+ // Do no more processing if the application is already trying
+ // to exit.
+ return;
+ }
+
+ // DEBUG
+ if (event instanceof TKeypressEvent) {
+ TKeypressEvent keypress = (TKeypressEvent)event;
+ if (keypress.key.equals(kbAltX)) {
+ quit = true;
+ return;
+ }
+ }
+ // DEBUG
+
+ // Special application-wide events -------------------------------
+
+ // Abort everything
+ if (event instanceof TCommandEvent) {
+ TCommandEvent command = (TCommandEvent)event;
+ if (command.cmd.equals(cmAbort)) {
+ quit = true;
+ return;
+ }
+ }
+
+ // Screen resize
+ if (event instanceof TResizeEvent) {
+ TResizeEvent resize = (TResizeEvent)event;
+ backend.screen.setDimensions(resize.width, resize.height);
+ desktopBottom = backend.screen.getHeight() - 1;
+ repaint = true;
+ mouseX = 0;
+ mouseY = 0;
+ continue;
+ }
+
+ // Peek at the mouse position
+ if (event instanceof TMouseEvent) {
+ TMouseEvent mouse = (TMouseEvent)event;
+ if ((mouseX != mouse.x) || (mouseY != mouse.y)) {
+ mouseX = mouse.x;
+ mouseY = mouse.y;
+ drawMouse();
+ }
+ }
+
+ /*
+
+ // Put into the main queue
+ addEvent(event);
+
+ // Have one of the two consumer Fibers peel the events off
+ // the queue.
+ if (secondaryEventFiber !is null) {
+ assert(secondaryEventFiber.state == Fiber.State.HOLD);
+
+ // Wake up the secondary handler for these events
+ secondaryEventFiber.call();
+ } else {
+ assert(primaryEventFiber.state == Fiber.State.HOLD);
+
+ // Wake up the primary handler for these events
+ primaryEventFiber.call();
+ }
+ */
+
+ } // for (TInputEvent event: events)
+
+ }
+
+ /**
+ * Do stuff when there is no user input.
+ */
+ private void doIdle() {
+ /*
+ // Now run any timers that have timed out
+ auto now = Clock.currTime;
+ TTimer [] keepTimers;
+ foreach (t; timers) {
+ if (t.nextTick < now) {
+ t.tick();
+ if (t.recurring == true) {
+ keepTimers ~= t;
+ }
+ } else {
+ keepTimers ~= t;
+ }
+ }
+ timers = keepTimers;
+
+ // Call onIdle's
+ foreach (w; windows) {
+ w.onIdle();
+ }
*/
+ }
- System.out.println("Hello");
+ /**
+ * Get the amount of time I can sleep before missing a Timer tick.
+ *
+ * @param timeout = initial (maximum) timeout
+ * @return number of milliseconds between now and the next timer event
+ */
+ protected int getSleepTime(int timeout) {
+ /*
+ auto now = Clock.currTime;
+ auto sleepTime = dur!("msecs")(timeout);
+ foreach (t; timers) {
+ if (t.nextTick < now) {
+ return 0;
+ }
+ if ((t.nextTick > now) &&
+ ((t.nextTick - now) < sleepTime)
+ ) {
+ sleepTime = t.nextTick - now;
+ }
+ }
+ assert(sleepTime.total!("msecs")() >= 0);
+ return cast(uint)sleepTime.total!("msecs")();
+ */
+ return 0;
}
+
}
return String.format("%s", type);
}
+ /**
+ * Comparison. All fields must match to return true.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (!(rhs instanceof TCommand)) {
+ return false;
+ }
+
+ TCommand that = (TCommand)rhs;
+ return (type == that.type);
+ }
+
static public final TCommand cmAbort = new TCommand(TCommand.Type.ABORT);
static public final TCommand cmExit = new TCommand(TCommand.Type.EXIT);
static public final TCommand cmQuit = new TCommand(TCommand.Type.EXIT);
this.shift = shift;
}
+ /**
+ * Comparison. All fields must match to return true.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (!(rhs instanceof TKeypress)) {
+ return false;
+ }
+
+ TKeypress that = (TKeypress)rhs;
+ return ((isKey == that.isKey) &&
+ (fnKey == that.fnKey) &&
+ (ch == that.ch) &&
+ (alt == that.alt) &&
+ (ctrl == that.ctrl) &&
+ (shift == that.shift));
+ }
+
/**
* Make human-readable description of this Keystroke.
*/
/**
* Invert a color in the same way as (CGA/VGA color XOR 0x7).
- *
- * @param color color to change
* @return the inverted color
*/
- static public Color invert(Color color) {
- switch (color.value) {
+ public Color invert() {
+ switch (value) {
case black:
return Color.WHITE;
case white:
return Color.BLUE;
}
throw new IllegalArgumentException("Invalid Color value: " +
- color.value);
+ value);
+ }
+
+ /**
+ * Comparison. All fields must match to return true.
+ */
+ @Override
+ public boolean equals(Object rhs) {
+ if (!(rhs instanceof Color)) {
+ return false;
+ }
+
+ Color that = (Color)rhs;
+ return (value == that.value);
}
}
*/
package jexer.bits;
-// Commonly used ASCII characters
-
-/*
-public static final byte C_NUL = 0x00; // NUL
-public static final byte C_SOH = 0x01; // SOH
-public static final byte C_STX = 0x02; // STX
-public static final byte C_EOT = 0x04; // EOT
-public static final byte C_ACK = 0x06; // ACK
-public static final byte C_LF = 0x0A; // Line feed '\n'
-public static final byte C_CR = 0x0D; // Carriage return '\r'
-public static final byte C_XON = 0x11; // XON, also known as DC1
-public static final byte C_XOFF = 0x13; // XOFF, also known as DC3
-public static final byte C_NAK = 0x15; // NAK
-public static final byte C_CAN = 0x18; // CAN
-public static final byte C_SUB = 0x1A; // SUB
-public static final byte C_ESC = 0x1B; // ESC
- */
-
/**
* Collection of special characters used by the windowing system.
*/
/**
* CP437 translation map
*/
- private static final char cp437_chars[] = {
+ static private final char cp437_chars[] = {
'\u2007', '\u263A', '\u263B', '\u2665', '\u2666', '\u2663', '\u2660', '\u2022',
'\u25D8', '\u25CB', '\u25D9', '\u2642', '\u2640', '\u266A', '\u266B', '\u263C',
};
- public static final char HATCH = cp437_chars[0xB0];
- public static final char DOUBLE_BAR = cp437_chars[0xCD];
- public static final char BOX = cp437_chars[0xFE];
- public static final char CHECK = cp437_chars[0xFB];
- public static final char TRIPLET = cp437_chars[0xF0];
- public static final char OMEGA = cp437_chars[0xEA];
- public static final char PI = cp437_chars[0xE3];
- public static final char UPARROW = cp437_chars[0x18];
- public static final char DOWNARROW = cp437_chars[0x19];
- public static final char RIGHTARROW = cp437_chars[0x1A];
- public static final char LEFTARROW = cp437_chars[0x1B];
- public static final char SINGLE_BAR = cp437_chars[0xC4];
- public static final char BACK_ARROWHEAD = cp437_chars[0x11];
- public static final char LRCORNER = cp437_chars[0xD9];
- public static final char URCORNER = cp437_chars[0xBF];
- public static final char LLCORNER = cp437_chars[0xC0];
- public static final char ULCORNER = cp437_chars[0xDA];
- public static final char DEGREE = cp437_chars[0xF8];
- public static final char PLUSMINUS = cp437_chars[0xF1];
- public static final char WINDOW_TOP = cp437_chars[0xCD];
- public static final char WINDOW_LEFT_TOP = cp437_chars[0xD5];
- public static final char WINDOW_RIGHT_TOP = cp437_chars[0xB8];
- public static final char WINDOW_SIDE = cp437_chars[0xB3];
- public static final char WINDOW_LEFT_BOTTOM = cp437_chars[0xD4];
- public static final char WINDOW_RIGHT_BOTTOM = cp437_chars[0xBE];
- public static final char WINDOW_LEFT_TEE = cp437_chars[0xC6];
- public static final char WINDOW_RIGHT_TEE = cp437_chars[0xB5];
- public static final char WINDOW_SIDE_DOUBLE = cp437_chars[0xBA];
- public static final char WINDOW_LEFT_TOP_DOUBLE = cp437_chars[0xC9];
- public static final char WINDOW_RIGHT_TOP_DOUBLE = cp437_chars[0xBB];
- public static final char WINDOW_LEFT_BOTTOM_DOUBLE = cp437_chars[0xC8];
- public static final char WINDOW_RIGHT_BOTTOM_DOUBLE = cp437_chars[0xBC];
+ static public final char HATCH = cp437_chars[0xB0];
+ static public final char DOUBLE_BAR = cp437_chars[0xCD];
+ static public final char BOX = cp437_chars[0xFE];
+ static public final char CHECK = cp437_chars[0xFB];
+ static public final char TRIPLET = cp437_chars[0xF0];
+ static public final char OMEGA = cp437_chars[0xEA];
+ static public final char PI = cp437_chars[0xE3];
+ static public final char UPARROW = cp437_chars[0x18];
+ static public final char DOWNARROW = cp437_chars[0x19];
+ static public final char RIGHTARROW = cp437_chars[0x1A];
+ static public final char LEFTARROW = cp437_chars[0x1B];
+ static public final char SINGLE_BAR = cp437_chars[0xC4];
+ static public final char BACK_ARROWHEAD = cp437_chars[0x11];
+ static public final char LRCORNER = cp437_chars[0xD9];
+ static public final char URCORNER = cp437_chars[0xBF];
+ static public final char LLCORNER = cp437_chars[0xC0];
+ static public final char ULCORNER = cp437_chars[0xDA];
+ static public final char DEGREE = cp437_chars[0xF8];
+ static public final char PLUSMINUS = cp437_chars[0xF1];
+ static public final char WINDOW_TOP = cp437_chars[0xCD];
+ static public final char WINDOW_LEFT_TOP = cp437_chars[0xD5];
+ static public final char WINDOW_RIGHT_TOP = cp437_chars[0xB8];
+ static public final char WINDOW_SIDE = cp437_chars[0xB3];
+ static public final char WINDOW_LEFT_BOTTOM = cp437_chars[0xD4];
+ static public final char WINDOW_RIGHT_BOTTOM = cp437_chars[0xBE];
+ static public final char WINDOW_LEFT_TEE = cp437_chars[0xC6];
+ static public final char WINDOW_RIGHT_TEE = cp437_chars[0xB5];
+ static public final char WINDOW_SIDE_DOUBLE = cp437_chars[0xBA];
+ static public final char WINDOW_LEFT_TOP_DOUBLE = cp437_chars[0xC9];
+ static public final char WINDOW_RIGHT_TOP_DOUBLE = cp437_chars[0xBB];
+ static public final char WINDOW_LEFT_BOTTOM_DOUBLE = cp437_chars[0xC8];
+ static public final char WINDOW_RIGHT_BOTTOM_DOUBLE = cp437_chars[0xBC];
}
package jexer.io;
import java.io.BufferedReader;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
* X3.64 / ECMA-48 type terminals e.g. xterm, linux, vt100, ansi.sys,
* etc.
*/
-public class ECMA48Terminal {
+public class ECMA48Terminal implements Runnable {
/**
* The session information
*/
public SessionInfo session;
+ /**
+ * The event queue, filled up by a thread reading on input
+ */
+ private List<TInputEvent> eventQueue;
+
+ /**
+ * If true, we want the reader thread to exit gracefully.
+ */
+ private boolean stopReaderThread;
+
+ /**
+ * The reader thread
+ */
+ private Thread readerThread;
+
/**
* Parameters being collected. E.g. if the string is \033[1;3m, then
* params[0] will be 1 and params[1] will be 3.
/**
* The terminal's input. If an InputStream is not specified in the
- * constructor, then this InputReader will be bound to System.in with
- * UTF-8 encoding.
+ * constructor, then this InputStreamReader will be bound to System.in
+ * with UTF-8 encoding.
*/
private Reader input;
+ /**
+ * The terminal's raw InputStream. If an InputStream is not specified in
+ * the constructor, then this InputReader will be bound to System.in.
+ * This is used by run() to see if bytes are available() before calling
+ * (Reader)input.read().
+ */
+ private InputStream inputStream;
+
/**
* The terminal's output. If an OutputStream is not specified in the
* constructor, then this PrintWriter will be bound to System.out with
*/
private void doStty(boolean mode) {
String [] cmdRaw = {
- "/bin/sh", "-c", "stty raw < /dev/tty"
+ "/bin/sh", "-c", "stty -ignbrk -brkint -parmrk -istrip -inlcr -igncr -icrnl -ixon -opost -echo -echonl -icanon -isig -iexten -parenb cs8 min 1 < /dev/tty"
};
String [] cmdCooked = {
- "/bin/sh", "-c", "stty cooked < /dev/tty"
+ "/bin/sh", "-c", "stty sane cooked < /dev/tty"
};
try {
- System.out.println("spawn stty");
-
Process process;
if (mode == true) {
process = Runtime.getRuntime().exec(cmdRaw);
public ECMA48Terminal(InputStream input, OutputStream output) throws UnsupportedEncodingException {
reset();
- mouse1 = false;
- mouse2 = false;
- mouse3 = false;
+ mouse1 = false;
+ mouse2 = false;
+ mouse3 = false;
+ stopReaderThread = false;
if (input == null) {
- this.input = new InputStreamReader(System.in, "UTF-8");
+ // inputStream = System.in;
+ inputStream = new FileInputStream(FileDescriptor.in);
sttyRaw();
setRawMode = true;
} else {
- this.input = new InputStreamReader(input);
+ inputStream = input;
}
+ this.input = new InputStreamReader(inputStream, "UTF-8");
+
// TODO: include TelnetSocket from NIB and have it implement
// SessionInfo
if (input instanceof SessionInfo) {
// Hang onto the window size
windowResize = new TResizeEvent(TResizeEvent.Type.Screen,
session.getWindowWidth(), session.getWindowHeight());
+
+ // Spin up the input reader
+ eventQueue = new LinkedList<TInputEvent>();
+ readerThread = new Thread(this);
+ readerThread.start();
}
/**
* Restore terminal to normal state
*/
public void shutdown() {
+
+ // System.err.println("=== shutdown() ==="); System.err.flush();
+
+ // Tell the reader thread to stop looking at input
+ stopReaderThread = true;
+ try {
+ readerThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ // Disable mouse reporting and show cursor
+ output.printf("%s%s%s", mouse(false), cursor(true), normal());
+ output.flush();
+
if (setRawMode) {
sttyCooked();
setRawMode = false;
+ // We don't close System.in/out
+ } else {
+ // Shut down the streams, this should wake up the reader thread
+ // and make it exit.
+ try {
+ if (input != null) {
+ input.close();
+ input = null;
+ }
+ if (output != null) {
+ output.close();
+ output = null;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
- // Disable mouse reporting and show cursor
- output.printf("%s%s%s", mouse(false), cursor(true), normal());
}
/**
*/
private void reset() {
state = ParseState.GROUND;
+ params = new ArrayList<String>();
paramI = 0;
params.clear();
params.add("");
// System.err.printf("controlChar: %02x\n", ch);
switch (ch) {
- case '\r':
- // ENTER
+ case 0x0D:
+ // Carriage return --> ENTER
+ event.key = kbEnter;
+ break;
+ case 0x0A:
+ // Linefeed --> ENTER
event.key = kbEnter;
break;
case 0x1B:
*/
public List<TInputEvent> getEvents() {
List<TInputEvent> events = new LinkedList<TInputEvent>();
+
+ synchronized(this) {
+ if (eventQueue.size() > 0) {
+ events.addAll(eventQueue);
+ eventQueue.clear();
+ }
+ }
+
+ // TEST: drop a cmAbort
+ // events.add(new jexer.event.TCommandEvent(jexer.TCommand.cmAbort));
+ // events.add(new jexer.event.TKeypressEvent(kbAltX));
+
return events;
}
TKeypressEvent keypress;
Date now = new Date();
+ /*
// ESCDELAY type timeout
if (state == ParseState.ESCAPE) {
long escDelay = now.getTime() - escapeTime;
reset();
}
}
+ */
if (noChar == true) {
int newWidth = session.getWindowWidth();
return "\033[?1003;1005l\033[?1049l";
}
+ /**
+ * Read function runs on a separate thread.
+ */
+ public void run() {
+ boolean done = false;
+ // available() will often return > 1, so we need to read in chunks to
+ // stay caught up.
+ char [] readBuffer = new char[128];
+
+ while ((done == false) && (stopReaderThread == false)) {
+ try {
+ // We assume that if inputStream has bytes available, then
+ // input won't block on read().
+ int n = inputStream.available();
+ if (n > 0) {
+ if (readBuffer.length < n) {
+ // The buffer wasn't big enough, make it huger
+ readBuffer = new char[readBuffer.length * 2];
+ }
+
+ int rc = input.read(readBuffer, 0, n);
+ // System.err.printf("read() %d", rc); System.err.flush();
+ if (rc == -1) {
+ // This is EOF
+ done = true;
+ } else {
+ for (int i = 0; i < rc; i++) {
+ int ch = readBuffer[i];
+
+ // System.err.printf("** READ 0x%x '%c'", ch, ch);
+ List<TInputEvent> events = getEvents((char)ch);
+ synchronized (this) {
+ /*
+ System.err.printf("adding %d events\n",
+ events.size());
+ */
+ eventQueue.addAll(events);
+ }
+ }
+ }
+ } else {
+ // Wait 5 millis for more data
+ Thread.sleep(5);
+ }
+ // System.err.println("end while loop"); System.err.flush();
+ } catch (InterruptedException e) {
+ // SQUASH
+ } catch (IOException e) {
+ e.printStackTrace();
+ done = true;
+ }
+ } // while ((done == false) && (stopReaderThread == false))
+ // System.err.println("*** run() exiting..."); System.err.flush();
+ }
+
}
"/bin/sh", "-c", "stty size < /dev/tty"
};
try {
- System.out.println("spawn stty");
-
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
String line = in.readLine();