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
.SwingSessionInfo
;
55 import static jexer
.TCommand
.*;
56 import static jexer
.TKeypress
.*;
59 * This class reads keystrokes and mouse events from an Swing JFrame.
61 public final class SwingTerminal
implements ComponentListener
, KeyListener
,
62 MouseListener
, MouseMotionListener
,
63 MouseWheelListener
, WindowListener
{
68 private SwingScreen screen
;
71 * The session information.
73 private SwingSessionInfo 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
;
95 * The last reported mouse X position.
97 private int oldMouseX
= -1;
100 * The last reported mouse Y position.
102 private int oldMouseY
= -1;
105 * true if mouse1 was down. Used to report mouse1 on the release event.
107 private boolean mouse1
= false;
110 * true if mouse2 was down. Used to report mouse2 on the release event.
112 private boolean mouse2
= false;
115 * true if mouse3 was down. Used to report mouse3 on the release event.
117 private boolean mouse3
= false;
120 * Check if there are events in the queue.
122 * @return if true, getEvents() has something to return to the backend
124 public boolean hasEvents() {
125 synchronized (eventQueue
) {
126 return (eventQueue
.size() > 0);
131 * Constructor sets up state for getEvent().
133 * @param listener the object this backend needs to wake up when new
135 * @param screen the top-level Swing frame
137 public SwingTerminal(final Object listener
, final SwingScreen screen
) {
138 this.listener
= listener
;
139 this.screen
= screen
;
143 sessionInfo
= screen
.getSessionInfo();
144 eventQueue
= new LinkedList
<TInputEvent
>();
146 screen
.frame
.addKeyListener(this);
147 screen
.frame
.addWindowListener(this);
148 screen
.frame
.addComponentListener(this);
149 screen
.frame
.addMouseListener(this);
150 screen
.frame
.addMouseMotionListener(this);
151 screen
.frame
.addMouseWheelListener(this);
155 * Return any events in the IO queue.
157 * @param queue list to append new events to
159 public void getEvents(final List
<TInputEvent
> queue
) {
160 synchronized (eventQueue
) {
161 if (eventQueue
.size() > 0) {
162 synchronized (queue
) {
163 queue
.addAll(eventQueue
);
171 * Pass Swing keystrokes into the event queue.
173 * @param key keystroke received
175 public void keyReleased(final KeyEvent key
) {
176 // Ignore release events
180 * Pass Swing keystrokes into the event queue.
182 * @param key keystroke received
184 public void keyTyped(final KeyEvent key
) {
185 // Ignore typed events
189 * Pass Swing keystrokes into the event queue.
191 * @param key keystroke received
193 public void keyPressed(final KeyEvent key
) {
195 boolean shift
= false;
196 boolean ctrl
= false;
198 boolean isKey
= false;
199 if (key
.isActionKey()) {
202 ch
= key
.getKeyChar();
204 alt
= key
.isAltDown();
205 ctrl
= key
.isControlDown();
206 shift
= key
.isShiftDown();
209 System.err.printf("Swing Key: %s\n", key);
210 System.err.printf(" isKey: %s\n", isKey);
211 System.err.printf(" alt: %s\n", alt);
212 System.err.printf(" ctrl: %s\n", ctrl);
213 System.err.printf(" shift: %s\n", shift);
214 System.err.printf(" ch: %s\n", ch);
217 // Special case: not return the bare modifier presses
218 switch (key
.getKeyCode()) {
219 case KeyEvent
.VK_ALT
:
221 case KeyEvent
.VK_ALT_GRAPH
:
223 case KeyEvent
.VK_CONTROL
:
225 case KeyEvent
.VK_SHIFT
:
227 case KeyEvent
.VK_META
:
233 TKeypress keypress
= null;
235 switch (key
.getKeyCode()) {
237 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
241 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
245 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
249 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
253 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
257 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
261 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
265 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
269 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
272 case KeyEvent
.VK_F10
:
273 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
276 case KeyEvent
.VK_F11
:
277 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
280 case KeyEvent
.VK_F12
:
281 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
284 case KeyEvent
.VK_HOME
:
285 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
288 case KeyEvent
.VK_END
:
289 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
292 case KeyEvent
.VK_PAGE_UP
:
293 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
296 case KeyEvent
.VK_PAGE_DOWN
:
297 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
300 case KeyEvent
.VK_INSERT
:
301 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
304 case KeyEvent
.VK_DELETE
:
305 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
308 case KeyEvent
.VK_RIGHT
:
309 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
312 case KeyEvent
.VK_LEFT
:
313 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
317 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
320 case KeyEvent
.VK_DOWN
:
321 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
324 case KeyEvent
.VK_TAB
:
325 // Special case: distinguish TAB vs BTAB
327 keypress
= kbShiftTab
;
332 case KeyEvent
.VK_ENTER
:
333 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
336 case KeyEvent
.VK_ESCAPE
:
337 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
340 case KeyEvent
.VK_BACK_SPACE
:
341 // Special case: return it as kbBackspace (Ctrl-H)
342 keypress
= new TKeypress(false, 0, 'H', false, true, false);
345 // Unsupported, ignore
350 if (keypress
== null) {
353 keypress
= kbBackspace
;
366 keypress
= kbShiftTab
;
375 if (!alt
&& ctrl
&& !shift
) {
376 ch
= KeyEvent
.getKeyText(key
.getKeyCode()).charAt(0);
378 // Not a special key, put it together
379 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
383 // Save it and we are done.
384 synchronized (eventQueue
) {
385 eventQueue
.add(new TKeypressEvent(keypress
));
387 synchronized (listener
) {
388 listener
.notifyAll();
393 * Pass window events into the event queue.
395 * @param event window event received
397 public void windowActivated(final WindowEvent event
) {
398 // Force a total repaint
399 synchronized (screen
) {
400 screen
.clearPhysical();
405 * Pass window events into the event queue.
407 * @param event window event received
409 public void windowClosed(final WindowEvent event
) {
414 * Pass window events into the event queue.
416 * @param event window event received
418 public void windowClosing(final WindowEvent event
) {
419 // Drop a cmAbort and walk away
420 synchronized (eventQueue
) {
421 eventQueue
.add(new TCommandEvent(cmAbort
));
423 synchronized (listener
) {
424 listener
.notifyAll();
429 * Pass window events into the event queue.
431 * @param event window event received
433 public void windowDeactivated(final WindowEvent event
) {
438 * Pass window events into the event queue.
440 * @param event window event received
442 public void windowDeiconified(final WindowEvent event
) {
447 * Pass window events into the event queue.
449 * @param event window event received
451 public void windowIconified(final WindowEvent event
) {
456 * Pass window events into the event queue.
458 * @param event window event received
460 public void windowOpened(final WindowEvent event
) {
465 * Pass component events into the event queue.
467 * @param event component event received
469 public void componentHidden(final ComponentEvent event
) {
474 * Pass component events into the event queue.
476 * @param event component event received
478 public void componentShown(final ComponentEvent event
) {
483 * Pass component events into the event queue.
485 * @param event component event received
487 public void componentMoved(final ComponentEvent event
) {
492 * Pass component events into the event queue.
494 * @param event component event received
496 public void componentResized(final ComponentEvent event
) {
497 // Drop a new TResizeEvent into the queue
498 sessionInfo
.queryWindowSize();
499 synchronized (eventQueue
) {
500 TResizeEvent windowResize
= new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
501 sessionInfo
.getWindowWidth(), sessionInfo
.getWindowHeight());
502 eventQueue
.add(windowResize
);
504 synchronized (listener
) {
505 listener
.notifyAll();
510 * Pass mouse events into the event queue.
512 * @param mouse mouse event received
514 public void mouseDragged(final MouseEvent mouse
) {
515 int modifiers
= mouse
.getModifiersEx();
516 boolean eventMouse1
= false;
517 boolean eventMouse2
= false;
518 boolean eventMouse3
= false;
519 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
522 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
525 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
528 mouse1
= eventMouse1
;
529 mouse2
= eventMouse2
;
530 mouse3
= eventMouse3
;
531 int x
= screen
.textColumn(mouse
.getX());
532 int y
= screen
.textRow(mouse
.getY());
534 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
535 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
537 synchronized (eventQueue
) {
538 eventQueue
.add(mouseEvent
);
540 synchronized (listener
) {
541 listener
.notifyAll();
546 * Pass mouse events into the event queue.
548 * @param mouse mouse event received
550 public void mouseMoved(final MouseEvent mouse
) {
551 int x
= screen
.textColumn(mouse
.getX());
552 int y
= screen
.textRow(mouse
.getY());
553 if ((x
== oldMouseX
) && (y
== oldMouseY
)) {
554 // Bail out, we've moved some pixels but not a whole text cell.
560 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
561 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
563 synchronized (eventQueue
) {
564 eventQueue
.add(mouseEvent
);
566 synchronized (listener
) {
567 listener
.notifyAll();
572 * Pass mouse events into the event queue.
574 * @param mouse mouse event received
576 public void mouseClicked(final MouseEvent mouse
) {
581 * Pass mouse events into the event queue.
583 * @param mouse mouse event received
585 public void mouseEntered(final MouseEvent mouse
) {
590 * Pass mouse events into the event queue.
592 * @param mouse mouse event received
594 public void mouseExited(final MouseEvent mouse
) {
599 * Pass mouse events into the event queue.
601 * @param mouse mouse event received
603 public void mousePressed(final MouseEvent mouse
) {
604 int modifiers
= mouse
.getModifiersEx();
605 boolean eventMouse1
= false;
606 boolean eventMouse2
= false;
607 boolean eventMouse3
= false;
608 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
611 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
614 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
617 mouse1
= eventMouse1
;
618 mouse2
= eventMouse2
;
619 mouse3
= eventMouse3
;
620 int x
= screen
.textColumn(mouse
.getX());
621 int y
= screen
.textRow(mouse
.getY());
623 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
624 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
626 synchronized (eventQueue
) {
627 eventQueue
.add(mouseEvent
);
629 synchronized (listener
) {
630 listener
.notifyAll();
635 * Pass mouse events into the event queue.
637 * @param mouse mouse event received
639 public void mouseReleased(final MouseEvent mouse
) {
640 int modifiers
= mouse
.getModifiersEx();
641 boolean eventMouse1
= false;
642 boolean eventMouse2
= false;
643 boolean eventMouse3
= false;
644 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
647 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
650 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
665 int x
= screen
.textColumn(mouse
.getX());
666 int y
= screen
.textRow(mouse
.getY());
668 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
669 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false);
671 synchronized (eventQueue
) {
672 eventQueue
.add(mouseEvent
);
674 synchronized (listener
) {
675 listener
.notifyAll();
680 * Pass mouse events into the event queue.
682 * @param mouse mouse event received
684 public void mouseWheelMoved(final MouseWheelEvent mouse
) {
685 int modifiers
= mouse
.getModifiersEx();
686 boolean eventMouse1
= false;
687 boolean eventMouse2
= false;
688 boolean eventMouse3
= false;
689 boolean mouseWheelUp
= false;
690 boolean mouseWheelDown
= false;
691 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
694 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
697 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
700 mouse1
= eventMouse1
;
701 mouse2
= eventMouse2
;
702 mouse3
= eventMouse3
;
703 int x
= screen
.textColumn(mouse
.getX());
704 int y
= screen
.textRow(mouse
.getY());
705 if (mouse
.getWheelRotation() > 0) {
706 mouseWheelDown
= true;
708 if (mouse
.getWheelRotation() < 0) {
712 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
713 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
);
715 synchronized (eventQueue
) {
716 eventQueue
.add(mouseEvent
);
718 synchronized (listener
) {
719 listener
.notifyAll();