From 69a8c36844309a07dfe7c8c7576d9c0e47be3303 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Sun, 4 Aug 2019 13:03:12 -0500 Subject: [PATCH] sixel support for TTerminalWindow --- src/jexer/TImageWindow.java | 5 +- src/jexer/TScrollableWindow.java | 1 + src/jexer/TTerminalWindow.java | 18 ++- src/jexer/tterminal/ECMA48.java | 54 +++++++- src/jexer/tterminal/Sixel.java | 224 +++++++++++-------------------- 5 files changed, 145 insertions(+), 157 deletions(-) diff --git a/src/jexer/TImageWindow.java b/src/jexer/TImageWindow.java index 86b0d6d..057f6ac 100644 --- a/src/jexer/TImageWindow.java +++ b/src/jexer/TImageWindow.java @@ -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()); diff --git a/src/jexer/TScrollableWindow.java b/src/jexer/TScrollableWindow.java index 6817f40..1e260b3 100644 --- a/src/jexer/TScrollableWindow.java +++ b/src/jexer/TScrollableWindow.java @@ -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); diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index 818a52f..9780e29 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -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) { diff --git a/src/jexer/tterminal/ECMA48.java b/src/jexer/tterminal/ECMA48.java index f2b485e..c94d7d2 100644 --- a/src/jexer/tterminal/ECMA48.java +++ b/src/jexer/tterminal/ECMA48.java @@ -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. diff --git a/src/jexer/tterminal/Sixel.java b/src/jexer/tterminal/Sixel.java index 8d8429b..de3232c 100644 --- a/src/jexer/tterminal/Sixel.java +++ b/src/jexer/tterminal/Sixel.java @@ -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; + } } -- 2.27.0