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