performance
[fanfix.git] / src / jexer / tterminal / ECMA48.java
index c393ca50d46b3f5bd1909ff78e2d312f1f3d8cb7..ce06c36bebfb8fd525b262e3097359695c15dd34 100644 (file)
@@ -326,29 +326,29 @@ public class ECMA48 implements Runnable {
      * Physical display width.  We start at 80x24, but the user can resize us
      * bigger/smaller.
      */
-    private int width;
+    private int width = 80;
 
     /**
      * Physical display height.  We start at 80x24, but the user can resize
      * us bigger/smaller.
      */
-    private int height;
+    private int height = 24;
 
     /**
      * Top margin of the scrolling region.
      */
-    private int scrollRegionTop;
+    private int scrollRegionTop = 0;
 
     /**
      * Bottom margin of the scrolling region.
      */
-    private int scrollRegionBottom;
+    private int scrollRegionBottom = height - 1;
 
     /**
      * Right margin column number.  This can be selected by the remote side
      * to be 80/132 (rightMargin values 79/131), or it can be (width - 1).
      */
-    private int rightMargin;
+    private int rightMargin = 79;
 
     /**
      * Last character printed.
@@ -360,7 +360,7 @@ public class ECMA48 implements Runnable {
      * 132), but the line does NOT wrap until another character is written to
      * column 1 of the next line, after which the cursor moves to column 2.
      */
-    private boolean wrapLineFlag;
+    private boolean wrapLineFlag = false;
 
     /**
      * VT220 single shift flag.
@@ -673,6 +673,8 @@ public class ECMA48 implements Runnable {
         for (int i = 0; i < height; i++) {
             display.add(new DisplayLine(currentState.attr));
         }
+        assert (currentState.cursorY < height);
+        assert (currentState.cursorX < width);
 
         // Spin up the input reader
         readerThread = new Thread(this);
@@ -1023,11 +1025,6 @@ public class ECMA48 implements Runnable {
         // the input streams.
         if (stopReaderThread == false) {
             stopReaderThread = true;
-            try {
-                readerThread.join(1000);
-            } catch (InterruptedException e) {
-                // SQUASH
-            }
         }
 
         // Now close the output stream.
@@ -1207,8 +1204,8 @@ public class ECMA48 implements Runnable {
         int delta = height - this.height;
         this.height = height;
         scrollRegionBottom += delta;
-        if (scrollRegionBottom < 0) {
-            scrollRegionBottom = height;
+        if ((scrollRegionBottom < 0) || (scrollRegionTop > height - 1)) {
+            scrollRegionBottom = height - 1;
         }
         if (scrollRegionTop >= scrollRegionBottom) {
             scrollRegionTop = 0;
@@ -1378,8 +1375,13 @@ public class ECMA48 implements Runnable {
         currentState            = new SaveableState();
         savedState              = new SaveableState();
         scanState               = ScanState.GROUND;
-        width                   = 80;
-        height                  = 24;
+        if (displayListener != null) {
+            width = displayListener.getDisplayWidth();
+            height = displayListener.getDisplayHeight();
+        } else {
+            width               = 80;
+            height              = 24;
+        }
         scrollRegionTop         = 0;
         scrollRegionBottom      = height - 1;
         rightMargin             = width - 1;
@@ -1387,11 +1389,6 @@ public class ECMA48 implements Runnable {
         arrowKeyMode            = ArrowKeyMode.ANSI;
         keypadMode              = KeypadMode.Numeric;
         wrapLineFlag            = false;
-        if (displayListener != null) {
-            width = displayListener.getDisplayWidth();
-            height = displayListener.getDisplayHeight();
-            rightMargin         = width - 1;
-        }
 
         // Flags
         shiftOut                = false;
@@ -1474,7 +1471,6 @@ public class ECMA48 implements Runnable {
      * Handle a linefeed.
      */
     private void linefeed() {
-
         if (currentState.cursorY < scrollRegionBottom) {
             // Increment screen y
             currentState.cursorY++;
@@ -1685,35 +1681,45 @@ public class ECMA48 implements Runnable {
         if (mouseEncoding == MouseEncoding.SGR) {
             sb.append((char) 0x1B);
             sb.append("[<");
+            int buttons = 0;
 
             if (mouse.isMouse1()) {
                 if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
-                    sb.append("32;");
+                    buttons = 32;
                 } else {
-                    sb.append("0;");
+                    buttons = 0;
                 }
             } else if (mouse.isMouse2()) {
                 if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
-                    sb.append("33;");
+                    buttons = 33;
                 } else {
-                    sb.append("1;");
+                    buttons = 1;
                 }
             } else if (mouse.isMouse3()) {
                 if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
-                    sb.append("34;");
+                    buttons = 34;
                 } else {
-                    sb.append("2;");
+                    buttons = 2;
                 }
             } else if (mouse.isMouseWheelUp()) {
-                sb.append("64;");
+                buttons = 64;
             } else if (mouse.isMouseWheelDown()) {
-                sb.append("65;");
+                buttons = 65;
             } else {
                 // This is motion with no buttons down.
-                sb.append("35;");
+                buttons = 35;
+            }
+            if (mouse.isAlt()) {
+                buttons |= 0x08;
+            }
+            if (mouse.isCtrl()) {
+                buttons |= 0x10;
+            }
+            if (mouse.isShift()) {
+                buttons |= 0x04;
             }
 
-            sb.append(String.format("%d;%d", mouse.getX() + 1,
+            sb.append(String.format("%d;%d;%d", buttons, mouse.getX() + 1,
                     mouse.getY() + 1));
 
             if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
@@ -1727,35 +1733,46 @@ public class ECMA48 implements Runnable {
             sb.append((char) 0x1B);
             sb.append('[');
             sb.append('M');
+            int buttons = 0;
             if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
-                sb.append((char) (0x03 + 32));
+                buttons = 0x03 + 32;
             } else if (mouse.isMouse1()) {
                 if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
-                    sb.append((char) (0x00 + 32 + 32));
+                    buttons = 0x00 + 32 + 32;
                 } else {
-                    sb.append((char) (0x00 + 32));
+                    buttons = 0x00 + 32;
                 }
             } else if (mouse.isMouse2()) {
                 if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
-                    sb.append((char) (0x01 + 32 + 32));
+                    buttons = 0x01 + 32 + 32;
                 } else {
-                    sb.append((char) (0x01 + 32));
+                    buttons = 0x01 + 32;
                 }
             } else if (mouse.isMouse3()) {
                 if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
-                    sb.append((char) (0x02 + 32 + 32));
+                    buttons = 0x02 + 32 + 32;
                 } else {
-                    sb.append((char) (0x02 + 32));
+                    buttons = 0x02 + 32;
                 }
             } else if (mouse.isMouseWheelUp()) {
-                sb.append((char) (0x04 + 64));
+                buttons = 0x04 + 64;
             } else if (mouse.isMouseWheelDown()) {
-                sb.append((char) (0x05 + 64));
+                buttons = 0x05 + 64;
             } else {
                 // This is motion with no buttons down.
-                sb.append((char) (0x03 + 32));
+                buttons = 0x03 + 32;
+            }
+            if (mouse.isAlt()) {
+                buttons |= 0x08;
+            }
+            if (mouse.isCtrl()) {
+                buttons |= 0x10;
+            }
+            if (mouse.isShift()) {
+                buttons |= 0x04;
             }
 
+            sb.append((char) (buttons & 0xFF));
             sb.append((char) (mouse.getX() + 33));
             sb.append((char) (mouse.getY() + 33));
         }
