-- | -- Module : System.Dzen.Process -- Copyright : (c) 2009 Felipe A. Lessa -- License : GPL 3 (see the LICENSE file in the distribution) -- -- Maintainer : felipe.lessa@gmail.com -- Stability : experimental -- Portability : semi-portable (uses MPTC and type families) -- -- Functions for creating supplies and running @dzen@. module System.Dzen.Process (-- * Simple interface runDzen ,(##) -- * Powerful interface ,createDzen ,createDzen' ) where import Control.Concurrent import Control.Monad import Data.Function import System.IO import System.Process hiding (proc) import System.Dzen.Base -- | Pipes a 'Printer' to a fresh instance of @dzen@. It runs -- the following commands: -- -- (1) Start @dzen@ with the supplied executable and arguments. -- -- (2) Call the supply to get an input. -- -- (3) Apply the input to the printer. -- -- (4) Write the printer's output to @dzen@'s standard input. -- -- (5) Sleeps for the specified delay using 'threadDelay'. -- -- (6) Go back to step 2. -- -- You may want to use this function inside a 'forkIO' if, -- for example, you're inside @xmonad@. runDzen :: FilePath -- ^ Path to @dzen@ executable, probably @\"dzen2\"@ -> [String] -- ^ Arguments for @dzen@. -> Int -- ^ Delay between suplies in milliseconds. May be zero. -> Printer a -- ^ @Printer@ to be used. -> IO a -- ^ Supply of inputs. -> IO () runDzen path args delay printer get = do handle <- createDzen' path args let put s = hPutStrLn handle s >> threadDelay (delay * 1000) applyForever printer get put -- | This is the same as @liftM2 (,)@, but with as a convenient -- operator with right infixity (the same as '+++'). For example, -- suppose you have printers -- -- > prA :: Printer a -- > prB :: Printer b -- > prC :: Printer c -- -- and supply functions -- -- > getA :: m a -- > getB :: m b -- > getC :: m c -- -- for some monad @m@. The final printer -- -- > prFinal = prA +++ prB +++ prC -- -- will be of type @Printer (a,(b,c))@, so you may use -- as its supply function -- -- > getFinal = getA ## getB ## getC -- -- which is of type @m (a,(b,c))@. (##) :: Monad m => m a -> m b -> m (a,b) (##) = liftM2 (,) infixr 4 ## -- | Runs a @dzen@ instance and returns its @stdin@ pipe. -- Both @stdout@ and @stderr@ of the new process will -- be the same as this process'. The pipe returned is -- already line buffered. -- -- The first string is interpreted as a shell command -- to start @dzen@. Some examples of usage: -- -- > createDzen (RawCommand "dzen2" ["-p"]) -- > createDzen (ShellCommand "dzen2 -l 8 -bg #331100") createDzen :: CmdSpec -> IO Handle createDzen cmd = createProcess proc >>= extract where proc = (shell "") {cmdspec = cmd ,std_in = CreatePipe ,std_out = Inherit ,std_err = Inherit} extract (Just handle, Nothing, Nothing, _) = do hSetBuffering handle LineBuffering return handle extract _ = do fail "createDzen: extract: (un)expected pipes" -- | Like @createDzen@, but never uses a shell (which is good). createDzen' :: FilePath -- ^ @dzen@ executable, likely @\"dzen2\"@ -> [String] -- ^ Arguments to @dzen@. -> IO Handle createDzen' = (createDzen .) . RawCommand