/**
* Get keyboard, mouse, and screen resize events.
*
+ * @param queue list to append new events to
* @param timeout maximum amount of time (in millis) to wait for an
* event. 0 means to return immediately, i.e. perform a poll.
- * @return events received, or an empty list if the timeout was reached
- * first
*/
@Override
- public List<TInputEvent> getEvents(int timeout) {
- return terminal.getEvents();
+ public void getEvents(List<TInputEvent> queue, int timeout) {
+ if (timeout > 0) {
+ // Try to sleep, let the terminal's input thread wake me up if
+ // something came in.
+ synchronized (terminal) {
+ try {
+ terminal.wait(timeout);
+ if (terminal.hasEvents()) {
+ // System.err.println("getEvents()");
+ terminal.getIdleEvents(queue);
+ } else {
+ // If I got here, then I timed out. Call
+ // terminal.getIdleEvents() to pick up stragglers
+ // like bare resize.
+ // System.err.println("getIdleEvents()");
+ terminal.getIdleEvents(queue);
+ }
+ } catch (InterruptedException e) {
+ // Spurious interrupt, pretend it was like a timeout.
+ // System.err.println("[interrupt] getEvents()");
+ terminal.getIdleEvents(queue);
+ }
+ }
+ } else {
+ // Asking for a poll, go get it.
+ System.err.println("[polled] getEvents()");
+ terminal.getEvents(queue);
+ }
}
/**
return output;
}
+ /**
+ * Check if there are events in the queue.
+ *
+ * @return if true, getEvents() has something to return to the backend
+ */
+ public boolean hasEvents() {
+ synchronized (eventQueue) {
+ return (eventQueue.size() > 0);
+ }
+ }
+
/**
* Call 'stty cooked' to set cooked mode.
*/
/**
* Return any events in the IO queue.
*
- * @return list of new events (which may be empty)
+ * @param queue list to append new events to
*/
- public List<TInputEvent> getEvents() {
- List<TInputEvent> events = new LinkedList<TInputEvent>();
-
- synchronized(this) {
+ public void getEvents(List<TInputEvent> queue) {
+ synchronized (eventQueue) {
if (eventQueue.size() > 0) {
- events.addAll(eventQueue);
+ queue.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;
}
/**
- * Parses the next character of input to see if an InputEvent is fully
- * here.
+ * Return any events in the IO queue due to timeout.
*
- * @param ch Unicode code point
- * @return list of new events (which may be empty)
+ * @param queue list to append new events to
*/
- public List<TInputEvent> getEvents(char ch) {
- return getEvents(ch, false);
+ public void getIdleEvents(List<TInputEvent> queue) {
+
+ // Check for new window size
+ session.queryWindowSize();
+ int newWidth = session.getWindowWidth();
+ int newHeight = session.getWindowHeight();
+ if ((newWidth != windowResize.width) ||
+ (newHeight != windowResize.height)) {
+ TResizeEvent event = new TResizeEvent(TResizeEvent.Type.Screen,
+ newWidth, newHeight);
+ windowResize.width = newWidth;
+ windowResize.height = newHeight;
+ synchronized (eventQueue) {
+ eventQueue.add(event);
+ }
+ }
+
+ synchronized (eventQueue) {
+ if (eventQueue.size() > 0) {
+ queue.addAll(eventQueue);
+ eventQueue.clear();
+ }
+ }
}
/**
* Parses the next character of input to see if an InputEvent is
* fully here.
*
+ * @param events list to append new events to
* @param ch Unicode code point
- * @param noChar if true, ignore ch. This is currently used to return a
- * bare ESC and RESIZE events.
- * @return list of new events (which may be empty)
*/
- public List<TInputEvent> getEvents(char ch, boolean noChar) {
- List<TInputEvent> events = new LinkedList<TInputEvent>();
+ private void processChar(List<TInputEvent> events, char ch) {
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();
- int newHeight = session.getWindowHeight();
- if ((newWidth != windowResize.width) ||
- (newHeight != windowResize.height)) {
- TResizeEvent event = new TResizeEvent(TResizeEvent.Type.Screen,
- newWidth, newHeight);
- windowResize.width = newWidth;
- windowResize.height = newHeight;
- events.add(event);
- }
-
- // Nothing else to do, bail out
- return events;
- }
// System.err.printf("state: %s ch %c\r\n", state, ch);
if (ch == 0x1B) {
state = ParseState.ESCAPE;
escapeTime = now.getTime();
- return events;
+ return;
}
if (ch <= 0x1F) {
// Control character
events.add(controlChar(ch));
reset();
- return events;
+ return;
}
if (ch >= 0x20) {
keypress.key.ch = ch;
events.add(keypress);
reset();
- return events;
+ return;
}
break;
keypress.key.alt = true;
events.add(keypress);
reset();
- return events;
+ return;
}
if (ch == 'O') {
// This will be one of the function keys
state = ParseState.ESCAPE_INTERMEDIATE;
- return events;
+ return;
}
// '[' goes to CSI_ENTRY
if (ch == '[') {
state = ParseState.CSI_ENTRY;
- return events;
+ return;
}
// Everything else is assumed to be Alt-keystroke
}
events.add(keypress);
reset();
- return events;
+ return;
case ESCAPE_INTERMEDIATE:
if ((ch >= 'P') && (ch <= 'S')) {
}
events.add(keypress);
reset();
- return events;
+ return;
}
// Unknown keystroke, ignore
reset();
- return events;
+ return;
case CSI_ENTRY:
// Numbers - parameter values
if ((ch >= '0') && (ch <= '9')) {
params.set(paramI, params.get(paramI) + ch);
state = ParseState.CSI_PARAM;
- return events;
+ return;
}
// Parameter separator
if (ch == ';') {
paramI++;
params.set(paramI, "");
- return events;
+ return;
}
if ((ch >= 0x30) && (ch <= 0x7E)) {
}
events.add(keypress);
reset();
- return events;
+ return;
case 'B':
// Down
keypress = new TKeypressEvent();
}
events.add(keypress);
reset();
- return events;
+ return;
case 'C':
// Right
keypress = new TKeypressEvent();
}
events.add(keypress);
reset();
- return events;
+ return;
case 'D':
// Left
keypress = new TKeypressEvent();
}
events.add(keypress);
reset();
- return events;
+ return;
case 'H':
// Home
keypress = new TKeypressEvent();
keypress.key.fnKey = TKeypress.HOME;
events.add(keypress);
reset();
- return events;
+ return;
case 'F':
// End
keypress = new TKeypressEvent();
keypress.key.fnKey = TKeypress.END;
events.add(keypress);
reset();
- return events;
+ return;
case 'Z':
// CBT - Cursor backward X tab stops (default 1)
keypress = new TKeypressEvent();
keypress.key.fnKey = TKeypress.BTAB;
events.add(keypress);
reset();
- return events;
+ return;
case 'M':
// Mouse position
state = ParseState.MOUSE;
- return events;
+ return;
default:
break;
}
// Unknown keystroke, ignore
reset();
- return events;
+ return;
case CSI_PARAM:
// Numbers - parameter values
if ((ch >= '0') && (ch <= '9')) {
params.set(paramI, params.get(paramI) + ch);
state = ParseState.CSI_PARAM;
- return events;
+ return;
}
// Parameter separator
if (ch == ';') {
paramI++;
params.set(paramI, "");
- return events;
+ return;
}
if (ch == '~') {
events.add(csiFnKey());
reset();
- return events;
+ return;
}
if ((ch >= 0x30) && (ch <= 0x7E)) {
}
events.add(keypress);
reset();
- return events;
+ return;
case 'B':
// Down
keypress = new TKeypressEvent();
}
events.add(keypress);
reset();
- return events;
+ return;
case 'C':
// Right
keypress = new TKeypressEvent();
}
events.add(keypress);
reset();
- return events;
+ return;
case 'D':
// Left
keypress = new TKeypressEvent();
}
events.add(keypress);
reset();
- return events;
+ return;
default:
break;
}
// Unknown keystroke, ignore
reset();
- return events;
+ return;
case MOUSE:
params.set(0, params.get(paramI) + ch);
events.add(parseMouse());
reset();
}
- return events;
+ return;
default:
break;
}
// This "should" be impossible to reach
- return events;
+ return;
}
/**
// available() will often return > 1, so we need to read in chunks to
// stay caught up.
char [] readBuffer = new char[128];
+ List<TInputEvent> events = new LinkedList<TInputEvent>();
while ((done == false) && (stopReaderThread == false)) {
try {
} 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);
+ processChar(events, (char)ch);
+ if (events.size() > 0) {
+ // Add to the queue for the backend thread to
+ // be able to obtain.
+ synchronized (eventQueue) {
+ eventQueue.addAll(events);
+ }
+ // Now wake up the backend
+ synchronized (this) {
+ this.notifyAll();
+ }
+ events.clear();
}
}
}