X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2FTTerminalWindow.java;h=31834eebc2324bc02f9e7261277d3994a108ca1b;hb=6f8ff91a29056209f9fd5f40e2dcf1ae285e0210;hp=96043e85af96849dc2ceaea5b09d83b1387e0c0d;hpb=fe0770f988e64fc0ccafd3d3b086b4a0eb559d3b;p=fanfix.git diff --git a/src/jexer/TTerminalWindow.java b/src/jexer/TTerminalWindow.java index 96043e8..31834ee 100644 --- a/src/jexer/TTerminalWindow.java +++ b/src/jexer/TTerminalWindow.java @@ -30,9 +30,11 @@ package jexer; import java.io.IOException; import java.lang.reflect.Field; +import java.text.MessageFormat; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.ResourceBundle; import jexer.bits.Cell; import jexer.bits.CellAttributes; @@ -40,13 +42,21 @@ import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import jexer.event.TResizeEvent; import jexer.tterminal.DisplayLine; +import jexer.tterminal.DisplayListener; import jexer.tterminal.ECMA48; import static jexer.TKeypress.*; /** * TTerminalWindow exposes a ECMA-48 / ANSI X3.64 style terminal in a window. */ -public class TTerminalWindow extends TScrollableWindow { +public class TTerminalWindow extends TScrollableWindow + implements DisplayListener { + + + /** + * Translated strings. + */ + private static final ResourceBundle i18n = ResourceBundle.getBundle(TTerminalWindow.class.getName()); /** * The emulator. @@ -58,6 +68,13 @@ public class TTerminalWindow extends TScrollableWindow { */ private Process shell; + /** + * If true, we are using the ptypipe utility to support dynamic window + * resizing. ptypipe is available at + * https://github.com/klamonte/ptypipe . + */ + private boolean ptypipe = false; + /** * Claim the keystrokes the emulator will need. */ @@ -129,17 +146,11 @@ public class TTerminalWindow extends TScrollableWindow { } /** - * Public constructor spawns a shell. + * Spawn the shell. * - * @param application TApplication that manages this window - * @param x column relative to parent - * @param y row relative to parent - * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param commandLine the command line to execute */ - public TTerminalWindow(final TApplication application, final int x, - final int y, final int flags) { - - super(application, "Terminal", x, y, 80 + 2, 24 + 2, flags); + private void spawnShell(final String commandLine) { vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2); setBottomValue(0); @@ -148,34 +159,8 @@ public class TTerminalWindow extends TScrollableWindow { ECMA48.DeviceType deviceType = ECMA48.DeviceType.XTERM; try { - String [] cmdShellWindows = { - "cmd.exe" - }; - - // You cannot run a login shell in a bare Process interactively, - // due to libc's behavior of buffering when stdin/stdout aren't a - // tty. Use 'script' instead to run a shell in a pty. And - // because BSD and GNU differ on the '-f' vs '-F' flags, we need - // two different commands. Lovely. - String [] cmdShellGNU = { - "script", "-fqe", "/dev/null" - }; - String [] cmdShellBSD = { - "script", "-q", "-F", "/dev/null" - }; - // Spawn a shell and pass its I/O to the other constructor. - - ProcessBuilder pb; - if (System.getProperty("os.name").startsWith("Windows")) { - pb = new ProcessBuilder(cmdShellWindows); - } else if (System.getProperty("os.name").startsWith("Mac")) { - pb = new ProcessBuilder(cmdShellBSD); - } else if (System.getProperty("os.name").startsWith("Linux")) { - pb = new ProcessBuilder(cmdShellGNU); - } else { - // When all else fails, assume GNU. - pb = new ProcessBuilder(cmdShellGNU); - } + String [] command = commandLine.split("\\s"); + ProcessBuilder pb = new ProcessBuilder(command); Map env = pb.environment(); env.put("TERM", ECMA48.deviceTypeTerm(deviceType)); env.put("LANG", ECMA48.deviceTypeLang(deviceType, "en")); @@ -184,9 +169,11 @@ public class TTerminalWindow extends TScrollableWindow { pb.redirectErrorStream(true); shell = pb.start(); emulator = new ECMA48(deviceType, shell.getInputStream(), - shell.getOutputStream()); + shell.getOutputStream(), this); } catch (IOException e) { - messageBox("Error", "Error launching shell: " + e.getMessage()); + messageBox(i18n.getString("errorLaunchingShellTitle"), + MessageFormat.format(i18n.getString("errorLaunchingShellText"), + e.getMessage())); } // Setup the scroll bars @@ -197,7 +184,92 @@ public class TTerminalWindow extends TScrollableWindow { addShortcutKeys(); // Add shortcut text - newStatusBar("Terminal session executing..."); + newStatusBar(i18n.getString("statusBarRunning")); + } + + /** + * Public constructor spawns a custom command line. + * + * @param application TApplication that manages this window + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE + * @param commandLine the command line to execute + */ + public TTerminalWindow(final TApplication application, final int x, + final int y, final int flags, final String commandLine) { + + super(application, i18n.getString("windowTitle"), x, y, + 80 + 2, 24 + 2, flags); + + String cmdShellWindows = "cmd.exe /c" + commandLine; + String cmdShellGNU = "script -fqe /dev/null -c " + commandLine; + String cmdShellBSD = "script -q -F /dev/null -c " + commandLine; + String cmdShellPtypipe = "ptypipe " + commandLine; + + // Spawn a shell and pass its I/O to the other constructor. + if ((System.getProperty("jexer.TTerminal.ptypipe") != null) + && (System.getProperty("jexer.TTerminal.ptypipe"). + equals("true")) + ) { + ptypipe = true; + spawnShell(cmdShellPtypipe); + } else if (System.getProperty("os.name").startsWith("Windows")) { + spawnShell(cmdShellWindows); + } else if (System.getProperty("os.name").startsWith("Mac")) { + spawnShell(cmdShellBSD); + } else if (System.getProperty("os.name").startsWith("Linux")) { + spawnShell(cmdShellGNU); + } else { + // When all else fails, assume GNU. + spawnShell(cmdShellGNU); + } + } + + /** + * Public constructor spawns a shell. + * + * @param application TApplication that manages this window + * @param x column relative to parent + * @param y row relative to parent + * @param flags mask of CENTERED, MODAL, or RESIZABLE + */ + public TTerminalWindow(final TApplication application, final int x, + final int y, final int flags) { + + super(application, i18n.getString("windowTitle"), x, y, + 80 + 2, 24 + 2, flags); + + String cmdShellWindows = "cmd.exe"; + + // You cannot run a login shell in a bare Process interactively, due + // to libc's behavior of buffering when stdin/stdout aren't a tty. + // Use 'script' instead to run a shell in a pty. And because BSD and + // GNU differ on the '-f' vs '-F' flags, we need two different + // commands. Lovely. + String cmdShellGNU = "script -fqe /dev/null"; + String cmdShellBSD = "script -q -F /dev/null"; + + // ptypipe is another solution that permits dynamic window resizing. + String cmdShellPtypipe = "ptypipe /bin/bash --login"; + + // Spawn a shell and pass its I/O to the other constructor. + if ((System.getProperty("jexer.TTerminal.ptypipe") != null) + && (System.getProperty("jexer.TTerminal.ptypipe"). + equals("true")) + ) { + ptypipe = true; + spawnShell(cmdShellPtypipe); + } else if (System.getProperty("os.name").startsWith("Windows")) { + spawnShell(cmdShellWindows); + } else if (System.getProperty("os.name").startsWith("Mac")) { + spawnShell(cmdShellBSD); + } else if (System.getProperty("os.name").startsWith("Linux")) { + spawnShell(cmdShellGNU); + } else { + // When all else fails, assume GNU. + spawnShell(cmdShellGNU); + } } /** @@ -323,6 +395,37 @@ public class TTerminalWindow extends TScrollableWindow { } + /** + * Called by emulator when fresh data has come in. + */ + public void displayChanged() { + doRepaint(); + } + + /** + * Function to call to obtain the display width. + * + * @return the number of columns in the display + */ + public int getDisplayWidth() { + if (ptypipe) { + return getWidth() - 2; + } + return 80; + } + + /** + * Function to call to obtain the display height. + * + * @return the number of rows in the display + */ + public int getDisplayHeight() { + if (ptypipe) { + return getHeight() - 2; + } + return 24; + } + /** * Handle window close. */ @@ -368,13 +471,13 @@ public class TTerminalWindow extends TScrollableWindow { try { int rc = shell.exitValue(); // The emulator exited on its own, all is fine - setTitle(String.format("%s [Completed - %d]", - getTitle(), rc)); + setTitle(MessageFormat.format(i18n. + getString("windowTitleCompleted"), getTitle(), rc)); shell = null; emulator.close(); clearShortcutKeypresses(); - statusBar.setText("Terminal session completed, exit " + - "code " + rc + "."); + statusBar.setText(MessageFormat.format(i18n. + getString("statusBarCompleted"), rc)); } catch (IllegalThreadStateException e) { // The emulator thread has exited, but the shell Process // hasn't figured that out yet. Do nothing, we will see @@ -385,13 +488,13 @@ public class TTerminalWindow extends TScrollableWindow { try { int rc = shell.exitValue(); // If we got here, the shell died. - setTitle(String.format("%s [Completed - %d]", - getTitle(), rc)); + setTitle(MessageFormat.format(i18n. + getString("windowTitleCompleted"), getTitle(), rc)); shell = null; emulator.close(); clearShortcutKeypresses(); - statusBar.setText("Terminal session completed, exit " + - "code " + rc + "."); + statusBar.setText(MessageFormat.format(i18n. + getString("statusBarCompleted"), rc)); } catch (IllegalThreadStateException e) { // The shell is still running, do nothing. } @@ -419,6 +522,14 @@ public class TTerminalWindow extends TScrollableWindow { // Get out of scrollback setVerticalValue(0); + + if (ptypipe) { + emulator.setWidth(getWidth() - 2); + emulator.setHeight(getHeight() - 2); + + emulator.writeRemote("\033[8;" + (getHeight() - 2) + ";" + + (getWidth() - 2) + "t"); + } } return;