stubs for AWTBackend
authorKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 16 Mar 2015 03:03:55 +0000 (23:03 -0400)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 16 Mar 2015 03:03:55 +0000 (23:03 -0400)
Makefile
README.md
build.xml
src/jexer/TApplication.java
src/jexer/backend/AWTBackend.java [new file with mode: 0644]
src/jexer/io/AWTScreen.java [new file with mode: 0644]
src/jexer/io/AWTTerminal.java [new file with mode: 0644]
src/jexer/io/ECMA48Screen.java

index 8a3c72e26648666fbaae6ae1bc81dc6cdf6d3cfe..5955e4a6c43109819baeae863ca616c688dbc4fe 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -120,7 +120,7 @@ run:        jexer run-demo1
 all-demos:     jexer
 
 run-demo1:     all-demos
-       java -cp $(TARGET_DIR) jexer.demos.Demo1
+       java -Djexer.AWT=true -cp $(TARGET_DIR) jexer.demos.Demo1
 
 clean:
        -rm -r $(ANT_TARGET_DIR)
index 353fc47e3d77cc92130710ce5c7e1536a7e21833..8939c4e133c6d532ebb12512ccad14804e981d98 100644 (file)
--- a/README.md
+++ b/README.md
@@ -9,6 +9,21 @@ library, see [Sergio Sigala's updated
 version](http://tvision.sourceforge.net/) that runs on many more
 platforms.
 
+Two backends are available:
+
+* A command-line ECMA-48 / ANSI X3.64 type terminal (tested on Linux +
+  xterm) via System.in and System.out.  Input/output is handled
+  through terminal escape sequences generated by the library itself:
+  ncurses is not required or linked to.  xterm mouse tracking using
+  UTF8 coordinates is supported.  This is the default backend.
+
+* Java Swing/AWT UI.  This backend can be selected by setting
+  jexer.AWT=true.
+
+A demo application showing the existing UI controls is available via
+'java -jar jexer.jar' or 'java -Djexer.AWT=true -jar jexer.jar' .
+
+
 
 License
 -------
@@ -62,6 +77,7 @@ Many tasks remain before calling this version 1.0:
 
 0.0.2:
 
+- ECMA48Backend running on socket
 - TTreeView
 - TDirectoryList
 - TFileOpen
index e9d3555b3452897c85ef04806f261fe7f39736eb..35aee170952567746104662c5cdc2173b519b39b 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -56,7 +56,9 @@
     </target>
 
     <target name="run" depends="jar">
-      <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
+      <java jar="${jar.dir}/${ant.project.name}.jar" fork="true">
+       <arg value="-Djexer.AWT=true"/>
+      </java>
     </target>
 
     <target name="clean-build" depends="clean,jar"/>
index f4b79970b30609a59fdd2682eeb7fd3ba1e875ec..88b329f6f62158668562b821be59d235b67725c5 100644 (file)
@@ -51,6 +51,7 @@ import jexer.event.TMenuEvent;
 import jexer.event.TMouseEvent;
 import jexer.event.TResizeEvent;
 import jexer.backend.Backend;
+import jexer.backend.AWTBackend;
 import jexer.backend.ECMA48Backend;
 import jexer.io.Screen;
 import jexer.menu.TMenu;
@@ -311,7 +312,12 @@ public class TApplication {
     public TApplication(final InputStream input,
         final OutputStream output) throws UnsupportedEncodingException {
 
-        backend         = new ECMA48Backend(input, output);
+        if (System.getProperty("jexer.AWT", "false").equals("true")) {
+            backend     = new AWTBackend();
+        } else {
+            backend     = new ECMA48Backend(input, output);
+        }
+
         theme           = new ColorTheme();
         desktopBottom   = getScreen().getHeight() - 1;
         fillEventQueue  = new ArrayList<TInputEvent>();
@@ -1506,5 +1512,5 @@ public class TApplication {
 
         return new TInputBox(this, title, caption, text);
     }
-    
+
 }
diff --git a/src/jexer/backend/AWTBackend.java b/src/jexer/backend/AWTBackend.java
new file mode 100644 (file)
index 0000000..e76f0bc
--- /dev/null
@@ -0,0 +1,115 @@
+/**
+ * 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.backend;
+
+import java.util.List;
+
+import jexer.event.TInputEvent;
+import jexer.io.AWTScreen;
+import jexer.io.AWTTerminal;
+
+/**
+ * This class uses standard AWT calls to handle screen, keyboard, and mouse
+ * I/O.
+ */
+public final class AWTBackend extends Backend {
+
+    /**
+     * Input events are processed by this Terminal.
+     */
+    private AWTTerminal terminal;
+
+    /**
+     * Public constructor.
+     */
+    public AWTBackend() {
+        // Create a screen
+        AWTScreen screen = new AWTScreen();
+        this.screen = screen;
+        // Create the listeners
+        terminal = new AWTTerminal(screen);
+    }
+
+    /**
+     * Sync the logical screen to the physical device.
+     */
+    @Override
+    public void flushScreen() {
+        screen.flushPhysical();
+    }
+
+    /**
+     * Get keyboard, mouse, and screen resize events.
+     *
+     * @param queue list to append new events to
+     * @param timeout maximum amount of time (in millis) to wait for an
+     * event.  0 means to return immediately, i.e. perform a poll.
+     */
+    @Override
+    public void getEvents(final List<TInputEvent> queue, final int timeout) {
+        if (timeout > 0) {
+            // Try to sleep, let the terminal's input thread wake me up if
+            // something came in.
+            synchronized (terminal) {
+                try {
+                    terminal.wait(timeout);
+                    if (terminal.hasEvents()) {
+                        // System.err.println("getEvents()");
+                        terminal.getEvents(queue);
+                    } else {
+                        // If I got here, then I timed out.  Call
+                        // terminal.getIdleEvents() to pick up stragglers
+                        // like bare resize.
+                        // System.err.println("getIdleEvents()");
+                        terminal.getIdleEvents(queue);
+                    }
+                } catch (InterruptedException e) {
+                    // Spurious interrupt, pretend it was like a timeout.
+                    // System.err.println("[interrupt] getEvents()");
+                    terminal.getIdleEvents(queue);
+                }
+            }
+        } else {
+            // Asking for a poll, go get it.
+            // System.err.println("[polled] getEvents()");
+            terminal.getEvents(queue);
+        }
+    }
+
+    /**
+     * Close the I/O, restore the console, etc.
+     */
+    @Override
+    public void shutdown() {
+        terminal.shutdown();
+    }
+
+}
diff --git a/src/jexer/io/AWTScreen.java b/src/jexer/io/AWTScreen.java
new file mode 100644 (file)
index 0000000..e10e0ec
--- /dev/null
@@ -0,0 +1,197 @@
+/**
+ * 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.io;
+
+import jexer.bits.Cell;
+import jexer.bits.CellAttributes;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * This Screen implementation draws to a Java AWT Frame.
+ */
+public final class AWTScreen extends Screen {
+
+    /**
+     * AWTFrame is our top-level hook into the AWT system.
+     */
+    class AWTFrame extends Frame {
+
+        /**
+         * The TUI Screen data.
+         */
+        AWTScreen screen;
+
+        /**
+         * Width of a character cell.
+         */
+        private int textWidth = 1;
+
+        /**
+         * Height of a character cell.
+         */
+        private int textHeight = 1;
+
+        /**
+         * Top pixel value.
+         */
+        private int top = 30;
+        
+        /**
+         * Left pixel value.
+         */
+        private int left = 30;
+         
+        /**
+         * Public constructor.
+         */
+        public AWTFrame() {
+            setTitle("Jexer Application");
+            setBackground(java.awt.Color.black);
+            setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+            setFont(new Font("Liberation Mono", Font.BOLD, 16));
+            // setFont(new Font(Font.MONOSPACED, Font.PLAIN, 16));
+            setSize(100, 100);
+            setVisible(true);
+        }
+
+        /**
+         * Resize to font dimensions.
+         */
+        public void resizeToScreen() {
+            Graphics gr        = getGraphics();
+            FontMetrics fm     = gr.getFontMetrics();
+            textWidth          = fm.charWidth('m');
+            textHeight         = fm.getHeight();
+            setSize((textWidth + 1) * screen.width + (2 * left),
+                (textHeight + 1) * screen.height + (2 * top));
+
+            System.err.printf("W: %d H: %d\n", textWidth, textHeight);
+        }
+
+        /**
+         * Paint redraws the whole screen.
+         *
+         * @param gr the AWT Graphics context
+         */
+        @Override
+        public void paint(Graphics gr) {
+
+            for (int y = 0; y < screen.height; y++) {
+                for (int x = 0; x < screen.width; x++) {
+                    Cell lCell = screen.logical[x][y];
+                    Cell pCell = screen.physical[x][y];
+
+                    int xPixel = x * (textWidth + 1) + left;
+                    int yPixel = y * (textHeight + 1) + top - y;
+
+                    if (!lCell.equals(pCell)) {
+                        // Draw the background rectangle, then the foreground
+                        // character.
+                        if (lCell.getBackColor().equals(jexer.bits.Color.BLACK)) {
+                            gr.setColor(Color.black);
+                        } else if (lCell.getBackColor().equals(jexer.bits.Color.RED)) {
+                            gr.setColor(Color.red);
+                        } else if (lCell.getBackColor().equals(jexer.bits.Color.BLUE)) {
+                            gr.setColor(Color.blue);
+                        } else if (lCell.getBackColor().equals(jexer.bits.Color.GREEN)) {
+                            gr.setColor(Color.green);
+                        } else if (lCell.getBackColor().equals(jexer.bits.Color.YELLOW)) {
+                            gr.setColor(Color.yellow);
+                        } else if (lCell.getBackColor().equals(jexer.bits.Color.CYAN)) {
+                            gr.setColor(Color.cyan);
+                        } else if (lCell.getBackColor().equals(jexer.bits.Color.MAGENTA)) {
+                            gr.setColor(Color.magenta);
+                        } else if (lCell.getBackColor().equals(jexer.bits.Color.WHITE)) {
+                            gr.setColor(Color.white);
+                        }
+                        gr.fillRect(xPixel, yPixel, textWidth + 1,
+                            textHeight + 2);
+
+                        if (lCell.getForeColor().equals(jexer.bits.Color.BLACK)) {
+                            gr.setColor(Color.black);
+                        } else if (lCell.getForeColor().equals(jexer.bits.Color.RED)) {
+                            gr.setColor(Color.red);
+                        } else if (lCell.getForeColor().equals(jexer.bits.Color.BLUE)) {
+                            gr.setColor(Color.blue);
+                        } else if (lCell.getForeColor().equals(jexer.bits.Color.GREEN)) {
+                            gr.setColor(Color.green);
+                        } else if (lCell.getForeColor().equals(jexer.bits.Color.YELLOW)) {
+                            gr.setColor(Color.yellow);
+                        } else if (lCell.getForeColor().equals(jexer.bits.Color.CYAN)) {
+                            gr.setColor(Color.cyan);
+                        } else if (lCell.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
+                            gr.setColor(Color.magenta);
+                        } else if (lCell.getForeColor().equals(jexer.bits.Color.WHITE)) {
+                            gr.setColor(Color.white);
+                        }
+                        char [] chars = new char[1];
+                        chars[0] = lCell.getChar();
+                        gr.drawChars(chars, 0, 1, xPixel,
+                            yPixel + textHeight - 2);
+
+                        // Physical is always updated
+                        physical[x][y].setTo(lCell);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * The raw AWT Frame.
+     */
+    private AWTFrame frame;
+
+    /**
+     * Public constructor.
+     */
+    public AWTScreen() {
+        frame = new AWTFrame();
+        frame.screen = this;
+        frame.resizeToScreen();
+    }
+
+    /**
+     * Push the logical screen to the physical device.
+     */
+    @Override
+    public void flushPhysical() {
+        Graphics gr = frame.getGraphics();
+        frame.paint(gr);
+    }
+}
diff --git a/src/jexer/io/AWTTerminal.java b/src/jexer/io/AWTTerminal.java
new file mode 100644 (file)
index 0000000..c9ddc13
--- /dev/null
@@ -0,0 +1,163 @@
+/**
+ * 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.io;
+
+import java.util.List;
+import java.util.LinkedList;
+
+import jexer.TKeypress;
+import jexer.bits.Color;
+import jexer.event.TInputEvent;
+import jexer.event.TKeypressEvent;
+import jexer.event.TMouseEvent;
+import jexer.event.TResizeEvent;
+import jexer.session.SessionInfo;
+import jexer.session.TSessionInfo;
+import static jexer.TKeypress.*;
+
+/**
+ * This class reads keystrokes and mouse events from an AWT Frame.
+ */
+public final class AWTTerminal {
+
+    /**
+     * The session information.
+     */
+    private SessionInfo sessionInfo;
+
+    /**
+     * Getter for sessionInfo.
+     *
+     * @return the SessionInfo
+     */
+    public SessionInfo getSessionInfo() {
+        return sessionInfo;
+    }
+
+    /**
+     * The event queue, filled up by a thread reading on input.
+     */
+    private List<TInputEvent> eventQueue;
+
+    /**
+     * If true, we want the reader thread to exit gracefully.
+     */
+    private boolean stopReaderThread;
+
+    /**
+     * The reader thread.
+     */
+    private Thread readerThread;
+
+    /**
+     * true if mouse1 was down.  Used to report mouse1 on the release event.
+     */
+    private boolean mouse1;
+
+    /**
+     * true if mouse2 was down.  Used to report mouse2 on the release event.
+     */
+    private boolean mouse2;
+
+    /**
+     * true if mouse3 was down.  Used to report mouse3 on the release event.
+     */
+    private boolean mouse3;
+
+    /**
+     * Check if there are events in the queue.
+     *
+     * @return if true, getEvents() has something to return to the backend
+     */
+    public boolean hasEvents() {
+        synchronized (eventQueue) {
+            return (eventQueue.size() > 0);
+        }
+    }
+
+    /**
+     * Constructor sets up state for getEvent().
+     *
+     * @param screen the top-level AWT frame 
+     */
+    public AWTTerminal(final AWTScreen screen) {
+        mouse1           = false;
+        mouse2           = false;
+        mouse3           = false;
+        stopReaderThread = false;
+        sessionInfo      = new TSessionInfo();
+        eventQueue       = new LinkedList<TInputEvent>();
+    }
+
+    /**
+     * Restore terminal to normal state.
+     */
+    public void shutdown() {
+        // System.err.println("=== shutdown() ==="); System.err.flush();
+    }
+
+    /**
+     * Return any events in the IO queue.
+     *
+     * @param queue list to append new events to
+     */
+    public void getEvents(final List<TInputEvent> queue) {
+        synchronized (eventQueue) {
+            if (eventQueue.size() > 0) {
+                synchronized (queue) {
+                    queue.addAll(eventQueue);
+                }
+                eventQueue.clear();
+            }
+        }
+    }
+
+    /**
+     * Return any events in the IO queue due to timeout.
+     *
+     * @param queue list to append new events to
+     */
+    public void getIdleEvents(final List<TInputEvent> queue) {
+
+        // Insert any polling action here...
+
+        // Return any events that showed up
+        synchronized (eventQueue) {
+            if (eventQueue.size() > 0) {
+                synchronized (queue) {
+                    queue.addAll(eventQueue);
+                }
+                eventQueue.clear();
+            }
+        }
+    }
+
+}
index 57fa88f60bf1a1e76d6777fe739ac6ae3a59c4f5..aa71796c3a508d02fbaba7f04b8fd4cbce30b265 100644 (file)
@@ -238,7 +238,7 @@ public final class ECMA48Screen extends Screen {
      * @return escape sequences string that provides the updates to the
      * physical screen
      */
-    public String flushString() {
+    private String flushString() {
         if (!dirty) {
             assert (!reallyCleared);
             return "";