0.0.5
-- Multiscreen support:
- - cmAbort to cmScreenDisconnected
- - cmScreenConnected
-
- TApplication
- getAllWindows()
- Expose menu management functions (addMenu, getMenu, getAllMenus,
0.0.6
+- Finish up multiscreen support:
+ - cmAbort to cmScreenDisconnected
+ - cmScreenConnected
+ - Handle screen resizes
+
- TSpinner
- TComboBox
- TCalendar
Jexer Work Log
==============
+August 8, 2017
+
+Multiscreen is looking really cool! Demo6 now brings up three
+screens, including one that is inside a TWindow of a different
+application.
+
August 7, 2017
Had trouble sleeping, what with a bunch of imaginative thoughts for
import jexer.backend.Screen;
import jexer.backend.SwingBackend;
import jexer.backend.ECMA48Backend;
+import jexer.backend.TWindowBackend;
import jexer.menu.TMenu;
import jexer.menu.TMenuItem;
import static jexer.TCommand.*;
* @return the Screen
*/
public final Screen getScreen() {
- return backend.getScreen();
+ if (backend instanceof TWindowBackend) {
+ // We are being rendered to a TWindow. We can't use its
+ // getScreen() method because that is how it is rendering to a
+ // hardware backend somewhere. Instead use its getOtherScreen()
+ // method.
+ return ((TWindowBackend) backend).getOtherScreen();
+ } else {
+ return backend.getScreen();
+ }
}
/**
*/
public TApplication(final Backend backend) {
this.backend = backend;
+ backend.setListener(this);
TApplicationImpl();
}
this.shift = shift;
}
+ /**
+ * Create a duplicate instance.
+ *
+ * @return duplicate intance
+ */
+ public TKeypress dup() {
+ TKeypress keypress = new TKeypress(isFunctionKey, keyCode, ch,
+ alt, ctrl, shift);
+ return keypress;
+ }
+
/**
* Comparison check. All fields must match to return true.
*
*/
public final class ECMA48Backend extends GenericBackend {
+ /**
+ * Public constructor will use System.in and System.out and UTF-8
+ * encoding. On non-Windows systems System.in will be put in raw mode;
+ * shutdown() will (blindly!) put System.in in cooked mode.
+ *
+ * @throws UnsupportedEncodingException if an exception is thrown when
+ * creating the InputStreamReader
+ */
+ public ECMA48Backend() throws UnsupportedEncodingException {
+ this(null, null, null);
+ }
+
/**
* Public constructor.
*
this.output.printf("%s%s", mouse(true), xtermMetaSendsEscape(true));
this.output.flush();
+ // Query the screen size
+ sessionInfo.queryWindowSize();
+ setDimensions(sessionInfo.getWindowWidth(),
+ sessionInfo.getWindowHeight());
+
// Hang onto the window size
windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
if (System.getProperty("jexer.ECMA48.rgbColor") != null) {
if (System.getProperty("jexer.ECMA48.rgbColor").equals("true")) {
doRgbColor = true;
+ } else {
+ doRgbColor = false;
}
}
readerThread = new Thread(this);
readerThread.start();
- // Query the screen size
- setDimensions(sessionInfo.getWindowWidth(),
- sessionInfo.getWindowHeight());
-
// Clear the screen
this.output.write(clearAll());
this.output.flush();
this.output.printf("%s%s", mouse(true), xtermMetaSendsEscape(true));
this.output.flush();
+ // Query the screen size
+ sessionInfo.queryWindowSize();
+ setDimensions(sessionInfo.getWindowWidth(),
+ sessionInfo.getWindowHeight());
+
// Hang onto the window size
windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
if (System.getProperty("jexer.ECMA48.rgbColor") != null) {
if (System.getProperty("jexer.ECMA48.rgbColor").equals("true")) {
doRgbColor = true;
+ } else {
+ doRgbColor = false;
}
}
readerThread = new Thread(this);
readerThread.start();
- // Query the screen size
- setDimensions(sessionInfo.getWindowWidth(),
- sessionInfo.getWindowHeight());
-
// Clear the screen
this.output.write(clearAll());
this.output.flush();
synchronized (eventQueue) {
eventQueue.addAll(events);
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
events.clear();
}
eventQueue.addAll(events);
}
events.clear();
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
return attr;
}
+ /**
+ * Get the cell at one location.
+ *
+ * @param x column coordinate. 0 is the left-most column.
+ * @param y row coordinate. 0 is the top-most row.
+ * @return the character + attributes
+ */
+ public Cell getCharXY(final int x, final int y) {
+ Cell cell = new Cell();
+ if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
+ cell.setTo(logical[x][y]);
+ }
+ return cell;
+ }
+
/**
* Set the attributes at one location.
*
cursorVisible = false;
}
+ /**
+ * Get the cursor visibility.
+ *
+ * @return true if the cursor is visible
+ */
+ public boolean isCursorVisible() {
+ return cursorVisible;
+ }
+
+ /**
+ * Get the cursor X position.
+ *
+ * @return the cursor x column position
+ */
+ public int getCursorX() {
+ return cursorX;
+ }
+
+ /**
+ * Get the cursor Y position.
+ *
+ * @return the cursor y row position
+ */
+ public int getCursorY() {
+ return cursorY;
+ }
+
/**
* Set the window title. Default implementation does nothing.
*
*/
public MultiBackend(final Backend backend) {
backends.add(backend);
- multiScreen = new MultiScreen(backend.getScreen());
+ if (backend instanceof TWindowBackend) {
+ multiScreen = new MultiScreen(((TWindowBackend) backend).getOtherScreen());
+ } else {
+ multiScreen = new MultiScreen(backend.getScreen());
+ }
}
/**
*/
public void addBackend(final Backend backend) {
backends.add(backend);
- multiScreen.addScreen(backend.getScreen());
+ if (backend instanceof TWindowBackend) {
+ multiScreen.addScreen(((TWindowBackend) backend).getOtherScreen());
+ } else {
+ multiScreen.addScreen(backend.getScreen());
+ }
}
/**
*/
public void removeBackend(final Backend backend) {
if (backends.size() > 1) {
- multiScreen.removeScreen(backend.getScreen());
+ if (backend instanceof TWindowBackend) {
+ multiScreen.removeScreen(((TWindowBackend) backend).getOtherScreen());
+ } else {
+ multiScreen.removeScreen(backend.getScreen());
+ }
backends.remove(backend);
}
}
*
* @return the SessionInfo
*/
- public final SessionInfo getSessionInfo() {
+ public SessionInfo getSessionInfo() {
return backends.get(0).getSessionInfo();
}
*
* @return the Screen
*/
- public final Screen getScreen() {
+ public Screen getScreen() {
return multiScreen;
}
return screens.get(0).getAttrXY(x, y);
}
+ /**
+ * Get the cell at one location.
+ *
+ * @param x column coordinate. 0 is the left-most column.
+ * @param y row coordinate. 0 is the top-most row.
+ * @return the character + attributes
+ */
+ public Cell getCharXY(final int x, final int y) {
+ return screens.get(0).getCharXY(x, y);
+ }
+
/**
* Set the attributes at one location.
*
}
}
+ /**
+ * Get the cursor visibility.
+ *
+ * @return true if the cursor is visible
+ */
+ public boolean isCursorVisible() {
+ return screens.get(0).isCursorVisible();
+ }
+
+ /**
+ * Get the cursor X position.
+ *
+ * @return the cursor x column position
+ */
+ public int getCursorX() {
+ return screens.get(0).getCursorX();
+ }
+
+ /**
+ * Get the cursor Y position.
+ *
+ * @return the cursor y row position
+ */
+ public int getCursorY() {
+ return screens.get(0).getCursorY();
+ }
+
/**
* Set the window title.
*
*/
public CellAttributes getAttrXY(final int x, final int y);
+ /**
+ * Get the cell at one location.
+ *
+ * @param x column coordinate. 0 is the left-most column.
+ * @param y row coordinate. 0 is the top-most row.
+ * @return the character + attributes
+ */
+ public Cell getCharXY(final int x, final int y);
+
/**
* Set the attributes at one location.
*
*/
public void hideCursor();
+ /**
+ * Get the cursor visibility.
+ *
+ * @return true if the cursor is visible
+ */
+ public boolean isCursorVisible();
+
+ /**
+ * Get the cursor X position.
+ *
+ * @return the cursor x column position
+ */
+ public int getCursorX();
+
+ /**
+ * Get the cursor Y position.
+ *
+ * @return the cursor y row position
+ */
+ public int getCursorY();
+
/**
* Set the window title.
*
*/
public final class SwingBackend extends GenericBackend {
+ /**
+ * Public constructor. The window will be 80x25 with font size 20 pts.
+ */
+ public SwingBackend() {
+ this(null, 80, 25, 20);
+ }
+
/**
* Public constructor. The window will be 80x25 with font size 20 pts.
*
this(listener, 80, 25, 20);
}
+ /**
+ * Public constructor will spawn a new JFrame with font size 20 pts.
+ *
+ * @param windowWidth the number of text columns to start with
+ * @param windowHeight the number of text rows to start with
+ */
+ public SwingBackend(final int windowWidth, final int windowHeight) {
+ this(null, windowWidth, windowHeight, 20);
+ }
+
+ /**
+ * Public constructor will spawn a new JFrame.
+ *
+ * @param windowWidth the number of text columns to start with
+ * @param windowHeight the number of text rows to start with
+ * @param fontSize the size in points. Good values to pick are: 16, 20,
+ * 22, and 24.
+ */
+ public SwingBackend(final int windowWidth, final int windowHeight,
+ final int fontSize) {
+
+ this(null, windowWidth, windowHeight, fontSize);
+ }
+
/**
* Public constructor will spawn a new JFrame.
*
this.textHeight = textHeight;
}
+ /**
+ * Public constructor.
+ *
+ * @param swing the Swing JFrame or JComponent
+ * @param textWidth the width of a cell in pixels
+ * @param textHeight the height of a cell in pixels
+ * @param width the number of columns
+ * @param height the number of rows
+ */
+ public SwingSessionInfo(final SwingComponent swing, final int textWidth,
+ final int textHeight, final int width, final int height) {
+
+ this.swing = swing;
+ this.textWidth = textWidth;
+ this.textHeight = textHeight;
+ this.windowWidth = width;
+ this.windowHeight = height;
+ }
+
/**
* Re-query the text window size.
*/
// monospace.
textHeight = fm.getMaxAscent() + maxDescent - leading;
- // TODO: is this still necessary?
if (gotTerminus == true) {
textHeight++;
}
// Pull the system property for triple buffering.
if (System.getProperty("jexer.Swing.tripleBuffer") != null) {
- if (System.getProperty("jexer.Swing.tripleBuffer").
- equals("false")) {
-
+ if (System.getProperty("jexer.Swing.tripleBuffer").equals("true")) {
+ SwingComponent.tripleBuffer = true;
+ } else {
SwingComponent.tripleBuffer = false;
}
}
SwingTerminal.this.sessionInfo =
new SwingSessionInfo(SwingTerminal.this.swing,
SwingTerminal.this.textWidth,
- SwingTerminal.this.textHeight);
+ SwingTerminal.this.textHeight,
+ windowWidth, windowHeight);
SwingTerminal.this.setDimensions(sessionInfo.getWindowWidth(),
sessionInfo.getWindowHeight());
}
/**
- * Public constructor creates a new JFrame to render to.
+ * Public constructor renders to an existing JComponent.
*
* @param component the Swing component to render to
* @param windowWidth the number of text columns to start with
synchronized (eventQueue) {
eventQueue.add(new TKeypressEvent(keypress));
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
synchronized (eventQueue) {
eventQueue.add(new TCommandEvent(cmAbort));
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
sessionInfo.getWindowWidth(), sessionInfo.getWindowHeight());
eventQueue.add(windowResize);
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
synchronized (eventQueue) {
eventQueue.add(mouseEvent);
}
- synchronized (listener) {
- listener.notifyAll();
+ if (listener != null) {
+ synchronized (listener) {
+ listener.notifyAll();
+ }
}
}
// NOP
}
+ /**
+ * Public constructor.
+ */
+ public TSessionInfo() {
+ this(80, 24);
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param width the number of columns
+ * @param height the number of rows
+ */
+ public TSessionInfo(final int width, final int height) {
+ this.windowWidth = width;
+ this.windowHeight = height;
+ }
+
}
--- /dev/null
+/*
+ * Jexer - Java Text User Interface
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (C) 2017 Kevin Lamonte
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.backend;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import jexer.bits.CellAttributes;
+import jexer.event.TInputEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import jexer.TApplication;
+import jexer.TWindow;
+
+/**
+ * TWindowBackend uses a window in one TApplication to provide a backend for
+ * another TApplication.
+ *
+ * Note that TWindow has its own getScreen() and setTitle() functions.
+ * Clients in TWindowBackend's application won't be able to use it to get at
+ * the other application's screen. getOtherScreen() has been provided.
+ */
+public class TWindowBackend extends TWindow implements Backend {
+
+ /**
+ * The listening object that run() wakes up on new input.
+ */
+ private Object listener;
+
+ /**
+ * The object to sync on in draw(). This is normally otherScreen, but it
+ * could also be a MultiScreen.
+ */
+ private Object drawLock;
+
+ /**
+ * The event queue, filled up by a thread reading on input.
+ */
+ private List<TInputEvent> eventQueue;
+
+ /**
+ * The screen to use.
+ */
+ 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.
+ */
+ private int otherMouseY = -1;
+
+ /**
+ * The session information.
+ */
+ private SessionInfo sessionInfo;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Getter for sessionInfo.
+ *
+ * @return the SessionInfo
+ */
+ public final SessionInfo getSessionInfo() {
+ return sessionInfo;
+ }
+
+ /**
+ * Public constructor. Window will be located at (0, 0).
+ *
+ * @param listener the object this backend needs to wake up when new
+ * input comes in
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param width width of window
+ * @param height height of window
+ */
+ public TWindowBackend(final Object listener,
+ final TApplication application, final String title,
+ final int width, final int height) {
+
+ super(application, title, width, height);
+
+ this.listener = listener;
+ eventQueue = new LinkedList<TInputEvent>();
+ sessionInfo = new TSessionInfo(width, height);
+ otherScreen = new LogicalScreen();
+ otherScreen.setDimensions(width - 2, height - 2);
+ drawLock = otherScreen;
+ }
+
+ /**
+ * Public constructor. Window will be located at (0, 0).
+ *
+ * @param listener the object this backend needs to wake up when new
+ * input comes in
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param width width of window
+ * @param height height of window
+ * @param flags bitmask of RESIZABLE, CENTERED, or MODAL
+ */
+ public TWindowBackend(final Object listener,
+ final TApplication application, final String title,
+ final int width, final int height, final int flags) {
+
+ super(application, title, width, height, flags);
+
+ this.listener = listener;
+ eventQueue = new LinkedList<TInputEvent>();
+ sessionInfo = new TSessionInfo(width, height);
+ otherScreen = new LogicalScreen();
+ otherScreen.setDimensions(width - 2, height - 2);
+ drawLock = otherScreen;
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param listener the object this backend needs to wake up when new
+ * input comes in
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of window
+ * @param height height of window
+ */
+ public TWindowBackend(final Object listener,
+ final TApplication application, final String title,
+ final int x, final int y, final int width, final int height) {
+
+ super(application, title, x, y, width, height);
+
+ this.listener = listener;
+ eventQueue = new LinkedList<TInputEvent>();
+ sessionInfo = new TSessionInfo(width, height);
+ otherScreen = new LogicalScreen();
+ otherScreen.setDimensions(width - 2, height - 2);
+ drawLock = otherScreen;
+ }
+
+ /**
+ * Public constructor.
+ *
+ * @param listener the object this backend needs to wake up when new
+ * input comes in
+ * @param application TApplication that manages this window
+ * @param title window title, will be centered along the top border
+ * @param x column relative to parent
+ * @param y row relative to parent
+ * @param width width of window
+ * @param height height of window
+ * @param flags mask of RESIZABLE, CENTERED, or MODAL
+ */
+ public TWindowBackend(final Object listener,
+ final TApplication application, final String title,
+ final int x, final int y, final int width, final int height,
+ final int flags) {
+
+ super(application, title, x, y, width, height, flags);
+
+ this.listener = listener;
+ eventQueue = new LinkedList<TInputEvent>();
+ sessionInfo = new TSessionInfo(width, height);
+ otherScreen = new LogicalScreen();
+ otherScreen.setDimensions(width - 2, height - 2);
+ drawLock = otherScreen;
+ }
+
+ /**
+ * Subclasses must provide an implementation that syncs the logical
+ * screen to the physical device.
+ */
+ public void flushScreen() {
+ // NOP
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 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);
+ }
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @param mouse mouse event
+ * @return true if mouse is currently in the otherScreen window.
+ */
+ protected boolean mouseOnOtherScreen(final TMouseEvent mouse) {
+ if ((mouse.getY() >= 1)
+ && (mouse.getY() <= otherScreen.getHeight())
+ && (mouse.getX() >= 1)
+ && (mouse.getX() <= otherScreen.getWidth())
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handle mouse button presses.
+ *
+ * @param mouse mouse button event
+ */
+ @Override
+ public void onMouseDown(final TMouseEvent mouse) {
+ if (mouseOnOtherScreen(mouse)) {
+ TMouseEvent event = mouse.dup();
+ event.setX(mouse.getX() - 1);
+ event.setY(mouse.getY() - 1);
+ event.setAbsoluteX(event.getX());
+ event.setAbsoluteY(event.getY());
+ synchronized (eventQueue) {
+ eventQueue.add(event);
+ }
+ synchronized (listener) {
+ listener.notifyAll();
+ }
+ }
+ super.onMouseDown(mouse);
+ }
+
+ /**
+ * Handle mouse button releases.
+ *
+ * @param mouse mouse button release event
+ */
+ @Override
+ public void onMouseUp(final TMouseEvent mouse) {
+ if (mouseOnOtherScreen(mouse)) {
+ TMouseEvent event = mouse.dup();
+ event.setX(mouse.getX() - 1);
+ event.setY(mouse.getY() - 1);
+ event.setAbsoluteX(event.getX());
+ event.setAbsoluteY(event.getY());
+ synchronized (eventQueue) {
+ eventQueue.add(event);
+ }
+ synchronized (listener) {
+ listener.notifyAll();
+ }
+ }
+ super.onMouseUp(mouse);
+ }
+
+ /**
+ * Handle mouse movements.
+ *
+ * @param mouse mouse motion event
+ */
+ @Override
+ public void onMouseMotion(final TMouseEvent mouse) {
+ if (mouseOnOtherScreen(mouse)) {
+ TMouseEvent event = mouse.dup();
+ event.setX(mouse.getX() - 1);
+ event.setY(mouse.getY() - 1);
+ event.setAbsoluteX(event.getX());
+ event.setAbsoluteY(event.getY());
+ otherMouseX = event.getX() + 1;
+ otherMouseY = event.getY() + 2;
+ synchronized (eventQueue) {
+ eventQueue.add(event);
+ }
+ synchronized (listener) {
+ listener.notifyAll();
+ }
+ } else {
+ otherMouseX = -1;
+ otherMouseY = -1;
+ }
+ super.onMouseMotion(mouse);
+ }
+
+ /**
+ * Handle keystrokes.
+ *
+ * @param keypress keystroke event
+ */
+ @Override
+ public void onKeypress(final TKeypressEvent keypress) {
+ TKeypressEvent event = keypress.dup();
+ synchronized (eventQueue) {
+ eventQueue.add(event);
+ }
+ synchronized (listener) {
+ listener.notifyAll();
+ }
+ }
+
+}
*/
private void addApplications() {
- // Spin up the frame
- JFrame frame = new JFrame();
- frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
- frame.addWindowListener(this);
-
- // Create two panels with two applications, each with a different
- // font size.
+ /*
+ * In this demo we will create two swing panels with two
+ * independently running applications, each with a different font
+ * size.
+ */
+
+ /*
+ * First we create a panel to put it on. We need this to pass to
+ * SwingBackend's constructor, so that it knows not to create a new
+ * frame.
+ */
JPanel app1Panel = new JPanel();
- SwingBackend app1Backend = new SwingBackend(app1Panel, new Object(),
+
+ /*
+ * Next, we create the Swing backend. The "listener" (second
+ * argument, set to null) is what the backend wakes up on every event
+ * received. Typically this is the TApplication. TApplication sets
+ * it in its constructor, so we can pass null here and be fine.
+ */
+ SwingBackend app1Backend = new SwingBackend(app1Panel, null,
80, 25, 16);
+ // Now that we have the backend, construct the TApplication.
app1 = new DemoApplication(app1Backend);
- app1Backend.setListener(app1);
+ /*
+ * The second panel is the same sequence, except that we also change
+ * the font from the default Terminus to JVM monospaced.
+ */
JPanel app2Panel = new JPanel();
- SwingBackend app2Backend = new SwingBackend(app2Panel, new Object(),
+ SwingBackend app2Backend = new SwingBackend(app2Panel, null,
80, 25, 18);
app2 = new DemoApplication(app2Backend);
Font font = new Font(Font.MONOSPACED, Font.PLAIN, 18);
app2Backend.setFont(font);
- app2Backend.setListener(app2);
+
+ /*
+ * Now that the applications are ready, spin them off on their
+ * threads.
+ */
(new Thread(app1)).start();
(new Thread(app2)).start();
+ /*
+ * The rest of this is standard Swing. Set up a frame, a split pane,
+ * put each of the panels on it, and make it visible.
+ */
+ JFrame frame = new JFrame();
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ frame.addWindowListener(this);
JSplitPane mainPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
app1Panel, app2Panel);
mainPane.setOneTouchExpandable(true);
*/
package jexer.demos;
+import jexer.TApplication;
import jexer.backend.*;
/**
*/
public static void main(final String [] args) {
try {
+
+ /*
+ * In this demo we will create two applications spanning three
+ * screens. One of the applications will have both an ECMA48
+ * screen and a Swing screen, with all I/O mirrored between them.
+ * The second application will have a Swing screen containing a
+ * window showing the first application, also mirroring I/O
+ * between the window and the other two screens.
+ */
+
/*
- * Spin up a Swing backend to match the ECMA48 backend on
- * System.in/out.
+ * We create the first screen and use it to establish a
+ * MultiBackend.
*/
- ECMA48Backend ecmaBackend = new ECMA48Backend(new Object(), null,
- null);
+ ECMA48Backend ecmaBackend = new ECMA48Backend();
MultiBackend multiBackend = new MultiBackend(ecmaBackend);
+
+ /*
+ * Now we create the first application (a standard demo).
+ */
DemoApplication demoApp = new DemoApplication(multiBackend);
+
+ /*
+ * We will need the width and height of the ECMA48 screen, so get
+ * the Screen reference now.
+ */
Screen multiScreen = multiBackend.getScreen();
- SwingBackend swingBackend = new SwingBackend(new Object(),
- multiScreen.getWidth(), multiScreen.getHeight(), 16);
+ /*
+ * Now we create the second screen (backend) for the first
+ * application. It will be the same size as the ECMA48 screen,
+ * with a font size of 16 points.
+ */
+ SwingBackend swingBackend = new SwingBackend(multiScreen.getWidth(),
+ multiScreen.getHeight(), 16);
+
+ /*
+ * Add this screen to the MultiBackend, and at this point we have
+ * one demo application spanning two physical screens.
+ */
multiBackend.addBackend(swingBackend);
- multiBackend.setListener(demoApp);
+ /*
+ * Time for the second application. This one will have a single
+ * window mirroring the contents of the first application. Let's
+ * make it a little larger than the first application's
+ * width/height.
+ */
+ int width = multiScreen.getWidth();
+ int height = multiScreen.getHeight();
+
+ /*
+ * Make a new Swing window for the second application.
+ */
+ SwingBackend monitorBackend = new SwingBackend(width + 5,
+ height + 5, 16);
+
+ /*
+ * Setup the second application, give it the basic file and
+ * window menus.
+ */
+ TApplication monitor = new TApplication(monitorBackend);
+ monitor.addFileMenu();
+ monitor.addWindowMenu();
+
+ /*
+ * Now add the third screen to the first application. We want to
+ * change the object it locks on in its draw() method to the
+ * MultiScreen, that will dramatically reduce (not totally
+ * eliminate) screen tearing/artifacts.
+ */
+ TWindowBackend windowBackend = new TWindowBackend(demoApp,
+ monitor, "Monitor Window", width + 2, height + 2);
+ windowBackend.setDrawLock(multiScreen);
+ multiBackend.addBackend(windowBackend);
+
+ /*
+ * Three screens, two applications: spin them up!
+ */
(new Thread(demoApp)).start();
+ (new Thread(monitor)).start();
} catch (Exception e) {
e.printStackTrace();
}
alt, ctrl, shift);
}
+ /**
+ * Create a duplicate instance.
+ *
+ * @return duplicate intance
+ */
+ public TKeypressEvent dup() {
+ TKeypressEvent keypress = new TKeypressEvent(key.dup());
+ return keypress;
+ }
+
/**
* Comparison check. All fields must match to return true.
*
return absoluteX;
}
+ /**
+ * Set absoluteX.
+ *
+ * @param absoluteX the new value
+ */
+ public void setAbsoluteX(final int absoluteX) {
+ this.absoluteX = absoluteX;
+ }
+
/**
* Mouse Y - absolute screen coordinate.
*/
return absoluteY;
}
+ /**
+ * Set absoluteY.
+ *
+ * @param absoluteY the new value
+ */
+ public void setAbsoluteY(final int absoluteY) {
+ this.absoluteY = absoluteY;
+ }
+
/**
* Mouse button 1 (left button).
*/
this.mouseWheelDown = mouseWheelDown;
}
+ /**
+ * Create a duplicate instance.
+ *
+ * @return duplicate intance
+ */
+ public TMouseEvent dup() {
+ TMouseEvent mouse = new TMouseEvent(type, x, y, absoluteX, absoluteY,
+ mouse1, mouse2, mouse3, mouseWheelUp, mouseWheelDown);
+ return mouse;
+ }
+
/**
* Make human-readable description of this TMouseEvent.
*