X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTTerminalWindow.java;h=5970b6c77aff4791a389d13a7aa9a1012dbf5147;hb=ab215e38e98a76ad189ba537e37c420ae515e4c0;hp=6d06fc88530f4d698c99d6dc8c5244949e0df1cc;hpb=97bc3f29f12d64d27d49b0d61947d15dfa64d156;p=fanfix.git diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index 6d06fc8..5970b6c 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -123,6 +123,26 @@ 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; + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -306,102 +326,125 @@ 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 > 125) + && (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); - if (ch.isImage()) { - putCharXY(i + 1, row, ch); - continue; - } - - 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(); + 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 (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); + } } @@ -497,25 +540,21 @@ 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()); + 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 (System.getProperty("os.name").startsWith("Windows")) { - if (keypress.equals(kbEnter)) { - emulator.keypress(kbCtrlJ); - } + // 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.addUserEvent(new TKeypressEvent(kbCtrlJ)); } - - readEmulatorState(); - return; } + + readEmulatorState(); + return; } // Process is closed, honor "normal" TUI keystrokes @@ -548,13 +587,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 @@ -575,13 +612,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 @@ -602,13 +637,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 @@ -789,37 +822,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. */ @@ -906,10 +908,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) @@ -922,6 +922,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. @@ -1059,4 +1073,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; + } + }