* This class reads keystrokes and mouse events and emits output to ANSI
* X3.64 / ECMA-48 type terminals e.g. xterm, linux, vt100, ansi.sys, etc.
*/
-public class ECMA48Terminal implements Runnable {
+public final class ECMA48Terminal implements Runnable {
/**
* The session information.
*
* @return the SessionInfo
*/
- public final SessionInfo getSessionInfo() {
+ public SessionInfo getSessionInfo() {
return sessionInfo;
}
*/
private ArrayList<String> params;
- /**
- * params[paramI] is being appended to.
- */
- private int paramI;
-
/**
* States in the input parser.
*/
*/
private long escapeTime;
+ /**
+ * The time we last checked the window size. We try not to spawn stty
+ * more than once per second.
+ */
+ private long windowSizeTime;
+
/**
* true if mouse1 was down. Used to report mouse1 on the release event.
*/
*/
private PrintWriter output;
+ /**
+ * The listening object that run() wakes up on new input.
+ */
+ private Object listener;
+
/**
* When true, the terminal is sending non-UTF8 bytes when reporting mouse
* events.
};
try {
Process process;
- if (mode == true) {
+ if (mode) {
process = Runtime.getRuntime().exec(cmdRaw);
} else {
process = Runtime.getRuntime().exec(cmdCooked);
/**
* Constructor sets up state for getEvent().
*
+ * @param listener the object this backend needs to wake up when new
+ * input comes in
* @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
* @throws UnsupportedEncodingException if an exception is thrown when
* creating the InputStreamReader
*/
- public ECMA48Terminal(final InputStream input,
+ public ECMA48Terminal(final Object listener, final InputStream input,
final OutputStream output) throws UnsupportedEncodingException {
reset();
mouse2 = false;
mouse3 = false;
stopReaderThread = false;
+ this.listener = listener;
if (input == null) {
// inputStream = System.in;
private void reset() {
state = ParseState.GROUND;
params = new ArrayList<String>();
- paramI = 0;
params.clear();
params.add("");
}
// Unknown modifier, bail out
return null;
}
-
+
switch (key) {
case 1:
return new TKeypressEvent(kbHome, alt, ctrl, shift);
y = windowResize.getHeight() - 1;
}
- TMouseEvent event = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN);
- event.x = x;
- event.y = y;
- event.absoluteX = x;
- event.absoluteY = y;
+ TMouseEvent.Type eventType = TMouseEvent.Type.MOUSE_DOWN;
+ boolean eventMouse1 = false;
+ boolean eventMouse2 = false;
+ boolean eventMouse3 = false;
+ boolean eventMouseWheelUp = false;
+ boolean eventMouseWheelDown = false;
// System.err.printf("buttons: %04x\r\n", buttons);
switch (buttons) {
case 0:
- event.mouse1 = true;
+ eventMouse1 = true;
mouse1 = true;
break;
case 1:
- event.mouse2 = true;
+ eventMouse2 = true;
mouse2 = true;
break;
case 2:
- event.mouse3 = true;
+ eventMouse3 = true;
mouse3 = true;
break;
case 3:
// Release or Move
if (!mouse1 && !mouse2 && !mouse3) {
- event.type = TMouseEvent.Type.MOUSE_MOTION;
+ eventType = TMouseEvent.Type.MOUSE_MOTION;
} else {
- event.type = TMouseEvent.Type.MOUSE_UP;
+ eventType = TMouseEvent.Type.MOUSE_UP;
}
if (mouse1) {
mouse1 = false;
- event.mouse1 = true;
+ eventMouse1 = true;
}
if (mouse2) {
mouse2 = false;
- event.mouse2 = true;
+ eventMouse2 = true;
}
if (mouse3) {
mouse3 = false;
- event.mouse3 = true;
+ eventMouse3 = true;
}
break;
case 32:
// Dragging with mouse1 down
- event.mouse1 = true;
+ eventMouse1 = true;
mouse1 = true;
- event.type = TMouseEvent.Type.MOUSE_MOTION;
+ eventType = TMouseEvent.Type.MOUSE_MOTION;
break;
case 33:
// Dragging with mouse2 down
- event.mouse2 = true;
+ eventMouse2 = true;
mouse2 = true;
- event.type = TMouseEvent.Type.MOUSE_MOTION;
+ eventType = TMouseEvent.Type.MOUSE_MOTION;
break;
case 34:
// Dragging with mouse3 down
- event.mouse3 = true;
+ eventMouse3 = true;
mouse3 = true;
- event.type = TMouseEvent.Type.MOUSE_MOTION;
+ eventType = TMouseEvent.Type.MOUSE_MOTION;
break;
case 96:
// Dragging with mouse2 down after wheelUp
- event.mouse2 = true;
+ eventMouse2 = true;
mouse2 = true;
- event.type = TMouseEvent.Type.MOUSE_MOTION;
+ eventType = TMouseEvent.Type.MOUSE_MOTION;
break;
case 97:
// Dragging with mouse2 down after wheelDown
- event.mouse2 = true;
+ eventMouse2 = true;
mouse2 = true;
- event.type = TMouseEvent.Type.MOUSE_MOTION;
+ eventType = TMouseEvent.Type.MOUSE_MOTION;
break;
case 64:
- event.mouseWheelUp = true;
+ eventMouseWheelUp = true;
break;
case 65:
- event.mouseWheelDown = true;
+ eventMouseWheelDown = true;
break;
default:
// Unknown, just make it motion
- event.type = TMouseEvent.Type.MOUSE_MOTION;
+ eventType = TMouseEvent.Type.MOUSE_MOTION;
break;
}
- return event;
+ return new TMouseEvent(eventType, x, y, x, y,
+ eventMouse1, eventMouse2, eventMouse3,
+ eventMouseWheelUp, eventMouseWheelDown);
}
/**
public void getEvents(final List<TInputEvent> queue) {
synchronized (eventQueue) {
if (eventQueue.size() > 0) {
- queue.addAll(eventQueue);
+ synchronized (queue) {
+ queue.addAll(eventQueue);
+ }
eventQueue.clear();
}
}
*
* @param queue list to append new events to
*/
- public void getIdleEvents(final List<TInputEvent> queue) {
+ private void getIdleEvents(final List<TInputEvent> queue) {
+ Date now = new Date();
// Check for new window size
- sessionInfo.queryWindowSize();
- int newWidth = sessionInfo.getWindowWidth();
- int newHeight = sessionInfo.getWindowHeight();
- if ((newWidth != windowResize.getWidth())
- || (newHeight != windowResize.getHeight())
- ) {
- TResizeEvent event = new TResizeEvent(TResizeEvent.Type.SCREEN,
- newWidth, newHeight);
- windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
- newWidth, newHeight);
- synchronized (eventQueue) {
- eventQueue.add(event);
+ long windowSizeDelay = now.getTime() - windowSizeTime;
+ if (windowSizeDelay > 1000) {
+ sessionInfo.queryWindowSize();
+ int newWidth = sessionInfo.getWindowWidth();
+ int newHeight = sessionInfo.getWindowHeight();
+ if ((newWidth != windowResize.getWidth())
+ || (newHeight != windowResize.getHeight())
+ ) {
+ TResizeEvent event = new TResizeEvent(TResizeEvent.Type.SCREEN,
+ newWidth, newHeight);
+ windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
+ newWidth, newHeight);
+ queue.add(event);
}
+ windowSizeTime = now.getTime();
}
- synchronized (eventQueue) {
- if (eventQueue.size() > 0) {
- queue.addAll(eventQueue);
- eventQueue.clear();
+ // ESCDELAY type timeout
+ if (state == ParseState.ESCAPE) {
+ long escDelay = now.getTime() - escapeTime;
+ if (escDelay > 100) {
+ // After 0.1 seconds, assume a true escape character
+ queue.add(controlChar((char)0x1B, false));
+ reset();
}
}
}
case CSI_ENTRY:
// Numbers - parameter values
if ((ch >= '0') && (ch <= '9')) {
- params.set(paramI, params.get(paramI) + ch);
+ params.set(params.size() - 1,
+ params.get(params.size() - 1) + ch);
state = ParseState.CSI_PARAM;
return;
}
// Parameter separator
if (ch == ';') {
- paramI++;
- params.set(paramI, "");
+ params.add("");
return;
}
case CSI_PARAM:
// Numbers - parameter values
if ((ch >= '0') && (ch <= '9')) {
- params.set(paramI, params.get(paramI) + ch);
+ params.set(params.size() - 1,
+ params.get(params.size() - 1) + ch);
state = ParseState.CSI_PARAM;
return;
}
// Parameter separator
if (ch == ';') {
- paramI++;
- params.set(paramI, "");
+ params.add("");
return;
}
return;
case MOUSE:
- params.set(0, params.get(paramI) + ch);
+ params.set(0, params.get(params.size() - 1) + ch);
if (params.get(0).length() == 3) {
// We have enough to generate a mouse event
events.add(parseMouse());
readBuffer = new char[readBuffer.length * 2];
}
- int rc = input.read(readBuffer, 0, n);
+ int rc = input.read(readBuffer, 0, readBuffer.length);
// System.err.printf("read() %d", rc); System.err.flush();
if (rc == -1) {
// This is EOF
synchronized (eventQueue) {
eventQueue.addAll(events);
}
- // Now wake up the backend
- synchronized (this) {
- this.notifyAll();
+ synchronized (listener) {
+ listener.notifyAll();
}
events.clear();
}
}
}
} else {
- // Wait 5 millis for more data
- Thread.sleep(5);
+ getIdleEvents(events);
+ if (events.size() > 0) {
+ synchronized (eventQueue) {
+ eventQueue.addAll(events);
+ }
+ events.clear();
+ synchronized (listener) {
+ listener.notifyAll();
+ }
+ }
+
+ // Wait 10 millis for more data
+ Thread.sleep(10);
}
// System.err.println("end while loop"); System.err.flush();
} catch (InterruptedException e) {