many fixes
authorKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 21 Mar 2015 02:05:08 +0000 (22:05 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Sat, 21 Mar 2015 02:05:08 +0000 (22:05 -0400)
12 files changed:
README.md
src/jexer/TApplication.java
src/jexer/TTerminalWindow.java
src/jexer/TWindow.java
src/jexer/bits/GraphicsChars.java
src/jexer/event/TMouseEvent.java
src/jexer/io/AWTScreen.java
src/jexer/io/AWTTerminal.java
src/jexer/io/Screen.java
src/jexer/session/AWTSessionInfo.java
src/jexer/tterminal/DisplayLine.java
src/jexer/tterminal/ECMA48.java

index 058eb6f2d02667698054915c8ab61f1b53ed7369..570a0170c095aaf55cc2f6d15ef4ad13839f633f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -20,8 +20,9 @@ Two backends are available:
 
 * Java AWT UI.  This backend can be selected by setting
   jexer.AWT=true.  This is the default backend on Windows platforms.
-  AWT is VERY experimental, please consider filing bugs when you
-  encounter them.
+  AWT is experimental, please consider filing bugs when you encounter
+  them.  The default window size for AWT is 132x40, which is set in
+  jexer.session.AWTSession.
 
 A demo application showing the existing UI controls is available via
 'java -jar jexer.jar' or 'java -Djexer.AWT=true -jar jexer.jar' .
@@ -90,6 +91,9 @@ ambiguous.  This section describes such issues.
     is due to a Java limitation/interaction between blocking reads
     (necessary to get UTF8 translation correct) and file streams.
 
+  - See jexer.tterminal.ECMA48 for more specifics of terminal
+    emulation limitations.
+
 
 Roadmap
 -------
@@ -100,6 +104,7 @@ Many tasks remain before calling this version 1.0:
 
 - AWT:
   - Blinking cursor
+  - Handle kbTab (disable focus traversal BS)
 - ECMA48Backend running on socket
 - TTreeView
 - TDirectoryList
index 5813d6f3ca15402ebbee2fbc0f592744c4c5e8e3..0e5020a05896d3a843c0bc3303a5d77d3851b752 100644 (file)
@@ -122,17 +122,17 @@ public class TApplication {
                         }
 
                         synchronized (this) {
-                            /*
-                            System.err.printf("%s %s sleep\n", this, primary ?
-                                "primary" : "secondary");
-                             */
+                            if (debugThreads) {
+                                System.err.printf("%s %s sleep\n", this,
+                                    primary ? "primary" : "secondary");
+                            }
 
                             this.wait();
 
-                            /*
-                            System.err.printf("%s %s AWAKE\n", this, primary ?
-                                "primary" : "secondary");
-                             */
+                            if (debugThreads) {
+                                System.err.printf("%s %s AWAKE\n", this,
+                                    primary ? "primary" : "secondary");
+                            }
 
                             if ((!primary)
                                 && (application.secondaryEventReceiver == null)
@@ -141,13 +141,12 @@ public class TApplication {
                                 // got here then something went wrong with
                                 // the handoff between yield() and
                                 // closeWindow().
-
-                                System.err.printf("secondary exiting at wrong time, why?\n");
                                 synchronized (application.primaryEventHandler) {
                                     application.primaryEventHandler.notify();
                                 }
                                 application.secondaryEventHandler = null;
-                                return;
+                                throw new RuntimeException(
+                                        "secondary exited at wrong time");
                             }
                             break;
                         }
@@ -169,12 +168,12 @@ public class TApplication {
                     // the event.
                     boolean oldLock = lockHandleEvent();
                     assert (oldLock == false);
+                    application.repaint = true;
                     if (primary) {
                         primaryHandleEvent(event);
                     } else {
                         secondaryHandleEvent(event);
                     }
-                    application.repaint = true;
                     if ((!primary)
                         && (application.secondaryEventReceiver == null)
                     ) {
@@ -263,7 +262,7 @@ public class TApplication {
         synchronized (this) {
             // Wait for TApplication.run() to finish using the global state
             // before allowing further event processing.
-            while (lockoutHandleEvent == true);
+            while (lockoutHandleEvent == true) {}
 
             oldValue = insideHandleEvent;
             insideHandleEvent = true;
@@ -312,7 +311,7 @@ public class TApplication {
         lockoutHandleEvent = true;
         // Wait for the last event to finish processing before returning
         // control to TApplication.run().
-        while (insideHandleEvent == true);
+        while (insideHandleEvent == true) {}
 
         if (debugThreads) {
             System.err.printf(" XXX\n");
@@ -355,6 +354,16 @@ public class TApplication {
      */
     private int mouseY;
 
+    /**
+     * Old version of mouse coordinate X.
+     */
+    private int oldMouseX;
+
+    /**
+     * Old version mouse coordinate Y.
+     */
+    private int oldMouseY;
+
     /**
      * Event queue that is filled by run().
      */
@@ -420,12 +429,6 @@ public class TApplication {
      */
     private volatile boolean repaint = true;
 
-    /**
-     * When true, just flush updates from the screen.  This is only used to
-     * minimize physical writes for the mouse cursor.
-     */
-    private boolean flush = false;
-
     /**
      * Y coordinate of the top edge of the desktop.  For now this is a
      * constant.  Someday it would be nice to have a multi-line menu or
@@ -508,17 +511,17 @@ public class TApplication {
     }
 
     /**
-     * Invert the cell at the mouse pointer position.
+     * Invert the cell color at a position.  This is used to track the mouse.
+     *
+     * @param x column position
+     * @param y row position
      */
-    private void drawMouse() {
-        CellAttributes attr = getScreen().getAttrXY(mouseX, mouseY);
-        attr.setForeColor(attr.getForeColor().invert());
-        attr.setBackColor(attr.getBackColor().invert());
-        getScreen().putAttrXY(mouseX, mouseY, attr, false);
-        flush = true;
-
-        if (windows.size() == 0) {
-            repaint = true;
+    private void invertCell(final int x, final int y) {
+        synchronized (getScreen()) {
+            CellAttributes attr = getScreen().getAttrXY(x, y);
+            attr.setForeColor(attr.getForeColor().invert());
+            attr.setBackColor(attr.getBackColor().invert());
+            getScreen().putAttrXY(x, y, attr, false);
         }
     }
 
@@ -530,9 +533,20 @@ public class TApplication {
             System.err.printf("drawAll() enter\n");
         }
 
-        if ((flush) && (!repaint)) {
-            backend.flushScreen();
-            flush = false;
+        if (!repaint) {
+            synchronized (getScreen()) {
+                if ((oldMouseX != mouseX) || (oldMouseY != mouseY)) {
+                    // The only thing that has happened is the mouse moved.
+                    // Clear the old position and draw the new position.
+                    invertCell(oldMouseX, oldMouseY);
+                    invertCell(mouseX, mouseY);
+                    oldMouseX = mouseX;
+                    oldMouseY = mouseY;
+                }
+            }
+            if (getScreen().isDirty()) {
+                backend.flushScreen();
+            }
             return;
         }
 
@@ -598,7 +612,7 @@ public class TApplication {
         }
 
         // Draw the mouse pointer
-        drawMouse();
+        invertCell(mouseX, mouseY);
 
         // Place the cursor if it is visible
         TWidget activeWidget = null;
@@ -620,7 +634,6 @@ public class TApplication {
         backend.flushScreen();
 
         repaint = false;
-        flush = false;
     }
 
     /**
@@ -634,7 +647,9 @@ public class TApplication {
 
             // If I've got no updates to render, wait for something from the
             // backend or a timer.
-            if (!repaint && !flush) {
+            if (!repaint
+                && ((mouseX == oldMouseX) && (mouseY == oldMouseY))
+            ) {
                 // Never sleep longer than 100 millis, to get windows with
                 // background tasks an opportunity to update the display.
                 timeout = getSleepTime(100);
@@ -668,6 +683,7 @@ public class TApplication {
                     // I'm awake and don't care why, let's see what's going
                     // on out there.
                 }
+                repaint = true;
             }
 
             // Pull any pending I/O events
@@ -762,26 +778,32 @@ public class TApplication {
         // Screen resize
         if (event instanceof TResizeEvent) {
             TResizeEvent resize = (TResizeEvent) event;
-            getScreen().setDimensions(resize.getWidth(),
-                resize.getHeight());
-            desktopBottom = getScreen().getHeight() - 1;
-            repaint = true;
-            mouseX = 0;
-            mouseY = 0;
+            synchronized (getScreen()) {
+                getScreen().setDimensions(resize.getWidth(),
+                    resize.getHeight());
+                desktopBottom = getScreen().getHeight() - 1;
+                mouseX = 0;
+                mouseY = 0;
+                oldMouseX = 0;
+                oldMouseY = 0;
+            }
             return;
         }
 
         // Peek at the mouse position
         if (event instanceof TMouseEvent) {
             TMouseEvent mouse = (TMouseEvent) event;
-            if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) {
-                mouseX = mouse.getX();
-                mouseY = mouse.getY();
-                drawMouse();
+            synchronized (getScreen()) {
+                if ((mouseX != mouse.getX()) || (mouseY != mouse.getY())) {
+                    oldMouseX = mouseX;
+                    oldMouseY = mouseY;
+                    mouseX = mouse.getX();
+                    mouseY = mouse.getY();
+                }
             }
         }
 
-         // Put into the main queue
+        // Put into the main queue
         synchronized (drainEventQueue) {
             drainEventQueue.add(event);
         }
@@ -858,15 +880,16 @@ public class TApplication {
                 item = accelerators.get(keypressLowercase);
             }
             if (item != null) {
-                // Let the menu item dispatch
-                item.dispatch();
-                return;
-            } else {
-                // Handle the keypress
-                if (onKeypress(keypress)) {
+                if (item.getEnabled()) {
+                    // Let the menu item dispatch
+                    item.dispatch();
                     return;
                 }
             }
+            // Handle the keypress
+            if (onKeypress(keypress)) {
+                return;
+            }
         }
 
         if (event instanceof TCommandEvent) {
@@ -925,9 +948,6 @@ public class TApplication {
         secondaryEventReceiver = widget;
         secondaryEventHandler = new WidgetEventHandler(this, false);
         (new Thread(secondaryEventHandler)).start();
-
-        // Refresh
-        repaint = true;
     }
 
     /**
@@ -942,8 +962,6 @@ public class TApplication {
         boolean oldLock = unlockHandleEvent();
         assert (oldLock == true);
 
-        // System.err.printf("YIELD\n");
-
         while (secondaryEventReceiver != null) {
             synchronized (primaryEventHandler) {
                 try {
@@ -953,8 +971,6 @@ public class TApplication {
                 }
             }
         }
-
-        // System.err.printf("EXIT YIELD\n");
     }
 
     /**
@@ -992,7 +1008,7 @@ public class TApplication {
      * @param timeout = initial (maximum) timeout in millis
      * @return number of milliseconds between now and the next timer event
      */
-    protected long getSleepTime(final long timeout) {
+    private long getSleepTime(final long timeout) {
         Date now = new Date();
         long nowTime = now.getTime();
         long sleepTime = timeout;
@@ -1042,9 +1058,6 @@ public class TApplication {
         // Perform window cleanup
         window.onClose();
 
-        // Refresh screen
-        repaint = true;
-
         // Check if we are closing a TMessageBox or similar
         if (secondaryEventReceiver != null) {
             assert (secondaryEventHandler != null);
@@ -1107,8 +1120,6 @@ public class TApplication {
 
         } // synchronized (windows)
 
-        // Refresh
-        repaint = true;
     }
 
     /**
@@ -1211,7 +1222,6 @@ public class TApplication {
                     menu.setActive(false);
                 }
             }
-            repaint = true;
             return;
         }
 
@@ -1242,7 +1252,6 @@ public class TApplication {
                 // They switched menus
                 oldMenu.setActive(false);
             }
-            repaint = true;
             return;
         }
 
@@ -1278,7 +1287,6 @@ public class TApplication {
                     windows.get(0).setZ(window.getZ());
                     window.setZ(0);
                     window.setActive(true);
-                    repaint = true;
                     return;
                 }
             }
@@ -1300,7 +1308,6 @@ public class TApplication {
             }
             subMenus.clear();
         }
-        repaint = true;
     }
 
     /**
@@ -1312,7 +1319,6 @@ public class TApplication {
         assert (item != null);
         item.setActive(false);
         subMenus.remove(subMenus.size() - 1);
-        repaint = true;
     }
 
     /**
@@ -1343,7 +1349,6 @@ public class TApplication {
                 activeMenu.setActive(false);
                 activeMenu = menus.get(i);
                 activeMenu.setActive(true);
-                repaint = true;
                 return;
             }
         }
@@ -1363,29 +1368,24 @@ public class TApplication {
                     TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
                 quit = true;
             }
-            repaint = true;
             return true;
         }
 
         if (command.equals(cmShell)) {
             openTerminal(0, 0, TWindow.RESIZABLE);
-            repaint = true;
             return true;
         }
 
         if (command.equals(cmTile)) {
             tileWindows();
-            repaint = true;
             return true;
         }
         if (command.equals(cmCascade)) {
             cascadeWindows();
-            repaint = true;
             return true;
         }
         if (command.equals(cmCloseAll)) {
             closeAllWindows();
-            repaint = true;
             return true;
         }
 
@@ -1407,30 +1407,24 @@ public class TApplication {
                     TMessageBox.Type.YESNO).getResult() == TMessageBox.Result.YES) {
                 quit = true;
             }
-            // System.err.printf("onMenu MID_EXIT result: quit = %s\n", quit);
-            repaint = true;
             return true;
         }
 
         if (menu.getId() == TMenu.MID_SHELL) {
             openTerminal(0, 0, TWindow.RESIZABLE);
-            repaint = true;
             return true;
         }
 
         if (menu.getId() == TMenu.MID_TILE) {
             tileWindows();
-            repaint = true;
             return true;
         }
         if (menu.getId() == TMenu.MID_CASCADE) {
             cascadeWindows();
-            repaint = true;
             return true;
         }
         if (menu.getId() == TMenu.MID_CLOSE_ALL) {
             closeAllWindows();
-            repaint = true;
             return true;
         }
         return false;
@@ -1460,7 +1454,6 @@ public class TApplication {
                 ) {
                     activeMenu = menu;
                     menu.setActive(true);
-                    repaint = true;
                     return true;
                 }
             }
@@ -1478,8 +1471,6 @@ public class TApplication {
     public final void addAccelerator(final TMenuItem item,
         final TKeypress keypress) {
 
-        // System.err.printf("addAccelerator: key %s item %s\n", keypress, item);
-
         synchronized (accelerators) {
             assert (accelerators.get(keypress) == null);
             accelerators.put(keypress, item);
index 58e60e567c15387ffd82379f696af8e1f303ff5a..336c3ff8db7cd19c2a6761f98469cc869f0b6b78 100644 (file)
@@ -36,6 +36,7 @@ import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import jexer.bits.Cell;
 import jexer.bits.CellAttributes;
@@ -79,6 +80,9 @@ public class TTerminalWindow extends TWindow {
 
         super(application, "Terminal", x, y, 80 + 2, 24 + 2, flags);
 
+        // Assume XTERM
+        ECMA48.DeviceType deviceType = ECMA48.DeviceType.XTERM;
+
         try {
             String [] cmdShellWindows = {
                 "cmd.exe"
@@ -91,19 +95,21 @@ public class TTerminalWindow extends TWindow {
                 "script", "-fqe", "/dev/null"
             };
             // Spawn a shell and pass its I/O to the other constructor.
+
             ProcessBuilder pb;
             if (System.getProperty("os.name").startsWith("Windows")) {
                 pb = new ProcessBuilder(cmdShellWindows);
             } else {
                 pb = new ProcessBuilder(cmdShell);
             }
-            // shell = Runtime.getRuntime().exec(cmdShell);
-
-            // TODO: add LANG, TERM, LINES, and COLUMNS
+            Map<String, String> env = pb.environment();
+            env.put("TERM", ECMA48.deviceTypeTerm(deviceType));
+            env.put("LANG", ECMA48.deviceTypeLang(deviceType, "en"));
+            env.put("COLUMNS", "80");
+            env.put("LINES", "24");
             pb.redirectErrorStream(true);
             shell = pb.start();
-            emulator = new ECMA48(ECMA48.DeviceType.XTERM,
-                shell.getInputStream(),
+            emulator = new ECMA48(deviceType, shell.getInputStream(),
                 shell.getOutputStream());
         } catch (IOException e) {
             e.printStackTrace();
@@ -161,18 +167,13 @@ public class TTerminalWindow extends TWindow {
             List<DisplayLine> display = emulator.getDisplayBuffer();
 
             // Put together the visible rows
-            // System.err.printf("----------------------------\n");
-            // System.err.printf("vScroller.value %d\n", vScroller.getValue());
             int visibleHeight = getHeight() - 2;
-            // System.err.printf("visibleHeight %d\n", visibleHeight);
             int visibleBottom = scrollback.size() + display.size()
                 + vScroller.getValue();
-            // System.err.printf("visibleBottom %d\n", visibleBottom);
             assert (visibleBottom >= 0);
 
             List<DisplayLine> preceedingBlankLines = new LinkedList<DisplayLine>();
             int visibleTop = visibleBottom - visibleHeight;
-            // System.err.printf("visibleTop %d\n", visibleTop);
             if (visibleTop < 0) {
                 for (int i = visibleTop; i < 0; i++) {
                     preceedingBlankLines.add(emulator.getBlankDisplayLine());
@@ -184,16 +185,13 @@ public class TTerminalWindow extends TWindow {
             List<DisplayLine> displayLines = new LinkedList<DisplayLine>();
             displayLines.addAll(scrollback);
             displayLines.addAll(display);
-            // System.err.printf("displayLines.size %d\n", displayLines.size());
 
             List<DisplayLine> visibleLines = new LinkedList<DisplayLine>();
             visibleLines.addAll(preceedingBlankLines);
             visibleLines.addAll(displayLines.subList(visibleTop,
                     visibleBottom));
-            // System.err.printf("visibleLines.size %d\n", visibleLines.size());
 
             visibleHeight -= visibleLines.size();
-            // System.err.printf("visibleHeight %d\n", visibleHeight);
             assert (visibleHeight >= 0);
 
             // Now draw the emulator screen
@@ -275,7 +273,6 @@ public class TTerminalWindow extends TWindow {
                     setTitle(emulator.getScreenTitle());
                 }
             }
-            setMaximumWindowWidth(emulator.getWidth() + 2);
 
             // Check to see if the shell has died.
             if (!emulator.isReading() && (shell != null)) {
@@ -355,6 +352,31 @@ public class TTerminalWindow extends TWindow {
         } // synchronized (emulator)
     }
 
+    /**
+     * Check if a mouse press/release/motion event coordinate is over the
+     * emulator.
+     *
+     * @param mouse a mouse-based event
+     * @return whether or not the mouse is on the emulator
+     */
+    private final boolean mouseOnEmulator(final TMouseEvent mouse) {
+
+        synchronized (emulator) {
+            if (!emulator.isReading()) {
+                return false;
+            }
+        }
+
+        if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1)
+            && (mouse.getAbsoluteX() <  getAbsoluteX() + getWidth() - 1)
+            && (mouse.getAbsoluteY() >= getAbsoluteY() + 1)
+            && (mouse.getAbsoluteY() <  getAbsoluteY() + getHeight() - 1)
+        ) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Handle keystrokes.
      *
@@ -411,6 +433,11 @@ public class TTerminalWindow extends TWindow {
      */
     @Override
     public void onMouseDown(final TMouseEvent mouse) {
+        if (inWindowMove || inWindowResize) {
+            // TWindow needs to deal with this.
+            super.onMouseDown(mouse);
+            return;
+        }
 
         if (mouse.getMouseWheelUp()) {
             vScroller.decrement();
@@ -420,9 +447,72 @@ public class TTerminalWindow extends TWindow {
             vScroller.increment();
             return;
         }
+        if (mouseOnEmulator(mouse)) {
+            synchronized (emulator) {
+                mouse.setX(mouse.getX() - 1);
+                mouse.setY(mouse.getY() - 1);
+                emulator.mouse(mouse);
+                readEmulatorState();
+                return;
+            }
+        }
 
-        // Pass to children
+        // Emulator didn't consume it, pass it on
         super.onMouseDown(mouse);
     }
 
+    /**
+     * Handle mouse release events.
+     *
+     * @param mouse mouse button release event
+     */
+    @Override
+    public void onMouseUp(final TMouseEvent mouse) {
+        if (inWindowMove || inWindowResize) {
+            // TWindow needs to deal with this.
+            super.onMouseUp(mouse);
+            return;
+        }
+
+        if (mouseOnEmulator(mouse)) {
+            synchronized (emulator) {
+                mouse.setX(mouse.getX() - 1);
+                mouse.setY(mouse.getY() - 1);
+                emulator.mouse(mouse);
+                readEmulatorState();
+                return;
+            }
+        }
+
+        // Emulator didn't consume it, pass it on
+        super.onMouseUp(mouse);
+    }
+
+    /**
+     * Handle mouse motion events.
+     *
+     * @param mouse mouse motion event
+     */
+    @Override
+    public void onMouseMotion(final TMouseEvent mouse) {
+        if (inWindowMove || inWindowResize) {
+            // TWindow needs to deal with this.
+            super.onMouseMotion(mouse);
+            return;
+        }
+
+        if (mouseOnEmulator(mouse)) {
+            synchronized (emulator) {
+                mouse.setX(mouse.getX() - 1);
+                mouse.setY(mouse.getY() - 1);
+                emulator.mouse(mouse);
+                readEmulatorState();
+                return;
+            }
+        }
+
+        // Emulator didn't consume it, pass it on
+        super.onMouseMotion(mouse);
+    }
+
 }
index 0a74a40dcc830f8cf739c0ad0f56c9c7ea4f943a..e1d0299b5f7463cec7eec3dad3b50276f1042824 100644 (file)
@@ -143,13 +143,13 @@ public class TWindow extends TWidget {
      * If true, then the user clicked on the title bar and is moving the
      * window.
      */
-    private boolean inWindowMove = false;
+    protected boolean inWindowMove = false;
 
     /**
      * If true, then the user clicked on the bottom right corner and is
      * resizing the window.
      */
-    private boolean inWindowResize = false;
+    protected boolean inWindowResize = false;
 
     /**
      * If true, then the user selected "Size/Move" (or hit Ctrl-F5) and is
index 3f2ab92113a6a23783a028344e41cee1c6621147..5093bcf78a7ff13db0764ce57eafca5ce63237cf 100644 (file)
@@ -50,7 +50,10 @@ public final class GraphicsChars {
         '\u2666', '\u2663', '\u2660', '\u2022',
         '\u25D8', '\u25CB', '\u25D9', '\u2642',
         '\u2640', '\u266A', '\u266B', '\u263C',
-        '\u25BA', '\u25C4', '\u2195', '\u203C',
+        // Terminus has 25B6 and 25C0 here, which I believe are better
+        // Unicode equivalents anyway.
+        // '\u25BA', '\u25C4', '\u2195', '\u203C',
+        '\u25B6', '\u25C0', '\u2195', '\u203C',
         '\u00B6', '\u00A7', '\u25AC', '\u21A8',
         '\u2191', '\u2193', '\u2192', '\u2190',
         '\u221F', '\u2194', '\u25B2', '\u25BC',
index 8dcaed2778b462a59c407193b76532f0f90040f3..2f801ccfcfdc2c858df46013a99f4194d86cebcc 100644 (file)
@@ -58,8 +58,7 @@ public final class TMouseEvent extends TInputEvent {
     }
 
     /**
-     * Type of event, one of MOUSE_MOTION, MOUSE_UP, or MOUSE_DOWN, or
-     * KEYPRESS.
+     * Type of event, one of MOUSE_MOTION, MOUSE_UP, or MOUSE_DOWN.
      */
     private Type type;
 
index a3fc41f19fa03563ca292c5f4feed4e28fd0c33f..308a285c38074f5055bf8db292df0202e27b9f5b 100644 (file)
@@ -256,8 +256,6 @@ public final class AWTScreen extends Screen {
                 setFont(new Font(Font.MONOSPACED, Font.PLAIN, 24));
             }
             pack();
-            setVisible(true);
-            resizeToScreen();
 
             // Kill the X11 cursor
             // Transparent 16 x 16 pixel cursor image.
@@ -268,12 +266,13 @@ public final class AWTScreen extends Screen {
             Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(
                 cursorImg, new Point(0, 0), "blank cursor");
             setCursor(blankCursor);
+            getFontDimensions();
         }
 
         /**
-         * Resize to font dimensions.
+         * Figure out my font dimensions.
          */
-        public void resizeToScreen() {
+        private void getFontDimensions() {
             Graphics gr = getGraphics();
             FontMetrics fm = gr.getFontMetrics();
             maxDescent = fm.getMaxDescent();
@@ -284,7 +283,12 @@ public final class AWTScreen extends Screen {
             // This also produces the same number, but works better for ugly
             // monospace.
             textHeight = fm.getMaxAscent() + maxDescent - leading;
+        }
 
+        /**
+         * Resize to font dimensions.
+         */
+        public void resizeToScreen() {
             // Figure out the thickness of borders and use that to set the
             // final size.
             Insets insets = getInsets();
@@ -293,11 +297,6 @@ public final class AWTScreen extends Screen {
 
             setSize(textWidth * screen.width + insets.left + insets.right,
                 textHeight * screen.height + insets.top + insets.bottom);
-
-            /*
-            System.err.printf("W: %d H: %d MD: %d L: %d\n", textWidth,
-                textHeight, maxDescent, leading);
-             */
         }
 
         /**
@@ -336,7 +335,7 @@ public final class AWTScreen extends Screen {
             if (bounds != null) {
                 // Only update what is in the bounds
                 xCellMin = screen.textColumn(bounds.x);
-                xCellMax = screen.textColumn(bounds.x + bounds.width) + 1;
+                xCellMax = screen.textColumn(bounds.x + bounds.width);
                 if (xCellMax > screen.width) {
                     xCellMax = screen.width;
                 }
@@ -347,7 +346,7 @@ public final class AWTScreen extends Screen {
                     xCellMin = 0;
                 }
                 yCellMin = screen.textRow(bounds.y);
-                yCellMax = screen.textRow(bounds.y + bounds.height) + 1;
+                yCellMax = screen.textRow(bounds.y + bounds.height);
                 if (yCellMax > screen.height) {
                     yCellMax = screen.height;
                 }
@@ -357,6 +356,9 @@ public final class AWTScreen extends Screen {
                 if (yCellMin < 0) {
                     yCellMin = 0;
                 }
+            } else {
+                // We need a total repaint
+                reallyCleared = true;
             }
 
             // Prevent updates to the screen's data from the TApplication
@@ -409,7 +411,8 @@ public final class AWTScreen extends Screen {
                 reallyCleared = false;
             } // synchronized (screen)
         }
-    }
+
+    } // class AWTFrame
 
     /**
      * The raw AWT Frame.  Note package private access.
@@ -424,6 +427,16 @@ public final class AWTScreen extends Screen {
             SwingUtilities.invokeAndWait(new Runnable() {
                 public void run() {
                     AWTScreen.this.frame = new AWTFrame(AWTScreen.this);
+                    AWTScreen.this.sessionInfo =
+                        new AWTSessionInfo(AWTScreen.this.frame,
+                            frame.textWidth,
+                            frame.textHeight);
+
+                    AWTScreen.this.setDimensions(sessionInfo.getWindowWidth(),
+                        sessionInfo.getWindowHeight());
+
+                    AWTScreen.this.frame.resizeToScreen();
+                    AWTScreen.this.frame.setVisible(true);
                 }
             } );
         } catch (Exception e) {
@@ -431,14 +444,17 @@ public final class AWTScreen extends Screen {
         }
     }
 
+    /**
+     * The sessionInfo.
+     */
+    private AWTSessionInfo sessionInfo;
+
     /**
      * Create the AWTSessionInfo.  Note package private access.
      *
      * @return the sessionInfo
      */
     AWTSessionInfo getSessionInfo() {
-        AWTSessionInfo sessionInfo = new AWTSessionInfo(frame, frame.textWidth,
-            frame.textHeight);
         return sessionInfo;
     }
 
index 27ba80aa12b7180accfd394e9c340627fe32a9f6..783cffd4ec5f1fba6e6cd2397dd70a37d380a2a5 100644 (file)
@@ -219,7 +219,7 @@ public final class AWTTerminal implements ComponentListener, KeyListener,
         System.err.printf("   ctrl: %s\n", ctrl);
         System.err.printf("   shift: %s\n", shift);
         System.err.printf("   ch: %s\n", ch);
-         */
+        */
 
         // Special case: not return the bare modifier presses
         switch (key.getKeyCode()) {
@@ -368,6 +368,9 @@ public final class AWTTerminal implements ComponentListener, KeyListener,
             case 0x0D:
                 keypress = kbEnter;
                 break;
+            case 0x09:
+                keypress = kbTab;
+                break;
             case 0x7F:
                 keypress = kbDel;
                 break;
@@ -396,7 +399,10 @@ public final class AWTTerminal implements ComponentListener, KeyListener,
      */
     @Override
     public void windowActivated(final WindowEvent event) {
-        // Ignore
+        // Force a total repaint
+        synchronized (screen) {
+            screen.clearPhysical();
+        }
     }
 
     /**
index e3f72191809b73565397c98c8b8523c800b9f8be..1ecc4b185e0ee1eb8352211882c55bb39003d27b 100644 (file)
@@ -183,7 +183,7 @@ public abstract class Screen {
     /**
      * When true, logical != physical.
      */
-    protected boolean dirty;
+    protected volatile boolean dirty;
 
     /**
      * Get dirty flag.
@@ -225,7 +225,6 @@ public abstract class Screen {
      * @return attributes at (x, y)
      */
     public final CellAttributes getAttrXY(final int x, final int y) {
-
         CellAttributes attr = new CellAttributes();
         if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
             attr.setTo(logical[x][y]);
@@ -591,12 +590,24 @@ public abstract class Screen {
     }
 
     /**
-     * Force the screen to be fully cleared and redrawn on the next flush().
+     * Clear the logical screen.
      */
     public final void clear() {
         reset();
     }
 
+    /**
+     * Clear the physical screen.
+     */
+    public final void clearPhysical() {
+        dirty = true;
+        for (int row = 0; row < height; row++) {
+            for (int col = 0; col < width; col++) {
+                physical[col][row].reset();
+            }
+        }
+    }
+
     /**
      * Draw a box with a border and empty background.
      *
index 088da9e009ea3c459163124008cab2b57a804f60..1c9bf97aaf22da574d7d9bb9f34fa7815d4e07e5 100644 (file)
@@ -36,7 +36,7 @@ import java.awt.Insets;
 /**
  * AWTSessionInfo provides a session implementation with a callback into an
  * AWT Frame to support queryWindowSize().  The username is blank, language
- * is "en_US", with a 80x24 text window.
+ * is "en_US", with a 132x40 text window.
  */
 public final class AWTSessionInfo implements SessionInfo {
 
@@ -68,12 +68,12 @@ public final class AWTSessionInfo implements SessionInfo {
     /**
      * Text window width.
      */
-    private int windowWidth = 80;
+    private int windowWidth = 132;
 
     /**
      * Text window height.
      */
-    private int windowHeight = 24;
+    private int windowHeight = 40;
 
     /**
      * Username getter.
index 3f2cfd75b5a0685b59c3e56ba7bd0327e9a41481..a243cdbbc3844e8b023b088c133548b942c3d115 100644 (file)
@@ -173,7 +173,7 @@ public final class DisplayLine {
      * @param newCell the new Cell
      */
     public void replace(final int idx, final Cell newCell) {
-        chars[idx] = newCell;
+        chars[idx].setTo(newCell);
     }
 
     /**
@@ -208,8 +208,8 @@ public final class DisplayLine {
     }
 
     /**
-     * Delete character at the specified position, filling in new characters
-     * on the right with newCell.
+     * Delete character at the specified position, filling in the new
+     * character on the right with newCell.
      *
      * @param idx the character index
      * @param newCell the new Cell
index 66ee95e235f96be49b701c0d1336ca240f7b22f5..df157cf4af9bbdee13e7dcd245e45a1a12c1f767 100644 (file)
@@ -43,10 +43,11 @@ import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
+import jexer.TKeypress;
+import jexer.event.TMouseEvent;
 import jexer.bits.Color;
 import jexer.bits.Cell;
 import jexer.bits.CellAttributes;
-import jexer.TKeypress;
 import static jexer.TKeypress.*;
 
 /**
@@ -58,6 +59,11 @@ import static jexer.TKeypress.*;
  * caveats:
  *
  * <p>
+ * - The vttest scenario for VT220 8-bit controls (11.1.2.3) reports a
+ *   failure with XTERM.  This is due to vttest failing to decode the UTF-8
+ *   stream.
+ *
+ * <p>
  * - Smooth scrolling, printing, keyboard locking, keyboard leds, and tests
  *   from VT100 are not supported.
  *
@@ -142,24 +148,26 @@ public class ECMA48 implements Runnable {
     /**
      * Return the proper TERM environment variable for this device type.
      *
-     * @return "TERM=vt100", "TERM=xterm", etc.
+     * @param deviceType DeviceType.VT100, DeviceType, XTERM, etc.
+     * @return "vt100", "xterm", etc.
      */
-    public String deviceTypeTerm() {
-        switch (type) {
+    public static String deviceTypeTerm(final DeviceType deviceType) {
+        switch (deviceType) {
         case VT100:
-            return "TERM=vt100";
+            return "vt100";
 
         case VT102:
-            return "TERM=vt102";
+            return "vt102";
 
         case VT220:
-            return "TERM=vt220";
+            return "vt220";
 
         case XTERM:
-            return "TERM=xterm";
+            return "xterm";
 
         default:
-            throw new IllegalArgumentException("Invalid device type: " + type);
+            throw new IllegalArgumentException("Invalid device type: "
+                + deviceType);
         }
     }
 
@@ -168,27 +176,27 @@ public class ECMA48 implements Runnable {
      * about UTF-8, the others are defined by their standard to be either
      * 7-bit or 8-bit characters only.
      *
+     * @param deviceType DeviceType.VT100, DeviceType, XTERM, etc.
      * @param baseLang a base language without UTF-8 flag such as "C" or
      * "en_US"
      * @return "LANG=en_US", "LANG=en_US.UTF-8", etc.
      */
-    public String deviceTypeLang(final String baseLang) {
-        switch (type) {
+    public static String deviceTypeLang(final DeviceType deviceType,
+        final String baseLang) {
 
-        case VT100:
-            return "LANG=" + baseLang;
+        switch (deviceType) {
 
+        case VT100:
         case VT102:
-            return "LANG=" + baseLang;
-
         case VT220:
-            return "LANG=" + baseLang;
+            return baseLang;
 
         case XTERM:
-            return "LANG=" + baseLang + ".UTF-8";
+            return baseLang + ".UTF-8";
 
         default:
-            throw new IllegalArgumentException("Invalid device type: " + type);
+            throw new IllegalArgumentException("Invalid device type: "
+                + deviceType);
         }
     }
 
@@ -493,6 +501,35 @@ public class ECMA48 implements Runnable {
         G3_GL
     }
 
+    /**
+     * XTERM mouse reporting protocols.
+     */
+    private enum MouseProtocol {
+        OFF,
+        X10,
+        NORMAL,
+        BUTTONEVENT,
+        ANYEVENT
+    }
+
+    /**
+     * Which mouse protocol is active.
+     */
+    private MouseProtocol mouseProtocol = MouseProtocol.OFF;
+
+    /**
+     * XTERM mouse reporting encodings.
+     */
+    private enum MouseEncoding {
+        X10,
+        UTF8
+    }
+
+    /**
+     * Which mouse encoding is active.
+     */
+    private MouseEncoding mouseEncoding = MouseEncoding.X10;
+
     /**
      * Physical display width.  We start at 80x24, but the user can resize us
      * bigger/smaller.
@@ -598,12 +635,6 @@ public class ECMA48 implements Runnable {
         return screenTitle;
     }
 
-    /**
-     * Array of flags that have come in, e.g. '?' (DEC private mode), '=',
-     * '>' (&lt;), ...
-     */
-    private List<Character> csiFlags;
-
     /**
      * Parameter characters being collected.
      */
@@ -612,7 +643,7 @@ public class ECMA48 implements Runnable {
     /**
      * Non-csi collect buffer.
      */
-    private List<Character> collectBuffer;
+    private StringBuilder collectBuffer;
 
     /**
      * When true, use the G1 character set.
@@ -796,8 +827,7 @@ public class ECMA48 implements Runnable {
      */
     private void toGround() {
         csiParams.clear();
-        csiFlags.clear();
-        collectBuffer.clear();
+        collectBuffer = new StringBuilder(8);
         scanState = ScanState.GROUND;
     }
 
@@ -844,6 +874,10 @@ public class ECMA48 implements Runnable {
         s8c1t                   = false;
         printerControllerMode   = false;
 
+        // XTERM
+        mouseProtocol           = MouseProtocol.OFF;
+        mouseEncoding           = MouseEncoding.X10;
+
         // Tab stops
         resetTabStops();
 
@@ -871,9 +905,7 @@ public class ECMA48 implements Runnable {
         assert (inputStream != null);
         assert (outputStream != null);
 
-        csiFlags          = new ArrayList<Character>();
         csiParams         = new ArrayList<Integer>();
-        collectBuffer     = new ArrayList<Character>();
         tabStops          = new ArrayList<Integer>();
         scrollback        = new LinkedList<DisplayLine>();
         display           = new LinkedList<DisplayLine>();
@@ -1057,6 +1089,103 @@ public class ECMA48 implements Runnable {
         }
     }
 
+    /**
+     * Translate the mouse event to a VT100, VT220, or XTERM sequence and
+     * send to the remote side.
+     *
+     * @param mouse mouse event received from the local user
+     */
+    public void mouse(final TMouseEvent mouse) {
+        /*
+         * TODO:
+         *
+         * - Parse the mouse requests from the remote side regarding protocol
+         *   + encoding
+         *
+         * - Send mouse events to the other side.
+         *
+         *  - Handle the cursor (double invert).
+         */
+
+        // System.err.printf("Mouse: %s\n", mouse);
+
+        /*
+        if (mouseEncoding != MouseEncoding.UTF8) {
+            // We only support UTF8 encoding, bail out now.
+            return;
+        }
+         */
+
+        switch (mouseProtocol) {
+
+        case OFF:
+            // Do nothing
+            return;
+
+        case X10:
+            // Only report button presses
+            if (mouse.getType() != TMouseEvent.Type.MOUSE_DOWN) {
+                return;
+            }
+            break;
+
+        case NORMAL:
+            // Only report button presses and releases
+            if ((mouse.getType() != TMouseEvent.Type.MOUSE_DOWN)
+                && (mouse.getType() != TMouseEvent.Type.MOUSE_UP)
+            ) {
+                return;
+            }
+            break;
+
+        case BUTTONEVENT:
+            /*
+             * Only report button presses, button releases, and motions that
+             * have a button down (i.e. drag-and-drop).
+             */
+            if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
+                if (!mouse.getMouse1()
+                    && !mouse.getMouse2()
+                    && !mouse.getMouse3()
+                    && !mouse.getMouseWheelUp()
+                    && !mouse.getMouseWheelDown()
+                ) {
+                    return;
+                }
+            }
+            break;
+
+        case ANYEVENT:
+            // Report everything
+            break;
+        }
+
+        // Now encode the event
+        StringBuilder sb = new StringBuilder(6);
+        sb.append((char) 0x1B);
+        sb.append('[');
+        sb.append('M');
+        if (mouse.getMouse1()) {
+            sb.append((char) (0x00 + 32));
+        } else if (mouse.getMouse2()) {
+            sb.append((char) (0x01 + 32));
+        } else if (mouse.getMouse3()) {
+            sb.append((char) (0x02 + 32));
+        } else if (mouse.getMouseWheelUp()) {
+            sb.append((char) (0x04 + 64));
+        } else if (mouse.getMouseWheelDown()) {
+            sb.append((char) (0x05 + 64));
+        } else {
+            sb.append((char) (0x03 + 32));
+        }
+
+        sb.append((char) (mouse.getX() + 33));
+        sb.append((char) (mouse.getY() + 33));
+
+        // System.err.printf("Would write: \'%s\'\n", sb.toString());
+        writeRemote(sb.toString());
+    }
+
     /**
      * Translate the keyboard press to a VT100, VT220, or XTERM sequence and
      * send to the remote side.
@@ -1933,7 +2062,7 @@ public class ECMA48 implements Runnable {
      * @param ch character to save
      */
     private void collect(final char ch) {
-        collectBuffer.add(new Character(ch));
+        collectBuffer.append(ch);
     }
 
     /**
@@ -2000,9 +2129,10 @@ public class ECMA48 implements Runnable {
      */
     private void setToggle(final boolean value) {
         boolean decPrivateModeFlag = false;
-        for (Character ch: collectBuffer) {
-            if (ch == '?') {
+        for (int i = 0; i < collectBuffer.length(); i++) {
+            if (collectBuffer.charAt(i) == '?') {
                 decPrivateModeFlag = true;
+                break;
             }
         }
 
@@ -2256,6 +2386,58 @@ public class ECMA48 implements Runnable {
 
                 break;
 
+            case 1000:
+                if ((type == DeviceType.XTERM)
+                    && (decPrivateModeFlag == true)
+                ) {
+                    // Mouse: normal tracking mode
+                    if (value == true) {
+                        mouseProtocol = MouseProtocol.NORMAL;
+                    } else {
+                        mouseProtocol = MouseProtocol.OFF;
+                    }
+                }
+                break;
+
+            case 1002:
+                if ((type == DeviceType.XTERM)
+                    && (decPrivateModeFlag == true)
+                ) {
+                    // Mouse: normal tracking mode
+                    if (value == true) {
+                        mouseProtocol = MouseProtocol.BUTTONEVENT;
+                    } else {
+                        mouseProtocol = MouseProtocol.OFF;
+                    }
+                }
+                break;
+
+            case 1003:
+                if ((type == DeviceType.XTERM)
+                    && (decPrivateModeFlag == true)
+                ) {
+                    // Mouse: Any-event tracking mode
+                    if (value == true) {
+                        mouseProtocol = MouseProtocol.ANYEVENT;
+                    } else {
+                        mouseProtocol = MouseProtocol.OFF;
+                    }
+                }
+                break;
+
+            case 1005:
+                if ((type == DeviceType.XTERM)
+                    && (decPrivateModeFlag == true)
+                ) {
+                    // Mouse: UTF-8 coordinates
+                    if (value == true) {
+                        mouseEncoding = MouseEncoding.UTF8;
+                    } else {
+                        mouseEncoding = MouseEncoding.X10;
+                    }
+                }
+                break;
+
             default:
                 break;
 
@@ -2677,9 +2859,10 @@ public class ECMA48 implements Runnable {
         boolean honorProtected = false;
         boolean decPrivateModeFlag = false;
 
-        for (Character ch: collectBuffer) {
-            if (ch == '?') {
+        for (int i = 0; i < collectBuffer.length(); i++) {
+            if (collectBuffer.charAt(i) == '?') {
                 decPrivateModeFlag = true;
+                break;
             }
         }
 
@@ -2716,9 +2899,10 @@ public class ECMA48 implements Runnable {
         boolean honorProtected = false;
         boolean decPrivateModeFlag = false;
 
-        for (Character ch: collectBuffer) {
-            if (ch == '?') {
+        for (int i = 0; i < collectBuffer.length(); i++) {
+            if (collectBuffer.charAt(i) == '?') {
                 decPrivateModeFlag = true;
+                break;
             }
         }
 
@@ -3058,21 +3242,16 @@ public class ECMA48 implements Runnable {
     private void da() {
         int extendedFlag = 0;
         int i = 0;
-        Character [] chars = collectBuffer.toArray(new Character[0]);
-        StringBuilder args = new StringBuilder();
-        for (int j = 1; j < chars.length; j++) {
-            args.append(chars[j]);
-        }
-
-        if (chars.length > 0) {
-            if (chars[0] == '>') {
+        if (collectBuffer.length() > 0) {
+            String args = collectBuffer.substring(1);
+            if (collectBuffer.charAt(0) == '>') {
                 extendedFlag = 1;
-                if (chars.length >= 2) {
+                if (collectBuffer.length() >= 2) {
                     i = Integer.parseInt(args.toString());
                 }
-            } else if (chars[0] == '=') {
+            } else if (collectBuffer.charAt(0) == '=') {
                 extendedFlag = 2;
-                if (chars.length >= 2) {
+                if (collectBuffer.length() >= 2) {
                     i = Integer.parseInt(args.toString());
                 }
             } else {
@@ -3211,9 +3390,10 @@ public class ECMA48 implements Runnable {
     private void dsr() {
         boolean decPrivateModeFlag = false;
 
-        for (Character ch: collectBuffer) {
-            if (ch == '?') {
+        for (int i = 0; i < collectBuffer.length(); i++) {
+            if (collectBuffer.charAt(i) == '?') {
                 decPrivateModeFlag = true;
+                break;
             }
         }
 
@@ -3420,9 +3600,10 @@ public class ECMA48 implements Runnable {
      */
     private void printerFunctions() {
         boolean decPrivateModeFlag = false;
-        for (Character ch: collectBuffer) {
-            if (ch == '?') {
+        for (int i = 0; i < collectBuffer.length(); i++) {
+            if (collectBuffer.charAt(i) == '?') {
                 decPrivateModeFlag = true;
+                break;
             }
         }
 
@@ -3480,15 +3661,12 @@ public class ECMA48 implements Runnable {
      */
     private void oscPut(final char xtermChar) {
         // Collect first
-        collectBuffer.add(new Character(xtermChar));
+        collectBuffer.append(xtermChar);
 
         // Xterm cases...
         if (xtermChar == 0x07) {
-            Character [] chars = collectBuffer.toArray(new Character[0]);
-            StringBuilder args = new StringBuilder();
-            for (int j = 0; j < chars.length - 1; j++) {
-                args.append(chars[j]);
-            }
+            String args = collectBuffer.substring(0,
+                collectBuffer.length() - 1);
             String [] p = args.toString().split(";");
             if (p.length > 0) {
                 if ((p[0].equals("0")) || (p[0].equals("2"))) {
@@ -3960,150 +4138,150 @@ public class ECMA48 implements Runnable {
             if ((ch >= 0x30) && (ch <= 0x7E)) {
                 switch (ch) {
                 case '0':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '(')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '(')) {
                         // G0 --> Special graphics
                         currentState.g0Charset = CharacterSet.DRAWING;
                     }
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == ')')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == ')')) {
                         // G1 --> Special graphics
                         currentState.g1Charset = CharacterSet.DRAWING;
                     }
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> Special graphics
                             currentState.g2Charset = CharacterSet.DRAWING;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> Special graphics
                             currentState.g3Charset = CharacterSet.DRAWING;
                         }
                     }
                     break;
                 case '1':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '(')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '(')) {
                         // G0 --> Alternate character ROM standard character set
                         currentState.g0Charset = CharacterSet.ROM;
                     }
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == ')')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == ')')) {
                         // G1 --> Alternate character ROM standard character set
                         currentState.g1Charset = CharacterSet.ROM;
                     }
                     break;
                 case '2':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '(')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '(')) {
                         // G0 --> Alternate character ROM special graphics
                         currentState.g0Charset = CharacterSet.ROM_SPECIAL;
                     }
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == ')')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == ')')) {
                         // G1 --> Alternate character ROM special graphics
                         currentState.g1Charset = CharacterSet.ROM_SPECIAL;
                     }
                     break;
                 case '3':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '#')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '#')) {
                         // DECDHL - Double-height line (top half)
                         dechdl(true);
                     }
                     break;
                 case '4':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '#')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '#')) {
                         // DECDHL - Double-height line (bottom half)
                         dechdl(false);
                     }
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> DUTCH
                             currentState.g0Charset = CharacterSet.NRC_DUTCH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> DUTCH
                             currentState.g1Charset = CharacterSet.NRC_DUTCH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> DUTCH
                             currentState.g2Charset = CharacterSet.NRC_DUTCH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> DUTCH
                             currentState.g3Charset = CharacterSet.NRC_DUTCH;
                         }
                     }
                     break;
                 case '5':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '#')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '#')) {
                         // DECSWL - Single-width line
                         decswl();
                     }
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> FINNISH
                             currentState.g0Charset = CharacterSet.NRC_FINNISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> FINNISH
                             currentState.g1Charset = CharacterSet.NRC_FINNISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> FINNISH
                             currentState.g2Charset = CharacterSet.NRC_FINNISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> FINNISH
                             currentState.g3Charset = CharacterSet.NRC_FINNISH;
                         }
                     }
                     break;
                 case '6':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '#')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '#')) {
                         // DECDWL - Double-width line
                         decdwl();
                     }
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> NORWEGIAN
                             currentState.g0Charset = CharacterSet.NRC_NORWEGIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> NORWEGIAN
                             currentState.g1Charset = CharacterSet.NRC_NORWEGIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> NORWEGIAN
                             currentState.g2Charset = CharacterSet.NRC_NORWEGIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> NORWEGIAN
                             currentState.g3Charset = CharacterSet.NRC_NORWEGIAN;
                         }
@@ -4113,31 +4291,31 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> SWEDISH
                             currentState.g0Charset = CharacterSet.NRC_SWEDISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> SWEDISH
                             currentState.g1Charset = CharacterSet.NRC_SWEDISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> SWEDISH
                             currentState.g2Charset = CharacterSet.NRC_SWEDISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> SWEDISH
                             currentState.g3Charset = CharacterSet.NRC_SWEDISH;
                         }
                     }
                     break;
                 case '8':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '#')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '#')) {
                         // DECALN - Screen alignment display
                         decaln();
                     }
@@ -4150,23 +4328,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> DEC_SUPPLEMENTAL
                             currentState.g0Charset = CharacterSet.DEC_SUPPLEMENTAL;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> DEC_SUPPLEMENTAL
                             currentState.g1Charset = CharacterSet.DEC_SUPPLEMENTAL;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> DEC_SUPPLEMENTAL
                             currentState.g2Charset = CharacterSet.DEC_SUPPLEMENTAL;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> DEC_SUPPLEMENTAL
                             currentState.g3Charset = CharacterSet.DEC_SUPPLEMENTAL;
                         }
@@ -4176,23 +4354,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> SWISS
                             currentState.g0Charset = CharacterSet.NRC_SWISS;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> SWISS
                             currentState.g1Charset = CharacterSet.NRC_SWISS;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> SWISS
                             currentState.g2Charset = CharacterSet.NRC_SWISS;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> SWISS
                             currentState.g3Charset = CharacterSet.NRC_SWISS;
                         }
@@ -4203,52 +4381,52 @@ public class ECMA48 implements Runnable {
                 case '@':
                     break;
                 case 'A':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '(')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '(')) {
                         // G0 --> United Kingdom set
                         currentState.g0Charset = CharacterSet.UK;
                     }
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == ')')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == ')')) {
                         // G1 --> United Kingdom set
                         currentState.g1Charset = CharacterSet.UK;
                     }
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> United Kingdom set
                             currentState.g2Charset = CharacterSet.UK;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> United Kingdom set
                             currentState.g3Charset = CharacterSet.UK;
                         }
                     }
                     break;
                 case 'B':
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == '(')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == '(')) {
                         // G0 --> ASCII set
                         currentState.g0Charset = CharacterSet.US;
                     }
-                    if ((collectBuffer.size() == 1)
-                        && (collectBuffer.get(0) == ')')) {
+                    if ((collectBuffer.length() == 1)
+                        && (collectBuffer.charAt(0) == ')')) {
                         // G1 --> ASCII set
                         currentState.g1Charset = CharacterSet.US;
                     }
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> ASCII
                             currentState.g2Charset = CharacterSet.US;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> ASCII
                             currentState.g3Charset = CharacterSet.US;
                         }
@@ -4258,23 +4436,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> FINNISH
                             currentState.g0Charset = CharacterSet.NRC_FINNISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> FINNISH
                             currentState.g1Charset = CharacterSet.NRC_FINNISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> FINNISH
                             currentState.g2Charset = CharacterSet.NRC_FINNISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> FINNISH
                             currentState.g3Charset = CharacterSet.NRC_FINNISH;
                         }
@@ -4286,23 +4464,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> NORWEGIAN
                             currentState.g0Charset = CharacterSet.NRC_NORWEGIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> NORWEGIAN
                             currentState.g1Charset = CharacterSet.NRC_NORWEGIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> NORWEGIAN
                             currentState.g2Charset = CharacterSet.NRC_NORWEGIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> NORWEGIAN
                             currentState.g3Charset = CharacterSet.NRC_NORWEGIAN;
                         }
@@ -4312,8 +4490,8 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ' ')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ' ')) {
                             // S7C1T
                             s8c1t = false;
                         }
@@ -4323,8 +4501,8 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ' ')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ' ')) {
                             // S8C1T
                             s8c1t = true;
                         }
@@ -4334,23 +4512,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> SWEDISH
                             currentState.g0Charset = CharacterSet.NRC_SWEDISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> SWEDISH
                             currentState.g1Charset = CharacterSet.NRC_SWEDISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> SWEDISH
                             currentState.g2Charset = CharacterSet.NRC_SWEDISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> SWEDISH
                             currentState.g3Charset = CharacterSet.NRC_SWEDISH;
                         }
@@ -4363,23 +4541,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> GERMAN
                             currentState.g0Charset = CharacterSet.NRC_GERMAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> GERMAN
                             currentState.g1Charset = CharacterSet.NRC_GERMAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> GERMAN
                             currentState.g2Charset = CharacterSet.NRC_GERMAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> GERMAN
                             currentState.g3Charset = CharacterSet.NRC_GERMAN;
                         }
