From 55b4f29bc5389d9ce185efa9f6e14c5f3882ac70 Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Mon, 23 Mar 2015 13:03:16 -0400 Subject: [PATCH] network socket support, Swing blink/reverse --- README.md | 22 +++++---- src/jexer/TTerminalWindow.java | 17 +++++-- src/jexer/demos/Demo2.java | 68 ++++++++++++++++++++++++++++ src/jexer/demos/DemoApplication.java | 41 ++++++++++++++--- src/jexer/io/ECMA48Terminal.java | 1 + src/jexer/io/SwingScreen.java | 41 +++++++++++------ 6 files changed, 156 insertions(+), 34 deletions(-) create mode 100644 src/jexer/demos/Demo2.java diff --git a/README.md b/README.md index d014852..60734e6 100644 --- 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 diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index f85e346..bb91a63 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -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 index 0000000..35e40ab --- /dev/null +++ b/src/jexer/demos/Demo2.java @@ -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(); + } + } + +} diff --git a/src/jexer/demos/DemoApplication.java b/src/jexer/demos/DemoApplication.java index 4c5ba31..2260962 100644 --- a/src/jexer/demos/DemoApplication.java +++ b/src/jexer/demos/DemoApplication.java @@ -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(); } } diff --git a/src/jexer/io/ECMA48Terminal.java b/src/jexer/io/ECMA48Terminal.java index a4c2861..ca42db4 100644 --- a/src/jexer/io/ECMA48Terminal.java +++ b/src/jexer/io/ECMA48Terminal.java @@ -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, diff --git a/src/jexer/io/SwingScreen.java b/src/jexer/io/SwingScreen.java index 8677fdf..2c31d76 100644 --- a/src/jexer/io/SwingScreen.java +++ b/src/jexer/io/SwingScreen.java @@ -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; -- 2.27.0