MultiBackend and MultiScreen
authorKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 7 Aug 2017 23:36:42 +0000 (19:36 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 7 Aug 2017 23:36:42 +0000 (19:36 -0400)
12 files changed:
README.md
src/jexer/backend/Backend.java
src/jexer/backend/ECMA48Backend.java
src/jexer/backend/ECMA48Terminal.java
src/jexer/backend/GenericBackend.java
src/jexer/backend/MultiBackend.java [new file with mode: 0644]
src/jexer/backend/MultiScreen.java [new file with mode: 0644]
src/jexer/backend/SwingBackend.java
src/jexer/backend/SwingComponent.java
src/jexer/backend/TerminalReader.java
src/jexer/demos/Demo5.java
src/jexer/demos/Demo6.java [new file with mode: 0644]

index b4114f32a3186cd50900758933aa8c14fedb8957..b84a1f635d2f877f4fa6dbf30133f1b6c79b6bfc 100644 (file)
--- a/README.md
+++ b/README.md
@@ -149,6 +149,13 @@ follows:
   * 'java -cp jexer.jar jexer.demos.Demo4' .  This demonstrates hidden
     windows and a custom TDesktop.
 
+  * 'java -cp jexer.jar jexer.demos.Demo5' .  This demonstrates two
+    demo applications using different fonts in the same Swing frame.
+
+  * 'java -cp jexer.jar jexer.demos.Demo6' .  This demonstrates one
+    application performing I/O to two screens: an xterm screen and a
+    Swing screen.
+
 
 
 More Screenshots
index 2793ab00fa0eef73740b055cdd87f04dc8f92ace..d1863288c54dabd8dfe4f0bb5b59f9356397db8b 100644 (file)
@@ -81,4 +81,12 @@ public interface Backend {
      */
     public void setTitle(final String title);
 
+    /**
+     * 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);
+
 }
index ee7a10374f0332ec05139a5f4bf42cf7f51580ea..db390fb4eb0f37a8b57ad00cad2e58e2a1f8fe6f 100644 (file)
@@ -33,9 +33,6 @@ import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.io.Reader;
 import java.io.UnsupportedEncodingException;
-import java.util.List;
-
-import jexer.event.TInputEvent;
 
 /**
  * This class uses an xterm/ANSI X3.64/ECMA-48 type terminal to provide a
@@ -43,11 +40,6 @@ import jexer.event.TInputEvent;
  */
 public final class ECMA48Backend extends GenericBackend {
 
-    /**
-     * Input events are processed by this Terminal.
-     */
-    private ECMA48Terminal terminal;
-
     /**
      * Public constructor.
      *
@@ -70,10 +62,10 @@ public final class ECMA48Backend extends GenericBackend {
         terminal = new ECMA48Terminal(listener, input, output);
 
         // Keep the terminal's sessionInfo so that TApplication can see it
-        sessionInfo = terminal.getSessionInfo();
+        sessionInfo = ((ECMA48Terminal) terminal).getSessionInfo();
 
         // ECMA48Terminal is the screen too
-        screen = terminal;
+        screen = (ECMA48Terminal) terminal;
     }
 
     /**
@@ -99,10 +91,10 @@ public final class ECMA48Backend extends GenericBackend {
             setRawMode);
 
         // Keep the terminal's sessionInfo so that TApplication can see it
-        sessionInfo = terminal.getSessionInfo();
+        sessionInfo = ((ECMA48Terminal) terminal).getSessionInfo();
 
         // ECMA48Terminal is the screen too
-        screen = terminal;
+        screen = (ECMA48Terminal) terminal;
     }
 
     /**
@@ -122,42 +114,5 @@ public final class ECMA48Backend extends GenericBackend {
         this(listener, input, reader, writer, false);
     }
 
-    /**
-     * Sync the logical screen to the physical device.
-     */
-    @Override
-    public void flushScreen() {
-        screen.flushPhysical();
-    }
-
-    /**
-     * Get keyboard, mouse, and screen resize events.
-     *
-     * @param queue list to append new events to
-     */
-    @Override
-    public void getEvents(final List<TInputEvent> queue) {
-        if (terminal.hasEvents()) {
-            terminal.getEvents(queue);
-        }
-    }
-
-    /**
-     * Close the I/O, restore the console, etc.
-     */
-    @Override
-    public void shutdown() {
-        terminal.closeTerminal();
-    }
-
-    /**
-     * Set the window title.
-     *
-     * @param title the new title
-     */
-    @Override
-    public void setTitle(final String title) {
-        screen.setTitle(title);
-    }
 
 }
index 13704151df3037585b4d4d0f442593444a880834..6303f4fc8d0f5240c1287d42bd9876e04cc0222c 100644 (file)
@@ -195,6 +195,16 @@ public final class ECMA48Terminal extends LogicalScreen
      */
     private Object listener;
 
+    /**
+     * 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;
+    }
+
     /**
      * Get the output writer.
      *
index d96f7a93f468afc404a61888faf62bfcef337d7a..bf27e947607f1c7bea073b4edac0689f300e6067 100644 (file)
@@ -68,30 +68,52 @@ public abstract class GenericBackend implements Backend {
     }
 
     /**
-     * Subclasses must provide an implementation that syncs the logical
-     * screen to the physical device.
+     * Sync the logical screen to the physical device.
      */
-    public abstract void flushScreen();
+    public void flushScreen() {
+        screen.flushPhysical();
+    }
+
+    /**
+     * Input events are processed by this Terminal.
+     */
+    protected TerminalReader terminal;
 
     /**
-     * Subclasses must provide an implementation to get keyboard, mouse, and
-     * screen resize events.
+     * Get keyboard, mouse, and screen resize events.
      *
      * @param queue list to append new events to
      */
-    public abstract void getEvents(List<TInputEvent> queue);
+    public void getEvents(final List<TInputEvent> queue) {
+        if (terminal.hasEvents()) {
+            terminal.getEvents(queue);
+        }
+    }
 
     /**
-     * Subclasses must provide an implementation that closes sockets,
-     * restores console, etc.
+     * Close the I/O, restore the console, etc.
      */
-    public abstract void shutdown();
+    public void shutdown() {
+        terminal.closeTerminal();
+    }
 
     /**
-     * Subclasses must provide an implementation that sets the window title.
+     * Set the window title.
      *
      * @param title the new title
      */
-    public abstract void setTitle(final String title);
+    public void setTitle(final String title) {
+        screen.setTitle(title);
+    }
+
+    /**
+     * 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) {
+        terminal.setListener(listener);
+    }
 
 }
diff --git a/src/jexer/backend/MultiBackend.java b/src/jexer/backend/MultiBackend.java
new file mode 100644 (file)
index 0000000..a397c65
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * 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.event.TInputEvent;
+
+/**
+ * MultiBackend mirrors its I/O to several backends.
+ */
+public class MultiBackend implements Backend {
+
+    /**
+     * The screen to use.
+     */
+    private MultiScreen multiScreen;
+
+    /**
+     * The list of backends to use.
+     */
+    private List<Backend> backends = new LinkedList<Backend>();
+
+    /**
+     * Public constructor requires one backend.  Note that this backend's
+     * screen will be replaced with a MultiScreen.
+     *
+     * @param backend the backend to add
+     */
+    public MultiBackend(final Backend backend) {
+        backends.add(backend);
+        multiScreen = new MultiScreen(backend.getScreen());
+    }
+
+    /**
+     * Add a backend to the list.
+     *
+     * @param backend the backend to add
+     */
+    public void addBackend(final Backend backend) {
+        backends.add(backend);
+        multiScreen.addScreen(backend.getScreen());
+    }
+
+    /**
+     * Remove a backend from the list.
+     *
+     * @param backend the backend to remove
+     */
+    public void removeBackend(final Backend backend) {
+        if (backends.size() > 1) {
+            multiScreen.removeScreen(backend.getScreen());
+            backends.remove(backend);
+        }
+    }
+
+    /**
+     * Getter for sessionInfo.
+     *
+     * @return the SessionInfo
+     */
+    public final SessionInfo getSessionInfo() {
+        return backends.get(0).getSessionInfo();
+    }
+
+    /**
+     * Getter for screen.
+     *
+     * @return the Screen
+     */
+    public final Screen getScreen() {
+        return multiScreen;
+    }
+
+    /**
+     * Subclasses must provide an implementation that syncs the logical
+     * screen to the physical device.
+     */
+    public void flushScreen() {
+        for (Backend backend: backends) {
+            backend.flushScreen();
+        }
+    }
+
+    /**
+     * 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) {
+        for (Backend backend: backends) {
+            backend.getEvents(queue);
+        }
+    }
+
+    /**
+     * Subclasses must provide an implementation that closes sockets,
+     * restores console, etc.
+     */
+    public void shutdown() {
+        for (Backend backend: backends) {
+            backend.shutdown();
+        }
+    }
+
+    /**
+     * Subclasses must provide an implementation that sets the window title.
+     *
+     * @param title the new title
+     */
+    public void setTitle(final String title) {
+        for (Backend backend: backends) {
+            backend.setTitle(title);
+        }
+    }
+
+    /**
+     * 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) {
+        for (Backend backend: backends) {
+            backend.setListener(listener);
+        }
+    }
+
+}
diff --git a/src/jexer/backend/MultiScreen.java b/src/jexer/backend/MultiScreen.java
new file mode 100644 (file)
index 0000000..f7b61dd
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * 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.Cell;
+import jexer.bits.CellAttributes;
+
+/**
+ * MultiScreen mirrors its I/O to several screens.
+ */
+public class MultiScreen implements Screen {
+
+    /**
+     * The list of screens to use.
+     */
+    private List<Screen> screens = new LinkedList<Screen>();
+
+    /**
+     * Public constructor requires one screen.
+     *
+     * @param screen the screen to add
+     */
+    public MultiScreen(final Screen screen) {
+        screens.add(screen);
+    }
+
+    /**
+     * Add a screen to the list.
+     *
+     * @param screen the screen to add
+     */
+    public void addScreen(final Screen screen) {
+        screens.add(screen);
+    }
+
+    /**
+     * Remove a screen from the list.
+     *
+     * @param screen the screen to remove
+     */
+    public void removeScreen(final Screen screen) {
+        if (screens.size() > 1) {
+            screens.remove(screen);
+        }
+    }
+
+    /**
+     * Set drawing offset for x.
+     *
+     * @param offsetX new drawing offset
+     */
+    public void setOffsetX(final int offsetX) {
+        for (Screen screen: screens) {
+            screen.setOffsetX(offsetX);
+        }
+    }
+
+    /**
+     * Set drawing offset for y.
+     *
+     * @param offsetY new drawing offset
+     */
+    public void setOffsetY(final int offsetY) {
+        for (Screen screen: screens) {
+            screen.setOffsetY(offsetY);
+        }
+    }
+
+    /**
+     * Get right drawing clipping boundary.
+     *
+     * @return drawing boundary
+     */
+    public int getClipRight() {
+        return screens.get(0).getClipRight();
+    }
+
+    /**
+     * Set right drawing clipping boundary.
+     *
+     * @param clipRight new boundary
+     */
+    public void setClipRight(final int clipRight) {
+        for (Screen screen: screens) {
+            screen.setClipRight(clipRight);
+        }
+    }
+
+    /**
+     * Get bottom drawing clipping boundary.
+     *
+     * @return drawing boundary
+     */
+    public int getClipBottom() {
+        return screens.get(0).getClipBottom();
+    }
+
+    /**
+     * Set bottom drawing clipping boundary.
+     *
+     * @param clipBottom new boundary
+     */
+    public void setClipBottom(final int clipBottom) {
+        for (Screen screen: screens) {
+            screen.setClipBottom(clipBottom);
+        }
+    }
+
+    /**
+     * Get left drawing clipping boundary.
+     *
+     * @return drawing boundary
+     */
+    public int getClipLeft() {
+        return screens.get(0).getClipLeft();
+    }
+
+    /**
+     * Set left drawing clipping boundary.
+     *
+     * @param clipLeft new boundary
+     */
+    public void setClipLeft(final int clipLeft) {
+        for (Screen screen: screens) {
+            screen.setClipLeft(clipLeft);
+        }
+    }
+
+    /**
+     * Get top drawing clipping boundary.
+     *
+     * @return drawing boundary
+     */
+    public int getClipTop() {
+        return screens.get(0).getClipTop();
+    }
+
+    /**
+     * Set top drawing clipping boundary.
+     *
+     * @param clipTop new boundary
+     */
+    public void setClipTop(final int clipTop) {
+        for (Screen screen: screens) {
+            screen.setClipTop(clipTop);
+        }
+    }
+
+    /**
+     * Get dirty flag.
+     *
+     * @return if true, the logical screen is not in sync with the physical
+     * screen
+     */
+    public boolean isDirty() {
+        return screens.get(0).isDirty();
+    }
+
+    /**
+     * Get the attributes at one location.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @return attributes at (x, y)
+     */
+    public CellAttributes getAttrXY(final int x, final int y) {
+        return screens.get(0).getAttrXY(x, y);
+    }
+
+    /**
+     * Set the attributes at one location.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param attr attributes to use (bold, foreColor, backColor)
+     */
+    public void putAttrXY(final int x, final int y,
+        final CellAttributes attr) {
+
+        for (Screen screen: screens) {
+            screen.putAttrXY(x, y, attr);
+        }
+    }
+
+    /**
+     * Set the attributes at one location.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param attr attributes to use (bold, foreColor, backColor)
+     * @param clip if true, honor clipping/offset
+     */
+    public void putAttrXY(final int x, final int y,
+        final CellAttributes attr, final boolean clip) {
+
+        for (Screen screen: screens) {
+            screen.putAttrXY(x, y, attr, clip);
+        }
+    }
+
+    /**
+     * Fill the entire screen with one character with attributes.
+     *
+     * @param ch character to draw
+     * @param attr attributes to use (bold, foreColor, backColor)
+     */
+    public void putAll(final char ch, final CellAttributes attr) {
+        for (Screen screen: screens) {
+            screen.putAll(ch, attr);
+        }
+    }
+
+    /**
+     * Render one character with attributes.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param ch character + attributes to draw
+     */
+    public void putCharXY(final int x, final int y, final Cell ch) {
+        for (Screen screen: screens) {
+            screen.putCharXY(x, y, ch);
+        }
+    }
+
+    /**
+     * Render one character with attributes.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param ch character to draw
+     * @param attr attributes to use (bold, foreColor, backColor)
+     */
+    public void putCharXY(final int x, final int y, final char ch,
+        final CellAttributes attr) {
+
+        for (Screen screen: screens) {
+            screen.putCharXY(x, y, ch, attr);
+        }
+    }
+
+    /**
+     * Render one character without changing the underlying attributes.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param ch character to draw
+     */
+    public void putCharXY(final int x, final int y, final char ch) {
+        for (Screen screen: screens) {
+            screen.putCharXY(x, y, ch);
+        }
+    }
+
+    /**
+     * Render a string.  Does not wrap if the string exceeds the line.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param str string to draw
+     * @param attr attributes to use (bold, foreColor, backColor)
+     */
+    public void putStringXY(final int x, final int y, final String str,
+        final CellAttributes attr) {
+
+        for (Screen screen: screens) {
+            screen.putStringXY(x, y, str, attr);
+        }
+    }
+
+    /**
+     * Render a string without changing the underlying attribute.  Does not
+     * wrap if the string exceeds the line.
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param str string to draw
+     */
+    public void putStringXY(final int x, final int y, final String str) {
+        for (Screen screen: screens) {
+            screen.putStringXY(x, y, str);
+        }
+    }
+
+    /**
+     * Draw a vertical line from (x, y) to (x, y + n).
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param n number of characters to draw
+     * @param ch character to draw
+     * @param attr attributes to use (bold, foreColor, backColor)
+     */
+    public void vLineXY(final int x, final int y, final int n,
+        final char ch, final CellAttributes attr) {
+
+        for (Screen screen: screens) {
+            screen.vLineXY(x, y, n, ch, attr);
+        }
+    }
+
+    /**
+     * Draw a horizontal line from (x, y) to (x + n, y).
+     *
+     * @param x column coordinate.  0 is the left-most column.
+     * @param y row coordinate.  0 is the top-most row.
+     * @param n number of characters to draw
+     * @param ch character to draw
+     * @param attr attributes to use (bold, foreColor, backColor)
+     */
+    public void hLineXY(final int x, final int y, final int n,
+        final char ch, final CellAttributes attr) {
+
+        for (Screen screen: screens) {
+            screen.hLineXY(x, y, n, ch, attr);
+        }
+    }
+
+    /**
+     * Change the width.  Everything on-screen will be destroyed and must be
+     * redrawn.
+     *
+     * @param width new screen width
+     */
+    public void setWidth(final int width) {
+        for (Screen screen: screens) {
+            screen.setWidth(width);
+        }
+    }
+
+    /**
+     * Change the height.  Everything on-screen will be destroyed and must be
+     * redrawn.
+     *
+     * @param height new screen height
+     */
+    public void setHeight(final int height) {
+        for (Screen screen: screens) {
+            screen.setHeight(height);
+        }
+    }
+
+    /**
+     * Change the width and height.  Everything on-screen will be destroyed
+     * and must be redrawn.
+     *
+     * @param width new screen width
+     * @param height new screen height
+     */
+    public void setDimensions(final int width, final int height) {
+        for (Screen screen: screens) {
+            screen.setDimensions(width, height);
+        }
+    }
+
+    /**
+     * Get the height.
+     *
+     * @return current screen height
+     */
+    public int getHeight() {
+        return screens.get(0).getHeight();
+    }
+
+    /**
+     * Get the width.
+     *
+     * @return current screen width
+     */
+    public int getWidth() {
+        return screens.get(0).getWidth();
+    }
+
+    /**
+     * Reset screen to not-bold, white-on-black.  Also flushes the offset and
+     * clip variables.
+     */
+    public void reset() {
+        for (Screen screen: screens) {
+            screen.reset();
+        }
+    }
+
+    /**
+     * Flush the offset and clip variables.
+     */
+    public void resetClipping() {
+        for (Screen screen: screens) {
+            screen.resetClipping();
+        }
+    }
+
+    /**
+     * Clear the logical screen.
+     */
+    public void clear() {
+        for (Screen screen: screens) {
+            screen.clear();
+        }
+    }
+
+    /**
+     * Draw a box with a border and empty background.
+     *
+     * @param left left column of box.  0 is the left-most row.
+     * @param top top row of the box.  0 is the top-most row.
+     * @param right right column of box
+     * @param bottom bottom row of the box
+     * @param border attributes to use for the border
+     * @param background attributes to use for the background
+     */
+    public void drawBox(final int left, final int top,
+        final int right, final int bottom,
+        final CellAttributes border, final CellAttributes background) {
+
+        for (Screen screen: screens) {
+            screen.drawBox(left, top, right, bottom, border, background);
+        }
+    }
+
+    /**
+     * Draw a box with a border and empty background.
+     *
+     * @param left left column of box.  0 is the left-most row.
+     * @param top top row of the box.  0 is the top-most row.
+     * @param right right column of box
+     * @param bottom bottom row of the box
+     * @param border attributes to use for the border
+     * @param background attributes to use for the background
+     * @param borderType if 1, draw a single-line border; if 2, draw a
+     * double-line border; if 3, draw double-line top/bottom edges and
+     * single-line left/right edges (like Qmodem)
+     * @param shadow if true, draw a "shadow" on the box
+     */
+    public void drawBox(final int left, final int top,
+        final int right, final int bottom,
+        final CellAttributes border, final CellAttributes background,
+        final int borderType, final boolean shadow) {
+
+        for (Screen screen: screens) {
+            screen.drawBox(left, top, right, bottom, border, background,
+                borderType, shadow);
+        }
+    }
+
+    /**
+     * Draw a box shadow.
+     *
+     * @param left left column of box.  0 is the left-most row.
+     * @param top top row of the box.  0 is the top-most row.
+     * @param right right column of box
+     * @param bottom bottom row of the box
+     */
+    public void drawBoxShadow(final int left, final int top,
+        final int right, final int bottom) {
+
+        for (Screen screen: screens) {
+            screen.drawBoxShadow(left, top, right, bottom);
+        }
+    }
+
+    /**
+     * Classes must provide an implementation to push the logical screen to
+     * the physical device.
+     */
+    public void flushPhysical() {
+        for (Screen screen: screens) {
+            screen.flushPhysical();
+        }
+    }
+
+    /**
+     * Put the cursor at (x,y).
+     *
+     * @param visible if true, the cursor should be visible
+     * @param x column coordinate to put the cursor on
+     * @param y row coordinate to put the cursor on
+     */
+    public void putCursor(final boolean visible, final int x, final int y) {
+        for (Screen screen: screens) {
+            screen.putCursor(visible, x, y);
+        }
+    }
+
+    /**
+     * Hide the cursor.
+     */
+    public void hideCursor() {
+        for (Screen screen: screens) {
+            screen.hideCursor();
+        }
+    }
+
+    /**
+     * Set the window title.
+     *
+     * @param title the new title
+     */
+    public void setTitle(final String title) {
+        for (Screen screen: screens) {
+            screen.setTitle(title);
+        }
+    }
+
+}
index 876015e4d125874591dcf5da5bc32cfd694dd207..fc77968154e2802dac6aea4edbc09af6bb3c38c9 100644 (file)
  */
 package jexer.backend;
 
-import java.util.List;
+import java.awt.Font;
 import javax.swing.JComponent;
 
-import jexer.event.TInputEvent;
-
 /**
  * This class uses standard Swing calls to handle screen, keyboard, and mouse
  * I/O.
  */
 public final class SwingBackend extends GenericBackend {
 
-    /**
-     * Input events are processed by this Terminal.
-     */
-    private SwingTerminal terminal;
-
     /**
      * Public constructor.  The window will be 80x25 with font size 20 pts.
      *
@@ -72,10 +65,10 @@ public final class SwingBackend extends GenericBackend {
             listener);
 
         // Hang onto the session info
-        this.sessionInfo = terminal.getSessionInfo();
+        this.sessionInfo = ((SwingTerminal) terminal).getSessionInfo();
 
         // SwingTerminal is the screen too
-        screen = terminal;
+        screen = (SwingTerminal) terminal;
     }
 
     /**
@@ -97,58 +90,19 @@ public final class SwingBackend extends GenericBackend {
             fontSize, listener);
 
         // Hang onto the session info
-        this.sessionInfo = terminal.getSessionInfo();
+        this.sessionInfo = ((SwingTerminal) terminal).getSessionInfo();
 
         // SwingTerminal is the screen too
-        screen = terminal;
-    }
-
-    /**
-     * Sync the logical screen to the physical device.
-     */
-    @Override
-    public void flushScreen() {
-        screen.flushPhysical();
-    }
-
-    /**
-     * Get keyboard, mouse, and screen resize events.
-     *
-     * @param queue list to append new events to
-     */
-    @Override
-    public void getEvents(final List<TInputEvent> queue) {
-        if (terminal.hasEvents()) {
-            terminal.getEvents(queue);
-        }
-    }
-
-    /**
-     * Close the I/O, restore the console, etc.
-     */
-    @Override
-    public void shutdown() {
-        terminal.closeTerminal();
-    }
-
-    /**
-     * Set the window title.
-     *
-     * @param title the new title
-     */
-    @Override
-    public void setTitle(final String title) {
-        screen.setTitle(title);
+        screen = (SwingTerminal) terminal;
     }
 
     /**
-     * Set listener to a different Object.
+     * Set to a new font, and resize the screen to match its dimensions.
      *
-     * @param listener the new listening object that run() wakes up on new
-     * input
+     * @param font the new font
      */
-    public void setListener(final Object listener) {
-        terminal.setListener(listener);
+    public void setFont(final Font font) {
+        ((SwingTerminal) terminal).setFont(font);
     }
 
 }
index 84e147263476050530bfed776a948d3a9fcb8e76..48e4a44fa6b5b9f2db1765ec8f700f78137d38b3 100644 (file)
@@ -55,7 +55,7 @@ class SwingComponent {
     /**
      * If true, use triple buffering when drawing to a JFrame.
      */
-    public static boolean tripleBuffer = false;
+    public static boolean tripleBuffer = true;
 
     /**
      * Get the BufferStrategy object needed for triple-buffering.
index 48e4043f92b34bab0b208bd3e6453e26d5024360..69007321a100adb74ea987abaddabd6db6395834 100644 (file)
@@ -58,4 +58,12 @@ public interface TerminalReader {
      */
     public void closeTerminal();
 
+    /**
+     * 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);
+
 }
index 88d6a7cdf7247b07cf92a4c4c53481ade856806d..8763aa1f5193d47acf3d5e676fa70631e245a33d 100644 (file)
@@ -28,6 +28,7 @@
  */
 package jexer.demos;
 
+import java.awt.Font;
 import java.awt.event.WindowEvent;
 import java.awt.event.WindowListener;
 import javax.swing.JFrame;
@@ -143,7 +144,9 @@ public class Demo5 implements WindowListener {
         SwingBackend app2Backend = new SwingBackend(app2Panel, new Object(),
             80, 25, 18);
         app2 = new DemoApplication(app2Backend);
-        app1Backend.setListener(app2);
+        Font font = new Font(Font.MONOSPACED, Font.PLAIN, 18);
+        app2Backend.setFont(font);
+        app2Backend.setListener(app2);
         (new Thread(app1)).start();
         (new Thread(app2)).start();
 
diff --git a/src/jexer/demos/Demo6.java b/src/jexer/demos/Demo6.java
new file mode 100644 (file)
index 0000000..fba67a0
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.demos;
+
+import jexer.backend.*;
+
+/**
+ * This class shows off the use of MultiBackend and MultiScreen.
+ */
+public class Demo6 {
+
+    /**
+     * Main entry point.
+     *
+     * @param args Command line arguments
+     */
+    public static void main(final String [] args) {
+        try {
+            /*
+             * Spin up a Swing backend to match the ECMA48 backend on
+             * System.in/out.
+             */
+            ECMA48Backend ecmaBackend = new ECMA48Backend(new Object(), null,
+                null);
+            MultiBackend multiBackend = new MultiBackend(ecmaBackend);
+            DemoApplication demoApp = new DemoApplication(multiBackend);
+            Screen multiScreen = multiBackend.getScreen();
+
+            SwingBackend swingBackend = new SwingBackend(new Object(),
+                multiScreen.getWidth(), multiScreen.getHeight(), 16);
+            multiBackend.addBackend(swingBackend);
+            multiBackend.setListener(demoApp);
+
+            (new Thread(demoApp)).start();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}