Commit | Line | Data |
---|---|---|
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 | */ | |
31 | package jexer.io; | |
32 | ||
84614868 KL |
33 | import java.awt.event.KeyListener; |
34 | import java.awt.event.KeyEvent; | |
35 | import java.awt.event.MouseListener; | |
36 | import java.awt.event.WindowListener; | |
1ac2ccb1 KL |
37 | import java.util.List; |
38 | import java.util.LinkedList; | |
39 | ||
40 | import jexer.TKeypress; | |
41 | import jexer.bits.Color; | |
42 | import jexer.event.TInputEvent; | |
43 | import jexer.event.TKeypressEvent; | |
44 | import jexer.event.TMouseEvent; | |
45 | import jexer.event.TResizeEvent; | |
46 | import jexer.session.SessionInfo; | |
47 | import jexer.session.TSessionInfo; | |
48 | import static jexer.TKeypress.*; | |
49 | ||
50 | /** | |
51 | * This class reads keystrokes and mouse events from an AWT Frame. | |
52 | */ | |
84614868 KL |
53 | public final class AWTTerminal implements KeyListener { |
54 | ||
55 | /** | |
56 | * The backend Screen. | |
57 | */ | |
58 | private AWTScreen screen; | |
1ac2ccb1 KL |
59 | |
60 | /** | |
61 | * The session information. | |
62 | */ | |
63 | private SessionInfo sessionInfo; | |
64 | ||
65 | /** | |
66 | * Getter for sessionInfo. | |
67 | * | |
68 | * @return the SessionInfo | |
69 | */ | |
70 | public SessionInfo getSessionInfo() { | |
71 | return sessionInfo; | |
72 | } | |
73 | ||
74 | /** | |
75 | * The event queue, filled up by a thread reading on input. | |
76 | */ | |
77 | private List<TInputEvent> eventQueue; | |
78 | ||
79 | /** | |
80 | * If true, we want the reader thread to exit gracefully. | |
81 | */ | |
82 | private boolean stopReaderThread; | |
83 | ||
84 | /** | |
85 | * The reader thread. | |
86 | */ | |
87 | private Thread readerThread; | |
88 | ||
89 | /** | |
90 | * true if mouse1 was down. Used to report mouse1 on the release event. | |
91 | */ | |
92 | private boolean mouse1; | |
93 | ||
94 | /** | |
95 | * true if mouse2 was down. Used to report mouse2 on the release event. | |
96 | */ | |
97 | private boolean mouse2; | |
98 | ||
99 | /** | |
100 | * true if mouse3 was down. Used to report mouse3 on the release event. | |
101 | */ | |
102 | private boolean mouse3; | |
103 | ||
104 | /** | |
105 | * Check if there are events in the queue. | |
106 | * | |
107 | * @return if true, getEvents() has something to return to the backend | |
108 | */ | |
109 | public boolean hasEvents() { | |
110 | synchronized (eventQueue) { | |
111 | return (eventQueue.size() > 0); | |
112 | } | |
113 | } | |
114 | ||
115 | /** | |
116 | * Constructor sets up state for getEvent(). | |
117 | * | |
84614868 | 118 | * @param screen the top-level AWT frame |
1ac2ccb1 KL |
119 | */ |
120 | public AWTTerminal(final AWTScreen screen) { | |
84614868 | 121 | this.screen = screen; |
1ac2ccb1 KL |
122 | mouse1 = false; |
123 | mouse2 = false; | |
124 | mouse3 = false; | |
125 | stopReaderThread = false; | |
126 | sessionInfo = new TSessionInfo(); | |
127 | eventQueue = new LinkedList<TInputEvent>(); | |
84614868 KL |
128 | |
129 | screen.frame.addKeyListener(this); | |
1ac2ccb1 KL |
130 | } |
131 | ||
132 | /** | |
133 | * Restore terminal to normal state. | |
134 | */ | |
135 | public void shutdown() { | |
136 | // System.err.println("=== shutdown() ==="); System.err.flush(); | |
84614868 | 137 | screen.frame.dispose(); |
1ac2ccb1 KL |
138 | } |
139 | ||
140 | /** | |
141 | * Return any events in the IO queue. | |
142 | * | |
143 | * @param queue list to append new events to | |
144 | */ | |
145 | public void getEvents(final List<TInputEvent> queue) { | |
146 | synchronized (eventQueue) { | |
147 | if (eventQueue.size() > 0) { | |
148 | synchronized (queue) { | |
149 | queue.addAll(eventQueue); | |
150 | } | |
151 | eventQueue.clear(); | |
152 | } | |
153 | } | |
154 | } | |
155 | ||
156 | /** | |
157 | * Return any events in the IO queue due to timeout. | |
158 | * | |
159 | * @param queue list to append new events to | |
160 | */ | |
161 | public void getIdleEvents(final List<TInputEvent> queue) { | |
162 | ||
163 | // Insert any polling action here... | |
164 | ||
165 | // Return any events that showed up | |
166 | synchronized (eventQueue) { | |
167 | if (eventQueue.size() > 0) { | |
168 | synchronized (queue) { | |
169 | queue.addAll(eventQueue); | |
170 | } | |
171 | eventQueue.clear(); | |
172 | } | |
173 | } | |
174 | } | |
175 | ||
84614868 KL |
176 | /** |
177 | * Pass AWT keystrokes into the event queue. | |
178 | * | |
179 | * @param key keystroke received | |
180 | */ | |
181 | @Override | |
182 | public void keyReleased(final KeyEvent key) { | |
183 | // Ignore release events | |
184 | } | |
185 | ||
186 | /** | |
187 | * Pass AWT keystrokes into the event queue. | |
188 | * | |
189 | * @param key keystroke received | |
190 | */ | |
191 | @Override | |
192 | public void keyTyped(final KeyEvent key) { | |
193 | // Ignore typed events | |
194 | } | |
195 | ||
196 | /** | |
197 | * Pass AWT keystrokes into the event queue. | |
198 | * | |
199 | * @param key keystroke received | |
200 | */ | |
201 | @Override | |
202 | public void keyPressed(final KeyEvent key) { | |
203 | boolean alt = false; | |
204 | boolean shift = false; | |
205 | boolean ctrl = false; | |
206 | char ch = ' '; | |
207 | boolean isKey = false; | |
208 | int fnKey = 0; | |
209 | if (key.isActionKey()) { | |
210 | isKey = true; | |
211 | } else { | |
212 | ch = key.getKeyChar(); | |
213 | } | |
214 | alt = key.isAltDown(); | |
215 | ctrl = key.isControlDown(); | |
216 | shift = key.isShiftDown(); | |
217 | ||
218 | /* | |
219 | System.err.printf("AWT Key: %s\n", key); | |
220 | System.err.printf(" isKey: %s\n", isKey); | |
221 | System.err.printf(" alt: %s\n", alt); | |
222 | System.err.printf(" ctrl: %s\n", ctrl); | |
223 | System.err.printf(" shift: %s\n", shift); | |
224 | System.err.printf(" ch: %s\n", ch); | |
225 | */ | |
226 | ||
227 | // Special case: not return the bare modifier presses | |
228 | switch (key.getKeyCode()) { | |
229 | case KeyEvent.VK_ALT: | |
230 | return; | |
231 | case KeyEvent.VK_ALT_GRAPH: | |
232 | return; | |
233 | case KeyEvent.VK_CONTROL: | |
234 | return; | |
235 | case KeyEvent.VK_SHIFT: | |
236 | return; | |
237 | case KeyEvent.VK_META: | |
238 | return; | |
239 | default: | |
240 | break; | |
241 | } | |
242 | ||
243 | TKeypress keypress = null; | |
244 | if (isKey) { | |
245 | switch (key.getKeyCode()) { | |
246 | case KeyEvent.VK_F1: | |
247 | keypress = new TKeypress(true, TKeypress.F1, ' ', | |
248 | alt, ctrl, shift); | |
249 | break; | |
250 | case KeyEvent.VK_F2: | |
251 | keypress = new TKeypress(true, TKeypress.F2, ' ', | |
252 | alt, ctrl, shift); | |
253 | break; | |
254 | case KeyEvent.VK_F3: | |
255 | keypress = new TKeypress(true, TKeypress.F3, ' ', | |
256 | alt, ctrl, shift); | |
257 | break; | |
258 | case KeyEvent.VK_F4: | |
259 | keypress = new TKeypress(true, TKeypress.F4, ' ', | |
260 | alt, ctrl, shift); | |
261 | break; | |
262 | case KeyEvent.VK_F5: | |
263 | keypress = new TKeypress(true, TKeypress.F5, ' ', | |
264 | alt, ctrl, shift); | |
265 | break; | |
266 | case KeyEvent.VK_F6: | |
267 | keypress = new TKeypress(true, TKeypress.F6, ' ', | |
268 | alt, ctrl, shift); | |
269 | break; | |
270 | case KeyEvent.VK_F7: | |
271 | keypress = new TKeypress(true, TKeypress.F7, ' ', | |
272 | alt, ctrl, shift); | |
273 | break; | |
274 | case KeyEvent.VK_F8: | |
275 | keypress = new TKeypress(true, TKeypress.F8, ' ', | |
276 | alt, ctrl, shift); | |
277 | break; | |
278 | case KeyEvent.VK_F9: | |
279 | keypress = new TKeypress(true, TKeypress.F9, ' ', | |
280 | alt, ctrl, shift); | |
281 | break; | |
282 | case KeyEvent.VK_F10: | |
283 | keypress = new TKeypress(true, TKeypress.F10, ' ', | |
284 | alt, ctrl, shift); | |
285 | break; | |
286 | case KeyEvent.VK_F11: | |
287 | keypress = new TKeypress(true, TKeypress.F11, ' ', | |
288 | alt, ctrl, shift); | |
289 | break; | |
290 | case KeyEvent.VK_F12: | |
291 | keypress = new TKeypress(true, TKeypress.F12, ' ', | |
292 | alt, ctrl, shift); | |
293 | break; | |
294 | case KeyEvent.VK_HOME: | |
295 | keypress = new TKeypress(true, TKeypress.HOME, ' ', | |
296 | alt, ctrl, shift); | |
297 | break; | |
298 | case KeyEvent.VK_END: | |
299 | keypress = new TKeypress(true, TKeypress.END, ' ', | |
300 | alt, ctrl, shift); | |
301 | break; | |
302 | case KeyEvent.VK_PAGE_UP: | |
303 | keypress = new TKeypress(true, TKeypress.PGUP, ' ', | |
304 | alt, ctrl, shift); | |
305 | break; | |
306 | case KeyEvent.VK_PAGE_DOWN: | |
307 | keypress = new TKeypress(true, TKeypress.PGDN, ' ', | |
308 | alt, ctrl, shift); | |
309 | break; | |
310 | case KeyEvent.VK_INSERT: | |
311 | keypress = new TKeypress(true, TKeypress.INS, ' ', | |
312 | alt, ctrl, shift); | |
313 | break; | |
314 | case KeyEvent.VK_DELETE: | |
315 | keypress = new TKeypress(true, TKeypress.F1, ' ', | |
316 | alt, ctrl, shift); | |
317 | break; | |
318 | case KeyEvent.VK_RIGHT: | |
319 | keypress = new TKeypress(true, TKeypress.RIGHT, ' ', | |
320 | alt, ctrl, shift); | |
321 | break; | |
322 | case KeyEvent.VK_LEFT: | |
323 | keypress = new TKeypress(true, TKeypress.LEFT, ' ', | |
324 | alt, ctrl, shift); | |
325 | break; | |
326 | case KeyEvent.VK_UP: | |
327 | keypress = new TKeypress(true, TKeypress.UP, ' ', | |
328 | alt, ctrl, shift); | |
329 | break; | |
330 | case KeyEvent.VK_DOWN: | |
331 | keypress = new TKeypress(true, TKeypress.DOWN, ' ', | |
332 | alt, ctrl, shift); | |
333 | break; | |
334 | case KeyEvent.VK_TAB: | |
335 | // Special case: distinguish TAB vs BTAB | |
336 | if (shift) { | |
337 | keypress = kbShiftTab; | |
338 | } else { | |
339 | keypress = kbTab; | |
340 | } | |
341 | break; | |
342 | case KeyEvent.VK_ENTER: | |
343 | keypress = new TKeypress(true, TKeypress.ENTER, ' ', | |
344 | alt, ctrl, shift); | |
345 | break; | |
346 | case KeyEvent.VK_ESCAPE: | |
347 | keypress = new TKeypress(true, TKeypress.ESC, ' ', | |
348 | alt, ctrl, shift); | |
349 | break; | |
350 | case KeyEvent.VK_BACK_SPACE: | |
351 | // Special case: return it as kbBackspace (Ctrl-H) | |
352 | keypress = new TKeypress(false, 0, 'H', false, true, false); | |
353 | break; | |
354 | default: | |
355 | // Unsupported, ignore | |
356 | return; | |
357 | } | |
358 | } | |
359 | ||
360 | if (keypress == null) { | |
361 | switch (ch) { | |
362 | case 0x08: | |
363 | keypress = kbBackspace; | |
364 | break; | |
365 | case 0x0A: | |
366 | keypress = kbEnter; | |
367 | break; | |
368 | case 0x0D: | |
369 | keypress = kbEnter; | |
370 | break; | |
371 | default: | |
372 | if (!alt && ctrl && !shift) { | |
373 | ch = key.getKeyText(key.getKeyCode()).charAt(0); | |
374 | } | |
375 | // Not a special key, put it together | |
376 | keypress = new TKeypress(false, 0, ch, alt, ctrl, shift); | |
377 | } | |
378 | } | |
379 | ||
380 | // Save it and we are done. | |
381 | synchronized (eventQueue) { | |
382 | eventQueue.add(new TKeypressEvent(keypress)); | |
383 | } | |
384 | } | |
1ac2ccb1 | 385 | } |