From bd8d51fa0a33d6d27dba088c57791e1650512fc0 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Fri, 20 Mar 2015 22:05:08 -0400 Subject: [PATCH] many fixes --- README.md | 9 +- src/jexer/TApplication.java | 163 +++---- src/jexer/TTerminalWindow.java | 120 ++++- src/jexer/TWindow.java | 4 +- src/jexer/bits/GraphicsChars.java | 5 +- src/jexer/event/TMouseEvent.java | 3 +- src/jexer/io/AWTScreen.java | 44 +- src/jexer/io/AWTTerminal.java | 10 +- src/jexer/io/Screen.java | 17 +- src/jexer/session/AWTSessionInfo.java | 6 +- src/jexer/tterminal/DisplayLine.java | 6 +- src/jexer/tterminal/ECMA48.java | 642 +++++++++++++++++--------- 12 files changed, 667 insertions(+), 362 deletions(-) diff --git a/README.md b/README.md index 058eb6f..570a017 100644 --- 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 diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 5813d6f..0e5020a 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -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); diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index 58e60e5..336c3ff 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -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 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 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 preceedingBlankLines = new LinkedList(); 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 displayLines = new LinkedList(); displayLines.addAll(scrollback); displayLines.addAll(display); - // System.err.printf("displayLines.size %d\n", displayLines.size()); List visibleLines = new LinkedList(); 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); + } + } diff --git a/src/jexer/TWindow.java b/src/jexer/TWindow.java index 0a74a40..e1d0299 100644 --- a/src/jexer/TWindow.java +++ b/src/jexer/TWindow.java @@ -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 diff --git a/src/jexer/bits/GraphicsChars.java b/src/jexer/bits/GraphicsChars.java index 3f2ab92..5093bcf 100644 --- a/src/jexer/bits/GraphicsChars.java +++ b/src/jexer/bits/GraphicsChars.java @@ -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', diff --git a/src/jexer/event/TMouseEvent.java b/src/jexer/event/TMouseEvent.java index 8dcaed2..2f801cc 100644 --- a/src/jexer/event/TMouseEvent.java +++ b/src/jexer/event/TMouseEvent.java @@ -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; diff --git a/src/jexer/io/AWTScreen.java b/src/jexer/io/AWTScreen.java index a3fc41f..308a285 100644 --- a/src/jexer/io/AWTScreen.java +++ b/src/jexer/io/AWTScreen.java @@ -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; } diff --git a/src/jexer/io/AWTTerminal.java b/src/jexer/io/AWTTerminal.java index 27ba80a..783cffd 100644 --- a/src/jexer/io/AWTTerminal.java +++ b/src/jexer/io/AWTTerminal.java @@ -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(); + } } /** diff --git a/src/jexer/io/Screen.java b/src/jexer/io/Screen.java index e3f7219..1ecc4b1 100644 --- a/src/jexer/io/Screen.java +++ b/src/jexer/io/Screen.java @@ -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. * diff --git a/src/jexer/session/AWTSessionInfo.java b/src/jexer/session/AWTSessionInfo.java index 088da9e..1c9bf97 100644 --- a/src/jexer/session/AWTSessionInfo.java +++ b/src/jexer/session/AWTSessionInfo.java @@ -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. diff --git a/src/jexer/tterminal/DisplayLine.java b/src/jexer/tterminal/DisplayLine.java index 3f2cfd7..a243cdb 100644 --- a/src/jexer/tterminal/DisplayLine.java +++ b/src/jexer/tterminal/DisplayLine.java @@ -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 diff --git a/src/jexer/tterminal/ECMA48.java b/src/jexer/tterminal/ECMA48.java index 66ee95e..df157cf 100644 --- a/src/jexer/tterminal/ECMA48.java +++ b/src/jexer/tterminal/ECMA48.java @@ -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: * *

+ * - 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. + * + *

* - 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), '=', - * '>' (<), ... - */ - private List csiFlags; - /** * Parameter characters being collected. */ @@ -612,7 +643,7 @@ public class ECMA48 implements Runnable { /** * Non-csi collect buffer. */ - private List 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(); csiParams = new ArrayList(); - collectBuffer = new ArrayList(); tabStops = new ArrayList(); scrollback = new LinkedList(); display = new LinkedList(); @@ -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; } -- 2.27.0