2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 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]
29 package jexer
.backend
;
31 import java
.util
.LinkedList
;
32 import java
.util
.List
;
34 import jexer
.bits
.CellAttributes
;
35 import jexer
.event
.TInputEvent
;
36 import jexer
.event
.TKeypressEvent
;
37 import jexer
.event
.TMouseEvent
;
38 import jexer
.event
.TResizeEvent
;
39 import jexer
.TApplication
;
43 * TWindowBackend uses a window in one TApplication to provide a backend for
44 * another TApplication.
46 * Note that TWindow has its own getScreen() and setTitle() functions.
47 * Clients in TWindowBackend's application won't be able to use it to get at
48 * the other application's screen. getOtherScreen() has been provided.
50 public class TWindowBackend
extends TWindow
implements Backend
{
52 // ------------------------------------------------------------------------
53 // Variables --------------------------------------------------------------
54 // ------------------------------------------------------------------------
57 * The listening object that run() wakes up on new input.
59 private Object listener
;
62 * The object to sync on in draw(). This is normally otherScreen, but it
63 * could also be a MultiScreen.
65 private Object drawLock
;
68 * The event queue, filled up by a thread reading on input.
70 private List
<TInputEvent
> eventQueue
;
73 * The screen this window is monitoring.
75 private Screen otherScreen
;
78 * The application associated with otherScreen.
80 private TApplication otherApplication
;
83 * The session information.
85 private SessionInfo sessionInfo
;
88 * OtherScreen provides a hook to notify TWindowBackend of screen size
91 private class OtherScreen
extends LogicalScreen
{
94 * The TWindowBackend to notify.
96 private TWindowBackend window
;
101 public OtherScreen(final TWindowBackend window
) {
102 this.window
= window
;
106 * Resize the physical screen to match the logical screen dimensions.
109 public void resizeToScreen() {
110 window
.setWidth(getWidth() + 2);
111 window
.setHeight(getHeight() + 2);
117 // ------------------------------------------------------------------------
118 // Constructors -----------------------------------------------------------
119 // ------------------------------------------------------------------------
122 * Public constructor. Window will be located at (0, 0).
124 * @param listener the object this backend needs to wake up when new
126 * @param application TApplication that manages this window
127 * @param title window title, will be centered along the top border
128 * @param width width of window
129 * @param height height of window
131 public TWindowBackend(final Object listener
,
132 final TApplication application
, final String title
,
133 final int width
, final int height
) {
135 super(application
, title
, width
, height
);
137 this.listener
= listener
;
138 eventQueue
= new LinkedList
<TInputEvent
>();
139 sessionInfo
= new TSessionInfo(width
, height
);
140 otherScreen
= new OtherScreen(this);
141 otherScreen
.setDimensions(width
- 2, height
- 2);
142 drawLock
= otherScreen
;
143 setHiddenMouse(true);
147 * Public constructor. Window will be located at (0, 0).
149 * @param listener the object this backend needs to wake up when new
151 * @param application TApplication that manages this window
152 * @param title window title, will be centered along the top border
153 * @param width width of window
154 * @param height height of window
155 * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
157 public TWindowBackend(final Object listener
,
158 final TApplication application
, final String title
,
159 final int width
, final int height
, final int flags
) {
161 super(application
, title
, width
, height
, flags
);
163 this.listener
= listener
;
164 eventQueue
= new LinkedList
<TInputEvent
>();
165 sessionInfo
= new TSessionInfo(width
, height
);
166 otherScreen
= new OtherScreen(this);
167 otherScreen
.setDimensions(width
- 2, height
- 2);
168 drawLock
= otherScreen
;
169 setHiddenMouse(true);
173 * Public constructor.
175 * @param listener the object this backend needs to wake up when new
177 * @param application TApplication that manages this window
178 * @param title window title, will be centered along the top border
179 * @param x column relative to parent
180 * @param y row relative to parent
181 * @param width width of window
182 * @param height height of window
184 public TWindowBackend(final Object listener
,
185 final TApplication application
, final String title
,
186 final int x
, final int y
, final int width
, final int height
) {
188 super(application
, title
, x
, y
, width
, height
);
190 this.listener
= listener
;
191 eventQueue
= new LinkedList
<TInputEvent
>();
192 sessionInfo
= new TSessionInfo(width
, height
);
193 otherScreen
= new OtherScreen(this);
194 otherScreen
.setDimensions(width
- 2, height
- 2);
195 drawLock
= otherScreen
;
196 setHiddenMouse(true);
200 * Public constructor.
202 * @param listener the object this backend needs to wake up when new
204 * @param application TApplication that manages this window
205 * @param title window title, will be centered along the top border
206 * @param x column relative to parent
207 * @param y row relative to parent
208 * @param width width of window
209 * @param height height of window
210 * @param flags mask of RESIZABLE, CENTERED, or MODAL
212 public TWindowBackend(final Object listener
,
213 final TApplication application
, final String title
,
214 final int x
, final int y
, final int width
, final int height
,
217 super(application
, title
, x
, y
, width
, height
, flags
);
219 this.listener
= listener
;
220 eventQueue
= new LinkedList
<TInputEvent
>();
221 sessionInfo
= new TSessionInfo(width
, height
);
222 otherScreen
= new OtherScreen(this);
223 otherScreen
.setDimensions(width
- 2, height
- 2);
224 drawLock
= otherScreen
;
225 setHiddenMouse(true);
228 // ------------------------------------------------------------------------
229 // Event handlers ---------------------------------------------------------
230 // ------------------------------------------------------------------------
233 * Handle window/screen resize events.
235 * @param event resize event
238 public void onResize(final TResizeEvent event
) {
239 if (event
.getType() == TResizeEvent
.Type
.WIDGET
) {
240 int newWidth
= event
.getWidth() - 2;
241 int newHeight
= event
.getHeight() - 2;
242 if ((newWidth
!= otherScreen
.getWidth())
243 || (newHeight
!= otherScreen
.getHeight())
245 // I was resized, notify the screen I am watching to match my
247 synchronized (eventQueue
) {
248 eventQueue
.add(new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
249 newWidth
, newHeight
));
251 synchronized (listener
) {
252 listener
.notifyAll();
257 super.onResize(event
);
262 * Returns true if the mouse is currently in the otherScreen window.
264 * @param mouse mouse event
265 * @return true if mouse is currently in the otherScreen window.
267 protected boolean mouseOnOtherScreen(final TMouseEvent mouse
) {
268 if ((mouse
.getY() >= 1)
269 && (mouse
.getY() <= otherScreen
.getHeight())
270 && (mouse
.getX() >= 1)
271 && (mouse
.getX() <= otherScreen
.getWidth())
279 * Handle mouse button presses.
281 * @param mouse mouse button event
284 public void onMouseDown(final TMouseEvent mouse
) {
285 if (mouseOnOtherScreen(mouse
)) {
286 TMouseEvent event
= mouse
.dup();
287 event
.setX(mouse
.getX() - 1);
288 event
.setY(mouse
.getY() - 1);
289 event
.setAbsoluteX(event
.getX());
290 event
.setAbsoluteY(event
.getY());
291 synchronized (eventQueue
) {
292 eventQueue
.add(event
);
294 synchronized (listener
) {
295 listener
.notifyAll();
298 super.onMouseDown(mouse
);
302 * Handle mouse button releases.
304 * @param mouse mouse button release event
307 public void onMouseUp(final TMouseEvent mouse
) {
308 if (mouseOnOtherScreen(mouse
)) {
309 TMouseEvent event
= mouse
.dup();
310 event
.setX(mouse
.getX() - 1);
311 event
.setY(mouse
.getY() - 1);
312 event
.setAbsoluteX(event
.getX());
313 event
.setAbsoluteY(event
.getY());
314 synchronized (eventQueue
) {
315 eventQueue
.add(event
);
317 synchronized (listener
) {
318 listener
.notifyAll();
321 super.onMouseUp(mouse
);
325 * Handle mouse movements.
327 * @param mouse mouse motion event
330 public void onMouseMotion(final TMouseEvent mouse
) {
331 if (mouseOnOtherScreen(mouse
)) {
332 TMouseEvent event
= mouse
.dup();
333 event
.setX(mouse
.getX() - 1);
334 event
.setY(mouse
.getY() - 1);
335 event
.setAbsoluteX(event
.getX());
336 event
.setAbsoluteY(event
.getY());
337 synchronized (eventQueue
) {
338 eventQueue
.add(event
);
340 synchronized (listener
) {
341 listener
.notifyAll();
344 super.onMouseMotion(mouse
);
350 * @param keypress keystroke event
353 public void onKeypress(final TKeypressEvent keypress
) {
354 TKeypressEvent event
= keypress
.dup();
355 synchronized (eventQueue
) {
356 eventQueue
.add(event
);
358 synchronized (listener
) {
359 listener
.notifyAll();
363 // ------------------------------------------------------------------------
364 // TWindow ----------------------------------------------------------------
365 // ------------------------------------------------------------------------
368 * Draw the foreground colors grid.
373 // Sync on other screen, so that we do not draw in the middle of
374 // their screen update.
375 synchronized (drawLock
) {
379 // Draw every cell of the other screen
380 for (int y
= 0; y
< otherScreen
.getHeight(); y
++) {
381 for (int x
= 0; x
< otherScreen
.getWidth(); x
++) {
382 putCharXY(x
+ 1, y
+ 1, otherScreen
.getCharXY(x
, y
));
386 // If their cursor is visible, draw that here too.
387 if (otherScreen
.isCursorVisible()) {
388 setCursorX(otherScreen
.getCursorX() + 1);
389 setCursorY(otherScreen
.getCursorY() + 1);
390 setCursorVisible(true);
392 setCursorVisible(false);
396 // Check if the other application has died. If so, unset hidden
398 if (otherApplication
!= null) {
399 if (otherApplication
.isRunning() == false) {
400 setHiddenMouse(false);
407 * Subclasses should override this method to cleanup resources. This is
408 * called by application.closeWindow().
411 public void onClose() {
412 // TODO: send a screen disconnect
415 // ------------------------------------------------------------------------
416 // Backend ----------------------------------------------------------------
417 // ------------------------------------------------------------------------
420 * Getter for sessionInfo.
422 * @return the SessionInfo
424 public final SessionInfo
getSessionInfo() {
429 * Subclasses must provide an implementation that syncs the logical
430 * screen to the physical device.
432 public void flushScreen() {
433 getApplication().doRepaint();
437 * Check if there are events in the queue.
439 * @return if true, getEvents() has something to return to the application
441 public boolean hasEvents() {
442 synchronized (eventQueue
) {
443 return (eventQueue
.size() > 0);
448 * Subclasses must provide an implementation to get keyboard, mouse, and
449 * screen resize events.
451 * @param queue list to append new events to
453 public void getEvents(List
<TInputEvent
> queue
) {
454 synchronized (eventQueue
) {
455 if (eventQueue
.size() > 0) {
456 synchronized (queue
) {
457 queue
.addAll(eventQueue
);
465 * Subclasses must provide an implementation that closes sockets,
466 * restores console, etc.
468 public void shutdown() {
473 * Set listener to a different Object.
475 * @param listener the new listening object that run() wakes up on new
478 public void setListener(final Object listener
) {
479 this.listener
= listener
;
483 * Reload backend options from System properties.
485 public void reloadOptions() {
489 // ------------------------------------------------------------------------
490 // TWindowBackend ---------------------------------------------------------
491 // ------------------------------------------------------------------------
494 * Set the object to sync to in draw().
496 * @param drawLock the object to synchronize on
498 public void setDrawLock(final Object drawLock
) {
499 this.drawLock
= drawLock
;
503 * Getter for the other application's screen.
507 public Screen
getOtherScreen() {
512 * Set the other screen's application.
514 * @param application the application driving the other screen
516 public void setOtherApplication(final TApplication application
) {
517 this.otherApplication
= application
;