retrofit from GJexer
[nikiroo-utils.git] / src / jexer / backend / TWindowBackend.java
CommitLineData
3e074355
KL
1/*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
a69ed767 6 * Copyright (C) 2019 Kevin Lamonte
3e074355
KL
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
d36057df
KL
51 // ------------------------------------------------------------------------
52 // Variables --------------------------------------------------------------
53 // ------------------------------------------------------------------------
54
3e074355
KL
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
3e074355
KL
76 /**
77 * The session information.
78 */
79 private SessionInfo sessionInfo;
80
d36057df
KL
81 // ------------------------------------------------------------------------
82 // Constructors -----------------------------------------------------------
83 // ------------------------------------------------------------------------
3e074355
KL
84
85 /**
86 * Public constructor. Window will be located at (0, 0).
87 *
88 * @param listener the object this backend needs to wake up when new
89 * input comes in
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
94 */
95 public TWindowBackend(final Object listener,
96 final TApplication application, final String title,
97 final int width, final int height) {
98
99 super(application, title, width, height);
100
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;
00691e80 107 setHiddenMouse(true);
3e074355
KL
108 }
109
110 /**
111 * Public constructor. Window will be located at (0, 0).
112 *
113 * @param listener the object this backend needs to wake up when new
114 * input comes in
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
120 */
121 public TWindowBackend(final Object listener,
122 final TApplication application, final String title,
123 final int width, final int height, final int flags) {
124
125 super(application, title, width, height, flags);
126
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;
00691e80 133 setHiddenMouse(true);
3e074355
KL
134 }
135
136 /**
137 * Public constructor.
138 *
139 * @param listener the object this backend needs to wake up when new
140 * input comes in
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
147 */
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) {
151
152 super(application, title, x, y, width, height);
153
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;
00691e80 160 setHiddenMouse(true);
3e074355
KL
161 }
162
163 /**
164 * Public constructor.
165 *
166 * @param listener the object this backend needs to wake up when new
167 * input comes in
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
175 */
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,
179 final int flags) {
180
181 super(application, title, x, y, width, height, flags);
182
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;
00691e80 189 setHiddenMouse(true);
3e074355
KL
190 }
191
d36057df
KL
192 // ------------------------------------------------------------------------
193 // Event handlers ---------------------------------------------------------
194 // ------------------------------------------------------------------------
3e074355
KL
195
196 /**
197 * Returns true if the mouse is currently in the otherScreen window.
198 *
199 * @param mouse mouse event
200 * @return true if mouse is currently in the otherScreen window.
201 */
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())
207 ) {
208 return true;
209 }
210 return false;
211 }
212
213 /**
214 * Handle mouse button presses.
215 *
216 * @param mouse mouse button event
217 */
218 @Override
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);
228 }
229 synchronized (listener) {
230 listener.notifyAll();
231 }
232 }
233 super.onMouseDown(mouse);
234 }
235
236 /**
237 * Handle mouse button releases.
238 *
239 * @param mouse mouse button release event
240 */
241 @Override
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);
251 }
252 synchronized (listener) {
253 listener.notifyAll();
254 }
255 }
256 super.onMouseUp(mouse);
257 }
258
259 /**
260 * Handle mouse movements.
261 *
262 * @param mouse mouse motion event
263 */
264 @Override
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());
3e074355
KL
272 synchronized (eventQueue) {
273 eventQueue.add(event);
274 }
275 synchronized (listener) {
276 listener.notifyAll();
277 }
3e074355
KL
278 }
279 super.onMouseMotion(mouse);
280 }
281
282 /**
283 * Handle keystrokes.
284 *
285 * @param keypress keystroke event
286 */
287 @Override
288 public void onKeypress(final TKeypressEvent keypress) {
289 TKeypressEvent event = keypress.dup();
290 synchronized (eventQueue) {
291 eventQueue.add(event);
292 }
293 synchronized (listener) {
294 listener.notifyAll();
295 }
296 }
297
d36057df
KL
298 // ------------------------------------------------------------------------
299 // TWindow ----------------------------------------------------------------
300 // ------------------------------------------------------------------------
301
302 /**
303 * Draw the foreground colors grid.
304 */
305 @Override
306 public void draw() {
307
308 // Sync on other screen, so that we do not draw in the middle of
309 // their screen update.
310 synchronized (drawLock) {
311 // Draw the box
312 super.draw();
313
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));
318 }
319 }
320
d36057df
KL
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);
326 } else {
327 setCursorVisible(false);
328 }
329 }
330 }
331
332 /**
333 * Subclasses should override this method to cleanup resources. This is
334 * called by application.closeWindow().
335 */
336 @Override
337 public void onClose() {
338 // TODO: send a screen disconnect
339 }
340
341 // ------------------------------------------------------------------------
342 // Backend ----------------------------------------------------------------
343 // ------------------------------------------------------------------------
344
345 /**
346 * Getter for sessionInfo.
347 *
348 * @return the SessionInfo
349 */
350 public final SessionInfo getSessionInfo() {
351 return sessionInfo;
352 }
353
354 /**
355 * Subclasses must provide an implementation that syncs the logical
356 * screen to the physical device.
357 */
358 public void flushScreen() {
359 getApplication().doRepaint();
360 }
361
362 /**
363 * Check if there are events in the queue.
364 *
365 * @return if true, getEvents() has something to return to the application
366 */
367 public boolean hasEvents() {
368 synchronized (eventQueue) {
369 return (eventQueue.size() > 0);
370 }
371 }
372
373 /**
374 * Subclasses must provide an implementation to get keyboard, mouse, and
375 * screen resize events.
376 *
377 * @param queue list to append new events to
378 */
379 public void getEvents(List<TInputEvent> queue) {
380 synchronized (eventQueue) {
381 if (eventQueue.size() > 0) {
382 synchronized (queue) {
383 queue.addAll(eventQueue);
384 }
385 eventQueue.clear();
386 }
387 }
388 }
389
390 /**
391 * Subclasses must provide an implementation that closes sockets,
392 * restores console, etc.
393 */
394 public void shutdown() {
395 // NOP
396 }
397
398 /**
399 * Set listener to a different Object.
400 *
401 * @param listener the new listening object that run() wakes up on new
402 * input
403 */
404 public void setListener(final Object listener) {
405 this.listener = listener;
406 }
407
e23ea538
KL
408 /**
409 * Reload backend options from System properties.
410 */
411 public void reloadOptions() {
412 // NOP
413 }
414
d36057df
KL
415 // ------------------------------------------------------------------------
416 // TWindowBackend ---------------------------------------------------------
417 // ------------------------------------------------------------------------
418
419 /**
420 * Set the object to sync to in draw().
421 *
422 * @param drawLock the object to synchronize on
423 */
424 public void setDrawLock(final Object drawLock) {
425 this.drawLock = drawLock;
426 }
427
428 /**
429 * Getter for the other application's screen.
430 *
431 * @return the Screen
432 */
433 public Screen getOtherScreen() {
434 return otherScreen;
435 }
436
3e074355 437}