2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2017 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 mouse X position as seen on the other screen.
79 private int otherMouseX
= -1;
82 * The mouse Y position as seen on the other screen.
84 private int otherMouseY
= -1;
87 * The session information.
89 private SessionInfo sessionInfo
;
91 // ------------------------------------------------------------------------
92 // Constructors -----------------------------------------------------------
93 // ------------------------------------------------------------------------
96 * Public constructor. Window will be located at (0, 0).
98 * @param listener the object this backend needs to wake up when new
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
105 public TWindowBackend(final Object listener
,
106 final TApplication application
, final String title
,
107 final int width
, final int height
) {
109 super(application
, title
, width
, height
);
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
;
120 * Public constructor. Window will be located at (0, 0).
122 * @param listener the object this backend needs to wake up when new
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
130 public TWindowBackend(final Object listener
,
131 final TApplication application
, final String title
,
132 final int width
, final int height
, final int flags
) {
134 super(application
, title
, width
, height
, flags
);
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
;
145 * Public constructor.
147 * @param listener the object this backend needs to wake up when new
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
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
) {
160 super(application
, title
, x
, y
, width
, height
);
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
;
171 * Public constructor.
173 * @param listener the object this backend needs to wake up when new
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
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
,
188 super(application
, title
, x
, y
, width
, height
, flags
);
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
;
198 // ------------------------------------------------------------------------
199 // Event handlers ---------------------------------------------------------
200 // ------------------------------------------------------------------------
203 * Returns true if the mouse is currently in the otherScreen window.
205 * @param mouse mouse event
206 * @return true if mouse is currently in the otherScreen window.
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())
220 * Handle mouse button presses.
222 * @param mouse mouse button event
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
);
235 synchronized (listener
) {
236 listener
.notifyAll();
239 super.onMouseDown(mouse
);
243 * Handle mouse button releases.
245 * @param mouse mouse button release event
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
);
258 synchronized (listener
) {
259 listener
.notifyAll();
262 super.onMouseUp(mouse
);
266 * Handle mouse movements.
268 * @param mouse mouse motion event
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
);
283 synchronized (listener
) {
284 listener
.notifyAll();
290 super.onMouseMotion(mouse
);
296 * @param keypress keystroke event
299 public void onKeypress(final TKeypressEvent keypress
) {
300 TKeypressEvent event
= keypress
.dup();
301 synchronized (eventQueue
) {
302 eventQueue
.add(event
);
304 synchronized (listener
) {
305 listener
.notifyAll();
309 // ------------------------------------------------------------------------
310 // TWindow ----------------------------------------------------------------
311 // ------------------------------------------------------------------------
314 * Draw the foreground colors grid.
319 // Sync on other screen, so that we do not draw in the middle of
320 // their screen update.
321 synchronized (drawLock
) {
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
));
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);
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);
349 setCursorVisible(false);
355 * Subclasses should override this method to cleanup resources. This is
356 * called by application.closeWindow().
359 public void onClose() {
360 // TODO: send a screen disconnect
363 // ------------------------------------------------------------------------
364 // Backend ----------------------------------------------------------------
365 // ------------------------------------------------------------------------
368 * Getter for sessionInfo.
370 * @return the SessionInfo
372 public final SessionInfo
getSessionInfo() {
377 * Subclasses must provide an implementation that syncs the logical
378 * screen to the physical device.
380 public void flushScreen() {
381 getApplication().doRepaint();
385 * Check if there are events in the queue.
387 * @return if true, getEvents() has something to return to the application
389 public boolean hasEvents() {
390 synchronized (eventQueue
) {
391 return (eventQueue
.size() > 0);
396 * Subclasses must provide an implementation to get keyboard, mouse, and
397 * screen resize events.
399 * @param queue list to append new events to
401 public void getEvents(List
<TInputEvent
> queue
) {
402 synchronized (eventQueue
) {
403 if (eventQueue
.size() > 0) {
404 synchronized (queue
) {
405 queue
.addAll(eventQueue
);
413 * Subclasses must provide an implementation that closes sockets,
414 * restores console, etc.
416 public void shutdown() {
421 * Set listener to a different Object.
423 * @param listener the new listening object that run() wakes up on new
426 public void setListener(final Object listener
) {
427 this.listener
= listener
;
430 // ------------------------------------------------------------------------
431 // TWindowBackend ---------------------------------------------------------
432 // ------------------------------------------------------------------------
435 * Set the object to sync to in draw().
437 * @param drawLock the object to synchronize on
439 public void setDrawLock(final Object drawLock
) {
440 this.drawLock
= drawLock
;
444 * Getter for the other application's screen.
448 public Screen
getOtherScreen() {