X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTTerminalWindow.java;h=2106cf0e8c1c48ca59d1efe39a6e155e911e059c;hb=c348e2b5c815f5586d5b166ccdb4728b67fc5527;hp=c7456358df7193db8d8be27481ecac6133e863a5;hpb=0d86ab8480cabbe32fc87588304ddc795a4df14f;p=fanfix.git diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index c745635..2106cf0 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -123,6 +123,37 @@ public class TTerminalWindow extends TScrollableWindow */ private boolean haveTimer = false; + /** + * The last seen scrollback lines. + */ + private List scrollback; + + /** + * The last seen display lines. + */ + private List display; + + /** + * If true, the display has changed and needs updating. + */ + private volatile boolean dirty = true; + + /** + * Time that the display was last updated. + */ + private long lastUpdateTime = 0; + + /** + * If true, hide the mouse after typing a keystroke. + */ + private boolean hideMouseWhenTyping = true; + + /** + * If true, the mouse should not be displayed because a keystroke was + * typed. + */ + private boolean typingHidMouse = false; + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -265,6 +296,15 @@ public class TTerminalWindow extends TScrollableWindow this.closeOnExit = closeOnExit; + if (System.getProperty("jexer.TTerminal.shell") != null) { + String shell = System.getProperty("jexer.TTerminal.shell"); + if (shell.trim().startsWith("ptypipe")) { + ptypipe = true; + } + spawnShell(shell.split("\\s+")); + return; + } + String cmdShellWindows = "cmd.exe"; // You cannot run a login shell in a bare Process interactively, due @@ -306,96 +346,123 @@ public class TTerminalWindow extends TScrollableWindow */ @Override public void draw() { - // Synchronize against the emulator so we don't stomp on its reader - // thread. - synchronized (emulator) { - - // Update the scroll bars - reflowData(); - - // Draw the box using my superclass - super.draw(); - - List scrollback = emulator.getScrollbackBuffer(); - List display = emulator.getDisplayBuffer(); + int width = getDisplayWidth(); + boolean syncEmulator = false; + if ((System.currentTimeMillis() - lastUpdateTime >= 25) + && (dirty == true) + ) { + // Too much time has passed, draw it all. + syncEmulator = true; + } else if (emulator.isReading() && (dirty == false)) { + // Wait until the emulator has brought more data in. + syncEmulator = false; + } else if (!emulator.isReading() && (dirty == true)) { + // The emulator won't receive more data, update the display. + syncEmulator = true; + } - // Put together the visible rows - int visibleHeight = getHeight() - 2; - int visibleBottom = scrollback.size() + display.size() - + getVerticalValue(); - assert (visibleBottom >= 0); + if ((syncEmulator == true) + || (scrollback == null) + || (display == null) + ) { + // We want to minimize the amount of time we have the emulator + // locked. Grab a copy of its display. + synchronized (emulator) { + // Update the scroll bars + reflowData(); - List preceedingBlankLines = new ArrayList(); - int visibleTop = visibleBottom - visibleHeight; - if (visibleTop < 0) { - for (int i = visibleTop; i < 0; i++) { - preceedingBlankLines.add(emulator.getBlankDisplayLine()); + if ((scrollback == null) || emulator.isReading()) { + scrollback = copyBuffer(emulator.getScrollbackBuffer()); + display = copyBuffer(emulator.getDisplayBuffer()); } - visibleTop = 0; + width = emulator.getWidth(); } - assert (visibleTop >= 0); + dirty = false; + } - List displayLines = new ArrayList(); - displayLines.addAll(scrollback); - displayLines.addAll(display); + // Draw the box using my superclass + super.draw(); - List visibleLines = new ArrayList(); - visibleLines.addAll(preceedingBlankLines); - visibleLines.addAll(displayLines.subList(visibleTop, - visibleBottom)); + // Put together the visible rows + int visibleHeight = getHeight() - 2; + int visibleBottom = scrollback.size() + display.size() + + getVerticalValue(); + assert (visibleBottom >= 0); - visibleHeight -= visibleLines.size(); - assert (visibleHeight >= 0); + List preceedingBlankLines = new ArrayList(); + int visibleTop = visibleBottom - visibleHeight; + if (visibleTop < 0) { + for (int i = visibleTop; i < 0; i++) { + preceedingBlankLines.add(emulator.getBlankDisplayLine()); + } + visibleTop = 0; + } + assert (visibleTop >= 0); + + List displayLines = new ArrayList(); + displayLines.addAll(scrollback); + displayLines.addAll(display); + + List visibleLines = new ArrayList(); + visibleLines.addAll(preceedingBlankLines); + visibleLines.addAll(displayLines.subList(visibleTop, + visibleBottom)); + + visibleHeight -= visibleLines.size(); + assert (visibleHeight >= 0); + + // Now draw the emulator screen + int row = 1; + for (DisplayLine line: visibleLines) { + int widthMax = width; + if (line.isDoubleWidth()) { + widthMax /= 2; + } + if (widthMax > getWidth() - 2) { + widthMax = getWidth() - 2; + } + for (int i = 0; i < widthMax; i++) { + Cell ch = line.charAt(i); - // Now draw the emulator screen - int row = 1; - for (DisplayLine line: visibleLines) { - int widthMax = emulator.getWidth(); - if (line.isDoubleWidth()) { - widthMax /= 2; - } - if (widthMax > getWidth() - 2) { - widthMax = getWidth() - 2; + if (ch.isImage()) { + putCharXY(i + 1, row, ch); + continue; } - for (int i = 0; i < widthMax; i++) { - Cell ch = line.charAt(i); - Cell newCell = new Cell(); - newCell.setTo(ch); - boolean reverse = line.isReverseColor() ^ ch.isReverse(); - newCell.setReverse(false); - if (reverse) { - if (ch.getForeColorRGB() < 0) { - newCell.setBackColor(ch.getForeColor()); - newCell.setBackColorRGB(-1); - } else { - newCell.setBackColorRGB(ch.getForeColorRGB()); - } - if (ch.getBackColorRGB() < 0) { - newCell.setForeColor(ch.getBackColor()); - newCell.setForeColorRGB(-1); - } else { - newCell.setForeColorRGB(ch.getBackColorRGB()); - } + + Cell newCell = new Cell(ch); + boolean reverse = line.isReverseColor() ^ ch.isReverse(); + newCell.setReverse(false); + if (reverse) { + if (ch.getForeColorRGB() < 0) { + newCell.setBackColor(ch.getForeColor()); + newCell.setBackColorRGB(-1); + } else { + newCell.setBackColorRGB(ch.getForeColorRGB()); } - if (line.isDoubleWidth()) { - putDoubleWidthCharXY(line, (i * 2) + 1, row, newCell); + if (ch.getBackColorRGB() < 0) { + newCell.setForeColor(ch.getBackColor()); + newCell.setForeColorRGB(-1); } else { - putCharXY(i + 1, row, newCell); + newCell.setForeColorRGB(ch.getBackColorRGB()); } } - row++; - if (row == getHeight() - 1) { - // Don't overwrite the box edge - break; + if (line.isDoubleWidth()) { + putDoubleWidthCharXY(line, (i * 2) + 1, row, newCell); + } else { + putCharXY(i + 1, row, newCell); } } - CellAttributes background = new CellAttributes(); - // Fill in the blank lines on bottom - for (int i = 0; i < visibleHeight; i++) { - hLineXY(1, i + row, getWidth() - 2, ' ', background); + row++; + if (row == getHeight() - 1) { + // Don't overwrite the box edge + break; } - - } // synchronized (emulator) + } + CellAttributes background = new CellAttributes(); + // Fill in the blank lines on bottom + for (int i = 0; i < visibleHeight; i++) { + hLineXY(1, i + row, getWidth() - 2, ' ', background); + } } @@ -474,6 +541,9 @@ public class TTerminalWindow extends TScrollableWindow */ @Override public void onKeypress(final TKeypressEvent keypress) { + if (hideMouseWhenTyping) { + typingHidMouse = true; + } // Scrollback up/down if (keypress.equals(kbShiftPgUp) @@ -491,25 +561,24 @@ public class TTerminalWindow extends TScrollableWindow return; } - // Synchronize against the emulator so we don't stomp on its reader - // thread. - synchronized (emulator) { - if (emulator.isReading()) { - // Get out of scrollback - setVerticalValue(0); - emulator.keypress(keypress.getKey()); - - // UGLY HACK TIME! cmd.exe needs CRLF, not just CR, so if - // this is kBEnter then also send kbCtrlJ. - if (System.getProperty("os.name").startsWith("Windows")) { - if (keypress.equals(kbEnter)) { - emulator.keypress(kbCtrlJ); - } + if (emulator.isReading()) { + // Get out of scrollback + setVerticalValue(0); + emulator.addUserEvent(keypress); + + // UGLY HACK TIME! cmd.exe needs CRLF, not just CR, so if + // this is kBEnter then also send kbCtrlJ. + if (keypress.equals(kbEnter)) { + if (System.getProperty("os.name").startsWith("Windows") + && (System.getProperty("jexer.TTerminal.cmdHack", + "true").equals("true")) + ) { + emulator.addUserEvent(new TKeypressEvent(kbCtrlJ)); } - - readEmulatorState(); - return; } + + readEmulatorState(); + return; } // Process is closed, honor "normal" TUI keystrokes @@ -529,6 +598,10 @@ public class TTerminalWindow extends TScrollableWindow return; } + if (hideMouseWhenTyping) { + typingHidMouse = false; + } + // If the emulator is tracking mouse buttons, it needs to see wheel // events. if (emulator.getMouseProtocol() == ECMA48.MouseProtocol.OFF) { @@ -542,13 +615,11 @@ public class TTerminalWindow extends TScrollableWindow } } if (mouseOnEmulator(mouse)) { - synchronized (emulator) { - mouse.setX(mouse.getX() - 1); - mouse.setY(mouse.getY() - 1); - emulator.mouse(mouse); - readEmulatorState(); - return; - } + mouse.setX(mouse.getX() - 1); + mouse.setY(mouse.getY() - 1); + emulator.addUserEvent(mouse); + readEmulatorState(); + return; } // Emulator didn't consume it, pass it on @@ -568,14 +639,16 @@ public class TTerminalWindow extends TScrollableWindow return; } + if (hideMouseWhenTyping) { + typingHidMouse = false; + } + if (mouseOnEmulator(mouse)) { - synchronized (emulator) { - mouse.setX(mouse.getX() - 1); - mouse.setY(mouse.getY() - 1); - emulator.mouse(mouse); - readEmulatorState(); - return; - } + mouse.setX(mouse.getX() - 1); + mouse.setY(mouse.getY() - 1); + emulator.addUserEvent(mouse); + readEmulatorState(); + return; } // Emulator didn't consume it, pass it on @@ -595,14 +668,16 @@ public class TTerminalWindow extends TScrollableWindow return; } + if (hideMouseWhenTyping) { + typingHidMouse = false; + } + if (mouseOnEmulator(mouse)) { - synchronized (emulator) { - mouse.setX(mouse.getX() - 1); - mouse.setY(mouse.getY() - 1); - emulator.mouse(mouse); - readEmulatorState(); - return; - } + mouse.setX(mouse.getX() - 1); + mouse.setY(mouse.getY() - 1); + emulator.addUserEvent(mouse); + readEmulatorState(); + return; } // Emulator didn't consume it, pass it on @@ -613,6 +688,18 @@ public class TTerminalWindow extends TScrollableWindow // TTerminalWindow -------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Returns true if this window does not want the application-wide mouse + * cursor drawn over it. + * + * @return true if this window does not want the application-wide mouse + * cursor drawn over it + */ + @Override + public boolean hasHiddenMouse() { + return (super.hasHiddenMouse() || typingHidMouse); + } + /** * Claim the keystrokes the emulator will need. */ @@ -748,6 +835,13 @@ public class TTerminalWindow extends TScrollableWindow // Pass the correct text cell width/height to the emulator emulator.setTextWidth(getScreen().getTextWidth()); emulator.setTextHeight(getScreen().getTextHeight()); + + // Hide mouse when typing option + if (System.getProperty("jexer.TTerminal.hideMouseWhenTyping", + "true").equals("false")) { + + hideMouseWhenTyping = false; + } } /** @@ -783,37 +877,6 @@ public class TTerminalWindow extends TScrollableWindow } } - /** - * Called by emulator when fresh data has come in. - */ - public void displayChanged() { - getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT)); - } - - /** - * Function to call to obtain the display width. - * - * @return the number of columns in the display - */ - public int getDisplayWidth() { - if (ptypipe) { - return getWidth() - 2; - } - return 80; - } - - /** - * Function to call to obtain the display height. - * - * @return the number of rows in the display - */ - public int getDisplayHeight() { - if (ptypipe) { - return getHeight() - 2; - } - return 24; - } - /** * Hook for subclasses to be notified of the shell termination. */ @@ -900,10 +963,8 @@ public class TTerminalWindow extends TScrollableWindow */ private boolean mouseOnEmulator(final TMouseEvent mouse) { - synchronized (emulator) { - if (!emulator.isReading()) { - return false; - } + if (!emulator.isReading()) { + return false; } if ((mouse.getAbsoluteX() >= getAbsoluteX() + 1) @@ -916,6 +977,20 @@ public class TTerminalWindow extends TScrollableWindow return false; } + /** + * Copy a display buffer. + * + * @param buffer the buffer to copy + * @return a deep copy of the buffer's data + */ + private List copyBuffer(final List buffer) { + ArrayList result = new ArrayList(buffer.size()); + for (DisplayLine line: buffer) { + result.add(new DisplayLine(line)); + } + return result; + } + /** * Draw glyphs for a double-width or double-height VT100 cell to two * screen cells. @@ -965,8 +1040,7 @@ public class TTerminalWindow extends TScrollableWindow BufferedImage image; if (line.getDoubleHeight() == 1) { // Double-height top half: don't draw the underline. - Cell newCell = new Cell(); - newCell.setTo(cell); + Cell newCell = new Cell(cell); newCell.setUnderline(false); image = doubleFont.getImage(newCell, textWidth * 2, textHeight * 2, cursorBlinkVisible); @@ -977,10 +1051,8 @@ public class TTerminalWindow extends TScrollableWindow // Now that we have the double-wide glyph drawn, copy the right // pieces of it to the cells. - Cell left = new Cell(); - Cell right = new Cell(); - left.setTo(cell); - right.setTo(cell); + Cell left = new Cell(cell); + Cell right = new Cell(cell); right.setChar(' '); BufferedImage leftImage = null; BufferedImage rightImage = null; @@ -1032,7 +1104,7 @@ public class TTerminalWindow extends TScrollableWindow * The double-width font will be 2x this value. */ private void setupFont(final int fontSize) { - doubleFont = GlyphMaker.getDefault().size(fontSize * 2); + doubleFont = GlyphMaker.getInstance(fontSize * 2); // Special case: the ECMA48 backend needs to have a timer to drive // its blink state. @@ -1053,4 +1125,40 @@ public class TTerminalWindow extends TScrollableWindow } } + // ------------------------------------------------------------------------ + // DisplayListener -------------------------------------------------------- + // ------------------------------------------------------------------------ + + /** + * Called by emulator when fresh data has come in. + */ + public void displayChanged() { + dirty = true; + getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT)); + } + + /** + * Function to call to obtain the display width. + * + * @return the number of columns in the display + */ + public int getDisplayWidth() { + if (ptypipe) { + return getWidth() - 2; + } + return 80; + } + + /** + * Function to call to obtain the display height. + * + * @return the number of rows in the display + */ + public int getDisplayHeight() { + if (ptypipe) { + return getHeight() - 2; + } + return 24; + } + }