2 * Jexer - Java Text User Interface
4 * License: LGPLv3 or later
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.
10 * Copyright (C) 2015 Kevin Lamonte
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.
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.
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
28 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
33 import java
.awt
.event
.ComponentEvent
;
34 import java
.awt
.event
.ComponentListener
;
35 import java
.awt
.event
.KeyEvent
;
36 import java
.awt
.event
.KeyListener
;
37 import java
.awt
.event
.MouseEvent
;
38 import java
.awt
.event
.MouseListener
;
39 import java
.awt
.event
.MouseMotionListener
;
40 import java
.awt
.event
.MouseWheelEvent
;
41 import java
.awt
.event
.MouseWheelListener
;
42 import java
.awt
.event
.WindowEvent
;
43 import java
.awt
.event
.WindowListener
;
44 import java
.util
.List
;
45 import java
.util
.LinkedList
;
47 import jexer
.TKeypress
;
48 import jexer
.bits
.Color
;
49 import jexer
.event
.TCommandEvent
;
50 import jexer
.event
.TInputEvent
;
51 import jexer
.event
.TKeypressEvent
;
52 import jexer
.event
.TMouseEvent
;
53 import jexer
.event
.TResizeEvent
;
54 import jexer
.session
.SessionInfo
;
55 import jexer
.session
.AWTSessionInfo
;
56 import static jexer
.TCommand
.*;
57 import static jexer
.TKeypress
.*;
60 * This class reads keystrokes and mouse events from an AWT Frame.
62 public final class AWTTerminal
implements ComponentListener
, KeyListener
,
63 MouseListener
, MouseMotionListener
,
64 MouseWheelListener
, WindowListener
{
69 private AWTScreen screen
;
72 * The session information.
74 private AWTSessionInfo sessionInfo
;
77 * Getter for sessionInfo.
79 * @return the SessionInfo
81 public SessionInfo
getSessionInfo() {
86 * The event queue, filled up by a thread reading on input.
88 private List
<TInputEvent
> eventQueue
;
93 private Thread readerThread
;
96 * true if mouse1 was down. Used to report mouse1 on the release event.
98 private boolean mouse1
= false;
101 * true if mouse2 was down. Used to report mouse2 on the release event.
103 private boolean mouse2
= false;
106 * true if mouse3 was down. Used to report mouse3 on the release event.
108 private boolean mouse3
= false;
111 * Check if there are events in the queue.
113 * @return if true, getEvents() has something to return to the backend
115 public boolean hasEvents() {
116 synchronized (eventQueue
) {
117 return (eventQueue
.size() > 0);
122 * Constructor sets up state for getEvent().
124 * @param screen the top-level AWT frame
126 public AWTTerminal(final AWTScreen screen
) {
127 this.screen
= screen
;
131 sessionInfo
= screen
.getSessionInfo();
132 eventQueue
= new LinkedList
<TInputEvent
>();
134 screen
.frame
.addKeyListener(this);
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);
143 * Restore terminal to normal state.
145 public void shutdown() {
146 // System.err.println("=== shutdown() ==="); System.err.flush();
147 screen
.frame
.dispose();
151 * Return any events in the IO queue.
153 * @param queue list to append new events to
155 public void getEvents(final List
<TInputEvent
> queue
) {
156 synchronized (eventQueue
) {
157 if (eventQueue
.size() > 0) {
158 synchronized (queue
) {
159 queue
.addAll(eventQueue
);
167 * Return any events in the IO queue due to timeout.
169 * @param queue list to append new events to
171 public void getIdleEvents(final List
<TInputEvent
> queue
) {
173 // Insert any polling action here...
175 // Return any events that showed up
176 synchronized (eventQueue
) {
177 if (eventQueue
.size() > 0) {
178 synchronized (queue
) {
179 queue
.addAll(eventQueue
);
187 * Pass AWT keystrokes into the event queue.
189 * @param key keystroke received
192 public void keyReleased(final KeyEvent key
) {
193 // Ignore release events
197 * Pass AWT keystrokes into the event queue.
199 * @param key keystroke received
202 public void keyTyped(final KeyEvent key
) {
203 // Ignore typed events
207 * Pass AWT keystrokes into the event queue.
209 * @param key keystroke received
212 public void keyPressed(final KeyEvent key
) {
214 boolean shift
= false;
215 boolean ctrl
= false;
217 boolean isKey
= false;
219 if (key
.isActionKey()) {
222 ch
= key
.getKeyChar();
224 alt
= key
.isAltDown();
225 ctrl
= key
.isControlDown();
226 shift
= key
.isShiftDown();
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);
237 // Special case: not return the bare modifier presses
238 switch (key
.getKeyCode()) {
239 case KeyEvent
.VK_ALT
:
241 case KeyEvent
.VK_ALT_GRAPH
:
243 case KeyEvent
.VK_CONTROL
:
245 case KeyEvent
.VK_SHIFT
:
247 case KeyEvent
.VK_META
:
253 TKeypress keypress
= null;
255 switch (key
.getKeyCode()) {
257 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
261 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
265 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
269 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
273 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
277 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
281 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
285 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
289 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
292 case KeyEvent
.VK_F10
:
293 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
296 case KeyEvent
.VK_F11
:
297 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
300 case KeyEvent
.VK_F12
:
301 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
304 case KeyEvent
.VK_HOME
:
305 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
308 case KeyEvent
.VK_END
:
309 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
312 case KeyEvent
.VK_PAGE_UP
:
313 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
316 case KeyEvent
.VK_PAGE_DOWN
:
317 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
320 case KeyEvent
.VK_INSERT
:
321 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
324 case KeyEvent
.VK_DELETE
:
325 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
328 case KeyEvent
.VK_RIGHT
:
329 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
332 case KeyEvent
.VK_LEFT
:
333 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
337 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
340 case KeyEvent
.VK_DOWN
:
341 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
344 case KeyEvent
.VK_TAB
:
345 // Special case: distinguish TAB vs BTAB
347 keypress
= kbShiftTab
;
352 case KeyEvent
.VK_ENTER
:
353 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
356 case KeyEvent
.VK_ESCAPE
:
357 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
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);
365 // Unsupported, ignore
370 if (keypress
== null) {
373 keypress
= kbBackspace
;
385 if (!alt
&& ctrl
&& !shift
) {
386 ch
= key
.getKeyText(key
.getKeyCode()).charAt(0);
388 // Not a special key, put it together
389 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
393 // Save it and we are done.
394 synchronized (eventQueue
) {
395 eventQueue
.add(new TKeypressEvent(keypress
));
397 // Wake up the backend
398 synchronized (this) {
404 * Pass window events into the event queue.
406 * @param event window event received
409 public void windowActivated(final WindowEvent event
) {
414 * Pass window events into the event queue.
416 * @param event window event received
419 public void windowClosed(final WindowEvent event
) {
424 * Pass window events into the event queue.
426 * @param event window event received
429 public void windowClosing(final WindowEvent event
) {
430 // Drop a cmAbort and walk away
431 synchronized (eventQueue
) {
432 eventQueue
.add(new TCommandEvent(cmAbort
));
434 // Wake up the backend
435 synchronized (this) {
441 * Pass window events into the event queue.
443 * @param event window event received
446 public void windowDeactivated(final WindowEvent event
) {
451 * Pass window events into the event queue.
453 * @param event window event received
456 public void windowDeiconified(final WindowEvent event
) {
461 * Pass window events into the event queue.
463 * @param event window event received
466 public void windowIconified(final WindowEvent event
) {
471 * Pass window events into the event queue.
473 * @param event window event received
476 public void windowOpened(final WindowEvent event
) {
481 * Pass component events into the event queue.
483 * @param event component event received
486 public void componentHidden(final ComponentEvent event
) {
491 * Pass component events into the event queue.
493 * @param event component event received
496 public void componentShown(final ComponentEvent event
) {
501 * Pass component events into the event queue.
503 * @param event component event received
506 public void componentMoved(final ComponentEvent event
) {
511 * Pass component events into the event queue.
513 * @param event component event received
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
);
524 // Wake up the backend
525 synchronized (this) {
531 * Pass mouse events into the event queue.
533 * @param mouse mouse event received
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) {
544 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
547 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
550 mouse1
= eventMouse1
;
551 mouse2
= eventMouse2
;
552 mouse3
= eventMouse3
;
553 int x
= sessionInfo
.textColumn(mouse
.getX());
554 int y
= sessionInfo
.textRow(mouse
.getY());
556 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
557 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
559 synchronized (eventQueue
) {
560 eventQueue
.add(mouseEvent
);
562 // Wake up the backend
563 synchronized (this) {
569 * Pass mouse events into the event queue.
571 * @param mouse mouse event received
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);
580 synchronized (eventQueue
) {
581 eventQueue
.add(mouseEvent
);
583 // Wake up the backend
584 synchronized (this) {
590 * Pass mouse events into the event queue.
592 * @param mouse mouse event received
595 public void mouseClicked(final MouseEvent mouse
) {
600 * Pass mouse events into the event queue.
602 * @param mouse mouse event received
605 public void mouseEntered(final MouseEvent mouse
) {
610 * Pass mouse events into the event queue.
612 * @param mouse mouse event received
615 public void mouseExited(final MouseEvent mouse
) {
620 * Pass mouse events into the event queue.
622 * @param mouse mouse event received
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) {
633 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
636 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
639 mouse1
= eventMouse1
;
640 mouse2
= eventMouse2
;
641 mouse3
= eventMouse3
;
642 int x
= sessionInfo
.textColumn(mouse
.getX());
643 int y
= sessionInfo
.textRow(mouse
.getY());
645 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
646 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
648 synchronized (eventQueue
) {
649 eventQueue
.add(mouseEvent
);
651 // Wake up the backend
652 synchronized (this) {
658 * Pass mouse events into the event queue.
660 * @param mouse mouse event received
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) {
671 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
674 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
689 int x
= sessionInfo
.textColumn(mouse
.getX());
690 int y
= sessionInfo
.textRow(mouse
.getY());
692 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
693 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false);
695 synchronized (eventQueue
) {
696 eventQueue
.add(mouseEvent
);
698 // Wake up the backend
699 synchronized (this) {
705 * Pass mouse events into the event queue.
707 * @param mouse mouse event received
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) {
720 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
723 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
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;
734 if (mouse
.getWheelRotation() < 0) {
738 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
739 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
);
741 synchronized (eventQueue
) {
742 eventQueue
.add(mouseEvent
);
744 // Wake up the backend
745 synchronized (this) {