From 7ba0479c51e047296753fe5c6987c13dc848cd81 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Fri, 16 Mar 2012 01:22:04 +0100 Subject: add-dynamic-bars-module Ignore-this: 41347c8f894d8d0b5095dfad86784cf4 This adds the X.H.DynamicBars module. It allows per-screen status bars to be easily managed and dynamically handles the number of screens changing. darcs-hash:20120316002204-24e3f-67000bafe484d1ff78675210e30e8e73d88233f3.gz --- XMonad/Hooks/DynamicBars.hs | 136 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 XMonad/Hooks/DynamicBars.hs (limited to 'XMonad/Hooks') diff --git a/XMonad/Hooks/DynamicBars.hs b/XMonad/Hooks/DynamicBars.hs new file mode 100644 index 0000000..d8c40fe --- /dev/null +++ b/XMonad/Hooks/DynamicBars.hs @@ -0,0 +1,136 @@ +----------------------------------------------------------------------------- +-- | +-- Module : XMonad.Hooks.DynamicBars +-- Copyright : (c) Ben Boeckel 2012 +-- License : BSD-style (as xmonad) +-- +-- Maintainer : mathstuf@gmail.com +-- Stability : unstable +-- Portability : unportable +-- +-- Manage per-screen status bars. +-- +----------------------------------------------------------------------------- + +module XMonad.Hooks.DynamicBars ( + -- * Usage + -- $usage + DynamicStatusBar + , DynamicStatusBarCleanup + , dynStatusBarStartup + , dynStatusBarEventHook + , multiPP + ) where + +import Prelude + +import Control.Concurrent.MVar +import Control.Monad +import Control.Monad.Trans (lift) +import Control.Monad.Writer (WriterT, execWriterT, tell) + +import Data.Maybe +import Data.Monoid +import Data.Traversable (traverse) + +import Graphics.X11.Xinerama +import Graphics.X11.Xlib +import Graphics.X11.Xlib.Extras +import Graphics.X11.Xrandr + +import System.IO +import System.IO.Unsafe + +import XMonad +import qualified XMonad.StackSet as W +import XMonad.Hooks.DynamicLog + +-- $usage +-- Provides a few helper functions to manage per-screen status bars while +-- dynamically responding to screen changes. A startup action, event hook, and +-- a way to separate PP styles based on the screen's focus are provided: +-- +-- * The 'dynStatusBarStartup' hook which initializes the status bars. +-- +-- * The 'dynStatusBarEventHook' hook which respawns status bars when the +-- number of screens changes. +-- +-- * The 'multiPP' function which allows for different output based on whether +-- the screen for the status bar has focus. +-- +-- The hooks take a 'DynamicStatusBar' function which is given the id of the +-- screen to start up and returns the 'Handle' to the pipe to write to. The +-- 'DynamicStatusBarCleanup' argument should tear down previous instances. It +-- is called when the number of screens changes and on startup. +-- + +data DynStatusBarInfo = DynStatusBarInfo + { dsbInfoScreens :: [ScreenId] + , dsbInfoHandles :: [Handle] + } + +type DynamicStatusBar = ScreenId -> IO Handle +type DynamicStatusBarCleanup = IO () + +-- Global state +statusBarInfo :: MVar DynStatusBarInfo +statusBarInfo = unsafePerformIO $ newMVar (DynStatusBarInfo [] []) + +dynStatusBarStartup :: DynamicStatusBar -> DynamicStatusBarCleanup -> X () +dynStatusBarStartup sb cleanup = liftIO $ do + dpy <- openDisplay "" + xrrSelectInput dpy (defaultRootWindow dpy) rrScreenChangeNotifyMask + closeDisplay dpy + updateStatusBars sb cleanup + +dynStatusBarEventHook :: DynamicStatusBar -> DynamicStatusBarCleanup -> Event -> X All +dynStatusBarEventHook sb cleanup (RRScreenChangeNotifyEvent {}) = liftIO (updateStatusBars sb cleanup) >> return (All True) +dynStatusBarEventHook _ _ _ = return (All True) + +updateStatusBars :: DynamicStatusBar -> DynamicStatusBarCleanup -> IO () +updateStatusBars sb cleanup = liftIO $ do + dsbInfo <- takeMVar statusBarInfo + screens <- getScreens + if (screens /= (dsbInfoScreens dsbInfo)) + then do + mapM hClose (dsbInfoHandles dsbInfo) + cleanup + newHandles <- mapM sb screens + putMVar statusBarInfo (DynStatusBarInfo screens newHandles) + else putMVar statusBarInfo dsbInfo + +----------------------------------------------------------------------------- +-- The following code is from adamvo's xmonad.hs file. +-- http://www.haskell.org/haskellwiki/Xmonad/Config_archive/adamvo%27s_xmonad.hs + +multiPP :: PP -- ^ The PP to use if the screen is focused + -> PP -- ^ The PP to use otherwise + -> X () +multiPP focusPP unfocusPP = do + dsbInfo <- liftIO $ readMVar statusBarInfo + multiPP' dynamicLogString focusPP unfocusPP (dsbInfoHandles dsbInfo) + +multiPP' :: (PP -> X String) -> PP -> PP -> [Handle] -> X () +multiPP' dynlStr focusPP unfocusPP handles = do + state <- get + let pickPP :: WorkspaceId -> WriterT (Last XState) X String + pickPP ws = do + let isFoc = (ws ==) . W.tag . W.workspace . W.current $ windowset state + put state{ windowset = W.view ws $ windowset state } + out <- lift $ dynlStr $ if isFoc then focusPP else unfocusPP + when isFoc $ get >>= tell . Last . Just + return out + traverse put . getLast + =<< execWriterT . (io . zipWithM_ hPutStrLn handles <=< mapM pickPP) . catMaybes + =<< mapM screenWorkspace (zipWith const [0 .. ] handles) + return () + +getScreens :: IO [ScreenId] +getScreens = do + screens <- do + dpy <- openDisplay "" + rects <- getScreenInfo dpy + closeDisplay dpy + return rects + let ids = zip [0 .. ] screens + return $ map fst ids -- cgit v1.2.3