From e685a47d98ab73fde720b670dd8e0b4f677b827f Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Tue, 21 Mar 2017 14:55:55 -0400 Subject: [PATCH] Fix OOB, expose triple buffer --- README.md | 13 +++++- build.xml | 7 +++- docs/TODO.md | 10 +++++ docs/worklog.md | 25 +++++++++++ src/jexer/TApplication.java | 10 ++++- src/jexer/io/ReadTimeoutException.java | 5 +++ src/jexer/io/SwingScreen.java | 57 +++++++++++++++++--------- src/jexer/io/TimeoutInputStream.java | 6 +-- 8 files changed, 105 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 7cfe9b4..161b8e6 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,10 @@ Jexer - Java Text User Interface library This library implements a text-based windowing system reminiscient of Borland's [Turbo Vision](http://en.wikipedia.org/wiki/Turbo_Vision) system. (For those wishing to use the actual C++ Turbo Vision -library, see [Sergio Sigala's C++ version based on the public domain -sources released by Borland.](http://tvision.sourceforge.net/) ) +library, see [Sergio Sigala's C++ version based on the sources +released by Borland,](http://tvision.sourceforge.net/) or consider +Free Pascal's [Free Vision +library.](http://wiki.freepascal.org/Free_Vision)) Jexer currently supports three backends: @@ -167,6 +169,13 @@ The following properties control features of Jexer: Used by jexer.io.SwingScreen. Selects the cursor style to draw. Valid values are: underline, block, outline. Default: underline. + jexer.Swing.tripleBuffer + ------------------------ + + Used by jexer.io.SwingScreen. If false, use naive Swing thread + drawing. This may be faster on slower systems, but will also be + more likely to have screen tearing. Default: true. + Known Issues / Arbitrary Decisions diff --git a/build.xml b/build.xml index 284c53a..0209da0 100644 --- a/build.xml +++ b/build.xml @@ -30,9 +30,12 @@ - + + - + diff --git a/docs/TODO.md b/docs/TODO.md index 4f06210..97aabdd 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -8,6 +8,7 @@ Roadmap 0.0.5 - TEditor +- Eliminate all Eclipse warnings 0.0.6 @@ -17,6 +18,12 @@ Roadmap 0.0.7 +- Refactor SwingBackend to be embeddable + - jexer.Swing.blockMousePointer: false = do not invert cell, true + (default) is current behavior + - Make Demo4 with two separate Swing demos in a single JFrame. + - Make Demo5 mixing Swing and Jexer components + - THelpWindow - TText + clickable links - Index @@ -31,6 +38,7 @@ Roadmap 1.0.0 +- Maven artifact. 1.1.0 Wishlist @@ -64,6 +72,8 @@ Regression Checklist Release Checklist √ ------------------- +Eliminate all Eclipse warnings + Fix all marked TODOs in code Eliminate DEBUG, System.err prints diff --git a/docs/worklog.md b/docs/worklog.md index bed1e2d..379d3fc 100644 --- a/docs/worklog.md +++ b/docs/worklog.md @@ -1,6 +1,31 @@ Jexer Work Log ============== +March 21, 2017 + +I am starting to gear up for making Jexer a serious project now. I've +created its SourceForge project, linked it back to GitHub, have most +of its web page set up (looks like Qodem's), and released 0.0.4. And +then this morning saw an out-of-bounds exception if you kill the main +demo window. Glad I marked it Alpha on SourceForge... + +Yesterday I was digging around the other Turbo Vision derived projects +while populating the about page, and made a sad/happy-ish realization: +Embarcadero could probably get all of them shut down if it really +wanted to, including Free Vision. I uncovered some hidden history in +Free Vision, such that it appears that Graphics Vision had some +licensed Borland code in it, so there might be enough mud in the air +that Free Vision could be shut down the same way RHTVision was. But +even worse is the SCOTUS ruling on Oracle vs Google: if APIs are +copyrighted (regardless of their thoughts on fair use), then any +software that matches the API of a proprietary project might find +itself subject to an infringement case. So that too could shut down +the other API-compatible TV clones. + +Fortunately, Jexer (and D-TUI) is completely new, and has no API +compatibility with Turbo Vision. Jexer could be a new root to a whole +generation of TUI applications. + March 18, 2017 TStatusBar is working, as is "smart" window placement. Overall this diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 08ad1de..c2aa656 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -721,7 +721,10 @@ public class TApplication implements Runnable { // Draw each window in reverse Z order List sorted = new LinkedList(windows); Collections.sort(sorted); - TWindow topLevel = sorted.get(0); + TWindow topLevel = null; + if (sorted.size() > 0) { + topLevel = sorted.get(0); + } Collections.reverse(sorted); for (TWindow window: sorted) { window.drawChildren(); @@ -768,7 +771,10 @@ public class TApplication implements Runnable { } // Draw the status bar of the top-level window - TStatusBar statusBar = topLevel.getStatusBar(); + TStatusBar statusBar = null; + if (topLevel != null) { + statusBar = topLevel.getStatusBar(); + } if (statusBar != null) { getScreen().resetClipping(); statusBar.setWidth(getScreen().getWidth()); diff --git a/src/jexer/io/ReadTimeoutException.java b/src/jexer/io/ReadTimeoutException.java index 0a1bbc5..143845a 100644 --- a/src/jexer/io/ReadTimeoutException.java +++ b/src/jexer/io/ReadTimeoutException.java @@ -36,6 +36,11 @@ import java.io.IOException; */ public class ReadTimeoutException extends IOException { + /** + * Serializable version. + */ + private static final long serialVersionUID = 1; + /** * Construct an instance with a message. * diff --git a/src/jexer/io/SwingScreen.java b/src/jexer/io/SwingScreen.java index 79ba816..8da762a 100644 --- a/src/jexer/io/SwingScreen.java +++ b/src/jexer/io/SwingScreen.java @@ -59,7 +59,7 @@ public final class SwingScreen extends Screen { /** * If true, use triple buffering thread. */ - private static final boolean tripleBuffer = true; + private static boolean tripleBuffer = true; /** * Cursor style to draw. @@ -325,6 +325,12 @@ public final class SwingScreen extends Screen { cursorStyle = CursorStyle.BLOCK; } + if (System.getProperty("jexer.Swing.tripleBuffer"). + equals("false")) { + + SwingScreen.tripleBuffer = false; + } + setTitle("Jexer Application"); setBackground(Color.black); @@ -452,11 +458,19 @@ public final class SwingScreen extends Screen { } // Generate glyph and draw it. - - image = new BufferedImage(textWidth, textHeight, - BufferedImage.TYPE_INT_ARGB); - Graphics2D gr2 = image.createGraphics(); - gr2.setFont(getFont()); + Graphics2D gr2 = null; + int gr2x = xPixel; + int gr2y = yPixel; + if (tripleBuffer) { + image = new BufferedImage(textWidth, textHeight, + BufferedImage.TYPE_INT_ARGB); + gr2 = image.createGraphics(); + gr2.setFont(getFont()); + gr2x = 0; + gr2y = 0; + } else { + gr2 = (Graphics2D) gr; + } Cell cellColor = new Cell(); cellColor.setTo(cell); @@ -469,7 +483,7 @@ public final class SwingScreen extends Screen { // Draw the background rectangle, then the foreground character. gr2.setColor(attrToBackgroundColor(cellColor)); - gr2.fillRect(0, 0, textWidth, textHeight); + gr2.fillRect(gr2x, gr2y, textWidth, textHeight); // Handle blink and underline if (!cell.isBlink() @@ -478,25 +492,30 @@ public final class SwingScreen extends Screen { gr2.setColor(attrToForegroundColor(cellColor)); char [] chars = new char[1]; chars[0] = cell.getChar(); - gr2.drawChars(chars, 0, 1, 0 + textAdjustX, - 0 + textHeight - maxDescent + textAdjustY); + gr2.drawChars(chars, 0, 1, gr2x + textAdjustX, + gr2y + textHeight - maxDescent + textAdjustY); if (cell.isUnderline()) { - gr2.fillRect(0, 0 + textHeight - 2, textWidth, 2); + gr2.fillRect(gr2x, gr2y + textHeight - 2, textWidth, 2); } } - gr2.dispose(); - // We need a new key that will not be mutated by invertCell(). - Cell key = new Cell(); - key.setTo(cell); - if (cell.isBlink() && !cursorBlinkVisible) { - glyphCacheBlink.put(key, image); - } else { - glyphCache.put(key, image); + if (tripleBuffer) { + gr2.dispose(); + + // We need a new key that will not be mutated by + // invertCell(). + Cell key = new Cell(); + key.setTo(cell); + if (cell.isBlink() && !cursorBlinkVisible) { + glyphCacheBlink.put(key, image); + } else { + glyphCache.put(key, image); + } + + gr.drawImage(image, xPixel, yPixel, this); } - gr.drawImage(image, xPixel, yPixel, this); } /** diff --git a/src/jexer/io/TimeoutInputStream.java b/src/jexer/io/TimeoutInputStream.java index f1b140b..d540c60 100644 --- a/src/jexer/io/TimeoutInputStream.java +++ b/src/jexer/io/TimeoutInputStream.java @@ -130,7 +130,7 @@ public class TimeoutInputStream extends InputStream { try { // How long do we sleep for, eh? For now we will go with 2 // millis. - Thread.currentThread().sleep(2); + Thread.sleep(2); } catch (InterruptedException e) { // SQUASH } @@ -189,7 +189,7 @@ public class TimeoutInputStream extends InputStream { try { // How long do we sleep for, eh? For now we will go with // 2 millis. - Thread.currentThread().sleep(2); + Thread.sleep(2); } catch (InterruptedException e) { // SQUASH } @@ -264,7 +264,7 @@ public class TimeoutInputStream extends InputStream { try { // How long do we sleep for, eh? For now we will go with // 2 millis. - Thread.currentThread().sleep(2); + Thread.sleep(2); } catch (InterruptedException e) { // SQUASH } -- 2.27.0