@@ -4395,23 +4573,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> FRENCH_CA
                             currentState.g0Charset = CharacterSet.NRC_FRENCH_CA;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> FRENCH_CA
                             currentState.g1Charset = CharacterSet.NRC_FRENCH_CA;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> FRENCH_CA
                             currentState.g2Charset = CharacterSet.NRC_FRENCH_CA;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> FRENCH_CA
                             currentState.g3Charset = CharacterSet.NRC_FRENCH_CA;
                         }
@@ -4421,23 +4599,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> FRENCH
                             currentState.g0Charset = CharacterSet.NRC_FRENCH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> FRENCH
                             currentState.g1Charset = CharacterSet.NRC_FRENCH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> FRENCH
                             currentState.g2Charset = CharacterSet.NRC_FRENCH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> FRENCH
                             currentState.g3Charset = CharacterSet.NRC_FRENCH;
                         }
@@ -4454,23 +4632,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> ITALIAN
                             currentState.g0Charset = CharacterSet.NRC_ITALIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> ITALIAN
                             currentState.g1Charset = CharacterSet.NRC_ITALIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> ITALIAN
                             currentState.g2Charset = CharacterSet.NRC_ITALIAN;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> ITALIAN
                             currentState.g3Charset = CharacterSet.NRC_ITALIAN;
                         }
