X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fio%2FECMA48Terminal.java;h=7721330f8d852480d186c424c8965075ca89829d;hb=bb35d91958450cc7152d2063f1d6cd34c15e2a3d;hp=6defbe4937c856840a6f97e2ca6f9918f783ebbb;hpb=7b5261bc5b641e0769902f014e3b21f61050b02b;p=fanfix.git diff --git a/src/jexer/io/ECMA48Terminal.java b/src/jexer/io/ECMA48Terminal.java index 6defbe4..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(""); } @@ -406,41 +414,34 @@ public class ECMA48Terminal implements Runnable { * etc.). * * @param ch Unicode code point + * @param alt if true, set alt on the TKeypress * @return one TKeypress event, either a control character (e.g. isKey == * false, ch == 'A', ctrl == true), or a special key (e.g. isKey == true, * fnKey == ESC) */ - private TKeypressEvent controlChar(final char ch) { - TKeypressEvent event = new TKeypressEvent(); - + private TKeypressEvent controlChar(final char ch, final boolean alt) { // System.err.printf("controlChar: %02x\n", ch); switch (ch) { case 0x0D: // Carriage return --> ENTER - event.key = kbEnter; - break; + return new TKeypressEvent(kbEnter, alt, false, false); case 0x0A: // Linefeed --> ENTER - event.key = kbEnter; - break; + return new TKeypressEvent(kbEnter, alt, false, false); case 0x1B: // ESC - event.key = kbEsc; - break; + return new TKeypressEvent(kbEsc, alt, false, false); case '\t': // TAB - event.key = kbTab; - break; + return new TKeypressEvent(kbTab, alt, false, false); default: // Make all other control characters come back as the alphabetic // character with the ctrl field set. So SOH would be 'A' + // ctrl. - event.key = new TKeypress(false, 0, (char)(ch + 0x40), - false, true, false); - break; + return new TKeypressEvent(false, 0, (char)(ch + 0x40), + alt, true, false); } - return event; } /** @@ -457,220 +458,64 @@ public class ECMA48Terminal implements Runnable { if (params.size() > 1) { modifier = Integer.parseInt(params.get(1)); } - TKeypressEvent event = new TKeypressEvent(); + boolean alt = false; + boolean ctrl = false; + boolean shift = false; switch (modifier) { case 0: // No modifier - switch (key) { - case 1: - event.key = kbHome; - break; - case 2: - event.key = kbIns; - break; - case 3: - event.key = kbDel; - break; - case 4: - event.key = kbEnd; - break; - case 5: - event.key = kbPgUp; - break; - case 6: - event.key = kbPgDn; - break; - case 15: - event.key = kbF5; - break; - case 17: - event.key = kbF6; - break; - case 18: - event.key = kbF7; - break; - case 19: - event.key = kbF8; - break; - case 20: - event.key = kbF9; - break; - case 21: - event.key = kbF10; - break; - case 23: - event.key = kbF11; - break; - case 24: - event.key = kbF12; - break; - default: - // Unknown - return null; - } - break; case 2: // Shift - switch (key) { - case 1: - event.key = kbShiftHome; - break; - case 2: - event.key = kbShiftIns; - break; - case 3: - event.key = kbShiftDel; - break; - case 4: - event.key = kbShiftEnd; - break; - case 5: - event.key = kbShiftPgUp; - break; - case 6: - event.key = kbShiftPgDn; - break; - case 15: - event.key = kbShiftF5; - break; - case 17: - event.key = kbShiftF6; - break; - case 18: - event.key = kbShiftF7; - break; - case 19: - event.key = kbShiftF8; - break; - case 20: - event.key = kbShiftF9; - break; - case 21: - event.key = kbShiftF10; - break; - case 23: - event.key = kbShiftF11; - break; - case 24: - event.key = kbShiftF12; - break; - default: - // Unknown - return null; - } + shift = true; break; - case 3: // Alt - switch (key) { - case 1: - event.key = kbAltHome; - break; - case 2: - event.key = kbAltIns; - break; - case 3: - event.key = kbAltDel; - break; - case 4: - event.key = kbAltEnd; - break; - case 5: - event.key = kbAltPgUp; - break; - case 6: - event.key = kbAltPgDn; - break; - case 15: - event.key = kbAltF5; - break; - case 17: - event.key = kbAltF6; - break; - case 18: - event.key = kbAltF7; - break; - case 19: - event.key = kbAltF8; - break; - case 20: - event.key = kbAltF9; - break; - case 21: - event.key = kbAltF10; - break; - case 23: - event.key = kbAltF11; - break; - case 24: - event.key = kbAltF12; - break; - default: - // Unknown - return null; - } + alt = true; break; - case 5: // Ctrl - switch (key) { - case 1: - event.key = kbCtrlHome; - break; - case 2: - event.key = kbCtrlIns; - break; - case 3: - event.key = kbCtrlDel; - break; - case 4: - event.key = kbCtrlEnd; - break; - case 5: - event.key = kbCtrlPgUp; - break; - case 6: - event.key = kbCtrlPgDn; - break; - case 15: - event.key = kbCtrlF5; - break; - case 17: - event.key = kbCtrlF6; - break; - case 18: - event.key = kbCtrlF7; - break; - case 19: - event.key = kbCtrlF8; - break; - case 20: - event.key = kbCtrlF9; - break; - case 21: - event.key = kbCtrlF10; - break; - case 23: - event.key = kbCtrlF11; - break; - case 24: - event.key = kbCtrlF12; - break; - default: - // Unknown - return null; - } + ctrl = true; break; + default: + // Unknown modifier, bail out + return null; + } + switch (key) { + case 1: + return new TKeypressEvent(kbHome, alt, ctrl, shift); + case 2: + return new TKeypressEvent(kbIns, alt, ctrl, shift); + case 3: + return new TKeypressEvent(kbDel, alt, ctrl, shift); + case 4: + return new TKeypressEvent(kbEnd, alt, ctrl, shift); + case 5: + return new TKeypressEvent(kbPgUp, alt, ctrl, shift); + case 6: + return new TKeypressEvent(kbPgDn, alt, ctrl, shift); + case 15: + return new TKeypressEvent(kbF5, alt, ctrl, shift); + case 17: + return new TKeypressEvent(kbF6, alt, ctrl, shift); + case 18: + return new TKeypressEvent(kbF7, alt, ctrl, shift); + case 19: + return new TKeypressEvent(kbF8, alt, ctrl, shift); + case 20: + return new TKeypressEvent(kbF9, alt, ctrl, shift); + case 21: + return new TKeypressEvent(kbF10, alt, ctrl, shift); + case 23: + return new TKeypressEvent(kbF11, alt, ctrl, shift); + case 24: + return new TKeypressEvent(kbF12, alt, ctrl, shift); default: // Unknown return null; } - - // All OK, return a keypress - return event; } /** @@ -693,97 +538,100 @@ public class ECMA48Terminal implements Runnable { 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); } /** @@ -794,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(); } } @@ -805,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(); } } } @@ -840,19 +696,24 @@ public class ECMA48Terminal implements Runnable { */ private void processChar(final List events, final char ch) { - TKeypressEvent keypress; - Date now = new Date(); - // ESCDELAY type timeout + Date now = new Date(); if (state == ParseState.ESCAPE) { long escDelay = now.getTime() - escapeTime; if (escDelay > 250) { // After 0.25 seconds, assume a true escape character - events.add(controlChar((char)0x1B)); + events.add(controlChar((char)0x1B, false)); reset(); } } + // TKeypress fields + boolean ctrl = false; + boolean alt = false; + boolean shift = false; + char keyCh = ch; + TKeypress key; + // System.err.printf("state: %s ch %c\r\n", state, ch); switch (state) { @@ -866,17 +727,15 @@ public class ECMA48Terminal implements Runnable { if (ch <= 0x1F) { // Control character - events.add(controlChar(ch)); + events.add(controlChar(ch, false)); reset(); return; } if (ch >= 0x20) { // Normal character - keypress = new TKeypressEvent(); - keypress.key.isKey = false; - keypress.key.ch = ch; - events.add(keypress); + events.add(new TKeypressEvent(false, 0, ch, + false, false, false)); reset(); return; } @@ -886,9 +745,7 @@ public class ECMA48Terminal implements Runnable { case ESCAPE: if (ch <= 0x1F) { // ALT-Control character - keypress = controlChar(ch); - keypress.key.alt = true; - events.add(keypress); + events.add(controlChar(ch, true)); reset(); return; } @@ -906,39 +763,33 @@ public class ECMA48Terminal implements Runnable { } // Everything else is assumed to be Alt-keystroke - keypress = new TKeypressEvent(); - keypress.key.isKey = false; - keypress.key.ch = ch; - keypress.key.alt = true; if ((ch >= 'A') && (ch <= 'Z')) { - keypress.key.shift = true; + shift = true; } - events.add(keypress); + alt = true; + events.add(new TKeypressEvent(false, 0, ch, alt, ctrl, shift)); reset(); return; case ESCAPE_INTERMEDIATE: if ((ch >= 'P') && (ch <= 'S')) { // Function key - keypress = new TKeypressEvent(); - keypress.key.isKey = true; switch (ch) { case 'P': - keypress.key.fnKey = TKeypress.F1; + events.add(new TKeypressEvent(kbF1)); break; case 'Q': - keypress.key.fnKey = TKeypress.F2; + events.add(new TKeypressEvent(kbF2)); break; case 'R': - keypress.key.fnKey = TKeypress.F3; + events.add(new TKeypressEvent(kbF3)); break; case 'S': - keypress.key.fnKey = TKeypress.F4; + events.add(new TKeypressEvent(kbF4)); break; default: break; } - events.add(keypress); reset(); return; } @@ -950,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; } @@ -965,102 +816,81 @@ public class ECMA48Terminal implements Runnable { switch (ch) { case 'A': // Up - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.UP; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbUp, alt, ctrl, shift)); reset(); return; case 'B': // Down - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.DOWN; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbDown, alt, ctrl, shift)); reset(); return; case 'C': // Right - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.RIGHT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbRight, alt, ctrl, shift)); reset(); return; case 'D': // Left - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.LEFT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbLeft, alt, ctrl, shift)); reset(); return; case 'H': // Home - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.HOME; - events.add(keypress); + events.add(new TKeypressEvent(kbHome)); reset(); return; case 'F': // End - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.END; - events.add(keypress); + events.add(new TKeypressEvent(kbEnd)); reset(); return; case 'Z': // CBT - Cursor backward X tab stops (default 1) - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.BTAB; - events.add(keypress); + events.add(new TKeypressEvent(kbBackTab)); reset(); return; case 'M': @@ -1079,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; } @@ -1100,78 +930,66 @@ public class ECMA48Terminal implements Runnable { switch (ch) { case 'A': // Up - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.UP; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbUp, alt, ctrl, shift)); reset(); return; case 'B': // Down - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.DOWN; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbDown, alt, ctrl, shift)); reset(); return; case 'C': // Right - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.RIGHT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbRight, alt, ctrl, shift)); reset(); return; case 'D': // Left - keypress = new TKeypressEvent(); - keypress.key.isKey = true; - keypress.key.fnKey = TKeypress.LEFT; if (params.size() > 1) { if (params.get(1).equals("2")) { - keypress.key.shift = true; + shift = true; } if (params.get(1).equals("5")) { - keypress.key.ctrl = true; + ctrl = true; } if (params.get(1).equals("3")) { - keypress.key.alt = true; + alt = true; } } - events.add(keypress); + events.add(new TKeypressEvent(kbLeft, alt, ctrl, shift)); reset(); return; default: @@ -1184,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()); @@ -1619,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 @@ -1634,17 +1452,27 @@ 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 { - // 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) {