*
* The MIT License (MIT)
*
- * Copyright (C) 2017 Kevin Lamonte
+ * Copyright (C) 2019 Kevin Lamonte
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*/
package jexer.backend;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;
-import jexer.bits.CellAttributes;
+import jexer.TApplication;
+import jexer.TWindow;
+import jexer.event.TCommandEvent;
import jexer.event.TInputEvent;
import jexer.event.TKeypressEvent;
import jexer.event.TMouseEvent;
-import jexer.TApplication;
-import jexer.TWindow;
+import jexer.event.TResizeEvent;
+import static jexer.TCommand.*;
/**
* TWindowBackend uses a window in one TApplication to provide a backend for
*/
public class TWindowBackend extends TWindow implements Backend {
+ // ------------------------------------------------------------------------
+ // Variables --------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* The listening object that run() wakes up on new input.
*/
private List<TInputEvent> eventQueue;
/**
- * The screen to use.
+ * The screen this window is monitoring.
*/
private Screen otherScreen;
/**
- * The mouse X position as seen on the other screen.
- */
- private int otherMouseX = -1;
-
- /**
- * The mouse Y position as seen on the other screen.
+ * The application associated with otherScreen.
*/
- private int otherMouseY = -1;
+ private TApplication otherApplication;
/**
* The session information.
private SessionInfo sessionInfo;
/**
- * Set the object to sync to in draw().
- *
- * @param drawLock the object to synchronize on
+ * OtherScreen provides a hook to notify TWindowBackend of screen size
+ * changes.
*/
- public void setDrawLock(final Object drawLock) {
- this.drawLock = drawLock;
- }
+ private class OtherScreen extends LogicalScreen {
+
+ /**
+ * The TWindowBackend to notify.
+ */
+ private TWindowBackend window;
+
+ /**
+ * Public constructor.
+ */
+ public OtherScreen(final TWindowBackend window) {
+ this.window = window;
+ }
- /**
- * Getter for the other application's screen.
- *
- * @return the Screen
- */
- public Screen getOtherScreen() {
- return otherScreen;
- }
+ /**
+ * Resize the physical screen to match the logical screen dimensions.
+ */
+ @Override
+ public void resizeToScreen() {
+ window.setWidth(getWidth() + 2);
+ window.setHeight(getHeight() + 2);
+ }
- /**
- * Getter for sessionInfo.
- *
- * @return the SessionInfo
- */
- public final SessionInfo getSessionInfo() {
- return sessionInfo;
}
+
+ // ------------------------------------------------------------------------
+ // Constructors -----------------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* Public constructor. Window will be located at (0, 0).
*
super(application, title, width, height);
this.listener = listener;
- eventQueue = new LinkedList<TInputEvent>();
+ eventQueue = new ArrayList<TInputEvent>();
sessionInfo = new TSessionInfo(width, height);
- otherScreen = new LogicalScreen();
+ otherScreen = new OtherScreen(this);
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
/**
super(application, title, width, height, flags);
this.listener = listener;
- eventQueue = new LinkedList<TInputEvent>();
+ eventQueue = new ArrayList<TInputEvent>();
sessionInfo = new TSessionInfo(width, height);
- otherScreen = new LogicalScreen();
+ otherScreen = new OtherScreen(this);
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
/**
super(application, title, x, y, width, height);
this.listener = listener;
- eventQueue = new LinkedList<TInputEvent>();
+ eventQueue = new ArrayList<TInputEvent>();
sessionInfo = new TSessionInfo(width, height);
- otherScreen = new LogicalScreen();
+ otherScreen = new OtherScreen(this);
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
/**
super(application, title, x, y, width, height, flags);
this.listener = listener;
- eventQueue = new LinkedList<TInputEvent>();
+ eventQueue = new ArrayList<TInputEvent>();
sessionInfo = new TSessionInfo(width, height);
- otherScreen = new LogicalScreen();
+ otherScreen = new OtherScreen(this);
otherScreen.setDimensions(width - 2, height - 2);
drawLock = otherScreen;
+ setHiddenMouse(true);
}
- /**
- * Subclasses must provide an implementation that syncs the logical
- * screen to the physical device.
- */
- public void flushScreen() {
- // NOP
- }
+ // ------------------------------------------------------------------------
+ // Event handlers ---------------------------------------------------------
+ // ------------------------------------------------------------------------
/**
- * Subclasses must provide an implementation to get keyboard, mouse, and
- * screen resize events.
+ * Handle window/screen resize events.
*
- * @param queue list to append new events to
- */
- public void getEvents(List<TInputEvent> queue) {
- synchronized (eventQueue) {
- if (eventQueue.size() > 0) {
- synchronized (queue) {
- queue.addAll(eventQueue);
- }
- eventQueue.clear();
- }
- }
- }
-
- /**
- * Subclasses must provide an implementation that closes sockets,
- * restores console, etc.
- */
- public void shutdown() {
- // NOP
- }
-
- /**
- * Set listener to a different Object.
- *
- * @param listener the new listening object that run() wakes up on new
- * input
- */
- public void setListener(final Object listener) {
- this.listener = listener;
- }
-
- /**
- * Draw the foreground colors grid.
+ * @param event resize event
*/
@Override
- public void draw() {
-
- // Sync on other screen, so that we do not draw in the middle of
- // their screen update.
- synchronized (drawLock) {
- // Draw the box
- super.draw();
-
- // Draw every cell of the other screen
- for (int y = 0; y < otherScreen.getHeight(); y++) {
- for (int x = 0; x < otherScreen.getWidth(); x++) {
- putCharXY(x + 1, y + 1, otherScreen.getCharXY(x, y));
+ public void onResize(final TResizeEvent event) {
+ if (event.getType() == TResizeEvent.Type.WIDGET) {
+ int newWidth = event.getWidth() - 2;
+ int newHeight = event.getHeight() - 2;
+ if ((newWidth != otherScreen.getWidth())
+ || (newHeight != otherScreen.getHeight())
+ ) {
+ // I was resized, notify the screen I am watching to match my
+ // new size.
+ synchronized (eventQueue) {
+ eventQueue.add(new TResizeEvent(TResizeEvent.Type.SCREEN,
+ newWidth, newHeight));
+ }
+ synchronized (listener) {
+ listener.notifyAll();
}
}
-
- // If the mouse pointer is over the other window, draw its
- // pointer again here. (Their TApplication drew it, then our
- // TApplication drew it again (undo-ing it), so now we draw it a
- // third time so that it is visible.)
- if ((otherMouseX != -1) && (otherMouseY != -1)) {
- CellAttributes attr = getAttrXY(otherMouseX, otherMouseY);
- attr.setForeColor(attr.getForeColor().invert());
- attr.setBackColor(attr.getBackColor().invert());
- putAttrXY(otherMouseX, otherMouseY, attr, false);
- }
-
- // If their cursor is visible, draw that here too.
- if (otherScreen.isCursorVisible()) {
- setCursorX(otherScreen.getCursorX() + 1);
- setCursorY(otherScreen.getCursorY() + 1);
- setCursorVisible(true);
- } else {
- setCursorVisible(false);
- }
+ return;
+ } else {
+ super.onResize(event);
}
}
- /**
- * Subclasses should override this method to cleanup resources. This is
- * called by application.closeWindow().
- */
- public void onClose() {
- // TODO: send a screen disconnect
- }
-
/**
* Returns true if the mouse is currently in the otherScreen window.
*
event.setY(mouse.getY() - 1);
event.setAbsoluteX(event.getX());
event.setAbsoluteY(event.getY());
- otherMouseX = event.getX() + getX() + 1;
- otherMouseY = event.getY() + getY() + 1;
synchronized (eventQueue) {
eventQueue.add(event);
}
synchronized (listener) {
listener.notifyAll();
}
- } else {
- otherMouseX = -1;
- otherMouseY = -1;
}
super.onMouseMotion(mouse);
}
}
}
+ // ------------------------------------------------------------------------
+ // TWindow ----------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
+ /**
+ * Draw the foreground colors grid.
+ */
+ @Override
+ public void draw() {
+
+ // Sync on other screen, so that we do not draw in the middle of
+ // their screen update.
+ synchronized (drawLock) {
+ // Draw the box
+ super.draw();
+
+ // Draw every cell of the other screen
+ for (int y = 0; y < otherScreen.getHeight(); y++) {
+ for (int x = 0; x < otherScreen.getWidth(); x++) {
+ putCharXY(x + 1, y + 1, otherScreen.getCharXY(x, y));
+ }
+ }
+
+ // If their cursor is visible, draw that here too.
+ if (otherScreen.isCursorVisible()) {
+ setCursorX(otherScreen.getCursorX() + 1);
+ setCursorY(otherScreen.getCursorY() + 1);
+ setCursorVisible(true);
+ } else {
+ setCursorVisible(false);
+ }
+ }
+
+ // Check if the other application has died. If so, unset hidden
+ // mouse.
+ if (otherApplication != null) {
+ if (otherApplication.isRunning() == false) {
+ setHiddenMouse(false);
+ }
+ }
+
+ }
+
+ /**
+ * Subclasses should override this method to cleanup resources. This is
+ * called by application.closeWindow().
+ */
+ @Override
+ public void onClose() {
+ synchronized (eventQueue) {
+ eventQueue.add(new TCommandEvent(cmBackendDisconnect));
+ }
+ }
+
+ // ------------------------------------------------------------------------
+ // Backend ----------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
+ /**
+ * Getter for sessionInfo.
+ *
+ * @return the SessionInfo
+ */
+ public final SessionInfo getSessionInfo() {
+ return sessionInfo;
+ }
+
+ /**
+ * Subclasses must provide an implementation that syncs the logical
+ * screen to the physical device.
+ */
+ public void flushScreen() {
+ getApplication().doRepaint();
+ }
+
+ /**
+ * Check if there are events in the queue.
+ *
+ * @return if true, getEvents() has something to return to the application
+ */
+ public boolean hasEvents() {
+ synchronized (eventQueue) {
+ return (eventQueue.size() > 0);
+ }
+ }
+
+ /**
+ * Subclasses must provide an implementation to get keyboard, mouse, and
+ * screen resize events.
+ *
+ * @param queue list to append new events to
+ */
+ public void getEvents(List<TInputEvent> queue) {
+ synchronized (eventQueue) {
+ if (eventQueue.size() > 0) {
+ synchronized (queue) {
+ queue.addAll(eventQueue);
+ }
+ eventQueue.clear();
+ }
+ }
+ }
+
+ /**
+ * Subclasses must provide an implementation that closes sockets,
+ * restores console, etc.
+ */
+ public void shutdown() {
+ // NOP
+ }
+
+ /**
+ * Set listener to a different Object.
+ *
+ * @param listener the new listening object that run() wakes up on new
+ * input
+ */
+ public void setListener(final Object listener) {
+ this.listener = listener;
+ }
+
+ /**
+ * Reload backend options from System properties.
+ */
+ public void reloadOptions() {
+ // NOP
+ }
+
+ // ------------------------------------------------------------------------
+ // TWindowBackend ---------------------------------------------------------
+ // ------------------------------------------------------------------------
+
+ /**
+ * Set the object to sync to in draw().
+ *
+ * @param drawLock the object to synchronize on
+ */
+ public void setDrawLock(final Object drawLock) {
+ this.drawLock = drawLock;
+ }
+
+ /**
+ * Getter for the other application's screen.
+ *
+ * @return the Screen
+ */
+ public Screen getOtherScreen() {
+ return otherScreen;
+ }
+
+ /**
+ * Set the other screen's application.
+ *
+ * @param application the application driving the other screen
+ */
+ public void setOtherApplication(final TApplication application) {
+ this.otherApplication = application;
+ }
+
}