From: Kevin Lamonte Date: Mon, 18 Feb 2019 15:55:02 +0000 (-0600) Subject: retrofit from gjexer X-Git-Tag: fanfix-3.0.1^2~192 X-Git-Url: https://git.nikiroo.be/?a=commitdiff_plain;h=9696a8f6da9a0d204740420d6d8571176ab81944;p=fanfix.git retrofit from gjexer --- diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 20406b1..4c317f3 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -1508,6 +1508,18 @@ public class TApplication implements Runnable { } } + /** + * Check if application is still running. + * + * @return true if the application is running + */ + public final boolean isRunning() { + if (quit == true) { + return false; + } + return true; + } + // ------------------------------------------------------------------------ // Screen refresh loop ---------------------------------------------------- // ------------------------------------------------------------------------ @@ -1777,6 +1789,10 @@ public class TApplication implements Runnable { // Flush the screen contents if ((images.size() > 0) || getScreen().isDirty()) { + if (debugThreads) { + System.err.printf("%d %s backend.flushScreen()\n", + System.currentTimeMillis(), Thread.currentThread()); + } backend.flushScreen(); } @@ -1908,7 +1924,8 @@ public class TApplication implements Runnable { assert (!window.isActive()); if (activeWindow != null) { - assert (activeWindow.getZ() == 0); + // TODO: see if this assertion is really necessary. + // assert (activeWindow.getZ() == 0); activeWindow.setActive(false); @@ -2241,6 +2258,21 @@ public class TApplication implements Runnable { return false; } + /** + * Check if there is a window with overridden menu flag on top. + * + * @return true if the active window is overriding the menu + */ + private boolean overrideMenuWindowActive() { + if (activeWindow != null) { + if (activeWindow.hasOverriddenMenu()) { + return true; + } + } + + return false; + } + /** * Close all open windows. */ @@ -2571,6 +2603,7 @@ public class TApplication implements Runnable { if ((mouse.getType() == TMouseEvent.Type.MOUSE_DOWN) && (mouse.isMouse1()) && (!modalWindowActive()) + && (!overrideMenuWindowActive()) && (mouse.getAbsoluteY() == 0) ) { diff --git a/src/jexer/TButton.java b/src/jexer/TButton.java index 9c0e98b..83ff2d2 100644 --- a/src/jexer/TButton.java +++ b/src/jexer/TButton.java @@ -253,7 +253,6 @@ public class TButton extends TWidget { putCharXY(1 + mnemonic.getShortcutIdx(), 0, mnemonic.getShortcut(), menuMnemonicColor); } - } } diff --git a/src/jexer/TImageWindow.java b/src/jexer/TImageWindow.java index d38cd25..a4a54a9 100644 --- a/src/jexer/TImageWindow.java +++ b/src/jexer/TImageWindow.java @@ -31,6 +31,7 @@ package jexer; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.util.ResourceBundle; import javax.imageio.ImageIO; import jexer.event.TKeypressEvent; @@ -43,6 +44,11 @@ import static jexer.TKeypress.*; */ public class TImageWindow extends TScrollableWindow { + /** + * Translated strings. + */ + private static final ResourceBundle i18n = ResourceBundle.getBundle(TImageWindow.class.getName()); + // ------------------------------------------------------------------------ // Constants -------------------------------------------------------------- // ------------------------------------------------------------------------ @@ -124,6 +130,8 @@ public class TImageWindow extends TScrollableWindow { setBottomValue(imageField.getRows() - imageField.getHeight()); setLeftValue(0); setRightValue(imageField.getColumns() - imageField.getWidth()); + + statusBar = newStatusBar(i18n.getString("statusBar")); } // ------------------------------------------------------------------------ diff --git a/src/jexer/TImageWindow.properties b/src/jexer/TImageWindow.properties new file mode 100644 index 0000000..c9494e3 --- /dev/null +++ b/src/jexer/TImageWindow.properties @@ -0,0 +1 @@ +statusBar=Alt-\u2190\u2192: Rotate Left/Right Alt-\u2191\u2193: Bigger/Smaller \u2190\u2192\u2191\u2193: Pan diff --git a/src/jexer/TKeypress.java b/src/jexer/TKeypress.java index 5808545..9cb4932 100644 --- a/src/jexer/TKeypress.java +++ b/src/jexer/TKeypress.java @@ -487,6 +487,26 @@ public class TKeypress { 0, 'y', true, false, false); public static final TKeypress kbAltZ = new TKeypress(false, 0, 'z', true, false, false); + public static final TKeypress kbAlt0 = new TKeypress(false, + 0, '0', true, false, false); + public static final TKeypress kbAlt1 = new TKeypress(false, + 0, '1', true, false, false); + public static final TKeypress kbAlt2 = new TKeypress(false, + 0, '2', true, false, false); + public static final TKeypress kbAlt3 = new TKeypress(false, + 0, '3', true, false, false); + public static final TKeypress kbAlt4 = new TKeypress(false, + 0, '4', true, false, false); + public static final TKeypress kbAlt5 = new TKeypress(false, + 0, '5', true, false, false); + public static final TKeypress kbAlt6 = new TKeypress(false, + 0, '6', true, false, false); + public static final TKeypress kbAlt7 = new TKeypress(false, + 0, '7', true, false, false); + public static final TKeypress kbAlt8 = new TKeypress(false, + 0, '8', true, false, false); + public static final TKeypress kbAlt9 = new TKeypress(false, + 0, '9', true, false, false); public static final TKeypress kbCtrlA = new TKeypress(false, 0, 'A', false, true, false); public static final TKeypress kbCtrlB = new TKeypress(false, diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index b55e7b0..a243a43 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -789,7 +789,7 @@ public class TTerminalWindow extends TScrollableWindow // thread. synchronized (emulator) { setHiddenMouse(emulator.hasHiddenMousePointer()); - + setCursorX(emulator.getCursorX() + 1); setCursorY(emulator.getCursorY() + 1 + (getHeight() - 2 - emulator.getHeight()) diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index e50e16f..1694549 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -91,6 +91,11 @@ public class TWindow extends TWidget { */ public static final int HIDEONCLOSE = 0x40; + /** + * Menus cannot be used when this window is active (default no). + */ + public static final int OVERRIDEMENU = 0x80; + // ------------------------------------------------------------------------ // Variables -------------------------------------------------------------- // ------------------------------------------------------------------------ @@ -1251,6 +1256,20 @@ public class TWindow extends TWidget { return false; } + /** + * Returns true if this window does not want menus to work while it is + * visible. + * + * @return true if this window does not want menus to work while it is + * visible + */ + public final boolean hasOverriddenMenu() { + if ((flags & OVERRIDEMENU) != 0) { + return true; + } + return false; + } + /** * Retrieve the background color. * diff --git a/src/jexer/backend/ECMA48Terminal.java b/src/jexer/backend/ECMA48Terminal.java index 7416819..0cbd4f0 100644 --- a/src/jexer/backend/ECMA48Terminal.java +++ b/src/jexer/backend/ECMA48Terminal.java @@ -480,9 +480,9 @@ public class ECMA48Terminal extends LogicalScreen int red, green, blue; if (imageX < image.getWidth() - 1) { int pXpY = ditheredImage.getRGB(imageX + 1, imageY); - red = (int) ((pXpY >>> 16) & 0xFF) + (7 * redError); - green = (int) ((pXpY >>> 8) & 0xFF) + (7 * greenError); - blue = (int) ( pXpY & 0xFF) + (7 * blueError); + red = ((pXpY >>> 16) & 0xFF) + (7 * redError); + green = ((pXpY >>> 8) & 0xFF) + (7 * greenError); + blue = ( pXpY & 0xFF) + (7 * blueError); red = clamp(red); green = clamp(green); blue = clamp(blue); @@ -493,9 +493,9 @@ public class ECMA48Terminal extends LogicalScreen if (imageY < image.getHeight() - 1) { int pXpYp = ditheredImage.getRGB(imageX + 1, imageY + 1); - red = (int) ((pXpYp >>> 16) & 0xFF) + redError; - green = (int) ((pXpYp >>> 8) & 0xFF) + greenError; - blue = (int) ( pXpYp & 0xFF) + blueError; + red = ((pXpYp >>> 16) & 0xFF) + redError; + green = ((pXpYp >>> 8) & 0xFF) + greenError; + blue = ( pXpYp & 0xFF) + blueError; red = clamp(red); green = clamp(green); blue = clamp(blue); @@ -509,9 +509,9 @@ public class ECMA48Terminal extends LogicalScreen int pXYp = ditheredImage.getRGB(imageX, imageY + 1); - red = (int) ((pXmYp >>> 16) & 0xFF) + (3 * redError); - green = (int) ((pXmYp >>> 8) & 0xFF) + (3 * greenError); - blue = (int) ( pXmYp & 0xFF) + (3 * blueError); + red = ((pXmYp >>> 16) & 0xFF) + (3 * redError); + green = ((pXmYp >>> 8) & 0xFF) + (3 * greenError); + blue = ( pXmYp & 0xFF) + (3 * blueError); red = clamp(red); green = clamp(green); blue = clamp(blue); @@ -519,9 +519,9 @@ public class ECMA48Terminal extends LogicalScreen pXmYp |= ((green & 0xFF) << 8) | (blue & 0xFF); ditheredImage.setRGB(imageX - 1, imageY + 1, pXmYp); - red = (int) ((pXYp >>> 16) & 0xFF) + (5 * redError); - green = (int) ((pXYp >>> 8) & 0xFF) + (5 * greenError); - blue = (int) ( pXYp & 0xFF) + (5 * blueError); + red = ((pXYp >>> 16) & 0xFF) + (5 * redError); + green = ((pXYp >>> 8) & 0xFF) + (5 * greenError); + blue = ( pXYp & 0xFF) + (5 * blueError); red = clamp(red); green = clamp(green); blue = clamp(blue); @@ -1238,6 +1238,19 @@ public class ECMA48Terminal extends LogicalScreen flush(); } + /** + * Resize the physical screen to match the logical screen dimensions. + */ + @Override + public void resizeToScreen() { + // Send dtterm/xterm sequences, which will probably not work because + // allowWindowOps is defaulted to false. + String resizeString = String.format("\033[8;%d;%dt", getHeight(), + getWidth()); + this.output.write(resizeString); + this.output.flush(); + } + // ------------------------------------------------------------------------ // TerminalReader --------------------------------------------------------- // ------------------------------------------------------------------------ @@ -2213,6 +2226,9 @@ public class ECMA48Terminal extends LogicalScreen // Check for new window size long windowSizeDelay = nowTime - windowSizeTime; if (windowSizeDelay > 1000) { + int oldTextWidth = getTextWidth(); + int oldTextHeight = getTextHeight(); + sessionInfo.queryWindowSize(); int newWidth = sessionInfo.getWindowWidth(); int newHeight = sessionInfo.getWindowHeight(); @@ -2221,14 +2237,24 @@ public class ECMA48Terminal extends LogicalScreen || (newHeight != windowResize.getHeight()) ) { + // Request xterm report window dimensions in pixels again. + // Between now and then, ensure that the reported text cell + // size is the same by setting widthPixels and heightPixels + // to match the new dimensions. + widthPixels = oldTextWidth * newWidth; + heightPixels = oldTextHeight * newHeight; + if (debugToStderr) { System.err.println("Screen size changed, old size " + windowResize); System.err.println(" new size " + newWidth + " x " + newHeight); + System.err.println(" old pixels " + + oldTextWidth + " x " + oldTextHeight); + System.err.println(" new pixels " + + getTextWidth() + " x " + getTextHeight()); } - // Request xterm report window dimensions in pixels again. this.output.printf("%s", xtermReportWindowPixelDimensions()); this.output.flush(); @@ -2798,6 +2824,15 @@ public class ECMA48Terminal extends LogicalScreen rgbArray = cells.get(i).getImage().getRGB(0, 0, imageWidth, imageHeight, null, 0, imageWidth); } + + /* + System.err.printf("calling image.setRGB(): %d %d %d %d %d\n", + i * imageWidth, 0, imageWidth, imageHeight, + 0, imageWidth); + System.err.printf(" fullWidth %d fullHeight %d cells.size() %d textWidth %d\n", + fullWidth, fullHeight, cells.size(), getTextWidth()); + */ + image.setRGB(i * imageWidth, 0, imageWidth, imageHeight, rgbArray, 0, imageWidth); if (imageHeight < fullHeight) { diff --git a/src/jexer/backend/LogicalScreen.java b/src/jexer/backend/LogicalScreen.java index b764831..24ba4af 100644 --- a/src/jexer/backend/LogicalScreen.java +++ b/src/jexer/backend/LogicalScreen.java @@ -564,6 +564,14 @@ public class LogicalScreen implements Screen { */ public final void setDimensions(final int width, final int height) { reallocate(width, height); + resizeToScreen(); + } + + /** + * Resize the physical screen to match the logical screen dimensions. + */ + public void resizeToScreen() { + // Subclasses are expected to override this. } /** @@ -889,6 +897,9 @@ public class LogicalScreen implements Screen { * @param y row coordinate. 0 is the top-most row. */ public final void unsetImageRow(final int y) { + if ((y < 0) || (y >= height)) { + return; + } for (int x = 0; x < width; x++) { if (logical[x][y].isImage()) { physical[x][y].unset(); diff --git a/src/jexer/backend/MultiScreen.java b/src/jexer/backend/MultiScreen.java index 0208410..880ee18 100644 --- a/src/jexer/backend/MultiScreen.java +++ b/src/jexer/backend/MultiScreen.java @@ -386,7 +386,20 @@ public class MultiScreen implements Screen { */ 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(); + } } } diff --git a/src/jexer/backend/SwingSessionInfo.java b/src/jexer/backend/SwingSessionInfo.java index 28668fd..2f74d70 100644 --- a/src/jexer/backend/SwingSessionInfo.java +++ b/src/jexer/backend/SwingSessionInfo.java @@ -179,6 +179,14 @@ public class SwingSessionInfo implements SessionInfo { Insets insets = swing.getInsets(); int width = swing.getWidth() - insets.left - insets.right; int height = swing.getHeight() - insets.top - insets.bottom; + // In theory, if Java reported pixel-perfect dimensions, the + // expressions above would precisely line up with the requested + // window size from SwingComponent.setDimensions(). In practice, + // there appears to be a small difference. Add half a text cell in + // both directions before the division to hopefully reach the same + // result as setDimensions() was supposed to give us. + width += (textWidth / 2); + height += (textHeight / 2); windowWidth = width / textWidth; windowHeight = height / textHeight; diff --git a/src/jexer/backend/SwingTerminal.java b/src/jexer/backend/SwingTerminal.java index 6a3b203..43c161f 100644 --- a/src/jexer/backend/SwingTerminal.java +++ b/src/jexer/backend/SwingTerminal.java @@ -1096,10 +1096,11 @@ public class SwingTerminal extends LogicalScreen } /** - * Resize to font dimensions. + * Resize the physical screen to match the logical screen dimensions. */ + @Override public void resizeToScreen() { - swing.setDimensions(textWidth * (width + 1), textHeight * (height + 1)); + swing.setDimensions(textWidth * width, textHeight * height); } /** diff --git a/src/jexer/backend/TWindowBackend.java b/src/jexer/backend/TWindowBackend.java index 10f95c6..a34ba78 100644 --- a/src/jexer/backend/TWindowBackend.java +++ b/src/jexer/backend/TWindowBackend.java @@ -35,6 +35,7 @@ import jexer.bits.CellAttributes; import jexer.event.TInputEvent; import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; +import jexer.event.TResizeEvent; import jexer.TApplication; import jexer.TWindow; @@ -69,15 +70,50 @@ public class TWindowBackend extends TWindow implements Backend { private List eventQueue; /** - * The screen to use. + * The screen this window is monitoring. */ private Screen otherScreen; + /** + * The application associated with otherScreen. + */ + private TApplication otherApplication; + /** * The session information. */ private SessionInfo sessionInfo; + /** + * OtherScreen provides a hook to notify TWindowBackend of screen size + * changes. + */ + private class OtherScreen extends LogicalScreen { + + /** + * The TWindowBackend to notify. + */ + private TWindowBackend window; + + /** + * Public constructor. + */ + public OtherScreen(final TWindowBackend window) { + this.window = window; + } + + /** + * Resize the physical screen to match the logical screen dimensions. + */ + @Override + public void resizeToScreen() { + window.setWidth(getWidth() + 2); + window.setHeight(getHeight() + 2); + } + + } + + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -101,7 +137,7 @@ public class TWindowBackend extends TWindow implements Backend { this.listener = listener; eventQueue = new LinkedList(); sessionInfo = new TSessionInfo(width, height); - otherScreen = new LogicalScreen(); + otherScreen = new OtherScreen(this); otherScreen.setDimensions(width - 2, height - 2); drawLock = otherScreen; setHiddenMouse(true); @@ -127,7 +163,7 @@ public class TWindowBackend extends TWindow implements Backend { this.listener = listener; eventQueue = new LinkedList(); sessionInfo = new TSessionInfo(width, height); - otherScreen = new LogicalScreen(); + otherScreen = new OtherScreen(this); otherScreen.setDimensions(width - 2, height - 2); drawLock = otherScreen; setHiddenMouse(true); @@ -154,7 +190,7 @@ public class TWindowBackend extends TWindow implements Backend { this.listener = listener; eventQueue = new LinkedList(); sessionInfo = new TSessionInfo(width, height); - otherScreen = new LogicalScreen(); + otherScreen = new OtherScreen(this); otherScreen.setDimensions(width - 2, height - 2); drawLock = otherScreen; setHiddenMouse(true); @@ -183,7 +219,7 @@ public class TWindowBackend extends TWindow implements Backend { this.listener = listener; eventQueue = new LinkedList(); sessionInfo = new TSessionInfo(width, height); - otherScreen = new LogicalScreen(); + otherScreen = new OtherScreen(this); otherScreen.setDimensions(width - 2, height - 2); drawLock = otherScreen; setHiddenMouse(true); @@ -193,6 +229,35 @@ public class TWindowBackend extends TWindow implements Backend { // Event handlers --------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Handle window/screen resize events. + * + * @param event resize event + */ + @Override + 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(); + } + } + return; + } else { + super.onResize(event); + } + } + /** * Returns true if the mouse is currently in the otherScreen window. * @@ -327,6 +392,15 @@ public class TWindowBackend extends TWindow implements Backend { setCursorVisible(false); } } + + // Check if the other application has died. If so, unset hidden + // mouse. + if (otherApplication != null) { + if (otherApplication.isRunning() == false) { + setHiddenMouse(false); + } + } + } /** @@ -434,4 +508,13 @@ public class TWindowBackend extends TWindow implements Backend { 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; + } + } diff --git a/src/jexer/demos/Demo6.java b/src/jexer/demos/Demo6.java index 236e7a2..4af7c87 100644 --- a/src/jexer/demos/Demo6.java +++ b/src/jexer/demos/Demo6.java @@ -28,6 +28,8 @@ */ package jexer.demos; +import java.util.ResourceBundle; + import jexer.TApplication; import jexer.backend.*; import jexer.demos.DemoApplication; @@ -37,6 +39,15 @@ import jexer.demos.DemoApplication; */ public class Demo6 { + /** + * Translated strings. + */ + private static final ResourceBundle i18n = ResourceBundle.getBundle(Demo6.class.getName()); + + // ------------------------------------------------------------------------ + // Demo6 ------------------------------------------------------------------ + // ------------------------------------------------------------------------ + /** * Main entry point. * @@ -117,8 +128,10 @@ public class Demo6 { * eliminate) screen tearing/artifacts. */ TWindowBackend windowBackend = new TWindowBackend(demoApp, - monitor, "Monitor Window", width + 2, height + 2); + monitor, i18n.getString("monitorWindow"), + width + 2, height + 2); windowBackend.setDrawLock(multiScreen); + windowBackend.setOtherApplication(demoApp); multiBackend.addBackend(windowBackend); /* diff --git a/src/jexer/demos/Demo6.properties b/src/jexer/demos/Demo6.properties new file mode 100644 index 0000000..450829a --- /dev/null +++ b/src/jexer/demos/Demo6.properties @@ -0,0 +1 @@ +monitorWindow=Monitor Window diff --git a/src/jexer/tterminal/ECMA48.java b/src/jexer/tterminal/ECMA48.java index ce3570b..00aa05b 100644 --- a/src/jexer/tterminal/ECMA48.java +++ b/src/jexer/tterminal/ECMA48.java @@ -1114,7 +1114,7 @@ public class ECMA48 implements Runnable { private void resetTabStops() { tabStops.clear(); for (int i = 0; (i * 8) <= rightMargin; i++) { - tabStops.add(new Integer(i * 8)); + tabStops.add(Integer.valueOf(i * 8)); } } @@ -1629,6 +1629,7 @@ public class ECMA48 implements Runnable { * @param keypress keypress received from the local user * @return string to transmit to the remote side */ + @SuppressWarnings("fallthrough") private String keypressToString(final TKeypress keypress) { if ((fullDuplex == false) && (!keypress.isFnKey())) { @@ -2367,13 +2368,13 @@ public class ECMA48 implements Runnable { switch (currentState.glLockshift) { case G1_GR: - assert (false); + throw new IllegalArgumentException("programming bug"); case G2_GR: - assert (false); + throw new IllegalArgumentException("programming bug"); case G3_GR: - assert (false); + throw new IllegalArgumentException("programming bug"); case G2_GL: // LS2 @@ -2394,10 +2395,10 @@ public class ECMA48 implements Runnable { switch (currentState.grLockshift) { case G2_GL: - assert (false); + throw new IllegalArgumentException("programming bug"); case G3_GL: - assert (false); + throw new IllegalArgumentException("programming bug"); case G1_GR: // LS1R @@ -2652,7 +2653,7 @@ public class ECMA48 implements Runnable { */ private void param(final byte ch) { if (csiParams.size() == 0) { - csiParams.add(new Integer(0)); + csiParams.add(Integer.valueOf(0)); } Integer x = csiParams.get(csiParams.size() - 1); if ((ch >= '0') && (ch <= '9')) { @@ -2662,7 +2663,7 @@ public class ECMA48 implements Runnable { } if (ch == ';') { - csiParams.add(new Integer(0)); + csiParams.add(Integer.valueOf(0)); } }