retrofit
[nikiroo-utils.git] / src / jexer / tterminal / ECMA48.java
index 8a88799026c054f064c29d2676fa2f50b8168781..33a35ca96f61c8e70631a5d4c6a72407aae5e955 100644 (file)
@@ -28,7 +28,7 @@
  */
 package jexer.tterminal;
 
-import java.awt.Graphics2D;
+import java.awt.Graphics;
 import java.awt.image.BufferedImage;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -274,7 +274,7 @@ public class ECMA48 implements Runnable {
     /**
      * The maximum number of lines in the scrollback buffer.
      */
-    private int maxScrollback = 10000;
+    private int scrollbackMax = 10000;
 
     /**
      * The terminal's input.  For type == XTERM, this is an InputStreamReader
@@ -506,6 +506,11 @@ public class ECMA48 implements Runnable {
      */
     private ArrayList<TInputEvent> userQueue = new ArrayList<TInputEvent>();
 
+    /**
+     * Number of bytes/characters passed to consume().
+     */
+    private long readCount = 0;
+
     /**
      * DECSC/DECRC save/restore a subset of the total state.  This class
      * encapsulates those specific flags/modes.
@@ -854,6 +859,34 @@ public class ECMA48 implements Runnable {
     // ECMA48 -----------------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Wait for a period of time to get output from the launched process.
+     *
+     * @param millis millis to wait for, or 0 to wait forever
+     * @return true if the launched process has emitted something
+     */
+    public boolean waitForOutput(final int millis) {
+        if (millis < 0) {
+            throw new IllegalArgumentException("timeout must be >= 0");
+        }
+        int waitedMillis = millis;
+        final int pollTimeout = 5;
+        while (true) {
+            if (readCount != 0) {
+                return true;
+            }
+            if ((millis > 0) && (waitedMillis < 0)){
+                return false;
+            }
+            try {
+                Thread.sleep(pollTimeout);
+            } catch (InterruptedException e) {
+                // SQUASH
+            }
+            waitedMillis -= pollTimeout;
+        }
+    }
+
     /**
      * Process keyboard and mouse events from the user.
      *
@@ -1025,11 +1058,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.
@@ -1227,10 +1255,29 @@ public class ECMA48 implements Runnable {
             display.add(line);
         }
         while (display.size() > height) {
-            scrollback.add(display.remove(0));
+            appendScrollbackLine(display.remove(0));
         }
     }
 
+    /**
+     * Get the maximum number of lines in the scrollback buffer.
+     *
+     * @return the maximum number of lines in the scrollback buffer
+     */
+    public int getScrollbackMax() {
+        return scrollbackMax;
+    }
+
+    /**
+     * Set the maximum number of lines for the scrollback buffer.
+     *
+     * @param scrollbackMax the maximum number of lines for the scrollback
+     * buffer
+     */
+    public final void setScrollbackMax(final int scrollbackMax) {
+        this.scrollbackMax = scrollbackMax;
+    }
+
     /**
      * Get visible cursor flag.
      *
@@ -1424,14 +1471,25 @@ public class ECMA48 implements Runnable {
         toGround();
     }
 
+    /**
+     * Append a to the scrollback buffer, clearing image data for lines more
+     * than three screenfuls in.
+     */
+    private void appendScrollbackLine(DisplayLine line) {
+        scrollback.add(line);
+        if (scrollback.size() > height * 3) {
+            scrollback.get(scrollback.size() - (height * 3)).clearImages();
+        }
+    }
+
     /**
      * Append a new line to the bottom of the display, adding lines off the
      * top to the scrollback buffer.
      */
     private void newDisplayLine() {
         // Scroll the top line off into the scrollback buffer
-        scrollback.add(display.get(0));
-        if (scrollback.size() > maxScrollback) {
+        appendScrollbackLine(display.get(0));
+        while (scrollback.size() > scrollbackMax) {
             scrollback.remove(0);
             scrollback.trimToSize();
         }
@@ -3199,10 +3257,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
                         }
                     }
                 }
@@ -4037,7 +4095,7 @@ public class ECMA48 implements Runnable {
 
                 case 8:
                     // Invisible
-                    // TODO
+                    // Not supported
                     break;
 
                 case 90:
@@ -4778,13 +4836,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);
@@ -4867,11 +4934,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);
 
@@ -4959,6 +5034,7 @@ public class ECMA48 implements Runnable {
      * @param ch character from the remote side
      */
     private void consume(final int ch) {
+        readCount++;
 
         // DEBUG
         // System.err.printf("%c STATE = %s\n", ch, scanState);
@@ -7293,8 +7369,21 @@ public class ECMA48 implements Runnable {
                 }
 
                 Cell cell = new Cell();
-                cell.setImage(image.getSubimage(x * textWidth,
-                        y * textHeight, width, height));
+                if ((width != textWidth) || (height != textHeight)) {
+                    BufferedImage newImage;
+                    newImage = new BufferedImage(textWidth, textHeight,
+                        BufferedImage.TYPE_INT_ARGB);
+
+                    Graphics gr = newImage.getGraphics();
+                    gr.drawImage(image.getSubimage(x * textWidth,
+                            y * textHeight, width, height),
+                        0, 0, null, null);
+                    gr.dispose();
+                    cell.setImage(newImage);
+                } else {
+                    cell.setImage(image.getSubimage(x * textWidth,
+                            y * textHeight, width, height));
+                }
 
                 cells[x][y] = cell;
             }
@@ -7306,9 +7395,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]);