------------------------------------------------------------------------------ -- | -- Module : Utils -- Copyright : (c) Mads N Noe 2010 -- Maintainer : mail (@) madsnoe.dk -- License : as-is -- -- Utility functions for XMonad. -- ------------------------------------------------------------------------------ module Utils where -- Haskell modules import Control.Concurrent.MVar import Control.Monad (unless, when, liftM) import Control.Monad.Trans (lift) import Data.List import Data.Monoid (Endo(Endo)) import System.IO.Error (isDoesNotExistError) import System.IO.Unsafe (unsafePerformIO) import System.Posix.Unistd(getSystemID, nodeName) import qualified Data.Map as M -- XMonad modules import XMonad import XMonad.Actions.CycleWS import XMonad.Actions.Warp (warpToWindow) import XMonad.Actions.WindowGo import XMonad.Hooks.DynamicHooks (oneShotHook) import XMonad.Hooks.FloatNext import XMonad.Layout.IndependentScreens import qualified XMonad.StackSet as W -- Other moduls import Graphics.X11.Xinerama import Graphics.X11.Xlib.Extras -- GENERAL -- | Perform k x if x return a 'Just' value. (?+) :: (Monad m) => m (Maybe a) -> (a -> m ()) -> m () x ?+ k = x >>= maybe (return ()) k infixl 1 ?+ -- | Helper function for use with monads. if_ :: t -> t -> Bool -> t if_ t f c = if c then t else f -- | Change type to "m ()" do_ :: (Monad m) => m a -> m () do_ x = x >> return () quote :: String -> String quote x = "'" ++ x ++ "'" -- WINDOW ACTIONS -- | Swap the focused window with the last window in the stack. swapBottom :: W.StackSet i l a s sd -> W.StackSet i l a s sd swapBottom = W.modify' $ \c -> case c of W.Stack _ _ [] -> c -- already bottom. W.Stack t ls rs -> W.Stack t (xs ++ x : ls) [] where (x:xs) = reverse rs -- | Swap the focused window with the following window, or if the window is -- floating, lower it to the bottom. swapOrLower :: X () swapOrLower = withFocused $ \w -> runQuery isFloat w >>= if_ (windows swapBottom) (windows W.swapDown) -- | Swap the focused window with the preceding window, or if the window is -- floating, raise it to the top. swapOrRaise :: X () swapOrRaise = withFocused $ \w -> runQuery isFloat w >>= if_ (windows W.swapMaster) (windows W.swapUp) -- spawnOnThisWS :: GHC.IOBase.IORef XMonad.Hooks.DynamicHooks.DynamicHooks-> Query Bool-> String-> X () spawnOnThisWS dhr q cmd = withWindowSet $ \ws -> do oneShotHook dhr q $ doF $ W.shift $ W.currentTag ws spawn cmd -- | Warp the mouse pointer to the focused window only if the workspace has -- no floating windows to steal the focus. warpToWindow' = withWindowSet $ \ws -> do let floats = M.keys $ W.floating ws visible = W.integrate' $ W.stack $ W.workspace $ W.current ws vf = floats `intersect` visible when (null vf) $ warpToWindow (1/2) (1/2) -- QUERIES ETC -- | Is the focused window the \"master window\" of the current workspace? isMaster :: Query Bool isMaster = ask >>= (\w -> liftX $ withWindowSet $ \ws -> return $ Just w == master ws) where master :: WindowSet -> Maybe Window master ws = case W.integrate' $ W.stack $ W.workspace $ W.current ws of [] -> Nothing (x:xs) -> Just x -- | Is the focused window a floating window? isFloat :: Query Bool isFloat = ask >>= (\w -> liftX $ withWindowSet $ \ws -> return $ M.member w $ W.floating ws) -- | Helper to read a property -- getProp :: Atom -> Window -> X (Maybe [CLong]) getProp a w = withDisplay $ \dpy -> io $ getWindowProperty32 dpy a w -- | Check if window is DIALOG window checkDialog :: Query Bool checkDialog = ask >>= \w -> liftX $ do a <- getAtom "_NET_WM_WINDOW_TYPE" dialog <- getAtom "_NET_WM_WINDOW_TYPE_DIALOG" mbr <- getProp a w case mbr of Just [r] -> return $ elem (fromIntegral r) [dialog] _ -> return False -- | Determine the number of physical screens. countScreens :: (MonadIO m, Integral i) => m i countScreens = liftM genericLength . liftIO $ openDisplay "" >>= getScreenInfo -- HOST -- | For use in cross host configutions. data Host = Laptop | Netbook deriving Eq -- | Determine the host. getHost = do host <- getSystemID case nodeName host of "mntnoe-laptop" -> return Laptop "mntnoe-netbook" -> return Netbook _ -> return Laptop -- MISC -- | Return a string that launches xterm with the given 'title', 'appName' and -- command to execute. xterm :: String -> String -> String xterm a e = concat ["xterm -wf -title '", e, "' -name '", a, "' -e '", e, "'"]