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)
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
-------
0.0.2:
+- ECMA48Backend running on socket
- TTreeView
- TDirectoryList
- TFileOpen
</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"/>
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;
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>();
return new TInputBox(this, title, caption, text);
}
-
+
}
--- /dev/null
+/**
+ * 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();
+ }
+
+}
--- /dev/null
+/**
+ * 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);
+ }
+}
--- /dev/null
+/**
+ * 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();
+ }
+ }
+ }
+
+}
* @return escape sequences string that provides the updates to the
* physical screen
*/
- public String flushString() {
+ private String flushString() {
if (!dirty) {
assert (!reallyCleared);
return "";