From: Kevin Lamonte Date: Fri, 20 Mar 2015 03:51:33 +0000 (-0400) Subject: first screenshot attempt X-Git-Tag: fanfix-3.0.1^2~325 X-Git-Url: https://git.nikiroo.be/?a=commitdiff_plain;h=bb35d91958450cc7152d2063f1d6cd34c15e2a3d;p=fanfix.git first screenshot attempt --- diff --git a/README.md b/README.md index 8d8ea20..058eb6f 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,13 @@ Some arbitrary design decisions had to be made when either the obviously expected behavior did not happen or when a specification was ambiguous. This section describes such issues. + TTerminalWindow + --------------- + + - TTerminalWindow will hang on input from the remote if the + TApplication is exited before closing the TTerminalWindow. This + is due to a Java limitation/interaction between blocking reads + (necessary to get UTF8 translation correct) and file streams. Roadmap @@ -107,7 +114,6 @@ Many tasks remain before calling this version 1.0: 0.0.4: - Bugs - - TTimer is jittery with I/O - TSubMenu keyboard mnemonic not working - Making TMenu keyboard accelerators active/inactive - TDirectoryList cannot be navigated only with keyboard @@ -144,3 +150,5 @@ Wishlist features (2.0): Screenshots ----------- +![Several Windows Open Including A Terminal](/screenshots/screenshot1.png?raw=true "Several Windows Open Including A Terminal") + diff --git a/screenshots/screenshot1.png b/screenshots/screenshot1.png new file mode 100644 index 0000000..10e9ec2 Binary files /dev/null and b/screenshots/screenshot1.png differ diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index e710895..5813d6f 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -1019,20 +1019,22 @@ public class TApplication { * @param window the window to remove */ public final void closeWindow(final TWindow window) { - int z = window.getZ(); - window.setZ(-1); - Collections.sort(windows); - windows.remove(0); - TWindow activeWindow = null; - for (TWindow w: windows) { - if (w.getZ() > z) { - w.setZ(w.getZ() - 1); - if (w.getZ() == 0) { - w.setActive(true); - assert (activeWindow == null); - activeWindow = w; - } else { - w.setActive(false); + synchronized (windows) { + int z = window.getZ(); + window.setZ(-1); + Collections.sort(windows); + windows.remove(0); + TWindow activeWindow = null; + for (TWindow w: windows) { + if (w.getZ() > z) { + w.setZ(w.getZ() - 1); + if (w.getZ() == 0) { + w.setActive(true); + assert (activeWindow == null); + activeWindow = w; + } else { + w.setActive(false); + } } } } @@ -1071,35 +1073,39 @@ public class TApplication { return; } - // Swap z/active between active window and the next in the list - int activeWindowI = -1; - for (int i = 0; i < windows.size(); i++) { - if (windows.get(i).getActive()) { - activeWindowI = i; - break; + synchronized (windows) { + + // Swap z/active between active window and the next in the list + int activeWindowI = -1; + for (int i = 0; i < windows.size(); i++) { + if (windows.get(i).getActive()) { + activeWindowI = i; + break; + } } - } - assert (activeWindowI >= 0); + assert (activeWindowI >= 0); - // Do not switch if a window is modal - if (windows.get(activeWindowI).isModal()) { - return; - } + // Do not switch if a window is modal + if (windows.get(activeWindowI).isModal()) { + return; + } - int nextWindowI; - if (forward) { - nextWindowI = (activeWindowI + 1) % windows.size(); - } else { - if (activeWindowI == 0) { - nextWindowI = windows.size() - 1; + int nextWindowI; + if (forward) { + nextWindowI = (activeWindowI + 1) % windows.size(); } else { - nextWindowI = activeWindowI - 1; + if (activeWindowI == 0) { + nextWindowI = windows.size() - 1; + } else { + nextWindowI = activeWindowI - 1; + } } - } - windows.get(activeWindowI).setActive(false); - windows.get(activeWindowI).setZ(windows.get(nextWindowI).getZ()); - windows.get(nextWindowI).setZ(0); - windows.get(nextWindowI).setActive(true); + windows.get(activeWindowI).setActive(false); + windows.get(activeWindowI).setZ(windows.get(nextWindowI).getZ()); + windows.get(nextWindowI).setZ(0); + windows.get(nextWindowI).setActive(true); + + } // synchronized (windows) // Refresh repaint = true; @@ -1111,17 +1117,19 @@ public class TApplication { * @param window new window to add */ public final void addWindow(final TWindow window) { - // Do not allow a modal window to spawn a non-modal window - if ((windows.size() > 0) && (windows.get(0).isModal())) { - assert (window.isModal()); - } - for (TWindow w: windows) { - w.setActive(false); - w.setZ(w.getZ() + 1); + synchronized (windows) { + // Do not allow a modal window to spawn a non-modal window + if ((windows.size() > 0) && (windows.get(0).isModal())) { + assert (window.isModal()); + } + for (TWindow w: windows) { + w.setActive(false); + w.setZ(w.getZ() + 1); + } + windows.add(window); + window.setActive(true); + window.setZ(0); } - windows.add(window); - window.setActive(true); - window.setZ(0); } /** @@ -1248,29 +1256,31 @@ public class TApplication { return; } - Collections.sort(windows); - if (windows.get(0).isModal()) { - // Modal windows don't switch - return; - } + synchronized (windows) { + Collections.sort(windows); + if (windows.get(0).isModal()) { + // Modal windows don't switch + return; + } - for (TWindow window: windows) { - assert (!window.isModal()); - if (window.mouseWouldHit(mouse)) { - if (window == windows.get(0)) { - // Clicked on the same window, nothing to do + for (TWindow window: windows) { + assert (!window.isModal()); + if (window.mouseWouldHit(mouse)) { + if (window == windows.get(0)) { + // Clicked on the same window, nothing to do + return; + } + + // We will be switching to another window + assert (windows.get(0).getActive()); + assert (!window.getActive()); + windows.get(0).setActive(false); + windows.get(0).setZ(window.getZ()); + window.setZ(0); + window.setActive(true); + repaint = true; return; } - - // We will be switching to another window - assert (windows.get(0).getActive()); - assert (!window.getActive()); - windows.get(0).setActive(false); - windows.get(0).setZ(window.getZ()); - window.setZ(0); - window.setActive(true); - repaint = true; - return; } } @@ -1578,8 +1588,11 @@ public class TApplication { if (activeMenu != null) { return; } - for (TWindow window: windows) { - closeWindow(window); + + synchronized (windows) { + for (TWindow window: windows) { + closeWindow(window); + } } } @@ -1588,52 +1601,54 @@ public class TApplication { * almost the same results as Turbo Pascal 7.0's IDE. */ private void tileWindows() { - // Don't do anything if we are in the menu - if (activeMenu != null) { - return; - } - int z = windows.size(); - if (z == 0) { - return; - } - int a = 0; - int b = 0; - a = (int)(Math.sqrt(z)); - int c = 0; - while (c < a) { - b = (z - c) / a; - if (((a * b) + c) == z) { - break; + synchronized (windows) { + // Don't do anything if we are in the menu + if (activeMenu != null) { + return; } - c++; - } - assert (a > 0); - assert (b > 0); - assert (c < a); - int newWidth = (getScreen().getWidth() / a); - int newHeight1 = ((getScreen().getHeight() - 1) / b); - int newHeight2 = ((getScreen().getHeight() - 1) / (b + c)); - - List sorted = new LinkedList(windows); - Collections.sort(sorted); - Collections.reverse(sorted); - for (int i = 0; i < sorted.size(); i++) { - int logicalX = i / b; - int logicalY = i % b; - if (i >= ((a - 1) * b)) { - logicalX = a - 1; - logicalY = i - ((a - 1) * b); + int z = windows.size(); + if (z == 0) { + return; + } + int a = 0; + int b = 0; + a = (int)(Math.sqrt(z)); + int c = 0; + while (c < a) { + b = (z - c) / a; + if (((a * b) + c) == z) { + break; + } + c++; } + assert (a > 0); + assert (b > 0); + assert (c < a); + int newWidth = (getScreen().getWidth() / a); + int newHeight1 = ((getScreen().getHeight() - 1) / b); + int newHeight2 = ((getScreen().getHeight() - 1) / (b + c)); + + List sorted = new LinkedList(windows); + Collections.sort(sorted); + Collections.reverse(sorted); + for (int i = 0; i < sorted.size(); i++) { + int logicalX = i / b; + int logicalY = i % b; + if (i >= ((a - 1) * b)) { + logicalX = a - 1; + logicalY = i - ((a - 1) * b); + } - TWindow w = sorted.get(i); - w.setX(logicalX * newWidth); - w.setWidth(newWidth); - if (i >= ((a - 1) * b)) { - w.setY((logicalY * newHeight2) + 1); - w.setHeight(newHeight2); - } else { - w.setY((logicalY * newHeight1) + 1); - w.setHeight(newHeight1); + TWindow w = sorted.get(i); + w.setX(logicalX * newWidth); + w.setWidth(newWidth); + if (i >= ((a - 1) * b)) { + w.setY((logicalY * newHeight2) + 1); + w.setHeight(newHeight2); + } else { + w.setY((logicalY * newHeight1) + 1); + w.setHeight(newHeight1); + } } } } @@ -1642,25 +1657,27 @@ public class TApplication { * Re-layout the open windows as overlapping cascaded windows. */ private void cascadeWindows() { - // Don't do anything if we are in the menu - if (activeMenu != null) { - return; - } - int x = 0; - int y = 1; - List sorted = new LinkedList(windows); - Collections.sort(sorted); - Collections.reverse(sorted); - for (TWindow window: sorted) { - window.setX(x); - window.setY(y); - x++; - y++; - if (x > getScreen().getWidth()) { - x = 0; + synchronized (windows) { + // Don't do anything if we are in the menu + if (activeMenu != null) { + return; } - if (y >= getScreen().getHeight()) { - y = 1; + int x = 0; + int y = 1; + List sorted = new LinkedList(windows); + Collections.sort(sorted); + Collections.reverse(sorted); + for (TWindow window: sorted) { + window.setX(x); + window.setY(y); + x++; + y++; + if (x > getScreen().getWidth()) { + x = 0; + } + if (y >= getScreen().getHeight()) { + y = 1; + } } } } diff --git a/src/jexer/io/ECMA48Terminal.java b/src/jexer/io/ECMA48Terminal.java index 9e9ffe3..7721330 100644 --- a/src/jexer/io/ECMA48Terminal.java +++ b/src/jexer/io/ECMA48Terminal.java @@ -1437,7 +1437,7 @@ public final class ECMA48Terminal implements Runnable { readBuffer = new char[readBuffer.length * 2]; } - int rc = input.read(readBuffer, 0, n); + int rc = input.read(readBuffer, 0, readBuffer.length); // System.err.printf("read() %d", rc); System.err.flush(); if (rc == -1) { // This is EOF diff --git a/src/jexer/tterminal/ECMA48.java b/src/jexer/tterminal/ECMA48.java index 41a5f4d..66ee95e 100644 --- a/src/jexer/tterminal/ECMA48.java +++ b/src/jexer/tterminal/ECMA48.java @@ -249,8 +249,34 @@ public class ECMA48 implements Runnable { // Synchronize so we don't stomp on the reader thread. synchronized (this) { - // Tell the reader thread to stop looking at input. It will - // close the input stream as it exits. + // Close the input stream + switch (type) { + case VT100: + case VT102: + case VT220: + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + // SQUASH + } + inputStream = null; + } + break; + case XTERM: + if (input != null) { + try { + input.close(); + } catch (IOException e) { + // SQUASH + } + input = null; + inputStream = null; + } + break; + } + + // Tell the reader thread to stop looking at input. if (stopReaderThread == false) { stopReaderThread = true; try { @@ -294,7 +320,7 @@ public class ECMA48 implements Runnable { /** * When true, the reader thread is expected to exit. */ - private boolean stopReaderThread = false; + private volatile boolean stopReaderThread = false; /** * The reader thread. @@ -329,7 +355,7 @@ public class ECMA48 implements Runnable { /** * The scrollback buffer characters + attributes. */ - private List scrollback; + private volatile List scrollback; /** * Get the scrollback buffer. @@ -343,7 +369,7 @@ public class ECMA48 implements Runnable { /** * The raw display buffer characters + attributes. */ - private List display; + private volatile List display; /** * Get the display buffer. @@ -5441,77 +5467,56 @@ public class ECMA48 implements Runnable { while (!done && !stopReaderThread) { try { - // We assume that if inputStream has bytes available, then - // input won't block on read(). int n = inputStream.available(); - if (n > 0) { - // System.err.printf("available() %d\n", n); System.err.flush(); - if (utf8) { - if (readBufferUTF8.length < n) { - // The buffer wasn't big enough, make it huger - int newSize = Math.max(readBufferUTF8.length * 2, n); - - readBufferUTF8 = new char[newSize]; - } - } else { - if (readBuffer.length < n) { - // The buffer wasn't big enough, make it huger - int newSize = Math.max(readBuffer.length * 2, n); - readBuffer = new byte[newSize]; - } - } + // System.err.printf("available() %d\n", n); System.err.flush(); + if (utf8) { + if (readBufferUTF8.length < n) { + // The buffer wasn't big enough, make it huger + int newSizeHalf = Math.max(readBufferUTF8.length, n); - int rc = -1; - if (utf8) { - rc = input.read(readBufferUTF8, 0, n); - } else { - rc = inputStream.read(readBuffer, 0, n); + readBufferUTF8 = new char[newSizeHalf * 2]; } - // System.err.printf("read() %d\n", rc); System.err.flush(); - if (rc == -1) { - // This is EOF - done = true; - } else { - for (int i = 0; i < rc; i++) { - int ch = 0; - if (utf8) { - ch = readBufferUTF8[i]; - } else { - ch = readBuffer[i]; - } - // Don't step on UI events - synchronized (this) { - consume((char)ch); - } - } + } else { + if (readBuffer.length < n) { + // The buffer wasn't big enough, make it huger + int newSizeHalf = Math.max(readBuffer.length, n); + readBuffer = new byte[newSizeHalf * 2]; } + } + + int rc = -1; + if (utf8) { + rc = input.read(readBufferUTF8, 0, + readBufferUTF8.length); } else { - // Wait 10 millis for more data - Thread.sleep(10); + rc = inputStream.read(readBuffer, 0, + readBuffer.length); + } + // System.err.printf("read() %d\n", rc); System.err.flush(); + if (rc == -1) { + // This is EOF + done = true; + } else { + for (int i = 0; i < rc; i++) { + int ch = 0; + if (utf8) { + ch = readBufferUTF8[i]; + } else { + ch = readBuffer[i]; + } + // Don't step on UI events + synchronized (this) { + consume((char)ch); + } + } } // System.err.println("end while loop"); System.err.flush(); - } catch (InterruptedException e) { - // SQUASH } catch (IOException e) { e.printStackTrace(); done = true; } } // while ((done == false) && (stopReaderThread == false)) - // Close the input stream - try { - if (utf8) { - input.close(); - input = null; - inputStream = null; - } else { - inputStream.close(); - inputStream = null; - } - } catch (IOException e) { - e.printStackTrace(); - } - // Let the rest of the world know that I am done. stopReaderThread = true;