----------------------------------------------------------------------------- -- | -- Module : XMonad.Util.Run -- Copyright : (C) 2007 Spencer Janssen, Andrea Rossato, glasser@mit.edu -- License : BSD-style (see LICENSE) -- -- Maintainer : Christian Thiemann -- Stability : unstable -- Portability : unportable -- -- This modules provides several commands to run an external process. -- It is composed of functions formerly defined in "XMonad.Util.Dmenu" (by -- Spencer Janssen), "XMonad.Util.Dzen" (by glasser\@mit.edu) and -- XMonad.Util.RunInXTerm (by Andrea Rossato). -- ----------------------------------------------------------------------------- module XMonad.Util.Run ( -- * Usage -- $usage runProcessWithInput, runProcessWithInputAndWait, safeSpawn, unsafeSpawn, runInTerm, safeRunInTerm, seconds, spawnPipe ) where import System.Posix.IO import System.Posix.Process (executeFile) import Control.Concurrent (threadDelay) import Control.Exception (try) import System.IO import System.Process (runInteractiveProcess, waitForProcess) import XMonad import Control.Monad -- $usage -- For an example usage of 'runInTerm' see "XMonad.Prompt.Ssh" -- -- For an example usage of 'runProcessWithInput' see -- "XMonad.Prompt.DirectoryPrompt", "XMonad.Util.Dmenu", -- "XMonad.Prompt.ShellPrompt", "XMonad.Actions.WmiiActions", -- "XMonad.Prompt.WorkspaceDir" -- -- For an example usage of 'runProcessWithInputAndWait' see -- "XMonad.Util.Dzen" -- | Return output if the command succeeded, otherwise return @()@. -- This corresponds to dmenu's notion of exit code 1 for a cancelled invocation. runProcessWithInput :: FilePath -> [String] -> String -> IO String runProcessWithInput cmd args input = do (pin, pout, perr, ph) <- runInteractiveProcess cmd args Nothing Nothing hPutStr pin input hClose pin output <- hGetContents pout when (output==output) $ return () hClose pout hClose perr waitForProcess ph return output -- | Wait is in us runProcessWithInputAndWait :: FilePath -> [String] -> String -> Int -> IO () runProcessWithInputAndWait cmd args input timeout = do doubleFork $ do (pin, pout, perr, ph) <- runInteractiveProcess cmd args Nothing Nothing hPutStr pin input hFlush pin threadDelay timeout hClose pin hClose pout hClose perr waitForProcess ph return () -- | Multiplies by ONE MILLION, for use with -- 'runProcessWithInputAndWait'. -- -- Use like: -- -- > (5.5 `seconds`) seconds :: Rational -> Int seconds = fromEnum . (* 1000000) -- | safeSpawn bypasses XMonad's 'spawn' command, because 'spawn' passes -- strings to \/bin\/sh to be interpreted as shell commands. This is -- often what one wants, but in many cases the passed string will contain -- shell metacharacters which one does not want interpreted as such (URLs -- particularly often have shell metacharacters like \'&\' in them). In -- this case, it is more useful to specify a file or program to be run -- and a string to give it as an argument so as to bypass the shell and -- be certain the program will receive the string as you typed it. -- unsafeSpawn is an alias for XMonad's 'spawn', to remind one that use -- of it can be, well, unsafe. -- Examples: -- -- > , ((modMask, xK_Print), unsafeSpawn "import -window root png:$HOME/xwd-$(date +%s)$$.png") -- > , ((modMask, xK_d ), safeSpawn "firefox" "") -- -- Note that the unsafeSpawn example must be unsafe and not safe because -- it makes use of shell interpretation by relying on @$HOME@ and -- interpolation, whereas the safeSpawn example can be safe because -- Firefox doesn't need any arguments if it is just being started. safeSpawn :: MonadIO m => FilePath -> String -> m () safeSpawn prog arg = liftIO (try (doubleFork $ executeFile prog True [arg] Nothing) >> return ()) unsafeSpawn :: MonadIO m => String -> m () unsafeSpawn = spawn -- | Run a given program in the preferred terminal emulator. This uses -- 'safeSpawn'. safeRunInTerm :: String -> X () safeRunInTerm command = asks (terminal . config) >>= \t -> safeSpawn t ("-e " ++ command) unsafeRunInTerm, runInTerm :: String -> X () unsafeRunInTerm command = asks (terminal . config) >>= \t -> unsafeSpawn $ t ++ " -e " ++ command runInTerm = unsafeRunInTerm -- | Launch an external application and return a 'Handle' to its standard input. spawnPipe :: String -> IO Handle spawnPipe x = do (rd, wr) <- createPipe setFdOption wr CloseOnExec True h <- fdToHandle wr hSetBuffering h LineBuffering doubleFork $ do dupTo rd stdInput executeFile "/bin/sh" False ["-c", x] Nothing return h