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