Merge branch 'subtree'
[fanfix.git] / src / jexer / TTerminalWidget.java
index d36ca2cb6e8a3bf8614b6ff632c18877efe06af1..bf51e6b5c2fd67b78b55d6e280e44f68d3b335aa 100644 (file)
  */
 package jexer;
 
-import java.awt.Font;
-import java.awt.FontMetrics;
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
-
-import java.io.InputStream;
+import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.ResourceBundle;
 
 import jexer.backend.ECMA48Terminal;
 import jexer.backend.GlyphMaker;
-import jexer.backend.MultiScreen;
 import jexer.backend.SwingTerminal;
 import jexer.bits.Cell;
-import jexer.bits.CellAttributes;
 import jexer.event.TCommandEvent;
 import jexer.event.TKeypressEvent;
 import jexer.event.TMenuEvent;
@@ -86,6 +79,11 @@ public class TTerminalWidget extends TScrollableWidget
      */
     private Process shell;
 
+    /**
+     * If true, something called 'ptypipe' is on the PATH and executable.
+     */
+    private static boolean ptypipeOnPath = false;
+
     /**
      * If true, we are using the ptypipe utility to support dynamic window
      * resizing.  ptypipe is available at
@@ -165,6 +163,13 @@ public class TTerminalWidget extends TScrollableWidget
     // Constructors -----------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Static constructor.
+     */
+    static {
+        checkForPtypipe();
+    }
+
     /**
      * Public constructor spawns a custom command line.
      *
@@ -238,6 +243,14 @@ public class TTerminalWidget extends TScrollableWidget
             fullCommand = new String[command.length + 1];
             fullCommand[0] = "ptypipe";
             System.arraycopy(command, 0, fullCommand, 1, command.length);
+        } else if (System.getProperty("jexer.TTerminal.ptypipe",
+                "auto").equals("auto")
+            && (ptypipeOnPath == 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";
@@ -253,12 +266,24 @@ public class TTerminalWidget extends TScrollableWidget
             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);
+            if (System.getProperty("jexer.TTerminal.setsid",
+                    "true").equals("false")
+            ) {
+                fullCommand = new String[5];
+                fullCommand[0] = "script";
+                fullCommand[1] = "-fqe";
+                fullCommand[2] = "/dev/null";
+                fullCommand[3] = "-c";
+                fullCommand[4] = stringArrayToString(command);
+            } else {
+                fullCommand = new String[6];
+                fullCommand[0] = "setsid";
+                fullCommand[1] = "script";
+                fullCommand[2] = "-fqe";
+                fullCommand[3] = "/dev/null";
+                fullCommand[4] = "-c";
+                fullCommand[5] = stringArrayToString(command);
+            }
         }
         spawnShell(fullCommand);
     }
@@ -322,6 +347,7 @@ public class TTerminalWidget extends TScrollableWidget
         // GNU differ on the '-f' vs '-F' flags, we need two different
         // commands.  Lovely.
         String cmdShellGNU = "script -fqe /dev/null";
+        String cmdShellGNUSetsid = "setsid script -fqe /dev/null";
         String cmdShellBSD = "script -q -F /dev/null";
 
         // ptypipe is another solution that permits dynamic window resizing.
@@ -334,12 +360,24 @@ public class TTerminalWidget extends TScrollableWidget
         ) {
             ptypipe = true;
             spawnShell(cmdShellPtypipe.split("\\s+"));
+        } else if (System.getProperty("jexer.TTerminal.ptypipe",
+                "auto").equals("auto")
+            && (ptypipeOnPath == 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+"));
+            if (System.getProperty("jexer.TTerminal.setsid",
+                    "true").equals("false")
+            ) {
+                spawnShell(cmdShellGNU.split("\\s+"));
+            } else {
+                spawnShell(cmdShellGNUSetsid.split("\\s+"));
+            }
         } else {
             // When all else fails, assume GNU.
             spawnShell(cmdShellGNU.split("\\s+"));
@@ -757,6 +795,43 @@ public class TTerminalWidget extends TScrollableWidget
     // TTerminalWidget --------------------------------------------------------
     // ------------------------------------------------------------------------
 
+    /**
+     * Check for 'ptypipe' on the path.  If available, set ptypipeOnPath.
+     */
+    private static void checkForPtypipe() {
+        String systemPath = System.getenv("PATH");
+        if (systemPath == null) {
+            return;
+        }
+
+        String [] paths = systemPath.split(File.pathSeparator);
+        if (paths == null) {
+            return;
+        }
+        if (paths.length == 0) {
+            return;
+        }
+        for (int i = 0; i < paths.length; i++) {
+            File path = new File(paths[i]);
+            if (path.exists() && path.isDirectory()) {
+                File [] files = path.listFiles();
+                if (files == null) {
+                    continue;
+                }
+                if (files.length == 0) {
+                    continue;
+                }
+                for (int j = 0; j < files.length; j++) {
+                    File file = files[j];
+                    if (file.canExecute() && file.getName().equals("ptypipe")) {
+                        ptypipeOnPath = true;
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Get the desired window title.
      *
@@ -910,10 +985,7 @@ public class TTerminalWidget extends TScrollableWidget
                     }
                 });
             }
-            if (getApplication() != null) {
-                getApplication().postEvent(new TMenuEvent(
-                    TMenu.MID_REPAINT));
-            }
+            app.doRepaint();
         }
     }
 
@@ -983,6 +1055,19 @@ public class TTerminalWidget extends TScrollableWidget
         } // synchronized (emulator)
     }
 
+    /**
+     * Wait for a period of time to get output from the launched process.
+     *
+     * @param millis millis to wait for, or 0 to wait forever
+     * @return true if the launched process has emitted something
+     */
+    public boolean waitForOutput(final int millis) {
+        if (emulator == null) {
+            return false;
+        }
+        return emulator.waitForOutput(millis);
+    }
+
     /**
      * Check if a mouse press/release/motion event coordinate is over the
      * emulator.
@@ -1189,6 +1274,15 @@ public class TTerminalWidget extends TScrollableWidget
         return 24;
     }
 
+    /**
+     * Get the exit value for the emulator.
+     *
+     * @return exit value
+     */
+    public int getExitValue() {
+        return exitValue;
+    }
+
     // ------------------------------------------------------------------------
     // EditMenuUser -----------------------------------------------------------
     // ------------------------------------------------------------------------