sixel support for TTerminalWindow
authorKevin Lamonte <kevin.lamonte@gmail.com>
Sun, 4 Aug 2019 18:03:12 +0000 (13:03 -0500)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Sun, 4 Aug 2019 18:03:12 +0000 (13:03 -0500)
src/jexer/TImageWindow.java
src/jexer/TScrollableWindow.java
src/jexer/TTerminalWindow.java
src/jexer/tterminal/ECMA48.java
src/jexer/tterminal/Sixel.java

index 86b0d6d62811d201b902646fe2fcd5d6ab16133d..057f6ac18f9cd707fcad9b2d2ed4d340d0bb64f3 100644 (file)
@@ -124,7 +124,10 @@ public class TImageWindow extends TScrollableWindow {
             setWidth(imageField.getColumns() + 2);
         }
 
-        hScroller = new THScroller(this, 17, getHeight() - 2, getWidth() - 20);
+        hScroller = new THScroller(this,
+            Math.min(Math.max(0, getWidth() - 17), 17),
+            getHeight() - 2,
+            getWidth() - Math.min(Math.max(0, getWidth() - 17), 17) - 3);
         vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
         setTopValue(0);
         setBottomValue(imageField.getRows() - imageField.getHeight());
index 6817f40e13a1fa90f927bc5243501eb298af0aa2..1e260b3f86e00c7a7cc07409e28e0e1aa646fe03 100644 (file)
@@ -166,6 +166,7 @@ public class TScrollableWindow extends TWindow implements Scrollable {
      */
     protected void placeScrollbars() {
         if (hScroller != null) {
+            hScroller.setX(Math.min(Math.max(0, getWidth() - 17), 17));
             hScroller.setY(getHeight() - 2);
             hScroller.setWidth(getWidth() - hScroller.getX() - 3);
             hScroller.setBigChange(getWidth() - hScroller.getX() - 3);
index 818a52f6eb6bbccc20dfc45042cad9653ba1b7be..9780e29d7d6643ad0c032801c6605d3320b91052 100644 (file)
@@ -550,13 +550,17 @@ public class TTerminalWindow extends TScrollableWindow
             return;
         }
 
-        if (mouse.isMouseWheelUp()) {
-            verticalDecrement();
-            return;
-        }
-        if (mouse.isMouseWheelDown()) {
-            verticalIncrement();
-            return;
+        // If the emulator is tracking mouse buttons, it needs to see wheel
+        // events.
+        if (emulator.getMouseProtocol() == ECMA48.MouseProtocol.OFF) {
+            if (mouse.isMouseWheelUp()) {
+                verticalDecrement();
+                return;
+            }
+            if (mouse.isMouseWheelDown()) {
+                verticalIncrement();
+                return;
+            }
         }
         if (mouseOnEmulator(mouse)) {
             synchronized (emulator) {
index f2b485eca078e949b75868575b6869a05e01b4c6..c94d7d2b8524f60290496f53e4b97cbe0bdec883 100644 (file)
@@ -213,7 +213,7 @@ public class ECMA48 implements Runnable {
     /**
      * XTERM mouse reporting protocols.
      */
-    private enum MouseProtocol {
+    public enum MouseProtocol {
         OFF,
         X10,
         NORMAL,
@@ -4620,6 +4620,30 @@ public class ECMA48 implements Runnable {
         }
     }
 
+    /**
+     * Perform xterm window operations.
+     */
+    private void xtermWindowOps() {
+        boolean xtermPrivateModeFlag = false;
+
+        for (int i = 0; i < collectBuffer.length(); i++) {
+            if (collectBuffer.charAt(i) == '?') {
+                xtermPrivateModeFlag = true;
+                break;
+            }
+        }
+
+        int i = getCsiParam(0, 0);
+
+        if (!xtermPrivateModeFlag) {
+            if (i == 14) {
+                // Report xterm window in pixels as CSI 4 ; height ; width t
+                writeRemote(String.format("\033[4;%d;%dt", textHeight * height,
+                        textWidth * width));
+            }
+        }
+    }
+
     /**
      * Run this input character through the ECMA48 state machine.
      *
@@ -5906,6 +5930,10 @@ public class ECMA48 implements Runnable {
                     }
                     break;
                 case 't':
+                    if (type == DeviceType.XTERM) {
+                        // Window operations
+                        xtermWindowOps();
+                    }
                     break;
                 case 'u':
                     // Restore cursor (ANSI.SYS)
@@ -6169,7 +6197,13 @@ public class ECMA48 implements Runnable {
                     decstbm();
                     break;
                 case 's':
+                    break;
                 case 't':
+                    if (type == DeviceType.XTERM) {
+                        // Window operations
+                        xtermWindowOps();
+                    }
+                    break;
                 case 'u':
                 case 'v':
                 case 'w':
@@ -6643,6 +6677,15 @@ public class ECMA48 implements Runnable {
         return hideMousePointer;
     }
 
+    /**
+     * Get the mouse protocol.
+     *
+     * @return MouseProtocol.OFF, MouseProtocol.X10, etc.
+     */
+    public MouseProtocol getMouseProtocol() {
+        return mouseProtocol;
+    }
+
     // ------------------------------------------------------------------------
     // Sixel support ----------------------------------------------------------
     // ------------------------------------------------------------------------
@@ -6670,13 +6713,16 @@ public class ECMA48 implements Runnable {
      * the text cells.
      */
     private void parseSixel() {
-        System.err.println("parseSixel(): '" + sixelParseBuffer.toString() +
-            "'");
+
+        /*
+        System.err.println("parseSixel(): '" + sixelParseBuffer.toString()
+            + "'");
+         */
 
         Sixel sixel = new Sixel(sixelParseBuffer.toString());
         BufferedImage image = sixel.getImage();
 
-        System.err.println("parseSixel(): image " + image);
+        // System.err.println("parseSixel(): image " + image);
 
         if (image == null) {
             // Sixel data was malformed in some way, bail out.
index 8d8429b8dd2c63cacacc689c50611c6f6db19d04..de3232c6588da3b1e6445a8d031b3804f8ab69eb 100644 (file)
@@ -49,10 +49,8 @@ public class Sixel {
     private enum ScanState {
         GROUND,
         QUOTE,
-        COLOR_ENTRY,
-        COLOR_PARAM,
-        COLOR_PIXELS,
-        SIXEL_REPEAT,
+        COLOR,
+        REPEAT,
     }
 
     // ------------------------------------------------------------------------
@@ -62,7 +60,7 @@ public class Sixel {
     /**
      * If true, enable debug messages.
      */
-    private static boolean DEBUG = true;
+    private static boolean DEBUG = false;
 
     /**
      * Number of pixels to increment when we need more horizontal room.
@@ -154,7 +152,7 @@ public class Sixel {
      */
     public BufferedImage getImage() {
         if ((width > 0) && (height > 0)) {
-            return image.getSubimage(0, 0, width, height);
+            return image.getSubimage(0, 0, width, height + 6);
         }
         return null;
     }
@@ -257,6 +255,8 @@ public class Sixel {
                 Integer.toHexString(n) + " color " + color);
         }
 
+        assert (n >= 0);
+
         if (x + rep > image.getWidth()) {
             // Resize the image, give us another max(rep, WIDTH_INCREASE)
             // pixels of horizontal length.
@@ -274,22 +274,22 @@ public class Sixel {
         }
 
         for (int i = 0; i < rep; i++) {
-            if ((n & 0x01) == 0x01) {
-                image.setRGB(x, height, rgb);
+            if ((n & 0x01) != 0) {
+                image.setRGB(x, height + 0, rgb);
             }
-            if ((n & 0x02) == 0x02) {
+            if ((n & 0x02) != 0) {
                 image.setRGB(x, height + 1, rgb);
             }
-            if ((n & 0x04) == 0x04) {
+            if ((n & 0x04) != 0) {
                 image.setRGB(x, height + 2, rgb);
             }
-            if ((n & 0x08) == 0x08) {
+            if ((n & 0x08) != 0) {
                 image.setRGB(x, height + 3, rgb);
             }
-            if ((n & 0x10) == 0x10) {
+            if ((n & 0x10) != 0) {
                 image.setRGB(x, height + 4, rgb);
             }
-            if ((n & 0x20) == 0x20) {
+            if ((n & 0x20) != 0) {
                 image.setRGB(x, height + 5, rgb);
             }
             x++;
@@ -310,10 +310,12 @@ public class Sixel {
             Color newColor = palette.get(idx);
             if (newColor != null) {
                 color = newColor;
+            } else {
+                System.err.println("COLOR " + idx + " NOT FOUND");
             }
 
             if (DEBUG) {
-                System.err.println("set color: " + color);
+                System.err.println("set color " + idx + " " + color);
             }
             return;
         }
@@ -329,6 +331,11 @@ public class Sixel {
             if (DEBUG) {
                 System.err.println("Palette color " + idx + " --> " + newColor);
             }
+        } else {
+            if (DEBUG) {
+                System.err.println("UNKNOWN COLOR TYPE " + type + ": " + type +
+                    " " + idx + " R " + red + " G " + green + " B " + blue);
+            }
         }
     }
 
@@ -342,164 +349,97 @@ public class Sixel {
         // DEBUG
         // System.err.printf("Sixel.consume() %c STATE = %s\n", ch, scanState);
 
-        switch (scanState) {
-
-        case GROUND:
-            switch (ch) {
-            case '#':
-                scanState = ScanState.COLOR_ENTRY;
-                return;
-            case '\"':
-                scanState = ScanState.QUOTE;
-                return;
-            default:
-                break;
-            }
-
-            if (ch == '!') {
-                // Repeat count
-                scanState = ScanState.SIXEL_REPEAT;
-            }
-            if (ch == '-') {
-                if (height + 6 < image.getHeight()) {
-                    // Resize the image, give us another HEIGHT_INCREASE
-                    // pixels of vertical length.
-                    resizeImage(image.getWidth(),
-                        image.getHeight() + HEIGHT_INCREASE);
-                }
-                height += 6;
-                x = 0;
-            }
-
-            if (ch == '$') {
-                x = 0;
+        // Between decimal 63 (inclusive) and 127 (exclusive) --> pixels
+        if ((ch >= 63) && (ch < 127)) {
+            if (scanState == ScanState.COLOR) {
+                setPalette();
+                toGround();
             }
+            addSixel(ch);
+            toGround();
             return;
+        }
 
-        case QUOTE:
-            switch (ch) {
-            case '#':
-                scanState = ScanState.COLOR_ENTRY;
-                return;
-            default:
-                break;
+        if (ch == '#') {
+            // Next color is here, parse what we had before.
+            if (scanState == ScanState.COLOR) {
+                setPalette();
+                toGround();
             }
-
-            // Ignore everything else in the quote header.
+            scanState = ScanState.COLOR;
             return;
+        }
 
-        case COLOR_ENTRY:
-            // Between decimal 63 (inclusive) and 189 (exclusive) --> pixels
-            if ((ch >= 63) && (ch < 189)) {
-                addSixel(ch);
-                return;
-            }
-
-            // 30-39, 3B           --> param, then switch to COLOR_PARAM
-            if ((ch >= '0') && (ch <= '9')) {
-                param((byte) ch);
-                scanState = ScanState.COLOR_PARAM;
-            }
-            if (ch == ';') {
-                param((byte) ch);
-                scanState = ScanState.COLOR_PARAM;
-            }
-
-            if (ch == '#') {
-                // Next color is here, parse what we had before.
+        if (ch == '!') {
+            // Repeat count
+            if (scanState == ScanState.COLOR) {
                 setPalette();
                 toGround();
             }
+            scanState = ScanState.REPEAT;
+            repeatCount = 0;
+            return;
+        }
 
-            if (ch == '!') {
+        if (ch == '-') {
+            if (scanState == ScanState.COLOR) {
                 setPalette();
                 toGround();
+            }
 
-                // Repeat count
-                scanState = ScanState.SIXEL_REPEAT;
+            if (height + 6 < image.getHeight()) {
+                // Resize the image, give us another HEIGHT_INCREASE
+                // pixels of vertical length.
+                resizeImage(image.getWidth(),
+                    image.getHeight() + HEIGHT_INCREASE);
             }
-            if (ch == '-') {
+            height += 6;
+            x = 0;
+            return;
+        }
+
+        if (ch == '$') {
+            if (scanState == ScanState.COLOR) {
                 setPalette();
                 toGround();
-
-                if (height + 6 < image.getHeight()) {
-                    // Resize the image, give us another HEIGHT_INCREASE
-                    // pixels of vertical length.
-                    resizeImage(image.getWidth(),
-                        image.getHeight() + HEIGHT_INCREASE);
-                }
-                height += 6;
-                x = 0;
             }
+            x = 0;
+            return;
+        }
 
-            if (ch == '$') {
+        if (ch == '"') {
+            if (scanState == ScanState.COLOR) {
                 setPalette();
                 toGround();
-
-                x = 0;
             }
+            scanState = ScanState.QUOTE;
             return;
+        }
 
-        case COLOR_PARAM:
+        switch (scanState) {
 
-            // Between decimal 63 (inclusive) and 189 (exclusive) --> pixels
-            if ((ch >= 63) && (ch < 189)) {
-                addSixel(ch);
-                return;
+        case GROUND:
+            // Unknown character.
+            if (DEBUG) {
+                System.err.println("UNKNOWN CHAR: " + ch);
             }
+            return;
+
+        case QUOTE:
+            // Ignore everything else in the quote header.
+            return;
 
-            // 30-39, 3B           --> param, then switch to COLOR_PARAM
+        case COLOR:
+            // 30-39, 3B --> param
             if ((ch >= '0') && (ch <= '9')) {
                 param((byte) ch);
             }
             if (ch == ';') {
                 param((byte) ch);
             }
-
-            if (ch == '#') {
-                // Next color is here, parse what we had before.
-                setPalette();
-                toGround();
-                scanState = ScanState.COLOR_ENTRY;
-            }
-
-            if (ch == '!') {
-                setPalette();
-                toGround();
-
-                // Repeat count
-                scanState = ScanState.SIXEL_REPEAT;
-            }
-            if (ch == '-') {
-                setPalette();
-                toGround();
-
-                if (height + 6 < image.getHeight()) {
-                    // Resize the image, give us another HEIGHT_INCREASE
-                    // pixels of vertical length.
-                    resizeImage(image.getWidth(),
-                        image.getHeight() + HEIGHT_INCREASE);
-                }
-                height += 6;
-                x = 0;
-            }
-
-            if (ch == '$') {
-                setPalette();
-                toGround();
-
-                x = 0;
-            }
             return;
 
-        case SIXEL_REPEAT:
-
-            // Between decimal 63 (inclusive) and 189 (exclusive) --> pixels
-            if ((ch >= 63) && (ch < 189)) {
-                addSixel(ch);
-                toGround();
-            }
-
+        case REPEAT:
             if ((ch >= '0') && (ch <= '9')) {
                 if (repeatCount == -1) {
                     repeatCount = (int) (ch - '0');
@@ -508,14 +448,8 @@ public class Sixel {
                     repeatCount += (int) (ch - '0');
                 }
             }
-
-            if (ch == '#') {
-                // Next color.
-                toGround();
-                scanState = ScanState.COLOR_ENTRY;
-            }
-
             return;
+
         }
 
     }