X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTApplication.java;h=7c96258b4405c00a5660788262a235a3c69c74d9;hb=9f613a0c54cb97e9305fd87ce8eb2f76ac82804e;hp=83dbd25e305e2a09eb43b1127f00323d7242e94f;hpb=e6469faa3f6895ec0ff9b7592a7348a321898b71;p=nikiroo-utils.git diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 83dbd25..7c96258 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -132,6 +132,11 @@ public class TApplication implements Runnable { */ private volatile WidgetEventHandler secondaryEventHandler; + /** + * The screen handler thread. + */ + private volatile ScreenHandler screenHandler; + /** * The widget receiving events from the secondary event handler thread. */ @@ -450,6 +455,96 @@ public class TApplication implements Runnable { } } + /** + * ScreenHandler pushes screen updates to the physical device. + */ + private class ScreenHandler implements Runnable { + /** + * The main application. + */ + private TApplication application; + + /** + * The dirty flag. + */ + private boolean dirty = false; + + /** + * Public constructor. + * + * @param application the main application + */ + public ScreenHandler(final TApplication application) { + this.application = application; + } + + /** + * The screen update loop. + */ + public void run() { + // Wrap everything in a try, so that if we go belly up we can let + // the user have their terminal back. + try { + runImpl(); + } catch (Throwable t) { + this.application.restoreConsole(); + t.printStackTrace(); + this.application.exit(); + } + } + + /** + * The update loop. + */ + private void runImpl() { + + // Loop forever + while (!application.quit) { + + // Wait until application notifies me + while (!application.quit) { + try { + synchronized (this) { + if (dirty) { + dirty = false; + break; + } + + // Always check within 50 milliseconds. + this.wait(50); + } + } catch (InterruptedException e) { + // SQUASH + } + } // while (!application.quit) + + assert (dirty == true); + + // Flush the screen contents + if (debugThreads) { + System.err.printf("%d %s backend.flushScreen()\n", + System.currentTimeMillis(), Thread.currentThread()); + } + synchronized (getScreen()) { + backend.flushScreen(); + } + } // while (true) (main runnable loop) + + // Shutdown the user I/O thread(s) + backend.shutdown(); + } + + /** + * Set the dirty flag. + */ + public void setDirty() { + synchronized (this) { + dirty = true; + } + } + + } + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -635,6 +730,10 @@ public class TApplication implements Runnable { public void run() { // System.err.println("*** TApplication.run() begins ***"); + // Start the screen updater thread + screenHandler = new ScreenHandler(this); + (new Thread(screenHandler)).start(); + // Start the main consumer thread primaryEventHandler = new WidgetEventHandler(this, true); (new Thread(primaryEventHandler)).start(); @@ -711,9 +810,6 @@ public class TApplication implements Runnable { } } - // Shutdown the user I/O thread(s) - backend.shutdown(); - // Close all the windows. This gives them an opportunity to release // resources. closeAllWindows(); @@ -886,6 +982,9 @@ public class TApplication implements Runnable { drawAll(); } + // Wake up the screen repainter + wakeScreenHandler(); + if (debugThreads) { System.err.printf(System.currentTimeMillis() + " " + Thread.currentThread() + " finishEventProcessing() END\n"); @@ -1323,6 +1422,19 @@ public class TApplication implements Runnable { } } + /** + * Wake the sleeping screen handler. + */ + private void wakeScreenHandler() { + if (!started) { + return; + } + + synchronized (screenHandler) { + screenHandler.notify(); + } + } + // ------------------------------------------------------------------------ // TApplication ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -1483,7 +1595,7 @@ public class TApplication implements Runnable { String version = getClass().getPackage().getImplementationVersion(); if (version == null) { // This is Java 9+, use a hardcoded string here. - version = "0.3.1"; + version = "0.3.2"; } messageBox(i18n.getString("aboutDialogTitle"), MessageFormat.format(i18n.getString("aboutDialogText"), version), @@ -1534,6 +1646,19 @@ public class TApplication implements Runnable { * @param y row position */ private void invertCell(final int x, final int y) { + invertCell(x, y, false); + } + + /** + * Invert the cell color at a position. This is used to track the mouse. + * + * @param x column position + * @param y row position + * @param onlyThisCell if true, only invert this cell + */ + private void invertCell(final int x, final int y, + final boolean onlyThisCell) { + if (debugThreads) { System.err.printf("%d %s invertCell() %d %d\n", System.currentTimeMillis(), Thread.currentThread(), x, y); @@ -1573,6 +1698,29 @@ public class TApplication implements Runnable { } } getScreen().putCharXY(x, y, cell); + if ((onlyThisCell == true) || (cell.getWidth() == Cell.Width.SINGLE)) { + return; + } + + // This cell is one half of a fullwidth glyph. Invert the other + // half. + if (cell.getWidth() == Cell.Width.LEFT) { + if (x < getScreen().getWidth() - 1) { + Cell rightHalf = getScreen().getCharXY(x + 1, y); + if (rightHalf.getWidth() == Cell.Width.RIGHT) { + invertCell(x + 1, y, true); + return; + } + } + } + assert (cell.getWidth() == Cell.Width.RIGHT); + + if (x > 0) { + Cell leftHalf = getScreen().getCharXY(x - 1, y); + if (leftHalf.getWidth() == Cell.Width.LEFT) { + invertCell(x - 1, y, true); + } + } } /** @@ -1639,7 +1787,7 @@ public class TApplication implements Runnable { oldDrawnMouseY = mouseY; } if (getScreen().isDirty()) { - backend.flushScreen(); + screenHandler.setDirty(); } return; } @@ -1790,15 +1938,9 @@ public class TApplication implements Runnable { getScreen().hideCursor(); } - // Flush the screen contents if (getScreen().isDirty()) { - if (debugThreads) { - System.err.printf("%d %s backend.flushScreen()\n", - System.currentTimeMillis(), Thread.currentThread()); - } - backend.flushScreen(); + screenHandler.setDirty(); } - repaint = false; }