hidden mouse pointer support
authorKevin Lamonte <kevin.lamonte@gmail.com>
Tue, 12 Feb 2019 02:57:31 +0000 (20:57 -0600)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Tue, 12 Feb 2019 02:57:31 +0000 (20:57 -0600)
src/jexer/TApplication.java
src/jexer/TTerminalWindow.java
src/jexer/TWidget.java
src/jexer/TWindow.java
src/jexer/backend/ECMA48Terminal.java
src/jexer/menu/TMenu.java
src/jexer/tterminal/ECMA48.java

index 41981daf3c3ca0e3943223534de125e0c943d835..161e161ab8cecaa6ecfcab62ff444761de75850f 100644 (file)
@@ -1522,7 +1522,26 @@ public class TApplication implements Runnable {
         if (debugThreads) {
             System.err.printf("%d %s invertCell() %d %d\n",
                 System.currentTimeMillis(), Thread.currentThread(), x, y);
+
+            if (activeWindow != null) {
+                System.err.println("activeWindow.hasHiddenMouse() " +
+                    activeWindow.hasHiddenMouse());
+            }
         }
+
+        // If this cell is on top of a visible window that has requested a
+        // hidden mouse, bail out.
+        if ((activeWindow != null) && (activeMenu == null)) {
+            if ((activeWindow.hasHiddenMouse() == true)
+                && (x > activeWindow.getX())
+                && (x < activeWindow.getX() + activeWindow.getWidth() - 1)
+                && (y > activeWindow.getY())
+                && (y < activeWindow.getY() + activeWindow.getHeight() - 1)
+            ) {
+                return;
+            }
+        }
+
         Cell cell = getScreen().getCharXY(x, y);
         if (cell.isImage()) {
             cell.invertImage();
index a624e6c50574709fe4acd969184c62a5d6dc76ae..ec4fd07ec7957cffe92943d001798ae2df79a41e 100644 (file)
@@ -788,7 +788,8 @@ public class TTerminalWindow extends TScrollableWindow
         // Synchronize against the emulator so we don't stomp on its reader
         // thread.
         synchronized (emulator) {
-
+            setHiddenMouse(emulator.hasHiddenMousePointer());
+            
             setCursorX(emulator.getCursorX() + 1);
             setCursorY(emulator.getCursorY() + 1
                 + (getHeight() - 2 - emulator.getHeight())
index e8cd1a0e7e68a79f83d6de41f685a39aad379b81..aa4cef89e24a2c6a7a4a68aeda9d0b9579ea6040 100644 (file)
@@ -1160,7 +1160,7 @@ public abstract class TWidget implements Comparable<TWidget> {
     public final void switchWidget(final boolean forward) {
 
         // No children: do nothing.
-        if (children.size() == 0) {
+        if ((children.size() == 0) || (activeChild == null)) {
             return;
         }
 
index a4a9c23a815d602e2525b07f28bfbfcd921744a7..e50e16fd1c5a9eec4f6b45c4e836a5d31b6bbdec 100644 (file)
@@ -185,6 +185,15 @@ public class TWindow extends TWidget {
      */
     protected TStatusBar statusBar = null;
 
+    /**
+     * A window may request that TApplication NOT draw the mouse cursor over
+     * it by setting this to true.  This is currently only used within Jexer
+     * by TTerminalWindow so that only the bottom-most instance of nested
+     * Jexer's draws the mouse within its application window.  But perhaps
+     * other applications can use it, so public getter/setter is provided.
+     */
+    private boolean hideMouse = false;
+
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -1346,4 +1355,26 @@ public class TWindow extends TWidget {
         }
     }
 
+    /**
+     * Returns true if this window does not want the application-wide mouse
+     * cursor drawn over it.
+     *
+     * @return true if this window does not want the application-wide mouse
+     * cursor drawn over it
+     */
+    public final boolean hasHiddenMouse() {
+        return hideMouse;
+    }
+
+    /**
+     * Set request to prevent the application-wide mouse cursor from being
+     * drawn over this window.
+     *
+     * @param hideMouse if true, this window does not want the
+     * application-wide mouse cursor drawn over it
+     */
+    public final void setHiddenMouse(final boolean hideMouse) {
+        this.hideMouse = hideMouse;
+    }
+
 }
index 2afa81a35500e2a99c17c6500c0b748a5f922926..7416819a3b73ed54f3da1518c1afbd68efcb12ce 100644 (file)
@@ -3385,6 +3385,11 @@ public class ECMA48Terminal extends LogicalScreen
      *
      * Note that this also sets the alternate/primary screen buffer.
      *
+     * Finally, also emit a Privacy Message sequence that Jexer recognizes to
+     * mean "hide the mouse pointer."  We have to use our own sequence to do
+     * this because there is no standard in xterm for unilaterally hiding the
+     * pointer all the time (regardless of typing).
+     *
      * @param on If true, enable mouse report and use the alternate screen
      * buffer.  If false disable mouse reporting and use the primary screen
      * buffer.
@@ -3392,9 +3397,9 @@ public class ECMA48Terminal extends LogicalScreen
      */
     private String mouse(final boolean on) {
         if (on) {
-            return "\033[?1002;1003;1005;1006h\033[?1049h";
+            return "\033[?1002;1003;1005;1006h\033[?1049h\033^hideMousePointer\033\\";
         }
-        return "\033[?1002;1003;1006;1005l\033[?1049l";
+        return "\033[?1002;1003;1006;1005l\033[?1049l\033^showMousePointer\033\\";
     }
 
 }
index dbbea82bd1a9509dcd2bdd6a4e576cec84d57176..7c3121230a3539a8b7158ac7a3ff1ee4ecde927b 100644 (file)
@@ -566,10 +566,10 @@ public class TMenu extends TWindow {
             break;
         case MID_SEARCH_AGAIN:
             label = i18n.getString("menuSearchAgain");
+            key = kbCtrlL;
             break;
         case MID_GOTO_LINE:
             label = i18n.getString("menuGotoLine");
-            key = kbCtrlL;
             break;
 
         case MID_TILE:
index b08a18d5c3070ab207367ab3e8817eb955c1ddbe..ce3570b43cdb0b60718171d3b2f797abfcb967e4 100644 (file)
@@ -302,6 +302,15 @@ public class ECMA48 implements Runnable {
      */
     private MouseEncoding mouseEncoding = MouseEncoding.X10;
 
+    /**
+     * A terminal may request that the mouse pointer be hidden using a
+     * Privacy Message containing either "hideMousePointer" or
+     * "showMousePointer".  This is currently only used within Jexer by
+     * TTerminalWindow so that only the bottom-most instance of nested
+     * Jexer's draws the mouse within its application window.
+     */
+    private boolean hideMousePointer = false;
+
     /**
      * Physical display width.  We start at 80x24, but the user can resize us
      * bigger/smaller.
@@ -4559,6 +4568,38 @@ public class ECMA48 implements Runnable {
         }
     }
 
+    /**
+     * Handle the SCAN_SOSPMAPC_STRING state.  This is currently only used by
+     * Jexer ECMA48Terminal to talk to ECMA48.
+     *
+     * @param pmChar the character received from the remote side
+     */
+    private void pmPut(final char pmChar) {
+        // System.err.println("pmPut: " + pmChar);
+
+        // Collect first
+        collectBuffer.append(pmChar);
+
+        // Xterm cases...
+        if (collectBuffer.toString().endsWith("\033\\")) {
+            String arg = null;
+            arg = collectBuffer.substring(0, collectBuffer.length() - 2);
+
+            // System.err.println("arg: '" + arg + "'");
+
+            if (arg.equals("hideMousePointer")) {
+                hideMousePointer = true;
+            }
+            if (arg.equals("showMousePointer")) {
+                hideMousePointer = false;
+            }
+
+            // Go to SCAN_GROUND state
+            toGround();
+            return;
+        }
+    }
+
     /**
      * Run this input character through the ECMA48 state machine.
      *
@@ -4588,9 +4629,11 @@ public class ECMA48 implements Runnable {
         // 0x1B == ESCAPE
         if (ch == 0x1B) {
             if ((type == DeviceType.XTERM)
-                && (scanState == ScanState.OSC_STRING)
+                && ((scanState == ScanState.OSC_STRING)
+                    || (scanState == ScanState.SOSPMAPC_STRING))
             ) {
                 // Xterm can pass ESCAPE to its OSC sequence.
+                // Jexer can pass ESCAPE to its PM sequence.
             } else if ((scanState != ScanState.DCS_ENTRY)
                 && (scanState != ScanState.DCS_INTERMEDIATE)
                 && (scanState != ScanState.DCS_IGNORE)
@@ -6446,6 +6489,15 @@ public class ECMA48 implements Runnable {
         case SOSPMAPC_STRING:
             // 00-17, 19, 1C-1F, 20-7F --> ignore
 
+            // Special case for Jexer: PM can pass one control character
+            if (ch == 0x1B) {
+                pmPut(ch);
+            }
+
+            if ((ch >= 0x20) && (ch <= 0x7F)) {
+                pmPut(ch);
+            }
+
             // 0x9C goes to GROUND
             if (ch == 0x9C) {
                 toGround();
@@ -6509,4 +6561,15 @@ public class ECMA48 implements Runnable {
         return currentState.cursorY;
     }
 
+    /**
+     * Returns true if this terminal has requested the mouse pointer be
+     * hidden.
+     *
+     * @return true if this terminal has requested the mouse pointer be
+     * hidden
+     */
+    public final boolean hasHiddenMousePointer() {
+        return hideMousePointer;
+    }
+
 }