@@ -3177,10 +3194,10 @@ public class ECMA48 implements Runnable {
                     if (decPrivateModeFlag == true) {
                         if (value == true) {
                             // Enable sixel scrolling (default).
-                            // TODO
+                            // Not supported
                         } else {
                             // Disable sixel scrolling.
-                            // TODO
+                            // Not supported
                         }
                     }
                 }
@@ -4015,7 +4032,7 @@ public class ECMA48 implements Runnable {
 
                 case 8:
                     // Invisible
-                    // TODO
+                    // Not supported
                     break;
 
                 case 90:
@@ -4400,6 +4417,9 @@ public class ECMA48 implements Runnable {
             // DECSTBM
             int top = getCsiParam(0, 1, 1, height) - 1;
             int bottom = getCsiParam(1, height, 1, height) - 1;
+            if (bottom > height - 1) {
+                bottom = height - 1;
+            }
 
             if (top > bottom) {
                 top = bottom;
@@ -4753,13 +4773,22 @@ public class ECMA48 implements Runnable {
     private void oscPut(final char xtermChar) {
         // System.err.println("oscPut: " + xtermChar);
 
+        boolean oscEnd = false;
+
+        if (xtermChar == 0x07) {
+            oscEnd = true;
+        }
+        if ((xtermChar == '\\')
+            && (collectBuffer.charAt(collectBuffer.length() - 1) == '\033')
+        ) {
+            oscEnd = true;
+        }
+
         // Collect first
         collectBuffer.append(xtermChar);
 
         // Xterm cases...
-        if ((xtermChar == 0x07)
-            || (collectBuffer.toString().endsWith("\033\\"))
-        ) {
+        if (oscEnd) {
             String args = null;
             if (xtermChar == 0x07) {
                 args = collectBuffer.substring(0, collectBuffer.length() - 1);
@@ -4842,11 +4871,19 @@ public class ECMA48 implements Runnable {
     private void pmPut(final char pmChar) {
         // System.err.println("pmPut: " + pmChar);
 
+        boolean pmEnd = false;
+
+        if ((pmChar == '\\')
+            && (collectBuffer.charAt(collectBuffer.length() - 1) == '\033')
+        ) {
+            pmEnd = true;
+        }
+
         // Collect first
         collectBuffer.append(pmChar);
 
         // Xterm cases...
-        if (collectBuffer.toString().endsWith("\033\\")) {
+        if (pmEnd) {
             String arg = null;
             arg = collectBuffer.substring(0, collectBuffer.length() - 2);
 
@@ -7107,8 +7144,7 @@ public class ECMA48 implements Runnable {
             return;
         }
 
-        java.util.Base64.Decoder base64 = java.util.Base64.getDecoder();
-        byte [] bytes = base64.decode(data);
+        byte [] bytes = StringUtils.fromBase64(data.getBytes());
         if (bytes.length != (imageWidth * imageHeight * 3)) {
             return;
         }
@@ -7154,8 +7190,7 @@ public class ECMA48 implements Runnable {
         boolean scroll = false;
         BufferedImage image = null;
         try {
-            java.util.Base64.Decoder base64 = java.util.Base64.getDecoder();
-            byte [] bytes = base64.decode(data);
+            byte [] bytes = StringUtils.fromBase64(data.getBytes());
 
             switch (type) {
             case 1:
@@ -7283,9 +7318,10 @@ public class ECMA48 implements Runnable {
             for (int x = 0; x < cellColumns; x++) {
                 assert (currentState.cursorX <= rightMargin);
 
-                // TODO: Render text of current cell first, then image over
-                // it (accounting for blank pixels).  For now, just copy the
-                // cell.
+                // A real sixel terminal would render the text of the current
+                // cell first, then image over it (accounting for blank
+                // pixels).  We do not support that.  A cell is either text,
+                // or image, but not a mix of image-over-text.
                 DisplayLine line = display.get(currentState.cursorY);
                 line.replace(currentState.cursorX, cells[x][y]);
 
@@ -7306,6 +7342,7 @@ public class ECMA48 implements Runnable {
                 // At the bottom, no more scrolling, done.
                 break;
             }
+
             cursorPosition(currentState.cursorY, x0);
         }