network socket support, Swing blink/reverse
authorKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 23 Mar 2015 17:03:16 +0000 (13:03 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 23 Mar 2015 17:03:16 +0000 (13:03 -0400)
README.md
src/jexer/TTerminalWindow.java
src/jexer/demos/Demo2.java [new file with mode: 0644]
src/jexer/demos/DemoApplication.java
src/jexer/io/ECMA48Terminal.java
src/jexer/io/SwingScreen.java

index d014852d18b2a6139ce463347badd152ea10a875..60734e65b63f445a0efd32451d39c42fe17d0272 100644 (file)
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ wishing to use the actual C++ Turbo Vision library, see [Sergio
 Sigala's updated version](http://tvision.sourceforge.net/) that runs
 on many more platforms.
 
-Two backends are available:
+Three backends are available:
 
 * System.in/out to a command-line ECMA-48 / ANSI X3.64 type terminal
   (tested on Linux + xterm).  I/O is handled through terminal escape
@@ -20,13 +20,20 @@ Two backends are available:
   are supported.  For the demo application, this is the default
   backend on non-Windows platforms.
 
+* The same command-line ECMA-48 / ANSI X3.64 type terminal as above,
+  but to any general InputStream/OutputStream.  See the file
+  jexer.demos.Demo2 for an example of running the demo over a TCP
+  socket.
+
 * Java Swing UI.  This backend can be selected by setting
   jexer.Swing=true.  The default window size for Swing is 132x40,
   which is set in jexer.session.SwingSession.  For the demo
   application, this is the default backend on Windows platforms.
 
 The demo application showing the existing UI controls is available via
-'java -jar jexer.jar' or 'java -Djexer.Swing=true -jar jexer.jar' .
+'java -jar jexer.jar', 'java -Djexer.Swing=true -jar jexer.jar', or
+'java -cp jexer.jar jexer.demos.Demo2 PORT' (where PORT is a number to
+run the TCP daemon on).
 
 Additional backends can be created by subclassing
 jexer.backend.Backend and passing it into the TApplication
@@ -82,7 +89,7 @@ public class MyApplication extends TApplication {
 }
 ```
 
-See the file demos/Demo1.java for detailed examples.
+See the files in jexer.demos for more detailed examples.
 
 
 
@@ -127,8 +134,9 @@ ambiguous.  This section describes such issues.
     ioctl(TIOCGWINSZ) but without requiring a native library.
 
   - jexer.io.ECMA48Terminal calls 'stty' to perform the equivalent of
-    cfmakeraw().  The terminal is (blindly!) put back in 'stty sane
-    cooked' mode when exiting.
+    cfmakeraw() when using System.in/out.  System.out is also
+    (blindly!)  put in 'stty sane cooked' mode when exiting.
+
 
 
 System Properties
@@ -156,10 +164,6 @@ Roadmap
 
 Many tasks remain before calling this version 1.0:
 
-0.0.2: STABILIZE EXISTING
-
-- ECMA48Backend running on socket
-
 0.0.3: FINISH PORTING
 
 - TTreeView
index f85e346e4a230f646e6e41635941a516e6281f72..bb91a6317453f665a08119b8a83f0564fd9e7fa3 100644 (file)
@@ -281,11 +281,18 @@ public class TTerminalWindow extends TWindow {
 
             // Check to see if the shell has died.
             if (!emulator.isReading() && (shell != null)) {
-                // The emulator exited on its own, all is fine
-                setTitle(String.format("%s [Completed - %d]",
-                        getTitle(), shell.exitValue()));
-                shell = null;
-                emulator.close();
+                try {
+                    int rc = shell.exitValue();
+                    // The emulator exited on its own, all is fine
+                    setTitle(String.format("%s [Completed - %d]",
+                            getTitle(), shell.exitValue()));
+                    shell = null;
+                    emulator.close();
+                } catch (IllegalThreadStateException e) {
+                    // The emulator thread has exited, but the shell Process
+                    // hasn't figured that out yet.  Do nothing, we will see
+                    // this in a future tick.
+                }
             } else if (emulator.isReading() && (shell != null)) {
                 // The shell might be dead, let's check
                 try {
diff --git a/src/jexer/demos/Demo2.java b/src/jexer/demos/Demo2.java
new file mode 100644 (file)
index 0000000..35e40ab
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Jexer - Java Text User Interface
+ *
+ * License: LGPLv3 or later
+ *
+ * This module is licensed under the GNU Lesser General Public License
+ * Version 3.  Please see the file "COPYING" in this directory for more
+ * information about the GNU Lesser General Public License Version 3.
+ *
+ *     Copyright (C) 2015  Kevin Lamonte
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * http://www.gnu.org/licenses/, or write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * @author Kevin Lamonte [kevin.lamonte@gmail.com]
+ * @version 1
+ */
+package jexer.demos;
+
+import java.net.*;
+
+/**
+ * This class is the main driver for a simple demonstration of Jexer's
+ * capabilities.  Rather than run locally, it serves a Jexer UI over a TCP
+ * port.
+ */
+public class Demo2 {
+
+    /**
+     * Main entry point.
+     *
+     * @param args Command line arguments
+     */
+    public static void main(final String [] args) {
+        try {
+            if (args.length == 0) {
+                System.err.printf("USAGE: java -cp jexer.jar jexer.demos.Demo2 port\n");
+                return;
+            }
+
+            int port = Integer.parseInt(args[0]);
+            ServerSocket server = new ServerSocket(port);
+            while (true) {
+                Socket socket = server.accept();
+                System.out.printf("New connection: %s\n", socket);
+                DemoApplication app = new DemoApplication(socket.getInputStream(),
+                    socket.getOutputStream());
+                (new Thread(app)).start();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}
index 4c5ba319cc97669a7bee02c2e93408017bc83188..22609620b5f2d56979b53f9c4d2588710b76cf92 100644 (file)
@@ -30,6 +30,8 @@
  */
 package jexer.demos;
 
+import java.io.*;
+
 import jexer.*;
 import jexer.event.*;
 import jexer.menu.*;
@@ -40,13 +42,9 @@ import jexer.menu.*;
 class DemoApplication extends TApplication {
 
     /**
-     * Public constructor.
-     *
-     * @param backendType one of the TApplication.BackendType values
-     * @throws Exception if TApplication can't instantiate the Backend.
+     * Add all the widgets of the demo.
      */
-    public DemoApplication(BackendType backendType) throws Exception {
-        super(backendType);
+    private void addAllWidgets() {
         new DemoMainWindow(this);
 
         // Add the menus
@@ -76,6 +74,35 @@ class DemoApplication extends TApplication {
         item = subMenu.addItem(2002, "&Normal (sub)");
 
         addWindowMenu();
-
+    }
+    
+    /**
+     * Public constructor.
+     *
+     * @param input an InputStream connected to the remote user, or null for
+     * System.in.  If System.in is used, then on non-Windows systems it will
+     * be put in raw mode; shutdown() will (blindly!) put System.in in cooked
+     * mode.  input is always converted to a Reader with UTF-8 encoding.
+     * @param output an OutputStream connected to the remote user, or null
+     * for System.out.  output is always converted to a Writer with UTF-8
+     * encoding.
+     * @throws UnsupportedEncodingException if an exception is thrown when
+     * creating the InputStreamReader
+     */
+    public DemoApplication(final InputStream input,
+        final OutputStream output) throws UnsupportedEncodingException {
+        super(input, output);
+        addAllWidgets();
+    }
+    
+    /**
+     * Public constructor.
+     *
+     * @param backendType one of the TApplication.BackendType values
+     * @throws Exception if TApplication can't instantiate the Backend.
+     */
+    public DemoApplication(BackendType backendType) throws Exception {
+        super(backendType);
+        addAllWidgets();
     }
 }
index a4c2861ba81f988d715881e076accfa85d36a713..ca42db470f25b67749ae715a1fb1536fa39cddfe 100644 (file)
@@ -332,6 +332,7 @@ public final class ECMA48Terminal implements Runnable {
 
         // Enable mouse reporting and metaSendsEscape
         this.output.printf("%s%s", mouse(true), xtermMetaSendsEscape(true));
+        this.output.flush();
 
         // Hang onto the window size
         windowResize = new TResizeEvent(TResizeEvent.Type.SCREEN,
index 8677fdff122d1746d5368b27812bab17cde8cb6b..2c31d765ac1f15f7e8b4fa93229e371b3ff7f695 100644 (file)
@@ -452,24 +452,38 @@ public final class SwingScreen extends Screen {
                         Cell lCell = screen.logical[x][y];
                         Cell pCell = screen.physical[x][y];
 
-                        if (!lCell.equals(pCell) || reallyCleared) {
+                        if (!lCell.equals(pCell)
+                            || lCell.isBlink()
+                            || reallyCleared) {
 
-                            /*
-                             * TODO:
-                             *   reverse
-                             *   blink
-                             *   underline
-                             */
+                            Cell lCellColor = new Cell();
+                            lCellColor.setTo(lCell);
+
+                            // Check for reverse
+                            if (lCell.isReverse()) {
+                                lCellColor.setForeColor(lCell.getBackColor());
+                                lCellColor.setBackColor(lCell.getForeColor());
+                            }
 
                             // Draw the background rectangle, then the
                             // foreground character.
-                            gr.setColor(attrToBackgroundColor(lCell));
+                            gr.setColor(attrToBackgroundColor(lCellColor));
                             gr.fillRect(xPixel, yPixel, textWidth, textHeight);
-                            gr.setColor(attrToForegroundColor(lCell));
-                            char [] chars = new char[1];
-                            chars[0] = lCell.getChar();
-                            gr.drawChars(chars, 0, 1, xPixel,
-                                yPixel + textHeight - maxDescent);
+
+                            // Handle blink and underline
+                            if (!lCell.isBlink()
+                                || (lCell.isBlink() && cursorBlinkVisible)
+                            ) {
+                                gr.setColor(attrToForegroundColor(lCellColor));
+                                char [] chars = new char[1];
+                                chars[0] = lCell.getChar();
+                                gr.drawChars(chars, 0, 1, xPixel,
+                                    yPixel + textHeight - maxDescent);
+                                if (lCell.isUnderline()) {
+                                    gr.fillRect(xPixel, yPixel + textHeight - 2,
+                                        textWidth, 2);
+                                }
+                            }
 
                             // Physical is always updated
                             physical[x][y].setTo(lCell);
@@ -607,6 +621,7 @@ public final class SwingScreen extends Screen {
                         || ((x == cursorX)
                             && (y == cursorY)
                             && cursorVisible)
+                        || lCell.isBlink()
                     ) {
                         if (xPixel < xMin) {
                             xMin = xPixel;