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
.event
.TCommandEvent
;
49 import jexer
.event
.TInputEvent
;
50 import jexer
.event
.TKeypressEvent
;
51 import jexer
.event
.TMouseEvent
;
52 import jexer
.event
.TResizeEvent
;
53 import jexer
.session
.SessionInfo
;
54 import jexer
.session
.AWTSessionInfo
;
55 import static jexer
.TCommand
.*;
56 import static jexer
.TKeypress
.*;
59 * This class reads keystrokes and mouse events from an AWT Frame.
61 public final class AWTTerminal
implements ComponentListener
, KeyListener
,
62 MouseListener
, MouseMotionListener
,
63 MouseWheelListener
, WindowListener
{
68 private AWTScreen screen
;
71 * The session information.
73 private AWTSessionInfo sessionInfo
;
76 * Getter for sessionInfo.
78 * @return the SessionInfo
80 public SessionInfo
getSessionInfo() {
85 * The listening object that run() wakes up on new input.
87 private Object listener
;
90 * The event queue, filled up by a thread reading on input.
92 private List
<TInputEvent
> eventQueue
;
97 private Thread readerThread
;
100 * The last reported mouse X position.
102 private int oldMouseX
= -1;
105 * The last reported mouse Y position.
107 private int oldMouseY
= -1;
110 * true if mouse1 was down. Used to report mouse1 on the release event.
112 private boolean mouse1
= false;
115 * true if mouse2 was down. Used to report mouse2 on the release event.
117 private boolean mouse2
= false;
120 * true if mouse3 was down. Used to report mouse3 on the release event.
122 private boolean mouse3
= false;
125 * Check if there are events in the queue.
127 * @return if true, getEvents() has something to return to the backend
129 public boolean hasEvents() {
130 synchronized (eventQueue
) {
131 return (eventQueue
.size() > 0);
136 * Constructor sets up state for getEvent().
138 * @param listener the object this backend needs to wake up when new
140 * @param screen the top-level AWT frame
142 public AWTTerminal(final Object listener
, final AWTScreen screen
) {
143 this.listener
= listener
;
144 this.screen
= screen
;
148 sessionInfo
= screen
.getSessionInfo();
149 eventQueue
= new LinkedList
<TInputEvent
>();
151 screen
.frame
.addKeyListener(this);
152 screen
.frame
.addWindowListener(this);
153 screen
.frame
.addComponentListener(this);
154 screen
.frame
.addMouseListener(this);
155 screen
.frame
.addMouseMotionListener(this);
156 screen
.frame
.addMouseWheelListener(this);
160 * Restore terminal to normal state.
162 public void shutdown() {
163 // System.err.println("=== shutdown() ==="); System.err.flush();
164 screen
.frame
.dispose();
168 * Return any events in the IO queue.
170 * @param queue list to append new events to
172 public void getEvents(final List
<TInputEvent
> queue
) {
173 synchronized (eventQueue
) {
174 if (eventQueue
.size() > 0) {
175 synchronized (queue
) {
176 queue
.addAll(eventQueue
);
184 * Pass AWT keystrokes into the event queue.
186 * @param key keystroke received
189 public void keyReleased(final KeyEvent key
) {
190 // Ignore release events
194 * Pass AWT keystrokes into the event queue.
196 * @param key keystroke received
199 public void keyTyped(final KeyEvent key
) {
200 // Ignore typed events
204 * Pass AWT keystrokes into the event queue.
206 * @param key keystroke received
209 public void keyPressed(final KeyEvent key
) {
211 boolean shift
= false;
212 boolean ctrl
= false;
214 boolean isKey
= false;
216 if (key
.isActionKey()) {
219 ch
= key
.getKeyChar();
221 alt
= key
.isAltDown();
222 ctrl
= key
.isControlDown();
223 shift
= key
.isShiftDown();
226 System.err.printf("AWT Key: %s\n", key);
227 System.err.printf(" isKey: %s\n", isKey);
228 System.err.printf(" alt: %s\n", alt);
229 System.err.printf(" ctrl: %s\n", ctrl);
230 System.err.printf(" shift: %s\n", shift);
231 System.err.printf(" ch: %s\n", ch);
234 // Special case: not return the bare modifier presses
235 switch (key
.getKeyCode()) {
236 case KeyEvent
.VK_ALT
:
238 case KeyEvent
.VK_ALT_GRAPH
:
240 case KeyEvent
.VK_CONTROL
:
242 case KeyEvent
.VK_SHIFT
:
244 case KeyEvent
.VK_META
:
250 TKeypress keypress
= null;
252 switch (key
.getKeyCode()) {
254 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
258 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
262 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
266 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
270 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
274 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
278 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
282 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
286 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
289 case KeyEvent
.VK_F10
:
290 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
293 case KeyEvent
.VK_F11
:
294 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
297 case KeyEvent
.VK_F12
:
298 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
301 case KeyEvent
.VK_HOME
:
302 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
305 case KeyEvent
.VK_END
:
306 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
309 case KeyEvent
.VK_PAGE_UP
:
310 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
313 case KeyEvent
.VK_PAGE_DOWN
:
314 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
317 case KeyEvent
.VK_INSERT
:
318 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
321 case KeyEvent
.VK_DELETE
:
322 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
325 case KeyEvent
.VK_RIGHT
:
326 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
329 case KeyEvent
.VK_LEFT
:
330 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
334 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
337 case KeyEvent
.VK_DOWN
:
338 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
341 case KeyEvent
.VK_TAB
:
342 // Special case: distinguish TAB vs BTAB
344 keypress
= kbShiftTab
;
349 case KeyEvent
.VK_ENTER
:
350 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
353 case KeyEvent
.VK_ESCAPE
:
354 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
357 case KeyEvent
.VK_BACK_SPACE
:
358 // Special case: return it as kbBackspace (Ctrl-H)
359 keypress
= new TKeypress(false, 0, 'H', false, true, false);
362 // Unsupported, ignore
367 if (keypress
== null) {
370 keypress
= kbBackspace
;
388 if (!alt
&& ctrl
&& !shift
) {
389 ch
= key
.getKeyText(key
.getKeyCode()).charAt(0);
391 // Not a special key, put it together
392 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
396 // Save it and we are done.
397 synchronized (eventQueue
) {
398 eventQueue
.add(new TKeypressEvent(keypress
));
400 synchronized (listener
) {
401 listener
.notifyAll();
406 * Pass window events into the event queue.
408 * @param event window event received
411 public void windowActivated(final WindowEvent event
) {
412 // Force a total repaint
413 synchronized (screen
) {
414 screen
.clearPhysical();
419 * Pass window events into the event queue.
421 * @param event window event received
424 public void windowClosed(final WindowEvent event
) {
429 * Pass window events into the event queue.
431 * @param event window event received
434 public void windowClosing(final WindowEvent event
) {
435 // Drop a cmAbort and walk away
436 synchronized (eventQueue
) {
437 eventQueue
.add(new TCommandEvent(cmAbort
));
439 synchronized (listener
) {
440 listener
.notifyAll();
445 * Pass window events into the event queue.
447 * @param event window event received
450 public void windowDeactivated(final WindowEvent event
) {
455 * Pass window events into the event queue.
457 * @param event window event received
460 public void windowDeiconified(final WindowEvent event
) {
465 * Pass window events into the event queue.
467 * @param event window event received
470 public void windowIconified(final WindowEvent event
) {
475 * Pass window events into the event queue.
477 * @param event window event received
480 public void windowOpened(final WindowEvent event
) {
485 * Pass component events into the event queue.
487 * @param event component event received
490 public void componentHidden(final ComponentEvent event
) {
495 * Pass component events into the event queue.
497 * @param event component event received
500 public void componentShown(final ComponentEvent event
) {
505 * Pass component events into the event queue.
507 * @param event component event received
510 public void componentMoved(final ComponentEvent event
) {
515 * Pass component events into the event queue.
517 * @param event component event received
520 public void componentResized(final ComponentEvent event
) {
521 // Drop a new TResizeEvent into the queue
522 sessionInfo
.queryWindowSize();
523 synchronized (eventQueue
) {
524 TResizeEvent windowResize
= new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
525 sessionInfo
.getWindowWidth(), sessionInfo
.getWindowHeight());
526 eventQueue
.add(windowResize
);
528 synchronized (listener
) {
529 listener
.notifyAll();
534 * Pass mouse events into the event queue.
536 * @param mouse mouse event received
539 public void mouseDragged(final MouseEvent mouse
) {
540 int modifiers
= mouse
.getModifiersEx();
541 boolean eventMouse1
= false;
542 boolean eventMouse2
= false;
543 boolean eventMouse3
= false;
544 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
547 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
550 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
553 mouse1
= eventMouse1
;
554 mouse2
= eventMouse2
;
555 mouse3
= eventMouse3
;
556 int x
= screen
.textColumn(mouse
.getX());
557 int y
= screen
.textRow(mouse
.getY());
559 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
560 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
562 synchronized (eventQueue
) {
563 eventQueue
.add(mouseEvent
);
565 synchronized (listener
) {
566 listener
.notifyAll();
571 * Pass mouse events into the event queue.
573 * @param mouse mouse event received
576 public void mouseMoved(final MouseEvent mouse
) {
577 int x
= screen
.textColumn(mouse
.getX());
578 int y
= screen
.textRow(mouse
.getY());
579 if ((x
== oldMouseX
) && (y
== oldMouseY
)) {
580 // Bail out, we've moved some pixels but not a whole text cell.
586 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
587 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
589 synchronized (eventQueue
) {
590 eventQueue
.add(mouseEvent
);
592 synchronized (listener
) {
593 listener
.notifyAll();
598 * Pass mouse events into the event queue.
600 * @param mouse mouse event received
603 public void mouseClicked(final MouseEvent mouse
) {
608 * Pass mouse events into the event queue.
610 * @param mouse mouse event received
613 public void mouseEntered(final MouseEvent mouse
) {
618 * Pass mouse events into the event queue.
620 * @param mouse mouse event received
623 public void mouseExited(final MouseEvent mouse
) {
628 * Pass mouse events into the event queue.
630 * @param mouse mouse event received
633 public void mousePressed(final MouseEvent mouse
) {
634 int modifiers
= mouse
.getModifiersEx();
635 boolean eventMouse1
= false;
636 boolean eventMouse2
= false;
637 boolean eventMouse3
= false;
638 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
641 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
644 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
647 mouse1
= eventMouse1
;
648 mouse2
= eventMouse2
;
649 mouse3
= eventMouse3
;
650 int x
= screen
.textColumn(mouse
.getX());
651 int y
= screen
.textRow(mouse
.getY());
653 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
654 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
656 synchronized (eventQueue
) {
657 eventQueue
.add(mouseEvent
);
659 synchronized (listener
) {
660 listener
.notifyAll();
665 * Pass mouse events into the event queue.
667 * @param mouse mouse event received
670 public void mouseReleased(final MouseEvent mouse
) {
671 int modifiers
= mouse
.getModifiersEx();
672 boolean eventMouse1
= false;
673 boolean eventMouse2
= false;
674 boolean eventMouse3
= false;
675 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
678 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
681 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
696 int x
= screen
.textColumn(mouse
.getX());
697 int y
= screen
.textRow(mouse
.getY());
699 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
700 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false);
702 synchronized (eventQueue
) {
703 eventQueue
.add(mouseEvent
);
705 synchronized (listener
) {
706 listener
.notifyAll();
711 * Pass mouse events into the event queue.
713 * @param mouse mouse event received
716 public void mouseWheelMoved(final MouseWheelEvent mouse
) {
717 int modifiers
= mouse
.getModifiersEx();
718 boolean eventMouse1
= false;
719 boolean eventMouse2
= false;
720 boolean eventMouse3
= false;
721 boolean mouseWheelUp
= false;
722 boolean mouseWheelDown
= false;
723 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
726 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
729 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
732 mouse1
= eventMouse1
;
733 mouse2
= eventMouse2
;
734 mouse3
= eventMouse3
;
735 int x
= screen
.textColumn(mouse
.getX());
736 int y
= screen
.textRow(mouse
.getY());
737 if (mouse
.getWheelRotation() > 0) {
738 mouseWheelDown
= true;
740 if (mouse
.getWheelRotation() < 0) {
744 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
745 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
);
747 synchronized (eventQueue
) {
748 eventQueue
.add(mouseEvent
);
750 synchronized (listener
) {
751 listener
.notifyAll();