X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fio%2FECMA48Terminal.java;h=7721330f8d852480d186c424c8965075ca89829d;hb=bb35d91958450cc7152d2063f1d6cd34c15e2a3d;hp=b608a69f832862c76719201d02d5b33758b9ea5d;hpb=8dc20d387df1c9344e324d6b8a0d0d7939a47940;p=fanfix.git diff --git a/src/jexer/io/ECMA48Terminal.java b/src/jexer/io/ECMA48Terminal.java index b608a69..7721330 100644 --- a/src/jexer/io/ECMA48Terminal.java +++ b/src/jexer/io/ECMA48Terminal.java @@ -61,7 +61,7 @@ import static jexer.TKeypress.*; * 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. @@ -73,7 +73,7 @@ public class ECMA48Terminal implements Runnable { * * @return the SessionInfo */ - public final SessionInfo getSessionInfo() { + public SessionInfo getSessionInfo() { return sessionInfo; } @@ -98,11 +98,6 @@ public class ECMA48Terminal implements Runnable { */ private ArrayList params; - /** - * params[paramI] is being appended to. - */ - private int paramI; - /** * States in the input parser. */ @@ -127,6 +122,12 @@ public class ECMA48Terminal implements Runnable { */ 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. */ @@ -181,6 +182,11 @@ public class ECMA48Terminal implements Runnable { */ 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. @@ -243,7 +249,7 @@ public class ECMA48Terminal implements Runnable { }; try { Process process; - if (mode == true) { + if (mode) { process = Runtime.getRuntime().exec(cmdRaw); } else { process = Runtime.getRuntime().exec(cmdCooked); @@ -278,6 +284,8 @@ public class ECMA48Terminal implements Runnable { /** * 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 @@ -288,7 +296,7 @@ public class ECMA48Terminal implements Runnable { * @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(); @@ -296,6 +304,7 @@ public class ECMA48Terminal implements Runnable { mouse2 = false; mouse3 = false; stopReaderThread = false; + this.listener = listener; if (input == null) { // inputStream = System.in; @@ -396,7 +405,6 @@ public class ECMA48Terminal implements Runnable { private void reset() { state = ParseState.GROUND; params = new ArrayList(); - paramI = 0; params.clear(); params.add(""); } @@ -634,7 +642,9 @@ public class ECMA48Terminal implements Runnable { public void getEvents(final List queue) { synchronized (eventQueue) { if (eventQueue.size() > 0) { - queue.addAll(eventQueue); + synchronized (queue) { + queue.addAll(eventQueue); + } eventQueue.clear(); } } @@ -645,28 +655,34 @@ public class ECMA48Terminal implements Runnable { * * @param queue list to append new events to */ - public void getIdleEvents(final List queue) { + private void getIdleEvents(final List 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(); } } } @@ -785,14 +801,14 @@ public class ECMA48Terminal implements Runnable { 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; } @@ -893,14 +909,14 @@ public class ECMA48Terminal implements Runnable { 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; } @@ -986,7 +1002,7 @@ public class ECMA48Terminal implements Runnable { 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()); @@ -1421,7 +1437,7 @@ public class ECMA48Terminal implements Runnable { 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 @@ -1436,15 +1452,25 @@ public class ECMA48Terminal implements Runnable { synchronized (eventQueue) { eventQueue.addAll(events); } - // Now wake up the backend - synchronized (this) { - this.notifyAll(); + synchronized (listener) { + listener.notifyAll(); } events.clear(); } } } } else { + 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); }