2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2016 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.awt
.event
.ComponentEvent
;
32 import java
.awt
.event
.ComponentListener
;
33 import java
.awt
.event
.KeyEvent
;
34 import java
.awt
.event
.KeyListener
;
35 import java
.awt
.event
.MouseEvent
;
36 import java
.awt
.event
.MouseListener
;
37 import java
.awt
.event
.MouseMotionListener
;
38 import java
.awt
.event
.MouseWheelEvent
;
39 import java
.awt
.event
.MouseWheelListener
;
40 import java
.awt
.event
.WindowEvent
;
41 import java
.awt
.event
.WindowListener
;
42 import java
.util
.List
;
43 import java
.util
.LinkedList
;
45 import jexer
.TKeypress
;
46 import jexer
.event
.TCommandEvent
;
47 import jexer
.event
.TInputEvent
;
48 import jexer
.event
.TKeypressEvent
;
49 import jexer
.event
.TMouseEvent
;
50 import jexer
.event
.TResizeEvent
;
51 import jexer
.session
.SessionInfo
;
52 import jexer
.session
.SwingSessionInfo
;
53 import static jexer
.TCommand
.*;
54 import static jexer
.TKeypress
.*;
57 * This class reads keystrokes and mouse events from an Swing JFrame.
59 public final class SwingTerminal
implements ComponentListener
, KeyListener
,
60 MouseListener
, MouseMotionListener
,
61 MouseWheelListener
, WindowListener
{
66 private SwingScreen screen
;
69 * The session information.
71 private SwingSessionInfo sessionInfo
;
74 * Getter for sessionInfo.
76 * @return the SessionInfo
78 public SessionInfo
getSessionInfo() {
83 * The listening object that run() wakes up on new input.
85 private Object listener
;
88 * The event queue, filled up by a thread reading on input.
90 private List
<TInputEvent
> eventQueue
;
93 * The last reported mouse X position.
95 private int oldMouseX
= -1;
98 * The last reported mouse Y position.
100 private int oldMouseY
= -1;
103 * true if mouse1 was down. Used to report mouse1 on the release event.
105 private boolean mouse1
= false;
108 * true if mouse2 was down. Used to report mouse2 on the release event.
110 private boolean mouse2
= false;
113 * true if mouse3 was down. Used to report mouse3 on the release event.
115 private boolean mouse3
= false;
118 * Check if there are events in the queue.
120 * @return if true, getEvents() has something to return to the backend
122 public boolean hasEvents() {
123 synchronized (eventQueue
) {
124 return (eventQueue
.size() > 0);
129 * Constructor sets up state for getEvent().
131 * @param listener the object this backend needs to wake up when new
133 * @param screen the top-level Swing frame
135 public SwingTerminal(final Object listener
, final SwingScreen screen
) {
136 this.listener
= listener
;
137 this.screen
= screen
;
141 sessionInfo
= screen
.getSessionInfo();
142 eventQueue
= new LinkedList
<TInputEvent
>();
144 screen
.frame
.addKeyListener(this);
145 screen
.frame
.addWindowListener(this);
146 screen
.frame
.addComponentListener(this);
147 screen
.frame
.addMouseListener(this);
148 screen
.frame
.addMouseMotionListener(this);
149 screen
.frame
.addMouseWheelListener(this);
153 * Return any events in the IO queue.
155 * @param queue list to append new events to
157 public void getEvents(final List
<TInputEvent
> queue
) {
158 synchronized (eventQueue
) {
159 if (eventQueue
.size() > 0) {
160 synchronized (queue
) {
161 queue
.addAll(eventQueue
);
169 * Pass Swing keystrokes into the event queue.
171 * @param key keystroke received
173 public void keyReleased(final KeyEvent key
) {
174 // Ignore release events
178 * Pass Swing keystrokes into the event queue.
180 * @param key keystroke received
182 public void keyTyped(final KeyEvent key
) {
183 // Ignore typed events
187 * Pass Swing keystrokes into the event queue.
189 * @param key keystroke received
191 public void keyPressed(final KeyEvent key
) {
193 boolean shift
= false;
194 boolean ctrl
= false;
196 boolean isKey
= false;
197 if (key
.isActionKey()) {
200 ch
= key
.getKeyChar();
202 alt
= key
.isAltDown();
203 ctrl
= key
.isControlDown();
204 shift
= key
.isShiftDown();
207 System.err.printf("Swing Key: %s\n", key);
208 System.err.printf(" isKey: %s\n", isKey);
209 System.err.printf(" alt: %s\n", alt);
210 System.err.printf(" ctrl: %s\n", ctrl);
211 System.err.printf(" shift: %s\n", shift);
212 System.err.printf(" ch: %s\n", ch);
215 // Special case: not return the bare modifier presses
216 switch (key
.getKeyCode()) {
217 case KeyEvent
.VK_ALT
:
219 case KeyEvent
.VK_ALT_GRAPH
:
221 case KeyEvent
.VK_CONTROL
:
223 case KeyEvent
.VK_SHIFT
:
225 case KeyEvent
.VK_META
:
231 TKeypress keypress
= null;
233 switch (key
.getKeyCode()) {
235 keypress
= new TKeypress(true, TKeypress
.F1
, ' ',
239 keypress
= new TKeypress(true, TKeypress
.F2
, ' ',
243 keypress
= new TKeypress(true, TKeypress
.F3
, ' ',
247 keypress
= new TKeypress(true, TKeypress
.F4
, ' ',
251 keypress
= new TKeypress(true, TKeypress
.F5
, ' ',
255 keypress
= new TKeypress(true, TKeypress
.F6
, ' ',
259 keypress
= new TKeypress(true, TKeypress
.F7
, ' ',
263 keypress
= new TKeypress(true, TKeypress
.F8
, ' ',
267 keypress
= new TKeypress(true, TKeypress
.F9
, ' ',
270 case KeyEvent
.VK_F10
:
271 keypress
= new TKeypress(true, TKeypress
.F10
, ' ',
274 case KeyEvent
.VK_F11
:
275 keypress
= new TKeypress(true, TKeypress
.F11
, ' ',
278 case KeyEvent
.VK_F12
:
279 keypress
= new TKeypress(true, TKeypress
.F12
, ' ',
282 case KeyEvent
.VK_HOME
:
283 keypress
= new TKeypress(true, TKeypress
.HOME
, ' ',
286 case KeyEvent
.VK_END
:
287 keypress
= new TKeypress(true, TKeypress
.END
, ' ',
290 case KeyEvent
.VK_PAGE_UP
:
291 keypress
= new TKeypress(true, TKeypress
.PGUP
, ' ',
294 case KeyEvent
.VK_PAGE_DOWN
:
295 keypress
= new TKeypress(true, TKeypress
.PGDN
, ' ',
298 case KeyEvent
.VK_INSERT
:
299 keypress
= new TKeypress(true, TKeypress
.INS
, ' ',
302 case KeyEvent
.VK_DELETE
:
303 keypress
= new TKeypress(true, TKeypress
.DEL
, ' ',
306 case KeyEvent
.VK_RIGHT
:
307 keypress
= new TKeypress(true, TKeypress
.RIGHT
, ' ',
310 case KeyEvent
.VK_LEFT
:
311 keypress
= new TKeypress(true, TKeypress
.LEFT
, ' ',
315 keypress
= new TKeypress(true, TKeypress
.UP
, ' ',
318 case KeyEvent
.VK_DOWN
:
319 keypress
= new TKeypress(true, TKeypress
.DOWN
, ' ',
322 case KeyEvent
.VK_TAB
:
323 // Special case: distinguish TAB vs BTAB
325 keypress
= kbShiftTab
;
330 case KeyEvent
.VK_ENTER
:
331 keypress
= new TKeypress(true, TKeypress
.ENTER
, ' ',
334 case KeyEvent
.VK_ESCAPE
:
335 keypress
= new TKeypress(true, TKeypress
.ESC
, ' ',
338 case KeyEvent
.VK_BACK_SPACE
:
339 // Special case: return it as kbBackspace (Ctrl-H)
340 keypress
= new TKeypress(false, 0, 'H', false, true, false);
343 // Unsupported, ignore
348 if (keypress
== null) {
351 keypress
= kbBackspace
;
364 keypress
= kbShiftTab
;
373 if (!alt
&& ctrl
&& !shift
) {
374 ch
= KeyEvent
.getKeyText(key
.getKeyCode()).charAt(0);
376 // Not a special key, put it together
377 keypress
= new TKeypress(false, 0, ch
, alt
, ctrl
, shift
);
381 // Save it and we are done.
382 synchronized (eventQueue
) {
383 eventQueue
.add(new TKeypressEvent(keypress
));
385 synchronized (listener
) {
386 listener
.notifyAll();
391 * Pass window events into the event queue.
393 * @param event window event received
395 public void windowActivated(final WindowEvent event
) {
396 // Force a total repaint
397 synchronized (screen
) {
398 screen
.clearPhysical();
403 * Pass window events into the event queue.
405 * @param event window event received
407 public void windowClosed(final WindowEvent event
) {
412 * Pass window events into the event queue.
414 * @param event window event received
416 public void windowClosing(final WindowEvent event
) {
417 // Drop a cmAbort and walk away
418 synchronized (eventQueue
) {
419 eventQueue
.add(new TCommandEvent(cmAbort
));
421 synchronized (listener
) {
422 listener
.notifyAll();
427 * Pass window events into the event queue.
429 * @param event window event received
431 public void windowDeactivated(final WindowEvent event
) {
436 * Pass window events into the event queue.
438 * @param event window event received
440 public void windowDeiconified(final WindowEvent event
) {
445 * Pass window events into the event queue.
447 * @param event window event received
449 public void windowIconified(final WindowEvent event
) {
454 * Pass window events into the event queue.
456 * @param event window event received
458 public void windowOpened(final WindowEvent event
) {
463 * Pass component events into the event queue.
465 * @param event component event received
467 public void componentHidden(final ComponentEvent event
) {
472 * Pass component events into the event queue.
474 * @param event component event received
476 public void componentShown(final ComponentEvent event
) {
481 * Pass component events into the event queue.
483 * @param event component event received
485 public void componentMoved(final ComponentEvent event
) {
490 * Pass component events into the event queue.
492 * @param event component event received
494 public void componentResized(final ComponentEvent event
) {
495 // Drop a new TResizeEvent into the queue
496 sessionInfo
.queryWindowSize();
497 synchronized (eventQueue
) {
498 TResizeEvent windowResize
= new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
499 sessionInfo
.getWindowWidth(), sessionInfo
.getWindowHeight());
500 eventQueue
.add(windowResize
);
502 synchronized (listener
) {
503 listener
.notifyAll();
508 * Pass mouse events into the event queue.
510 * @param mouse mouse event received
512 public void mouseDragged(final MouseEvent mouse
) {
513 int modifiers
= mouse
.getModifiersEx();
514 boolean eventMouse1
= false;
515 boolean eventMouse2
= false;
516 boolean eventMouse3
= false;
517 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
520 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
523 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
526 mouse1
= eventMouse1
;
527 mouse2
= eventMouse2
;
528 mouse3
= eventMouse3
;
529 int x
= screen
.textColumn(mouse
.getX());
530 int y
= screen
.textRow(mouse
.getY());
532 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
533 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
535 synchronized (eventQueue
) {
536 eventQueue
.add(mouseEvent
);
538 synchronized (listener
) {
539 listener
.notifyAll();
544 * Pass mouse events into the event queue.
546 * @param mouse mouse event received
548 public void mouseMoved(final MouseEvent mouse
) {
549 int x
= screen
.textColumn(mouse
.getX());
550 int y
= screen
.textRow(mouse
.getY());
551 if ((x
== oldMouseX
) && (y
== oldMouseY
)) {
552 // Bail out, we've moved some pixels but not a whole text cell.
558 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_MOTION
,
559 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
561 synchronized (eventQueue
) {
562 eventQueue
.add(mouseEvent
);
564 synchronized (listener
) {
565 listener
.notifyAll();
570 * Pass mouse events into the event queue.
572 * @param mouse mouse event received
574 public void mouseClicked(final MouseEvent mouse
) {
579 * Pass mouse events into the event queue.
581 * @param mouse mouse event received
583 public void mouseEntered(final MouseEvent mouse
) {
588 * Pass mouse events into the event queue.
590 * @param mouse mouse event received
592 public void mouseExited(final MouseEvent mouse
) {
597 * Pass mouse events into the event queue.
599 * @param mouse mouse event received
601 public void mousePressed(final MouseEvent mouse
) {
602 int modifiers
= mouse
.getModifiersEx();
603 boolean eventMouse1
= false;
604 boolean eventMouse2
= false;
605 boolean eventMouse3
= false;
606 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
609 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
612 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
615 mouse1
= eventMouse1
;
616 mouse2
= eventMouse2
;
617 mouse3
= eventMouse3
;
618 int x
= screen
.textColumn(mouse
.getX());
619 int y
= screen
.textRow(mouse
.getY());
621 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
622 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, false, false);
624 synchronized (eventQueue
) {
625 eventQueue
.add(mouseEvent
);
627 synchronized (listener
) {
628 listener
.notifyAll();
633 * Pass mouse events into the event queue.
635 * @param mouse mouse event received
637 public void mouseReleased(final MouseEvent mouse
) {
638 int modifiers
= mouse
.getModifiersEx();
639 boolean eventMouse1
= false;
640 boolean eventMouse2
= false;
641 boolean eventMouse3
= false;
642 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
645 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
648 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
663 int x
= screen
.textColumn(mouse
.getX());
664 int y
= screen
.textRow(mouse
.getY());
666 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_UP
,
667 x
, y
, x
, y
, eventMouse1
, eventMouse2
, eventMouse3
, false, false);
669 synchronized (eventQueue
) {
670 eventQueue
.add(mouseEvent
);
672 synchronized (listener
) {
673 listener
.notifyAll();
678 * Pass mouse events into the event queue.
680 * @param mouse mouse event received
682 public void mouseWheelMoved(final MouseWheelEvent mouse
) {
683 int modifiers
= mouse
.getModifiersEx();
684 boolean eventMouse1
= false;
685 boolean eventMouse2
= false;
686 boolean eventMouse3
= false;
687 boolean mouseWheelUp
= false;
688 boolean mouseWheelDown
= false;
689 if ((modifiers
& MouseEvent
.BUTTON1_DOWN_MASK
) != 0) {
692 if ((modifiers
& MouseEvent
.BUTTON2_DOWN_MASK
) != 0) {
695 if ((modifiers
& MouseEvent
.BUTTON3_DOWN_MASK
) != 0) {
698 mouse1
= eventMouse1
;
699 mouse2
= eventMouse2
;
700 mouse3
= eventMouse3
;
701 int x
= screen
.textColumn(mouse
.getX());
702 int y
= screen
.textRow(mouse
.getY());
703 if (mouse
.getWheelRotation() > 0) {
704 mouseWheelDown
= true;
706 if (mouse
.getWheelRotation() < 0) {
710 TMouseEvent mouseEvent
= new TMouseEvent(TMouseEvent
.Type
.MOUSE_DOWN
,
711 x
, y
, x
, y
, mouse1
, mouse2
, mouse3
, mouseWheelUp
, mouseWheelDown
);
713 synchronized (eventQueue
) {
714 eventQueue
.add(mouseEvent
);
716 synchronized (listener
) {
717 listener
.notifyAll();