X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTApplication.java;h=e9256a1b74955f6168290f372f56d8d5e4f8df0f;hb=7b5261bc5b641e0769902f014e3b21f61050b02b;hp=52bd4356134d9083bddd02e49c40baf96aeecdf5;hpb=623a1bd12877164ce2ae515e176b809d9c1a2041;p=fanfix.git diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index 52bd435..e9256a1 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -1,16 +1,11 @@ /** - * Jexer - Java Text User Interface - demonstration program - * - * Version: $Id$ - * - * Author: Kevin Lamonte, kevin.lamonte@gmail.com + * Jexer - Java Text User Interface * * License: LGPLv3 or later * - * Copyright: 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. + * 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 * @@ -29,6 +24,9 @@ * 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; @@ -59,27 +57,36 @@ public class TApplication { /** * Access to the physical screen, keyboard, and mouse. */ - public Backend backend; + private Backend backend; /** - * Actual mouse coordinate X + * Actual mouse coordinate X. */ private int mouseX; /** - * Actual mouse coordinate Y + * Actual mouse coordinate Y. */ private int mouseY; /** - * Event queue that will be drained by either primary or secondary Fiber + * Event queue that will be drained by either primary or secondary Fiber. */ private List eventQueue; /** * Windows and widgets pull colors from this ColorTheme. */ - public ColorTheme theme; + private ColorTheme theme; + + /** + * Get the color theme. + * + * @return the theme + */ + public final ColorTheme getTheme() { + return theme; + } /** * When true, exit the application. @@ -97,9 +104,11 @@ public class TApplication { public boolean flush = false; /** - * Y coordinate of the top edge of the desktop. + * Y coordinate of the top edge of the desktop. For now this is a + * constant. Someday it would be nice to have a multi-line menu or + * toolbars. */ - static public final int desktopTop = 1; + public static final int desktopTop = 1; /** * Y coordinate of the bottom edge of the desktop. @@ -116,184 +125,187 @@ public class TApplication { * @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 TApplication(InputStream input, OutputStream output) throws UnsupportedEncodingException { + public TApplication(final InputStream input, + final OutputStream output) throws UnsupportedEncodingException { - backend = new ECMA48Backend(input, output); - theme = new ColorTheme(); - desktopBottom = backend.screen.getHeight() - 1; - eventQueue = new LinkedList(); + backend = new ECMA48Backend(input, output); + theme = new ColorTheme(); + desktopBottom = backend.getScreen().getHeight() - 1; + eventQueue = new LinkedList(); } /** * Invert the cell at the mouse pointer position. */ private void drawMouse() { - CellAttributes attr = backend.screen.getAttrXY(mouseX, mouseY); - attr.foreColor = attr.foreColor.invert(); - attr.backColor = attr.backColor.invert(); - backend.screen.putAttrXY(mouseX, mouseY, attr, false); - flush = true; - - /* - if (windows.length == 0) { - repaint = true; - } - */ - // TODO: remove this repaint after the above if (windows.length == 0) - // can be used again. - repaint = true; + CellAttributes attr = backend.getScreen().getAttrXY(mouseX, mouseY); + attr.setForeColor(attr.getForeColor().invert()); + attr.setBackColor(attr.getBackColor().invert()); + backend.getScreen().putAttrXY(mouseX, mouseY, attr, false); + flush = true; + + /* + if (windows.length == 0) { + repaint = true; + } + */ + // TODO: remove this repaint after the above if (windows.length == 0) + // can be used again. + repaint = true; } /** * Draw everything. */ - final public void drawAll() { - if ((flush) && (!repaint)) { - backend.flushScreen(); - flush = false; - return; - } - - if (!repaint) { - return; - } - - // If true, the cursor is not visible - boolean cursor = false; - - // Start with a clean screen - backend.screen.clear(); - - // Draw the background - CellAttributes background = theme.getColor("tapplication.background"); - backend.screen.putAll(GraphicsChars.HATCH, background); - - /* - // Draw each window in reverse Z order - TWindow [] sorted = windows.dup; - sorted.sort.reverse; - foreach (w; sorted) { - w.drawChildren(); - } - - // Draw the blank menubar line - reset the screen clipping first so - // it won't trim it out. - backend.screen.resetClipping(); - backend.screen.hLineXY(0, 0, backend.screen.getWidth(), ' ', - theme.getColor("tmenu")); - // Now draw the menus. - int x = 1; - foreach (m; menus) { - CellAttributes menuColor; - CellAttributes menuMnemonicColor; - if (m.active) { - menuColor = theme.getColor("tmenu.highlighted"); - menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted"); - } else { - menuColor = theme.getColor("tmenu"); - menuMnemonicColor = theme.getColor("tmenu.mnemonic"); - } - // Draw the menu title - backend.screen.hLineXY(x, 0, cast(int)m.title.length + 2, ' ', - menuColor); - backend.screen.putStrXY(x + 1, 0, m.title, menuColor); - // Draw the highlight character - backend.screen.putCharXY(x + 1 + m.mnemonic.shortcutIdx, 0, - m.mnemonic.shortcut, menuMnemonicColor); - - if (m.active) { - m.drawChildren(); - // Reset the screen clipping so we can draw the next title. - backend.screen.resetClipping(); - } - x += m.title.length + 2; - } - - foreach (m; subMenus) { - // Reset the screen clipping so we can draw the next sub-menu. - backend.screen.resetClipping(); - m.drawChildren(); - } - */ - - // Draw the mouse pointer - drawMouse(); - - /* - // Place the cursor if it is visible - TWidget activeWidget = null; - if (sorted.length > 0) { - activeWidget = sorted[$ - 1].getActiveChild(); - if (activeWidget.hasCursor) { - backend.screen.putCursor(true, activeWidget.getCursorAbsoluteX(), - activeWidget.getCursorAbsoluteY()); - cursor = true; - } - } - - // Kill the cursor - if (cursor == false) { - backend.screen.hideCursor(); - } - */ - - // Flush the screen contents - backend.flushScreen(); - - repaint = false; - flush = false; + public final void drawAll() { + if ((flush) && (!repaint)) { + backend.flushScreen(); + flush = false; + return; + } + + if (!repaint) { + return; + } + + // If true, the cursor is not visible + boolean cursor = false; + + // Start with a clean screen + backend.getScreen().clear(); + + // Draw the background + CellAttributes background = theme.getColor("tapplication.background"); + backend.getScreen().putAll(GraphicsChars.HATCH, background); + + /* + // Draw each window in reverse Z order + TWindow [] sorted = windows.dup; + sorted.sort.reverse; + foreach (w; sorted) { + w.drawChildren(); + } + + // Draw the blank menubar line - reset the screen clipping first so + // it won't trim it out. + backend.getScreen().resetClipping(); + backend.getScreen().hLineXY(0, 0, backend.getScreen().getWidth(), ' ', + theme.getColor("tmenu")); + // Now draw the menus. + int x = 1; + foreach (m; menus) { + CellAttributes menuColor; + CellAttributes menuMnemonicColor; + if (m.active) { + menuColor = theme.getColor("tmenu.highlighted"); + menuMnemonicColor = theme.getColor("tmenu.mnemonic.highlighted"); + } else { + menuColor = theme.getColor("tmenu"); + menuMnemonicColor = theme.getColor("tmenu.mnemonic"); + } + // Draw the menu title + backend.getScreen().hLineXY(x, 0, cast(int)m.title.length + 2, ' ', + menuColor); + backend.getScreen().putStrXY(x + 1, 0, m.title, menuColor); + // Draw the highlight character + backend.getScreen().putCharXY(x + 1 + m.mnemonic.shortcutIdx, 0, + m.mnemonic.shortcut, menuMnemonicColor); + + if (m.active) { + m.drawChildren(); + // Reset the screen clipping so we can draw the next title. + backend.getScreen().resetClipping(); + } + x += m.title.length + 2; + } + + foreach (m; subMenus) { + // Reset the screen clipping so we can draw the next sub-menu. + backend.getScreen().resetClipping(); + m.drawChildren(); + } + */ + + // Draw the mouse pointer + drawMouse(); + + /* + // Place the cursor if it is visible + TWidget activeWidget = null; + if (sorted.length > 0) { + activeWidget = sorted[$ - 1].getActiveChild(); + if (activeWidget.hasCursor) { + backend.getScreen().putCursor(true, activeWidget.getCursorAbsoluteX(), + activeWidget.getCursorAbsoluteY()); + cursor = true; + } + } + + // Kill the cursor + if (cursor == false) { + backend.getScreen().hideCursor(); + } + */ + + // Flush the screen contents + backend.flushScreen(); + + repaint = false; + flush = false; } /** - * Run this application until it exits, using stdin and stdout + * Run this application until it exits. */ public final void run() { - List events = new LinkedList(); - - while (quit == false) { - // Timeout is in milliseconds, so default timeout after 1 second - // of inactivity. - int timeout = getSleepTime(1000); - - if (eventQueue.size() > 0) { - // Do not wait if there are definitely events waiting to be - // processed or a screen redraw to do. - timeout = 0; - } - - // Pull any pending input events - backend.getEvents(events, timeout); - metaHandleEvents(events); - events.clear(); - - // Process timers and call doIdle()'s - doIdle(); - - // Update the screen - drawAll(); - } - - /* - - // Shutdown the fibers - eventQueue.length = 0; - if (secondaryEventFiber !is null) { - assert(secondaryEventReceiver !is null); - secondaryEventReceiver = null; - if (secondaryEventFiber.state == Fiber.State.HOLD) { - // Wake up the secondary handler so that it can exit. - secondaryEventFiber.call(); - } - } - - if (primaryEventFiber.state == Fiber.State.HOLD) { - // Wake up the primary handler so that it can exit. - primaryEventFiber.call(); - } - */ - - backend.shutdown(); + List events = new LinkedList(); + + while (!quit) { + // Timeout is in milliseconds, so default timeout after 1 second + // of inactivity. + int timeout = getSleepTime(1000); + + if (eventQueue.size() > 0) { + // Do not wait if there are definitely events waiting to be + // processed or a screen redraw to do. + timeout = 0; + } + + // Pull any pending input events + backend.getEvents(events, timeout); + metaHandleEvents(events); + events.clear(); + + // Process timers and call doIdle()'s + doIdle(); + + // Update the screen + drawAll(); + } + + /* + + // Shutdown the fibers + eventQueue.length = 0; + if (secondaryEventFiber !is null) { + assert(secondaryEventReceiver !is null); + secondaryEventReceiver = null; + if (secondaryEventFiber.state == Fiber.State.HOLD) { + // Wake up the secondary handler so that it can exit. + secondaryEventFiber.call(); + } + } + + if (primaryEventFiber.state == Fiber.State.HOLD) { + // Wake up the primary handler so that it can exit. + primaryEventFiber.call(); + } + */ + + backend.shutdown(); } /** @@ -302,84 +314,85 @@ public class TApplication { * * @param events the input events to consume */ - private void metaHandleEvents(List events) { - - for (TInputEvent event: events) { - - /* - System.err.printf(String.format("metaHandleEvents event: %s\n", - event)); System.err.flush(); - */ - - if (quit == true) { - // Do no more processing if the application is already trying - // to exit. - return; - } - - // DEBUG - if (event instanceof TKeypressEvent) { - TKeypressEvent keypress = (TKeypressEvent)event; - if (keypress.key.equals(kbAltX)) { - quit = true; - return; - } - } - // DEBUG - - // Special application-wide events ------------------------------- - - // Abort everything - if (event instanceof TCommandEvent) { - TCommandEvent command = (TCommandEvent)event; - if (command.cmd.equals(cmAbort)) { - quit = true; - return; - } - } - - // Screen resize - if (event instanceof TResizeEvent) { - TResizeEvent resize = (TResizeEvent)event; - backend.screen.setDimensions(resize.width, resize.height); - desktopBottom = backend.screen.getHeight() - 1; - repaint = true; - mouseX = 0; - mouseY = 0; - continue; - } - - // Peek at the mouse position - if (event instanceof TMouseEvent) { - TMouseEvent mouse = (TMouseEvent)event; - if ((mouseX != mouse.x) || (mouseY != mouse.y)) { - mouseX = mouse.x; - mouseY = mouse.y; - drawMouse(); - } - } - - /* - - // Put into the main queue - addEvent(event); - - // Have one of the two consumer Fibers peel the events off - // the queue. - if (secondaryEventFiber !is null) { - assert(secondaryEventFiber.state == Fiber.State.HOLD); - - // Wake up the secondary handler for these events - secondaryEventFiber.call(); - } else { - assert(primaryEventFiber.state == Fiber.State.HOLD); - - // Wake up the primary handler for these events - primaryEventFiber.call(); - } - */ - - } // for (TInputEvent event: events) + private void metaHandleEvents(final List events) { + + for (TInputEvent event: events) { + + /* + System.err.printf(String.format("metaHandleEvents event: %s\n", + event)); System.err.flush(); + */ + + if (quit) { + // Do no more processing if the application is already trying + // to exit. + return; + } + + // DEBUG + if (event instanceof TKeypressEvent) { + TKeypressEvent keypress = (TKeypressEvent) event; + if (keypress.key.equals(kbAltX)) { + quit = true; + return; + } + } + // DEBUG + + // Special application-wide events ------------------------------- + + // Abort everything + if (event instanceof TCommandEvent) { + TCommandEvent command = (TCommandEvent) event; + if (command.getCmd().equals(cmAbort)) { + quit = true; + return; + } + } + + // Screen resize + if (event instanceof TResizeEvent) { + TResizeEvent resize = (TResizeEvent) event; + backend.getScreen().setDimensions(resize.getWidth(), + resize.getHeight()); + desktopBottom = backend.getScreen().getHeight() - 1; + repaint = true; + mouseX = 0; + mouseY = 0; + continue; + } + + // Peek at the mouse position + if (event instanceof TMouseEvent) { + TMouseEvent mouse = (TMouseEvent) event; + if ((mouseX != mouse.x) || (mouseY != mouse.y)) { + mouseX = mouse.x; + mouseY = mouse.y; + drawMouse(); + } + } + + /* + + // Put into the main queue + addEvent(event); + + // Have one of the two consumer Fibers peel the events off + // the queue. + if (secondaryEventFiber !is null) { + assert(secondaryEventFiber.state == Fiber.State.HOLD); + + // Wake up the secondary handler for these events + secondaryEventFiber.call(); + } else { + assert(primaryEventFiber.state == Fiber.State.HOLD); + + // Wake up the primary handler for these events + primaryEventFiber.call(); + } + */ + + } // for (TInputEvent event: events) } @@ -387,27 +400,27 @@ public class TApplication { * Do stuff when there is no user input. */ private void doIdle() { - /* - // Now run any timers that have timed out - auto now = Clock.currTime; - TTimer [] keepTimers; - foreach (t; timers) { - if (t.nextTick < now) { - t.tick(); - if (t.recurring == true) { - keepTimers ~= t; - } - } else { - keepTimers ~= t; - } - } - timers = keepTimers; - - // Call onIdle's - foreach (w; windows) { - w.onIdle(); - } - */ + /* + // Now run any timers that have timed out + auto now = Clock.currTime; + TTimer [] keepTimers; + foreach (t; timers) { + if (t.nextTick < now) { + t.tick(); + if (t.recurring == true) { + keepTimers ~= t; + } + } else { + keepTimers ~= t; + } + } + timers = keepTimers; + + // Call onIdle's + foreach (w; windows) { + w.onIdle(); + } + */ } /** @@ -416,25 +429,25 @@ public class TApplication { * @param timeout = initial (maximum) timeout * @return number of milliseconds between now and the next timer event */ - protected int getSleepTime(int timeout) { - /* - auto now = Clock.currTime; - auto sleepTime = dur!("msecs")(timeout); - foreach (t; timers) { - if (t.nextTick < now) { - return 0; - } - if ((t.nextTick > now) && - ((t.nextTick - now) < sleepTime) - ) { - sleepTime = t.nextTick - now; - } - } - assert(sleepTime.total!("msecs")() >= 0); - return cast(uint)sleepTime.total!("msecs")(); - */ - // TODO: fix timers. Until then, come back after 250 millis. - return 250; + protected int getSleepTime(final int timeout) { + /* + auto now = Clock.currTime; + auto sleepTime = dur!("msecs")(timeout); + foreach (t; timers) { + if (t.nextTick < now) { + return 0; + } + if ((t.nextTick > now) && + ((t.nextTick - now) < sleepTime) + ) { + sleepTime = t.nextTick - now; + } + } + assert(sleepTime.total!("msecs")() >= 0); + return cast(uint)sleepTime.total!("msecs")(); + */ + // TODO: fix timers. Until then, come back after 250 millis. + return 250; } }