import jexer.bits.Cell;
import jexer.bits.CellAttributes;
import jexer.event.TKeypressEvent;
+import jexer.event.TMenuEvent;
import jexer.event.TMouseEvent;
import jexer.event.TResizeEvent;
+import jexer.menu.TMenu;
import jexer.tterminal.DisplayLine;
import jexer.tterminal.DisplayListener;
import jexer.tterminal.ECMA48;
}
/**
- * Public constructor spawns a shell.
+ * Convert a string array to a whitespace-separated string.
*
- * @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 array the string array
+ * @return a single string
*/
- public TTerminalWindow(final TApplication application, final int x,
- final int y, final int flags) {
+ private String stringArrayToString(final String [] array) {
+ StringBuilder sb = new StringBuilder(array[0].length());
+ for (int i = 0; i < array.length; i++) {
+ sb.append(array[i]);
+ if (i < array.length - 1) {
+ sb.append(' ');
+ }
+ }
+ return sb.toString();
+ }
- super(application, i18n.getString("windowTitle"), x, y,
- 80 + 2, 24 + 2, flags);
+ /**
+ * Spawn the shell.
+ *
+ * @param command the command line to execute
+ */
+ private void spawnShell(final String [] command) {
+
+ /*
+ System.err.printf("spawnShell(): '%s'\n",
+ stringArrayToString(command));
+ */
vScroller = new TVScroller(this, getWidth() - 2, 0, getHeight() - 2);
setBottomValue(0);
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"
- };
- String [] cmdShellPtypipe = {
- "ptypipe", "/bin/bash", "--login"
- };
- // Spawn a shell and pass its I/O to the other constructor.
-
- ProcessBuilder pb;
- if ((System.getProperty("jexer.TTerminal.ptypipe") != null)
- && (System.getProperty("jexer.TTerminal.ptypipe").
- equals("true"))
- ) {
- pb = new ProcessBuilder(cmdShellPtypipe);
- ptypipe = true;
- } else 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);
- }
+ ProcessBuilder pb = new ProcessBuilder(command);
Map<String, String> env = pb.environment();
env.put("TERM", ECMA48.deviceTypeTerm(deviceType));
env.put("LANG", ECMA48.deviceTypeLang(deviceType, "en"));
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 commandLine the command line to execute
+ */
+ public TTerminalWindow(final TApplication application, final int x,
+ final int y, final String commandLine) {
+
+ this(application, x, y, RESIZABLE, commandLine.split("\\s"));
+ }
+
+ /**
+ * 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 command the command line to execute
+ */
+ public TTerminalWindow(final TApplication application, final int x,
+ final int y, final int flags, final String [] command) {
+
+ super(application, i18n.getString("windowTitle"), x, y,
+ 80 + 2, 24 + 2, flags);
+
+ String [] fullCommand;
+
+ // 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;
+ fullCommand = new String[command.length + 1];
+ fullCommand[0] = "ptypipe";
+ System.arraycopy(command, 0, fullCommand, 1, command.length);
+ } else if (System.getProperty("os.name").startsWith("Windows")) {
+ fullCommand = new String[3];
+ fullCommand[0] = "cmd";
+ fullCommand[1] = "/c";
+ fullCommand[2] = stringArrayToString(command);
+ } else if (System.getProperty("os.name").startsWith("Mac")) {
+ fullCommand = new String[6];
+ fullCommand[0] = "script";
+ fullCommand[1] = "-q";
+ fullCommand[2] = "-F";
+ fullCommand[3] = "/dev/null";
+ fullCommand[4] = "-c";
+ fullCommand[5] = stringArrayToString(command);
+ } else {
+ // Default: behave like Linux
+ fullCommand = new String[5];
+ fullCommand[0] = "script";
+ fullCommand[1] = "-fqe";
+ fullCommand[2] = "/dev/null";
+ fullCommand[3] = "-c";
+ fullCommand[4] = stringArrayToString(command);
+ }
+ spawnShell(fullCommand);
+ }
+
+ /**
+ * 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.split("\\s"));
+ } else if (System.getProperty("os.name").startsWith("Windows")) {
+ spawnShell(cmdShellWindows.split("\\s"));
+ } else if (System.getProperty("os.name").startsWith("Mac")) {
+ spawnShell(cmdShellBSD.split("\\s"));
+ } else if (System.getProperty("os.name").startsWith("Linux")) {
+ spawnShell(cmdShellGNU.split("\\s"));
+ } else {
+ // When all else fails, assume GNU.
+ spawnShell(cmdShellGNU.split("\\s"));
+ }
+ }
+
/**
* Terminate the child of the 'script' process used on POSIX. This may
* or may not work.
* Called by emulator when fresh data has come in.
*/
public void displayChanged() {
- doRepaint();
+ getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
}
/**
}
}
+ /**
+ * Hook for subclasses to be notified of the shell termination.
+ */
+ public void onShellExit() {
+ getApplication().postEvent(new TMenuEvent(TMenu.MID_REPAINT));
+ }
+
/**
* Copy out variables from the emulator that TTerminal has to expose on
* screen.
clearShortcutKeypresses();
statusBar.setText(MessageFormat.format(i18n.
getString("statusBarCompleted"), rc));
+ onShellExit();
} catch (IllegalThreadStateException e) {
// The emulator thread has exited, but the shell Process
// hasn't figured that out yet. Do nothing, we will see
clearShortcutKeypresses();
statusBar.setText(MessageFormat.format(i18n.
getString("statusBarCompleted"), rc));
+ onShellExit();
} catch (IllegalThreadStateException e) {
// The shell is still running, do nothing.
}