misc cleanup
[nikiroo-utils.git] / src / jexer / io / AWTTerminal.java
CommitLineData
1ac2ccb1
KL
1/**
2 * Jexer - Java Text User Interface
3 *
4 * License: LGPLv3 or later
5 *
6 * This module is licensed under the GNU Lesser General Public License
7 * Version 3. Please see the file "COPYING" in this directory for more
8 * information about the GNU Lesser General Public License Version 3.
9 *
10 * Copyright (C) 2015 Kevin Lamonte
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 3 of
15 * the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this program; if not, see
24 * http://www.gnu.org/licenses/, or write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * 02110-1301 USA
27 *
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
29 * @version 1
30 */
31package jexer.io;
32
30bd4abd
KL
33import java.awt.event.ComponentEvent;
34import java.awt.event.ComponentListener;
84614868 35import java.awt.event.KeyEvent;
30bd4abd
KL
36import java.awt.event.KeyListener;
37import java.awt.event.MouseEvent;
84614868 38import java.awt.event.MouseListener;
30bd4abd
KL
39import java.awt.event.MouseMotionListener;
40import java.awt.event.MouseWheelEvent;
41import java.awt.event.MouseWheelListener;
42import java.awt.event.WindowEvent;
84614868 43import java.awt.event.WindowListener;
1ac2ccb1
KL
44import java.util.List;
45import java.util.LinkedList;
46
47import jexer.TKeypress;
30bd4abd 48import jexer.event.TCommandEvent;
1ac2ccb1
KL
49import jexer.event.TInputEvent;
50import jexer.event.TKeypressEvent;
51import jexer.event.TMouseEvent;
52import jexer.event.TResizeEvent;
53import jexer.session.SessionInfo;
30bd4abd
KL
54import jexer.session.AWTSessionInfo;
55import static jexer.TCommand.*;
1ac2ccb1
KL
56import static jexer.TKeypress.*;
57
58/**
59 * This class reads keystrokes and mouse events from an AWT Frame.
60 */
30bd4abd
KL
61public final class AWTTerminal implements ComponentListener, KeyListener,
62 MouseListener, MouseMotionListener,
63 MouseWheelListener, WindowListener {
84614868
KL
64
65 /**
66 * The backend Screen.
67 */
68 private AWTScreen screen;
1ac2ccb1
KL
69
70 /**
71 * The session information.
72 */
30bd4abd 73 private AWTSessionInfo sessionInfo;
1ac2ccb1
KL
74
75 /**
76 * Getter for sessionInfo.
77 *
78 * @return the SessionInfo
79 */
80 public SessionInfo getSessionInfo() {
81 return sessionInfo;
82 }
83
84 /**
85 * The event queue, filled up by a thread reading on input.
86 */
87 private List<TInputEvent> eventQueue;
88
1ac2ccb1
KL
89 /**
90 * The reader thread.
91 */
92 private Thread readerThread;
93
94 /**
95 * true if mouse1 was down. Used to report mouse1 on the release event.
96 */
30bd4abd 97 private boolean mouse1 = false;
1ac2ccb1
KL
98
99 /**
100 * true if mouse2 was down. Used to report mouse2 on the release event.
101 */
30bd4abd 102 private boolean mouse2 = false;
1ac2ccb1
KL
103
104 /**
105 * true if mouse3 was down. Used to report mouse3 on the release event.
106 */
30bd4abd 107 private boolean mouse3 = false;
1ac2ccb1
KL
108
109 /**
110 * Check if there are events in the queue.
111 *
112 * @return if true, getEvents() has something to return to the backend
113 */
114 public boolean hasEvents() {
115 synchronized (eventQueue) {
116 return (eventQueue.size() > 0);
117 }
118 }
119
120 /**
121 * Constructor sets up state for getEvent().
122 *
84614868 123 * @param screen the top-level AWT frame
1ac2ccb1
KL
124 */
125 public AWTTerminal(final AWTScreen screen) {
84614868 126 this.screen = screen;
1ac2ccb1
KL
127 mouse1 = false;
128 mouse2 = false;
129 mouse3 = false;
30bd4abd 130 sessionInfo = screen.getSessionInfo();
1ac2ccb1 131 eventQueue = new LinkedList<TInputEvent>();
84614868
KL
132
133 screen.frame.addKeyListener(this);
30bd4abd
KL
134 screen.frame.addWindowListener(this);
135 screen.frame.addComponentListener(this);
136 screen.frame.addMouseListener(this);
137 screen.frame.addMouseMotionListener(this);
138 screen.frame.addMouseWheelListener(this);
1ac2ccb1
KL
139 }
140
141 /**
142 * Restore terminal to normal state.
143 */
144 public void shutdown() {
145 // System.err.println("=== shutdown() ==="); System.err.flush();
84614868 146 screen.frame.dispose();
1ac2ccb1
KL
147 }
148
149 /**
150 * Return any events in the IO queue.
151 *
152 * @param queue list to append new events to
153 */
154 public void getEvents(final List<TInputEvent> queue) {
155 synchronized (eventQueue) {
156 if (eventQueue.size() > 0) {
157 synchronized (queue) {
158 queue.addAll(eventQueue);
159 }
160 eventQueue.clear();
161 }
162 }
163 }
164
165 /**
166 * Return any events in the IO queue due to timeout.
167 *
168 * @param queue list to append new events to
169 */
170 public void getIdleEvents(final List<TInputEvent> queue) {
171
172 // Insert any polling action here...
173
174 // Return any events that showed up
175 synchronized (eventQueue) {
176 if (eventQueue.size() > 0) {
177 synchronized (queue) {
178 queue.addAll(eventQueue);
179 }
180 eventQueue.clear();
181 }
182 }
183 }
184
84614868
KL
185 /**
186 * Pass AWT keystrokes into the event queue.
187 *
188 * @param key keystroke received
189 */
190 @Override
191 public void keyReleased(final KeyEvent key) {
192 // Ignore release events
193 }
194
195 /**
196 * Pass AWT keystrokes into the event queue.
197 *
198 * @param key keystroke received
199 */
200 @Override
201 public void keyTyped(final KeyEvent key) {
202 // Ignore typed events
203 }
204
205 /**
206 * Pass AWT keystrokes into the event queue.
207 *
208 * @param key keystroke received
209 */
210 @Override
211 public void keyPressed(final KeyEvent key) {
212 boolean alt = false;
213 boolean shift = false;
214 boolean ctrl = false;
215 char ch = ' ';
216 boolean isKey = false;
217 int fnKey = 0;
218 if (key.isActionKey()) {
219 isKey = true;
220 } else {
221 ch = key.getKeyChar();
222 }
223 alt = key.isAltDown();
224 ctrl = key.isControlDown();
225 shift = key.isShiftDown();
226
227 /*
228 System.err.printf("AWT Key: %s\n", key);
229 System.err.printf(" isKey: %s\n", isKey);
230 System.err.printf(" alt: %s\n", alt);
231 System.err.printf(" ctrl: %s\n", ctrl);
232 System.err.printf(" shift: %s\n", shift);
233 System.err.printf(" ch: %s\n", ch);
234 */
235
236 // Special case: not return the bare modifier presses
237 switch (key.getKeyCode()) {
238 case KeyEvent.VK_ALT:
239 return;
240 case KeyEvent.VK_ALT_GRAPH:
241 return;
242 case KeyEvent.VK_CONTROL:
243 return;
244 case KeyEvent.VK_SHIFT:
245 return;
246 case KeyEvent.VK_META:
247 return;
248 default:
249 break;
250 }
251
252 TKeypress keypress = null;
253 if (isKey) {
254 switch (key.getKeyCode()) {
255 case KeyEvent.VK_F1:
256 keypress = new TKeypress(true, TKeypress.F1, ' ',
257 alt, ctrl, shift);
258 break;
259 case KeyEvent.VK_F2:
260 keypress = new TKeypress(true, TKeypress.F2, ' ',
261 alt, ctrl, shift);
262 break;
263 case KeyEvent.VK_F3:
264 keypress = new TKeypress(true, TKeypress.F3, ' ',
265 alt, ctrl, shift);
266 break;
267 case KeyEvent.VK_F4:
268 keypress = new TKeypress(true, TKeypress.F4, ' ',
269 alt, ctrl, shift);
270 break;
271 case KeyEvent.VK_F5:
272 keypress = new TKeypress(true, TKeypress.F5, ' ',
273 alt, ctrl, shift);
274 break;
275 case KeyEvent.VK_F6:
276 keypress = new TKeypress(true, TKeypress.F6, ' ',
277 alt, ctrl, shift);
278 break;
279 case KeyEvent.VK_F7:
280 keypress = new TKeypress(true, TKeypress.F7, ' ',
281 alt, ctrl, shift);
282 break;
283 case KeyEvent.VK_F8:
284 keypress = new TKeypress(true, TKeypress.F8, ' ',
285 alt, ctrl, shift);
286 break;
287 case KeyEvent.VK_F9:
288 keypress = new TKeypress(true, TKeypress.F9, ' ',
289 alt, ctrl, shift);
290 break;
291 case KeyEvent.VK_F10:
292 keypress = new TKeypress(true, TKeypress.F10, ' ',
293 alt, ctrl, shift);
294 break;
295 case KeyEvent.VK_F11:
296 keypress = new TKeypress(true, TKeypress.F11, ' ',
297 alt, ctrl, shift);
298 break;
299 case KeyEvent.VK_F12:
300 keypress = new TKeypress(true, TKeypress.F12, ' ',
301 alt, ctrl, shift);
302 break;
303 case KeyEvent.VK_HOME:
304 keypress = new TKeypress(true, TKeypress.HOME, ' ',
305 alt, ctrl, shift);
306 break;
307 case KeyEvent.VK_END:
308 keypress = new TKeypress(true, TKeypress.END, ' ',
309 alt, ctrl, shift);
310 break;
311 case KeyEvent.VK_PAGE_UP:
312 keypress = new TKeypress(true, TKeypress.PGUP, ' ',
313 alt, ctrl, shift);
314 break;
315 case KeyEvent.VK_PAGE_DOWN:
316 keypress = new TKeypress(true, TKeypress.PGDN, ' ',
317 alt, ctrl, shift);
318 break;
319 case KeyEvent.VK_INSERT:
320 keypress = new TKeypress(true, TKeypress.INS, ' ',
321 alt, ctrl, shift);
322 break;
323 case KeyEvent.VK_DELETE:
30bd4abd 324 keypress = new TKeypress(true, TKeypress.DEL, ' ',
84614868
KL
325 alt, ctrl, shift);
326 break;
327 case KeyEvent.VK_RIGHT:
328 keypress = new TKeypress(true, TKeypress.RIGHT, ' ',
329 alt, ctrl, shift);
330 break;
331 case KeyEvent.VK_LEFT:
332 keypress = new TKeypress(true, TKeypress.LEFT, ' ',
333 alt, ctrl, shift);
334 break;
335 case KeyEvent.VK_UP:
336 keypress = new TKeypress(true, TKeypress.UP, ' ',
337 alt, ctrl, shift);
338 break;
339 case KeyEvent.VK_DOWN:
340 keypress = new TKeypress(true, TKeypress.DOWN, ' ',
341 alt, ctrl, shift);
342 break;
343 case KeyEvent.VK_TAB:
344 // Special case: distinguish TAB vs BTAB
345 if (shift) {
346 keypress = kbShiftTab;
347 } else {
348 keypress = kbTab;
349 }
350 break;
351 case KeyEvent.VK_ENTER:
352 keypress = new TKeypress(true, TKeypress.ENTER, ' ',
353 alt, ctrl, shift);
354 break;
355 case KeyEvent.VK_ESCAPE:
356 keypress = new TKeypress(true, TKeypress.ESC, ' ',
357 alt, ctrl, shift);
358 break;
359 case KeyEvent.VK_BACK_SPACE:
360 // Special case: return it as kbBackspace (Ctrl-H)
361 keypress = new TKeypress(false, 0, 'H', false, true, false);
362 break;
363 default:
364 // Unsupported, ignore
365 return;
366 }
367 }
368
369 if (keypress == null) {
370 switch (ch) {
371 case 0x08:
372 keypress = kbBackspace;
373 break;
374 case 0x0A:
375 keypress = kbEnter;
376 break;
87a17f3c
KL
377 case 0x1B:
378 keypress = kbEsc;
379 break;
84614868
KL
380 case 0x0D:
381 keypress = kbEnter;
382 break;
30bd4abd
KL
383 case 0x7F:
384 keypress = kbDel;
385 break;
84614868
KL
386 default:
387 if (!alt && ctrl && !shift) {
388 ch = key.getKeyText(key.getKeyCode()).charAt(0);
389 }
390 // Not a special key, put it together
391 keypress = new TKeypress(false, 0, ch, alt, ctrl, shift);
392 }
393 }
394
395 // Save it and we are done.
396 synchronized (eventQueue) {
397 eventQueue.add(new TKeypressEvent(keypress));
398 }
30bd4abd
KL
399 // Wake up the backend
400 synchronized (this) {
401 this.notifyAll();
402 }
403 }
404
405 /**
406 * Pass window events into the event queue.
407 *
408 * @param event window event received
409 */
410 @Override
411 public void windowActivated(final WindowEvent event) {
412 // Ignore
413 }
414
415 /**
416 * Pass window events into the event queue.
417 *
418 * @param event window event received
419 */
420 @Override
421 public void windowClosed(final WindowEvent event) {
422 // Ignore
423 }
424
425 /**
426 * Pass window events into the event queue.
427 *
428 * @param event window event received
429 */
430 @Override
431 public void windowClosing(final WindowEvent event) {
432 // Drop a cmAbort and walk away
433 synchronized (eventQueue) {
434 eventQueue.add(new TCommandEvent(cmAbort));
435 }
436 // Wake up the backend
437 synchronized (this) {
438 this.notifyAll();
439 }
440 }
441
442 /**
443 * Pass window events into the event queue.
444 *
445 * @param event window event received
446 */
447 @Override
448 public void windowDeactivated(final WindowEvent event) {
449 // Ignore
450 }
451
452 /**
453 * Pass window events into the event queue.
454 *
455 * @param event window event received
456 */
457 @Override
458 public void windowDeiconified(final WindowEvent event) {
459 // Ignore
460 }
461
462 /**
463 * Pass window events into the event queue.
464 *
465 * @param event window event received
466 */
467 @Override
468 public void windowIconified(final WindowEvent event) {
469 // Ignore
470 }
471
472 /**
473 * Pass window events into the event queue.
474 *
475 * @param event window event received
476 */
477 @Override
478 public void windowOpened(final WindowEvent event) {
479 // Ignore
480 }
481
482 /**
483 * Pass component events into the event queue.
484 *
485 * @param event component event received
486 */
487 @Override
488 public void componentHidden(final ComponentEvent event) {
489 // Ignore
490 }
491
492 /**
493 * Pass component events into the event queue.
494 *
495 * @param event component event received
496 */
497 @Override
498 public void componentShown(final ComponentEvent event) {
499 // Ignore
500 }
501
502 /**
503 * Pass component events into the event queue.
504 *
505 * @param event component event received
506 */
507 @Override
508 public void componentMoved(final ComponentEvent event) {
509 // Ignore
510 }
511
512 /**
513 * Pass component events into the event queue.
514 *
515 * @param event component event received
516 */
517 @Override
518 public void componentResized(final ComponentEvent event) {
519 // Drop a new TResizeEvent into the queue
520 sessionInfo.queryWindowSize();
521 synchronized (eventQueue) {
522 TResizeEvent windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
523 sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
524 eventQueue.add(windowResize);
525 }
526 // Wake up the backend
527 synchronized (this) {
528 this.notifyAll();
529 }
530 }
531
532 /**
533 * Pass mouse events into the event queue.
534 *
535 * @param mouse mouse event received
536 */
537 @Override
538 public void mouseDragged(final MouseEvent mouse) {
539 int modifiers = mouse.getModifiersEx();
540 boolean eventMouse1 = false;
541 boolean eventMouse2 = false;
542 boolean eventMouse3 = false;
543 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
544 eventMouse1 = true;
545 }
546 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
547 eventMouse2 = true;
548 }
549 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
550 eventMouse3 = true;
551 }
552 mouse1 = eventMouse1;
553 mouse2 = eventMouse2;
554 mouse3 = eventMouse3;
87a17f3c
KL
555 int x = screen.textColumn(mouse.getX());
556 int y = screen.textRow(mouse.getY());
30bd4abd
KL
557
558 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION,
559 x, y, x, y, mouse1, mouse2, mouse3, false, false);
560
561 synchronized (eventQueue) {
562 eventQueue.add(mouseEvent);
563 }
564 // Wake up the backend
565 synchronized (this) {
566 this.notifyAll();
567 }
568 }
569
570 /**
571 * Pass mouse events into the event queue.
572 *
573 * @param mouse mouse event received
574 */
575 @Override
576 public void mouseMoved(final MouseEvent mouse) {
87a17f3c
KL
577 int x = screen.textColumn(mouse.getX());
578 int y = screen.textRow(mouse.getY());
30bd4abd
KL
579 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_MOTION,
580 x, y, x, y, mouse1, mouse2, mouse3, false, false);
581
582 synchronized (eventQueue) {
583 eventQueue.add(mouseEvent);
584 }
585 // Wake up the backend
586 synchronized (this) {
587 this.notifyAll();
588 }
84614868 589 }
30bd4abd
KL
590
591 /**
592 * Pass mouse events into the event queue.
593 *
594 * @param mouse mouse event received
595 */
596 @Override
597 public void mouseClicked(final MouseEvent mouse) {
598 // Ignore
599 }
600
601 /**
602 * Pass mouse events into the event queue.
603 *
604 * @param mouse mouse event received
605 */
606 @Override
607 public void mouseEntered(final MouseEvent mouse) {
608 // Ignore
609 }
610
611 /**
612 * Pass mouse events into the event queue.
613 *
614 * @param mouse mouse event received
615 */
616 @Override
617 public void mouseExited(final MouseEvent mouse) {
618 // Ignore
619 }
620
621 /**
622 * Pass mouse events into the event queue.
623 *
624 * @param mouse mouse event received
625 */
626 @Override
627 public void mousePressed(final MouseEvent mouse) {
628 int modifiers = mouse.getModifiersEx();
629 boolean eventMouse1 = false;
630 boolean eventMouse2 = false;
631 boolean eventMouse3 = false;
632 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
633 eventMouse1 = true;
634 }
635 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
636 eventMouse2 = true;
637 }
638 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
639 eventMouse3 = true;
640 }
641 mouse1 = eventMouse1;
642 mouse2 = eventMouse2;
643 mouse3 = eventMouse3;
87a17f3c
KL
644 int x = screen.textColumn(mouse.getX());
645 int y = screen.textRow(mouse.getY());
30bd4abd
KL
646
647 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN,
648 x, y, x, y, mouse1, mouse2, mouse3, false, false);
649
650 synchronized (eventQueue) {
651 eventQueue.add(mouseEvent);
652 }
653 // Wake up the backend
654 synchronized (this) {
655 this.notifyAll();
656 }
657 }
658
659 /**
660 * Pass mouse events into the event queue.
661 *
662 * @param mouse mouse event received
663 */
664 @Override
665 public void mouseReleased(final MouseEvent mouse) {
666 int modifiers = mouse.getModifiersEx();
667 boolean eventMouse1 = false;
668 boolean eventMouse2 = false;
669 boolean eventMouse3 = false;
670 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
671 eventMouse1 = true;
672 }
673 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
674 eventMouse2 = true;
675 }
676 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
677 eventMouse3 = true;
678 }
679 if (mouse1) {
680 mouse1 = false;
681 eventMouse1 = true;
682 }
683 if (mouse2) {
684 mouse2 = false;
685 eventMouse2 = true;
686 }
687 if (mouse3) {
688 mouse3 = false;
689 eventMouse3 = true;
690 }
87a17f3c
KL
691 int x = screen.textColumn(mouse.getX());
692 int y = screen.textRow(mouse.getY());
30bd4abd
KL
693
694 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_UP,
695 x, y, x, y, eventMouse1, eventMouse2, eventMouse3, false, false);
696
697 synchronized (eventQueue) {
698 eventQueue.add(mouseEvent);
699 }
700 // Wake up the backend
701 synchronized (this) {
702 this.notifyAll();
703 }
704 }
705
706 /**
707 * Pass mouse events into the event queue.
708 *
709 * @param mouse mouse event received
710 */
711 @Override
712 public void mouseWheelMoved(final MouseWheelEvent mouse) {
713 int modifiers = mouse.getModifiersEx();
714 boolean eventMouse1 = false;
715 boolean eventMouse2 = false;
716 boolean eventMouse3 = false;
717 boolean mouseWheelUp = false;
718 boolean mouseWheelDown = false;
719 if ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) {
720 eventMouse1 = true;
721 }
722 if ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) {
723 eventMouse2 = true;
724 }
725 if ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) {
726 eventMouse3 = true;
727 }
728 mouse1 = eventMouse1;
729 mouse2 = eventMouse2;
730 mouse3 = eventMouse3;
87a17f3c
KL
731 int x = screen.textColumn(mouse.getX());
732 int y = screen.textRow(mouse.getY());
30bd4abd
KL
733 if (mouse.getWheelRotation() > 0) {
734 mouseWheelDown = true;
735 }
736 if (mouse.getWheelRotation() < 0) {
737 mouseWheelUp = true;
738 }
739
740 TMouseEvent mouseEvent = new TMouseEvent(TMouseEvent.Type.MOUSE_DOWN,
741 x, y, x, y, mouse1, mouse2, mouse3, mouseWheelUp, mouseWheelDown);
742
743 synchronized (eventQueue) {
744 eventQueue.add(mouseEvent);
745 }
746 // Wake up the backend
747 synchronized (this) {
748 this.notifyAll();
749 }
750 }
751
1ac2ccb1 752}