Fix OOB, expose triple buffer
authorKevin Lamonte <kevin.lamonte@gmail.com>
Tue, 21 Mar 2017 18:55:55 +0000 (14:55 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Tue, 21 Mar 2017 18:55:55 +0000 (14:55 -0400)
README.md
build.xml
docs/TODO.md
docs/worklog.md
src/jexer/TApplication.java
src/jexer/io/ReadTimeoutException.java
src/jexer/io/SwingScreen.java
src/jexer/io/TimeoutInputStream.java

index 7cfe9b4c789a87f547b8926a02ac5a31570ef5af..161b8e6de0dc4043515367e4f22c4cd540a24fb5 100644 (file)
--- 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
index 284c53a66151ca5db6c1e954e7028edcdd88990f..0209da005156511c83c85703178043642ebd3d16 100644 (file)
--- a/build.xml
+++ b/build.xml
 
 <project name="jexer" basedir="." default="jar">
 
-  <!-- <property name="build.compiler" value="gcj"/> -->
+  <!--
+      I am using GCJ deliberately to test against Java 1.5 features.
+  -->
+  <property name="build.compiler" value="gcj"/>
 
-  <property name="version"       value="0.0.4"/>
+  <property name="version"       value="0.0.5"/>
   <property name="src.dir"       value="src"/>
   <property name="resources.dir" value="resources"/>
   <property name="build.dir"     value="build"/>
index 4f0621091b2eccf5e3d19f39550705ea279cbaf8..97aabdd5bb647c9633db247f6e9b7bdfaa80a35f 100644 (file)
@@ -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
index bed1e2d45f5fec68be249c77b2ea066997e782d2..379d3fcfe8a04721b3b777a0a6566516386fa346 100644 (file)
@@ -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
index 08ad1de436ec3d48504a9e5465869e98335ac7c0..c2aa656e805ac4ea4eb0dd8766c2aaa2eacb7840 100644 (file)
@@ -721,7 +721,10 @@ public class TApplication implements Runnable {
         // Draw each window in reverse Z order
         List<TWindow> sorted = new LinkedList<TWindow>(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());
index 0a1bbc554f87d39a92c777176e6d85fa282e9e23..143845af3c0f09ff55ff959dd82776eeb1f195a8 100644 (file)
@@ -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.
      *
index 79ba8163ddb50308c63ba635946841e2e91b8f9b..8da762af3158811c59427b5ab773a04ed9f1ed17 100644 (file)
@@ -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);
         }
 
         /**
index f1b140bcf31dd23f9160551940b24e2587d755dd..d540c60fb2137d392b001f18579082129db80cb6 100644 (file)
@@ -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
                 }