TStatusBar
[fanfix.git] / src / jexer / TTerminalWindow.java
index 6d97531aabbdb76e4d896127d284c3766a7e16f5..50c87d6c6d4db7c1329dca206e1e2e97891565a5 100644 (file)
@@ -32,6 +32,7 @@ import java.io.InputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -158,17 +159,27 @@ public class TTerminalWindow extends TWindow {
 
             // 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.
-            String [] cmdShell = {
+            // 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 {
-                pb = new ProcessBuilder(cmdShell);
+                // When all else fails, assume GNU.
+                pb = new ProcessBuilder(cmdShellGNU);
             }
             Map<String, String> env = pb.environment();
             env.put("TERM", ECMA48.deviceTypeTerm(deviceType));
@@ -189,6 +200,42 @@ public class TTerminalWindow extends TWindow {
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
+
+        // Add shortcut text
+        newStatusBar("Terminal session executing...");
+    }
+
+    /**
+     * Terminate the child of the 'script' process used on POSIX.  This may
+     * or may not work.
+     */
+    private void terminateShellChildProcess() {
+        int pid = -1;
+        if (shell.getClass().getName().equals("java.lang.UNIXProcess")) {
+            /* get the PID on unix/linux systems */
+            try {
+                Field field = shell.getClass().getDeclaredField("pid");
+                field.setAccessible(true);
+                pid = field.getInt(shell);
+            } catch (Throwable e) {
+                // SQUASH, this didn't work.  Just bail out quietly.
+                return;
+            }
+        }
+        if (pid != -1) {
+            // shell.destroy() works successfully at killing this side of
+            // 'script'.  But we need to make sure the other side (child
+            // process) is also killed.
+            String [] cmdKillIt = {
+                "pkill", "-P", Integer.toString(pid)
+            };
+            try {
+                Runtime.getRuntime().exec(cmdKillIt);
+            } catch (Throwable e) {
+                // SQUASH, this didn't work.  Just bail out quietly.
+                return;
+            }
+        }
     }
 
     /**
@@ -219,6 +266,9 @@ public class TTerminalWindow extends TWindow {
 
         // Claim the keystrokes the emulator will need.
         addShortcutKeys();
+
+        // Add shortcut text
+        newStatusBar("Terminal session executing...");
     }
 
     /**
@@ -314,10 +364,11 @@ public class TTerminalWindow extends TWindow {
     /**
      * Handle window close.
      */
-    @Override public void onClose() {
+    @Override
+    public void onClose() {
         emulator.close();
         if (shell != null) {
-            // System.err.println("shell.destroy()");
+            terminateShellChildProcess();
             shell.destroy();
             shell = null;
         }
@@ -362,6 +413,8 @@ public class TTerminalWindow extends TWindow {
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
+                    statusBar.setText("Terminal session completed, exit " +
+                        "code " + rc + ".");
                 } catch (IllegalThreadStateException e) {
                     // The emulator thread has exited, but the shell Process
                     // hasn't figured that out yet.  Do nothing, we will see
@@ -377,6 +430,8 @@ public class TTerminalWindow extends TWindow {
                     shell = null;
                     emulator.close();
                     clearShortcutKeypresses();
+                    statusBar.setText("Terminal session completed, exit " +
+                        "code " + rc + ".");
                 } catch (IllegalThreadStateException e) {
                     // The shell is still running, do nothing.
                 }