@@ -4480,23 +4658,23 @@ public class ECMA48 implements Runnable {
                     if ((type == DeviceType.VT220)
                         || (type == DeviceType.XTERM)) {
 
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '(')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '(')) {
                             // G0 --> SPANISH
                             currentState.g0Charset = CharacterSet.NRC_SPANISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == ')')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == ')')) {
                             // G1 --> SPANISH
                             currentState.g1Charset = CharacterSet.NRC_SPANISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '*')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '*')) {
                             // G2 --> SPANISH
                             currentState.g2Charset = CharacterSet.NRC_SPANISH;
                         }
-                        if ((collectBuffer.size() == 1)
-                            && (collectBuffer.get(0) == '+')) {
+                        if ((collectBuffer.length() == 1)
+                            && (collectBuffer.charAt(0) == '+')) {
                             // G3 --> SPANISH
                             currentState.g3Charset = CharacterSet.NRC_SPANISH;
                         }
@@ -5140,13 +5318,13 @@ public class ECMA48 implements Runnable {
                 case 'p':
                     if (((type == DeviceType.VT220)
                             || (type == DeviceType.XTERM))
-                        && (collectBuffer.get(collectBuffer.size() - 1) == '\"')
+                        && (collectBuffer.charAt(collectBuffer.length() - 1) == '\"')
                     ) {
                         // DECSCL - compatibility level
                         decscl();
                     }
                     if ((type == DeviceType.XTERM)
-                        && (collectBuffer.get(collectBuffer.size() - 1) == '!')
+                        && (collectBuffer.charAt(collectBuffer.length() - 1) == '!')
                     ) {
                         // DECSTR - Soft terminal reset
                         decstr();
@@ -5155,7 +5333,7 @@ public class ECMA48 implements Runnable {
                 case 'q':
                     if (((type == DeviceType.VT220)
                             || (type == DeviceType.XTERM))
-                        && (collectBuffer.get(collectBuffer.size() - 1) == '\"')
+                        && (collectBuffer.charAt(collectBuffer.length() - 1) == '\"')
                     ) {
                         // DECSCA
                         decsca();
@@ -5214,8 +5392,9 @@ public class ECMA48 implements Runnable {
                 collect(ch);
             }
             if (ch == 0x5C) {
-                if ((collectBuffer.size() > 0)
-                    && (collectBuffer.get(collectBuffer.size() - 1) == 0x1B)) {
+                if ((collectBuffer.length() > 0)
+                    && (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
+                ) {
                     toGround();
                 }
             }
@@ -5267,8 +5446,9 @@ public class ECMA48 implements Runnable {
                 collect(ch);
             }
             if (ch == 0x5C) {
-                if ((collectBuffer.size() > 0) &&
-                    (collectBuffer.get(collectBuffer.size() - 1) == 0x1B)) {
+                if ((collectBuffer.length() > 0) &&
+                    (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
+                ) {
                     toGround();
                 }
             }
@@ -5298,8 +5478,9 @@ public class ECMA48 implements Runnable {
                 collect(ch);
             }
             if (ch == 0x5C) {
-                if ((collectBuffer.size() > 0) &&
-                    (collectBuffer.get(collectBuffer.size() - 1) == 0x1B)) {
+                if ((collectBuffer.length() > 0) &&
+                    (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
+                ) {
                     toGround();
                 }
             }
@@ -5345,8 +5526,8 @@ public class ECMA48 implements Runnable {
                 collect(ch);
             }
             if (ch == 0x5C) {
-                if ((collectBuffer.size() > 0)
-                    && (collectBuffer.get(collectBuffer.size() - 1) == 0x1B)
+                if ((collectBuffer.length() > 0)
+                    && (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
                 ) {
                     toGround();
                 }
@@ -5413,12 +5594,12 @@ public class ECMA48 implements Runnable {
 
         case VT52_DIRECT_CURSOR_ADDRESS:
             // This is a special case for the VT52 sequence "ESC Y l c"
-            if (collectBuffer.size() == 0) {
+            if (collectBuffer.length() == 0) {
                 collect(ch);
-            } else if (collectBuffer.size() == 1) {
+            } else if (collectBuffer.length() == 1) {
                 // We've got the two characters, one in the buffer and the
                 // other in ch.
-                cursorPosition(collectBuffer.get(0) - '\040', ch - '\040');
+                cursorPosition(collectBuffer.charAt(0) - '\040', ch - '\040');
                 toGround();
             }
             return;
@@ -5432,6 +5613,9 @@ public class ECMA48 implements Runnable {
      * @return current cursor X
      */
     public final int getCursorX() {
+        if (display.get(currentState.cursorY).isDoubleWidth()) {
+            return currentState.cursorX * 2;
+        }
         return currentState.cursorX;
     }