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
.ArrayList
;
32 import java
.util
.List
;
34 import jexer
.TApplication
;
36 import jexer
.event
.TCommandEvent
;
37 import jexer
.event
.TInputEvent
;
38 import jexer
.event
.TKeypressEvent
;
39 import jexer
.event
.TMouseEvent
;
40 import jexer
.event
.TResizeEvent
;
41 import static jexer
.TCommand
.*;
44 * TWindowBackend uses a window in one TApplication to provide a backend for
45 * another TApplication.
47 * Note that TWindow has its own getScreen() and setTitle() functions.
48 * Clients in TWindowBackend's application won't be able to use it to get at
49 * the other application's screen. getOtherScreen() has been provided.
51 public class TWindowBackend
extends TWindow
implements Backend
{
53 // ------------------------------------------------------------------------
54 // Variables --------------------------------------------------------------
55 // ------------------------------------------------------------------------
58 * The listening object that run() wakes up on new input.
60 private Object listener
;
63 * The object to sync on in draw(). This is normally otherScreen, but it
64 * could also be a MultiScreen.
66 private Object drawLock
;
69 * The event queue, filled up by a thread reading on input.
71 private List
<TInputEvent
> eventQueue
;
74 * The screen this window is monitoring.
76 private Screen otherScreen
;
79 * The application associated with otherScreen.
81 private TApplication otherApplication
;
84 * The session information.
86 private SessionInfo sessionInfo
;
89 * OtherScreen provides a hook to notify TWindowBackend of screen size
92 private class OtherScreen
extends LogicalScreen
{
95 * The TWindowBackend to notify.
97 private TWindowBackend window
;
100 * Public constructor.
102 public OtherScreen(final TWindowBackend window
) {
103 this.window
= window
;
107 * Resize the physical screen to match the logical screen dimensions.
110 public void resizeToScreen() {
111 window
.setWidth(getWidth() + 2);
112 window
.setHeight(getHeight() + 2);
116 * Get the width of a character cell in pixels.
118 * @return the width in pixels of a character cell
121 public int getTextWidth() {
122 return window
.getScreen().getTextWidth();
126 * Get the height of a character cell in pixels.
128 * @return the height in pixels of a character cell
131 public int getTextHeight() {
132 return window
.getScreen().getTextHeight();
138 // ------------------------------------------------------------------------
139 // Constructors -----------------------------------------------------------
140 // ------------------------------------------------------------------------
143 * Public constructor. Window will be located at (0, 0).
145 * @param listener the object this backend needs to wake up when new
147 * @param application TApplication that manages this window
148 * @param title window title, will be centered along the top border
149 * @param width width of window
150 * @param height height of window
152 public TWindowBackend(final Object listener
,
153 final TApplication application
, final String title
,
154 final int width
, final int height
) {
156 super(application
, title
, width
, height
);
158 this.listener
= listener
;
159 eventQueue
= new ArrayList
<TInputEvent
>();
160 sessionInfo
= new TSessionInfo(width
, height
);
161 otherScreen
= new OtherScreen(this);
162 otherScreen
.setDimensions(width
- 2, height
- 2);
163 drawLock
= otherScreen
;
164 setHiddenMouse(true);
168 * Public constructor. Window will be located at (0, 0).
170 * @param listener the object this backend needs to wake up when new
172 * @param application TApplication that manages this window
173 * @param title window title, will be centered along the top border
174 * @param width width of window
175 * @param height height of window
176 * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
178 public TWindowBackend(final Object listener
,
179 final TApplication application
, final String title
,
180 final int width
, final int height
, final int flags
) {
182 super(application
, title
, width
, height
, flags
);
184 this.listener
= listener
;
185 eventQueue
= new ArrayList
<TInputEvent
>();
186 sessionInfo
= new TSessionInfo(width
, height
);
187 otherScreen
= new OtherScreen(this);
188 otherScreen
.setDimensions(width
- 2, height
- 2);
189 drawLock
= otherScreen
;
190 setHiddenMouse(true);
194 * Public constructor.
196 * @param listener the object this backend needs to wake up when new
198 * @param application TApplication that manages this window
199 * @param title window title, will be centered along the top border
200 * @param x column relative to parent
201 * @param y row relative to parent
202 * @param width width of window
203 * @param height height of window
205 public TWindowBackend(final Object listener
,
206 final TApplication application
, final String title
,
207 final int x
, final int y
, final int width
, final int height
) {
209 super(application
, title
, x
, y
, width
, height
);
211 this.listener
= listener
;
212 eventQueue
= new ArrayList
<TInputEvent
>();
213 sessionInfo
= new TSessionInfo(width
, height
);
214 otherScreen
= new OtherScreen(this);
215 otherScreen
.setDimensions(width
- 2, height
- 2);
216 drawLock
= otherScreen
;
217 setHiddenMouse(true);
221 * Public constructor.
223 * @param listener the object this backend needs to wake up when new
225 * @param application TApplication that manages this window
226 * @param title window title, will be centered along the top border
227 * @param x column relative to parent
228 * @param y row relative to parent
229 * @param width width of window
230 * @param height height of window
231 * @param flags mask of RESIZABLE, CENTERED, or MODAL
233 public TWindowBackend(final Object listener
,
234 final TApplication application
, final String title
,
235 final int x
, final int y
, final int width
, final int height
,
238 super(application
, title
, x
, y
, width
, height
, flags
);
240 this.listener
= listener
;
241 eventQueue
= new ArrayList
<TInputEvent
>();
242 sessionInfo
= new TSessionInfo(width
, height
);
243 otherScreen
= new OtherScreen(this);
244 otherScreen
.setDimensions(width
- 2, height
- 2);
245 drawLock
= otherScreen
;
246 setHiddenMouse(true);
249 // ------------------------------------------------------------------------
250 // Event handlers ---------------------------------------------------------
251 // ------------------------------------------------------------------------
254 * Handle window/screen resize events.
256 * @param event resize event
259 public void onResize(final TResizeEvent event
) {
260 if (event
.getType() == TResizeEvent
.Type
.WIDGET
) {
261 int newWidth
= event
.getWidth() - 2;
262 int newHeight
= event
.getHeight() - 2;
263 if ((newWidth
!= otherScreen
.getWidth())
264 || (newHeight
!= otherScreen
.getHeight())
266 // I was resized, notify the screen I am watching to match my
268 synchronized (eventQueue
) {
269 eventQueue
.add(new TResizeEvent(TResizeEvent
.Type
.SCREEN
,
270 newWidth
, newHeight
));
272 synchronized (listener
) {
273 listener
.notifyAll();
278 super.onResize(event
);
283 * Returns true if the mouse is currently in the otherScreen window.
285 * @param mouse mouse event
286 * @return true if mouse is currently in the otherScreen window.
288 protected boolean mouseOnOtherScreen(final TMouseEvent mouse
) {
289 if ((mouse
.getY() >= 1)
290 && (mouse
.getY() <= otherScreen
.getHeight())
291 && (mouse
.getX() >= 1)
292 && (mouse
.getX() <= otherScreen
.getWidth())
300 * Handle mouse button presses.
302 * @param mouse mouse button event
305 public void onMouseDown(final TMouseEvent mouse
) {
306 if (mouseOnOtherScreen(mouse
)) {
307 TMouseEvent event
= mouse
.dup();
308 event
.setX(mouse
.getX() - 1);
309 event
.setY(mouse
.getY() - 1);
310 event
.setAbsoluteX(event
.getX());
311 event
.setAbsoluteY(event
.getY());
312 synchronized (eventQueue
) {
313 eventQueue
.add(event
);
315 synchronized (listener
) {
316 listener
.notifyAll();
319 super.onMouseDown(mouse
);
323 * Handle mouse button releases.
325 * @param mouse mouse button release event
328 public void onMouseUp(final TMouseEvent mouse
) {
329 if (mouseOnOtherScreen(mouse
)) {
330 TMouseEvent event
= mouse
.dup();
331 event
.setX(mouse
.getX() - 1);
332 event
.setY(mouse
.getY() - 1);
333 event
.setAbsoluteX(event
.getX());
334 event
.setAbsoluteY(event
.getY());
335 synchronized (eventQueue
) {
336 eventQueue
.add(event
);
338 synchronized (listener
) {
339 listener
.notifyAll();
342 super.onMouseUp(mouse
);
346 * Handle mouse movements.
348 * @param mouse mouse motion event
351 public void onMouseMotion(final TMouseEvent mouse
) {
352 if (mouseOnOtherScreen(mouse
)) {
353 TMouseEvent event
= mouse
.dup();
354 event
.setX(mouse
.getX() - 1);
355 event
.setY(mouse
.getY() - 1);
356 event
.setAbsoluteX(event
.getX());
357 event
.setAbsoluteY(event
.getY());
358 synchronized (eventQueue
) {
359 eventQueue
.add(event
);
361 synchronized (listener
) {
362 listener
.notifyAll();
365 super.onMouseMotion(mouse
);
371 * @param keypress keystroke event
374 public void onKeypress(final TKeypressEvent keypress
) {
375 TKeypressEvent event
= keypress
.dup();
376 synchronized (eventQueue
) {
377 eventQueue
.add(event
);
379 synchronized (listener
) {
380 listener
.notifyAll();
384 // ------------------------------------------------------------------------
385 // TWindow ----------------------------------------------------------------
386 // ------------------------------------------------------------------------
389 * Draw the foreground colors grid.
394 // Sync on other screen, so that we do not draw in the middle of
395 // their screen update.
396 synchronized (drawLock
) {
400 // Draw every cell of the other screen
401 for (int y
= 0; y
< otherScreen
.getHeight(); y
++) {
402 for (int x
= 0; x
< otherScreen
.getWidth(); x
++) {
403 putCharXY(x
+ 1, y
+ 1, otherScreen
.getCharXY(x
, y
));
407 // If their cursor is visible, draw that here too.
408 if (otherScreen
.isCursorVisible()) {
409 setCursorX(otherScreen
.getCursorX() + 1);
410 setCursorY(otherScreen
.getCursorY() + 1);
411 setCursorVisible(true);
413 setCursorVisible(false);
417 // Check if the other application has died. If so, unset hidden
419 if (otherApplication
!= null) {
420 if (otherApplication
.isRunning() == false) {
421 setHiddenMouse(false);
428 * Subclasses should override this method to cleanup resources. This is
429 * called by application.closeWindow().
432 public void onClose() {
433 synchronized (eventQueue
) {
434 eventQueue
.add(new TCommandEvent(cmBackendDisconnect
));
438 // ------------------------------------------------------------------------
439 // Backend ----------------------------------------------------------------
440 // ------------------------------------------------------------------------
443 * Getter for sessionInfo.
445 * @return the SessionInfo
447 public final SessionInfo
getSessionInfo() {
452 * Subclasses must provide an implementation that syncs the logical
453 * screen to the physical device.
455 public void flushScreen() {
456 getApplication().doRepaint();
460 * Check if there are events in the queue.
462 * @return if true, getEvents() has something to return to the application
464 public boolean hasEvents() {
465 synchronized (eventQueue
) {
466 return (eventQueue
.size() > 0);
471 * Subclasses must provide an implementation to get keyboard, mouse, and
472 * screen resize events.
474 * @param queue list to append new events to
476 public void getEvents(List
<TInputEvent
> queue
) {
477 synchronized (eventQueue
) {
478 if (eventQueue
.size() > 0) {
479 synchronized (queue
) {
480 queue
.addAll(eventQueue
);
488 * Subclasses must provide an implementation that closes sockets,
489 * restores console, etc.
491 public void shutdown() {
496 * Set listener to a different Object.
498 * @param listener the new listening object that run() wakes up on new
501 public void setListener(final Object listener
) {
502 this.listener
= listener
;
506 * Reload backend options from System properties.
508 public void reloadOptions() {
512 // ------------------------------------------------------------------------
513 // TWindowBackend ---------------------------------------------------------
514 // ------------------------------------------------------------------------
517 * Set the object to sync to in draw().
519 * @param drawLock the object to synchronize on
521 public void setDrawLock(final Object drawLock
) {
522 this.drawLock
= drawLock
;
526 * Getter for the other application's screen.
530 public Screen
getOtherScreen() {
535 * Set the other screen's application.
537 * @param application the application driving the other screen
539 public void setOtherApplication(final TApplication application
) {
540 this.otherApplication
= application
;