#33 expose SwingComponent
[fanfix.git] / src / jexer / backend / TWindowBackend.java
... / ...
CommitLineData
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2017 Kevin Lamonte
7 *
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:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
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.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29package jexer.backend;
30
31import java.util.LinkedList;
32import java.util.List;
33
34import jexer.bits.CellAttributes;
35import jexer.event.TInputEvent;
36import jexer.event.TKeypressEvent;
37import jexer.event.TMouseEvent;
38import jexer.TApplication;
39import jexer.TWindow;
40
41/**
42 * TWindowBackend uses a window in one TApplication to provide a backend for
43 * another TApplication.
44 *
45 * Note that TWindow has its own getScreen() and setTitle() functions.
46 * Clients in TWindowBackend's application won't be able to use it to get at
47 * the other application's screen. getOtherScreen() has been provided.
48 */
49public class TWindowBackend extends TWindow implements Backend {
50
51 // ------------------------------------------------------------------------
52 // Variables --------------------------------------------------------------
53 // ------------------------------------------------------------------------
54
55 /**
56 * The listening object that run() wakes up on new input.
57 */
58 private Object listener;
59
60 /**
61 * The object to sync on in draw(). This is normally otherScreen, but it
62 * could also be a MultiScreen.
63 */
64 private Object drawLock;
65
66 /**
67 * The event queue, filled up by a thread reading on input.
68 */
69 private List<TInputEvent> eventQueue;
70
71 /**
72 * The screen to use.
73 */
74 private Screen otherScreen;
75
76 /**
77 * The mouse X position as seen on the other screen.
78 */
79 private int otherMouseX = -1;
80
81 /**
82 * The mouse Y position as seen on the other screen.
83 */
84 private int otherMouseY = -1;
85
86 /**
87 * The session information.
88 */
89 private SessionInfo sessionInfo;
90
91 // ------------------------------------------------------------------------
92 // Constructors -----------------------------------------------------------
93 // ------------------------------------------------------------------------
94
95 /**
96 * Public constructor. Window will be located at (0, 0).
97 *
98 * @param listener the object this backend needs to wake up when new
99 * input comes in
100 * @param application TApplication that manages this window
101 * @param title window title, will be centered along the top border
102 * @param width width of window
103 * @param height height of window
104 */
105 public TWindowBackend(final Object listener,
106 final TApplication application, final String title,
107 final int width, final int height) {
108
109 super(application, title, width, height);
110
111 this.listener = listener;
112 eventQueue = new LinkedList<TInputEvent>();
113 sessionInfo = new TSessionInfo(width, height);
114 otherScreen = new LogicalScreen();
115 otherScreen.setDimensions(width - 2, height - 2);
116 drawLock = otherScreen;
117 }
118
119 /**
120 * Public constructor. Window will be located at (0, 0).
121 *
122 * @param listener the object this backend needs to wake up when new
123 * input comes in
124 * @param application TApplication that manages this window
125 * @param title window title, will be centered along the top border
126 * @param width width of window
127 * @param height height of window
128 * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
129 */
130 public TWindowBackend(final Object listener,
131 final TApplication application, final String title,
132 final int width, final int height, final int flags) {
133
134 super(application, title, width, height, flags);
135
136 this.listener = listener;
137 eventQueue = new LinkedList<TInputEvent>();
138 sessionInfo = new TSessionInfo(width, height);
139 otherScreen = new LogicalScreen();
140 otherScreen.setDimensions(width - 2, height - 2);
141 drawLock = otherScreen;
142 }
143
144 /**
145 * Public constructor.
146 *
147 * @param listener the object this backend needs to wake up when new
148 * input comes in
149 * @param application TApplication that manages this window
150 * @param title window title, will be centered along the top border
151 * @param x column relative to parent
152 * @param y row relative to parent
153 * @param width width of window
154 * @param height height of window
155 */
156 public TWindowBackend(final Object listener,
157 final TApplication application, final String title,
158 final int x, final int y, final int width, final int height) {
159
160 super(application, title, x, y, width, height);
161
162 this.listener = listener;
163 eventQueue = new LinkedList<TInputEvent>();
164 sessionInfo = new TSessionInfo(width, height);
165 otherScreen = new LogicalScreen();
166 otherScreen.setDimensions(width - 2, height - 2);
167 drawLock = otherScreen;
168 }
169
170 /**
171 * Public constructor.
172 *
173 * @param listener the object this backend needs to wake up when new
174 * input comes in
175 * @param application TApplication that manages this window
176 * @param title window title, will be centered along the top border
177 * @param x column relative to parent
178 * @param y row relative to parent
179 * @param width width of window
180 * @param height height of window
181 * @param flags mask of RESIZABLE, CENTERED, or MODAL
182 */
183 public TWindowBackend(final Object listener,
184 final TApplication application, final String title,
185 final int x, final int y, final int width, final int height,
186 final int flags) {
187
188 super(application, title, x, y, width, height, flags);
189
190 this.listener = listener;
191 eventQueue = new LinkedList<TInputEvent>();
192 sessionInfo = new TSessionInfo(width, height);
193 otherScreen = new LogicalScreen();
194 otherScreen.setDimensions(width - 2, height - 2);
195 drawLock = otherScreen;
196 }
197
198 // ------------------------------------------------------------------------
199 // Event handlers ---------------------------------------------------------
200 // ------------------------------------------------------------------------
201
202 /**
203 * Returns true if the mouse is currently in the otherScreen window.
204 *
205 * @param mouse mouse event
206 * @return true if mouse is currently in the otherScreen window.
207 */
208 protected boolean mouseOnOtherScreen(final TMouseEvent mouse) {
209 if ((mouse.getY() >= 1)
210 && (mouse.getY() <= otherScreen.getHeight())
211 && (mouse.getX() >= 1)
212 && (mouse.getX() <= otherScreen.getWidth())
213 ) {
214 return true;
215 }
216 return false;
217 }
218
219 /**
220 * Handle mouse button presses.
221 *
222 * @param mouse mouse button event
223 */
224 @Override
225 public void onMouseDown(final TMouseEvent mouse) {
226 if (mouseOnOtherScreen(mouse)) {
227 TMouseEvent event = mouse.dup();
228 event.setX(mouse.getX() - 1);
229 event.setY(mouse.getY() - 1);
230 event.setAbsoluteX(event.getX());
231 event.setAbsoluteY(event.getY());
232 synchronized (eventQueue) {
233 eventQueue.add(event);
234 }
235 synchronized (listener) {
236 listener.notifyAll();
237 }
238 }
239 super.onMouseDown(mouse);
240 }
241
242 /**
243 * Handle mouse button releases.
244 *
245 * @param mouse mouse button release event
246 */
247 @Override
248 public void onMouseUp(final TMouseEvent mouse) {
249 if (mouseOnOtherScreen(mouse)) {
250 TMouseEvent event = mouse.dup();
251 event.setX(mouse.getX() - 1);
252 event.setY(mouse.getY() - 1);
253 event.setAbsoluteX(event.getX());
254 event.setAbsoluteY(event.getY());
255 synchronized (eventQueue) {
256 eventQueue.add(event);
257 }
258 synchronized (listener) {
259 listener.notifyAll();
260 }
261 }
262 super.onMouseUp(mouse);
263 }
264
265 /**
266 * Handle mouse movements.
267 *
268 * @param mouse mouse motion event
269 */
270 @Override
271 public void onMouseMotion(final TMouseEvent mouse) {
272 if (mouseOnOtherScreen(mouse)) {
273 TMouseEvent event = mouse.dup();
274 event.setX(mouse.getX() - 1);
275 event.setY(mouse.getY() - 1);
276 event.setAbsoluteX(event.getX());
277 event.setAbsoluteY(event.getY());
278 otherMouseX = event.getX() + getX() + 1;
279 otherMouseY = event.getY() + getY() + 1;
280 synchronized (eventQueue) {
281 eventQueue.add(event);
282 }
283 synchronized (listener) {
284 listener.notifyAll();
285 }
286 } else {
287 otherMouseX = -1;
288 otherMouseY = -1;
289 }
290 super.onMouseMotion(mouse);
291 }
292
293 /**
294 * Handle keystrokes.
295 *
296 * @param keypress keystroke event
297 */
298 @Override
299 public void onKeypress(final TKeypressEvent keypress) {
300 TKeypressEvent event = keypress.dup();
301 synchronized (eventQueue) {
302 eventQueue.add(event);
303 }
304 synchronized (listener) {
305 listener.notifyAll();
306 }
307 }
308
309 // ------------------------------------------------------------------------
310 // TWindow ----------------------------------------------------------------
311 // ------------------------------------------------------------------------
312
313 /**
314 * Draw the foreground colors grid.
315 */
316 @Override
317 public void draw() {
318
319 // Sync on other screen, so that we do not draw in the middle of
320 // their screen update.
321 synchronized (drawLock) {
322 // Draw the box
323 super.draw();
324
325 // Draw every cell of the other screen
326 for (int y = 0; y < otherScreen.getHeight(); y++) {
327 for (int x = 0; x < otherScreen.getWidth(); x++) {
328 putCharXY(x + 1, y + 1, otherScreen.getCharXY(x, y));
329 }
330 }
331
332 // If the mouse pointer is over the other window, draw its
333 // pointer again here. (Their TApplication drew it, then our
334 // TApplication drew it again (undo-ing it), so now we draw it a
335 // third time so that it is visible.)
336 if ((otherMouseX != -1) && (otherMouseY != -1)) {
337 CellAttributes attr = getAttrXY(otherMouseX, otherMouseY);
338 attr.setForeColor(attr.getForeColor().invert());
339 attr.setBackColor(attr.getBackColor().invert());
340 putAttrXY(otherMouseX, otherMouseY, attr, false);
341 }
342
343 // If their cursor is visible, draw that here too.
344 if (otherScreen.isCursorVisible()) {
345 setCursorX(otherScreen.getCursorX() + 1);
346 setCursorY(otherScreen.getCursorY() + 1);
347 setCursorVisible(true);
348 } else {
349 setCursorVisible(false);
350 }
351 }
352 }
353
354 /**
355 * Subclasses should override this method to cleanup resources. This is
356 * called by application.closeWindow().
357 */
358 @Override
359 public void onClose() {
360 // TODO: send a screen disconnect
361 }
362
363 // ------------------------------------------------------------------------
364 // Backend ----------------------------------------------------------------
365 // ------------------------------------------------------------------------
366
367 /**
368 * Getter for sessionInfo.
369 *
370 * @return the SessionInfo
371 */
372 public final SessionInfo getSessionInfo() {
373 return sessionInfo;
374 }
375
376 /**
377 * Subclasses must provide an implementation that syncs the logical
378 * screen to the physical device.
379 */
380 public void flushScreen() {
381 getApplication().doRepaint();
382 }
383
384 /**
385 * Check if there are events in the queue.
386 *
387 * @return if true, getEvents() has something to return to the application
388 */
389 public boolean hasEvents() {
390 synchronized (eventQueue) {
391 return (eventQueue.size() > 0);
392 }
393 }
394
395 /**
396 * Subclasses must provide an implementation to get keyboard, mouse, and
397 * screen resize events.
398 *
399 * @param queue list to append new events to
400 */
401 public void getEvents(List<TInputEvent> queue) {
402 synchronized (eventQueue) {
403 if (eventQueue.size() > 0) {
404 synchronized (queue) {
405 queue.addAll(eventQueue);
406 }
407 eventQueue.clear();
408 }
409 }
410 }
411
412 /**
413 * Subclasses must provide an implementation that closes sockets,
414 * restores console, etc.
415 */
416 public void shutdown() {
417 // NOP
418 }
419
420 /**
421 * Set listener to a different Object.
422 *
423 * @param listener the new listening object that run() wakes up on new
424 * input
425 */
426 public void setListener(final Object listener) {
427 this.listener = listener;
428 }
429
430 // ------------------------------------------------------------------------
431 // TWindowBackend ---------------------------------------------------------
432 // ------------------------------------------------------------------------
433
434 /**
435 * Set the object to sync to in draw().
436 *
437 * @param drawLock the object to synchronize on
438 */
439 public void setDrawLock(final Object drawLock) {
440 this.drawLock = drawLock;
441 }
442
443 /**
444 * Getter for the other application's screen.
445 *
446 * @return the Screen
447 */
448 public Screen getOtherScreen() {
449 return otherScreen;
450 }
451
452}