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
.TApplication
;
42 * TWindowBackend uses a window in one TApplication to provide a backend for
43 * another TApplication.
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.
49 public class TWindowBackend
extends TWindow
implements Backend
{
51 // ------------------------------------------------------------------------
52 // Variables --------------------------------------------------------------
53 // ------------------------------------------------------------------------
56 * The listening object that run() wakes up on new input.
58 private Object listener
;
61 * The object to sync on in draw(). This is normally otherScreen, but it
62 * could also be a MultiScreen.
64 private Object drawLock
;
67 * The event queue, filled up by a thread reading on input.
69 private List
<TInputEvent
> eventQueue
;
74 private Screen otherScreen
;
77 * The session information.
79 private SessionInfo sessionInfo
;
81 // ------------------------------------------------------------------------
82 // Constructors -----------------------------------------------------------
83 // ------------------------------------------------------------------------
86 * Public constructor. Window will be located at (0, 0).
88 * @param listener the object this backend needs to wake up when new
90 * @param application TApplication that manages this window
91 * @param title window title, will be centered along the top border
92 * @param width width of window
93 * @param height height of window
95 public TWindowBackend(final Object listener
,
96 final TApplication application
, final String title
,
97 final int width
, final int height
) {
99 super(application
, title
, width
, height
);
101 this.listener
= listener
;
102 eventQueue
= new LinkedList
<TInputEvent
>();
103 sessionInfo
= new TSessionInfo(width
, height
);
104 otherScreen
= new LogicalScreen();
105 otherScreen
.setDimensions(width
- 2, height
- 2);
106 drawLock
= otherScreen
;
107 setHiddenMouse(true);
111 * Public constructor. Window will be located at (0, 0).
113 * @param listener the object this backend needs to wake up when new
115 * @param application TApplication that manages this window
116 * @param title window title, will be centered along the top border
117 * @param width width of window
118 * @param height height of window
119 * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
121 public TWindowBackend(final Object listener
,
122 final TApplication application
, final String title
,
123 final int width
, final int height
, final int flags
) {
125 super(application
, title
, width
, height
, flags
);
127 this.listener
= listener
;
128 eventQueue
= new LinkedList
<TInputEvent
>();
129 sessionInfo
= new TSessionInfo(width
, height
);
130 otherScreen
= new LogicalScreen();
131 otherScreen
.setDimensions(width
- 2, height
- 2);
132 drawLock
= otherScreen
;
133 setHiddenMouse(true);
137 * Public constructor.
139 * @param listener the object this backend needs to wake up when new
141 * @param application TApplication that manages this window
142 * @param title window title, will be centered along the top border
143 * @param x column relative to parent
144 * @param y row relative to parent
145 * @param width width of window
146 * @param height height of window
148 public TWindowBackend(final Object listener
,
149 final TApplication application
, final String title
,
150 final int x
, final int y
, final int width
, final int height
) {
152 super(application
, title
, x
, y
, width
, height
);
154 this.listener
= listener
;
155 eventQueue
= new LinkedList
<TInputEvent
>();
156 sessionInfo
= new TSessionInfo(width
, height
);
157 otherScreen
= new LogicalScreen();
158 otherScreen
.setDimensions(width
- 2, height
- 2);
159 drawLock
= otherScreen
;
160 setHiddenMouse(true);
164 * Public constructor.
166 * @param listener the object this backend needs to wake up when new
168 * @param application TApplication that manages this window
169 * @param title window title, will be centered along the top border
170 * @param x column relative to parent
171 * @param y row relative to parent
172 * @param width width of window
173 * @param height height of window
174 * @param flags mask of RESIZABLE, CENTERED, or MODAL
176 public TWindowBackend(final Object listener
,
177 final TApplication application
, final String title
,
178 final int x
, final int y
, final int width
, final int height
,
181 super(application
, title
, x
, y
, width
, height
, flags
);
183 this.listener
= listener
;
184 eventQueue
= new LinkedList
<TInputEvent
>();
185 sessionInfo
= new TSessionInfo(width
, height
);
186 otherScreen
= new LogicalScreen();
187 otherScreen
.setDimensions(width
- 2, height
- 2);
188 drawLock
= otherScreen
;
189 setHiddenMouse(true);
192 // ------------------------------------------------------------------------
193 // Event handlers ---------------------------------------------------------
194 // ------------------------------------------------------------------------
197 * Returns true if the mouse is currently in the otherScreen window.
199 * @param mouse mouse event
200 * @return true if mouse is currently in the otherScreen window.
202 protected boolean mouseOnOtherScreen(final TMouseEvent mouse
) {
203 if ((mouse
.getY() >= 1)
204 && (mouse
.getY() <= otherScreen
.getHeight())
205 && (mouse
.getX() >= 1)
206 && (mouse
.getX() <= otherScreen
.getWidth())
214 * Handle mouse button presses.
216 * @param mouse mouse button event
219 public void onMouseDown(final TMouseEvent mouse
) {
220 if (mouseOnOtherScreen(mouse
)) {
221 TMouseEvent event
= mouse
.dup();
222 event
.setX(mouse
.getX() - 1);
223 event
.setY(mouse
.getY() - 1);
224 event
.setAbsoluteX(event
.getX());
225 event
.setAbsoluteY(event
.getY());
226 synchronized (eventQueue
) {
227 eventQueue
.add(event
);
229 synchronized (listener
) {
230 listener
.notifyAll();
233 super.onMouseDown(mouse
);
237 * Handle mouse button releases.
239 * @param mouse mouse button release event
242 public void onMouseUp(final TMouseEvent mouse
) {
243 if (mouseOnOtherScreen(mouse
)) {
244 TMouseEvent event
= mouse
.dup();
245 event
.setX(mouse
.getX() - 1);
246 event
.setY(mouse
.getY() - 1);
247 event
.setAbsoluteX(event
.getX());
248 event
.setAbsoluteY(event
.getY());
249 synchronized (eventQueue
) {
250 eventQueue
.add(event
);
252 synchronized (listener
) {
253 listener
.notifyAll();
256 super.onMouseUp(mouse
);
260 * Handle mouse movements.
262 * @param mouse mouse motion event
265 public void onMouseMotion(final TMouseEvent mouse
) {
266 if (mouseOnOtherScreen(mouse
)) {
267 TMouseEvent event
= mouse
.dup();
268 event
.setX(mouse
.getX() - 1);
269 event
.setY(mouse
.getY() - 1);
270 event
.setAbsoluteX(event
.getX());
271 event
.setAbsoluteY(event
.getY());
272 synchronized (eventQueue
) {
273 eventQueue
.add(event
);
275 synchronized (listener
) {
276 listener
.notifyAll();
279 super.onMouseMotion(mouse
);
285 * @param keypress keystroke event
288 public void onKeypress(final TKeypressEvent keypress
) {
289 TKeypressEvent event
= keypress
.dup();
290 synchronized (eventQueue
) {
291 eventQueue
.add(event
);
293 synchronized (listener
) {
294 listener
.notifyAll();
298 // ------------------------------------------------------------------------
299 // TWindow ----------------------------------------------------------------
300 // ------------------------------------------------------------------------
303 * Draw the foreground colors grid.
308 // Sync on other screen, so that we do not draw in the middle of
309 // their screen update.
310 synchronized (drawLock
) {
314 // Draw every cell of the other screen
315 for (int y
= 0; y
< otherScreen
.getHeight(); y
++) {
316 for (int x
= 0; x
< otherScreen
.getWidth(); x
++) {
317 putCharXY(x
+ 1, y
+ 1, otherScreen
.getCharXY(x
, y
));
321 // If their cursor is visible, draw that here too.
322 if (otherScreen
.isCursorVisible()) {
323 setCursorX(otherScreen
.getCursorX() + 1);
324 setCursorY(otherScreen
.getCursorY() + 1);
325 setCursorVisible(true);
327 setCursorVisible(false);
333 * Subclasses should override this method to cleanup resources. This is
334 * called by application.closeWindow().
337 public void onClose() {
338 // TODO: send a screen disconnect
341 // ------------------------------------------------------------------------
342 // Backend ----------------------------------------------------------------
343 // ------------------------------------------------------------------------
346 * Getter for sessionInfo.
348 * @return the SessionInfo
350 public final SessionInfo
getSessionInfo() {
355 * Subclasses must provide an implementation that syncs the logical
356 * screen to the physical device.
358 public void flushScreen() {
359 getApplication().doRepaint();
363 * Check if there are events in the queue.
365 * @return if true, getEvents() has something to return to the application
367 public boolean hasEvents() {
368 synchronized (eventQueue
) {
369 return (eventQueue
.size() > 0);
374 * Subclasses must provide an implementation to get keyboard, mouse, and
375 * screen resize events.
377 * @param queue list to append new events to
379 public void getEvents(List
<TInputEvent
> queue
) {
380 synchronized (eventQueue
) {
381 if (eventQueue
.size() > 0) {
382 synchronized (queue
) {
383 queue
.addAll(eventQueue
);
391 * Subclasses must provide an implementation that closes sockets,
392 * restores console, etc.
394 public void shutdown() {
399 * Set listener to a different Object.
401 * @param listener the new listening object that run() wakes up on new
404 public void setListener(final Object listener
) {
405 this.listener
= listener
;
409 * Reload backend options from System properties.
411 public void reloadOptions() {
415 // ------------------------------------------------------------------------
416 // TWindowBackend ---------------------------------------------------------
417 // ------------------------------------------------------------------------
420 * Set the object to sync to in draw().
422 * @param drawLock the object to synchronize on
424 public void setDrawLock(final Object drawLock
) {
425 this.drawLock
= drawLock
;
429 * Getter for the other application's screen.
433 public Screen
getOtherScreen() {