*
* 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.Cell;
import jexer.bits.CellAttributes;
+import jexer.bits.Clipboard;
/**
* MultiScreen mirrors its I/O to several screens.
*/
public class MultiScreen implements Screen {
+ // ------------------------------------------------------------------------
+ // Variables --------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
/**
* The list of screens to use.
*/
- private List<Screen> screens = new LinkedList<Screen>();
+ private List<Screen> screens = new ArrayList<Screen>();
+
+ // ------------------------------------------------------------------------
+ // Constructors -----------------------------------------------------------
+ // ------------------------------------------------------------------------
/**
* Public constructor requires one 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);
- }
- }
+ // ------------------------------------------------------------------------
+ // Screen -----------------------------------------------------------------
+ // ------------------------------------------------------------------------
/**
* Set drawing offset for x.
* @return drawing boundary
*/
public int getClipRight() {
- return screens.get(0).getClipRight();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipRight();
+ }
+ return 0;
}
/**
* @return drawing boundary
*/
public int getClipBottom() {
- return screens.get(0).getClipBottom();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipBottom();
+ }
+ return 0;
}
/**
* @return drawing boundary
*/
public int getClipLeft() {
- return screens.get(0).getClipLeft();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipLeft();
+ }
+ return 0;
}
/**
* @return drawing boundary
*/
public int getClipTop() {
- return screens.get(0).getClipTop();
+ if (screens.size() > 0) {
+ return screens.get(0).getClipTop();
+ }
+ return 0;
}
/**
* screen
*/
public boolean isDirty() {
- return screens.get(0).isDirty();
+ for (Screen screen: screens) {
+ if (screen.isDirty()) {
+ return true;
+ }
+ }
+ return false;
}
/**
* @return attributes at (x, y)
*/
public CellAttributes getAttrXY(final int x, final int y) {
- return screens.get(0).getAttrXY(x, y);
+ if (screens.size() > 0) {
+ return screens.get(0).getAttrXY(x, y);
+ }
+ return new CellAttributes();
+ }
+
+ /**
+ * 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) {
+ if (screens.size() > 0) {
+ return screens.get(0).getCharXY(x, y);
+ }
+ return new Cell();
}
/**
* @param ch character to draw
* @param attr attributes to use (bold, foreColor, backColor)
*/
- public void putAll(final char ch, final CellAttributes attr) {
+ public void putAll(final int ch, final CellAttributes attr) {
for (Screen screen: screens) {
screen.putAll(ch, attr);
}
* @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,
+ public void putCharXY(final int x, final int y, final int ch,
final CellAttributes attr) {
for (Screen screen: screens) {
* @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) {
+ public void putCharXY(final int x, final int y, final int ch) {
for (Screen screen: screens) {
screen.putCharXY(x, y, ch);
}
* @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) {
+ final int ch, final CellAttributes attr) {
for (Screen screen: screens) {
screen.vLineXY(x, y, n, ch, attr);
* @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) {
+ final int ch, final CellAttributes attr) {
for (Screen screen: screens) {
screen.hLineXY(x, y, n, ch, attr);
*/
public void setDimensions(final int width, final int height) {
for (Screen screen: screens) {
- screen.setDimensions(width, height);
+ // Do not blindly call setDimension() on every screen. Instead
+ // call it only on those screens that do not already have the
+ // requested dimension. With this very small check, we have the
+ // ability for ANY screen in the MultiBackend to resize ALL of
+ // the screens.
+ if ((screen.getWidth() != width)
+ || (screen.getHeight() != height)
+ ) {
+ screen.setDimensions(width, height);
+ } else {
+ // The screen that didn't change is probably the one that
+ // prompted the resize. Force it to repaint.
+ screen.clearPhysical();
+ }
}
}
* @return current screen height
*/
public int getHeight() {
- return screens.get(0).getHeight();
+ // Return the smallest height of the screens.
+ int height = 25;
+ if (screens.size() > 0) {
+ height = screens.get(0).getHeight();
+ }
+ for (Screen screen: screens) {
+ if (screen.getHeight() < height) {
+ height = screen.getHeight();
+ }
+ }
+ return height;
}
/**
* @return current screen width
*/
public int getWidth() {
- return screens.get(0).getWidth();
+ // Return the smallest width of the screens.
+ int width = 80;
+ if (screens.size() > 0) {
+ width = screens.get(0).getWidth();
+ }
+ for (Screen screen: screens) {
+ if (screen.getWidth() < width) {
+ width = screen.getWidth();
+ }
+ }
+ return width;
}
/**
}
}
+ /**
+ * Clear the physical screen.
+ */
+ public void clearPhysical() {
+ for (Screen screen: screens) {
+ screen.clearPhysical();
+ }
+ }
+
+ /**
+ * Unset every image cell on one row of the physical screen, forcing
+ * images on that row to be redrawn.
+ *
+ * @param y row coordinate. 0 is the top-most row.
+ */
+ public final void unsetImageRow(final int y) {
+ for (Screen screen: screens) {
+ screen.unsetImageRow(y);
+ }
+ }
+
/**
* Classes must provide an implementation to push the logical screen to
* the physical device.
}
}
+ /**
+ * Get the cursor visibility.
+ *
+ * @return true if the cursor is visible
+ */
+ public boolean isCursorVisible() {
+ if (screens.size() > 0) {
+ return screens.get(0).isCursorVisible();
+ }
+ return true;
+ }
+
+ /**
+ * Get the cursor X position.
+ *
+ * @return the cursor x column position
+ */
+ public int getCursorX() {
+ if (screens.size() > 0) {
+ return screens.get(0).getCursorX();
+ }
+ return 0;
+ }
+
+ /**
+ * Get the cursor Y position.
+ *
+ * @return the cursor y row position
+ */
+ public int getCursorY() {
+ if (screens.size() > 0) {
+ return screens.get(0).getCursorY();
+ }
+ return 0;
+ }
+
/**
* Set the window title.
*
}
}
+ // ------------------------------------------------------------------------
+ // MultiScreen ------------------------------------------------------------
+ // ------------------------------------------------------------------------
+
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * Get the width of a character cell in pixels.
+ *
+ * @return the width in pixels of a character cell
+ */
+ public int getTextWidth() {
+ int textWidth = 16;
+ for (Screen screen: screens) {
+ int newTextWidth = screen.getTextWidth();
+ if (newTextWidth < textWidth) {
+ textWidth = newTextWidth;
+ }
+ }
+ return textWidth;
+ }
+
+ /**
+ * Get the height of a character cell in pixels.
+ *
+ * @return the height in pixels of a character cell
+ */
+ public int getTextHeight() {
+ int textHeight = 20;
+ for (Screen screen: screens) {
+ int newTextHeight = screen.getTextHeight();
+ if (newTextHeight < textHeight) {
+ textHeight = newTextHeight;
+ }
+ }
+ return textHeight;
+ }
+
+ /**
+ * Invert the cell color at a position, including both halves of a
+ * double-width cell.
+ *
+ * @param x column position
+ * @param y row position
+ */
+ public void invertCell(final int x, final int y) {
+ for (Screen screen: screens) {
+ screen.invertCell(x, y);
+ }
+ }
+
+ /**
+ * Invert the cell color at a position.
+ *
+ * @param x column position
+ * @param y row position
+ * @param onlyThisCell if true, only invert this cell, otherwise invert
+ * both halves of a double-width cell if necessary
+ */
+ public void invertCell(final int x, final int y,
+ final boolean onlyThisCell) {
+
+ for (Screen screen: screens) {
+ screen.invertCell(x, y, onlyThisCell);
+ }
+ }
+
+ /**
+ * Set a selection area on the screen.
+ *
+ * @param x0 the starting X position of the selection
+ * @param y0 the starting Y position of the selection
+ * @param x1 the ending X position of the selection
+ * @param y1 the ending Y position of the selection
+ * @param rectangle if true, this is a rectangle select
+ */
+ public void setSelection(final int x0, final int y0,
+ final int x1, final int y1, final boolean rectangle) {
+
+ for (Screen screen: screens) {
+ screen.setSelection(x0, y0, x1, y1, rectangle);
+ }
+ }
+
+ /**
+ * Copy the screen selection area to the clipboard.
+ *
+ * @param clipboard the clipboard to use
+ * @param x0 the starting X position of the selection
+ * @param y0 the starting Y position of the selection
+ * @param x1 the ending X position of the selection
+ * @param y1 the ending Y position of the selection
+ * @param rectangle if true, this is a rectangle select
+ */
+ public void copySelection(final Clipboard clipboard,
+ final int x0, final int y0, final int x1, final int y1,
+ final boolean rectangle) {
+
+ // Only copy from the first screen.
+ if (screens.size() > 0) {
+ screens.get(0).copySelection(clipboard, x0, y0, x1, y1, rectangle);
+ }
+ }
+
}