Merge pull request #4 from mathstuf/dynamic-bars-partial-cleanup

X.H.DynamicBars: support per-monitor cleanup
This commit is contained in:
Brent Yorgey
2016-06-16 17:31:05 -04:00
committed by GitHub

View File

@@ -18,8 +18,11 @@ module XMonad.Hooks.DynamicBars (
-- $usage -- $usage
DynamicStatusBar DynamicStatusBar
, DynamicStatusBarCleanup , DynamicStatusBarCleanup
, DynamicStatusBarPartialCleanup
, dynStatusBarStartup , dynStatusBarStartup
, dynStatusBarStartup'
, dynStatusBarEventHook , dynStatusBarEventHook
, dynStatusBarEventHook'
, multiPP , multiPP
, multiPPFormat , multiPPFormat
) where ) where
@@ -30,6 +33,7 @@ import Control.Monad
import Control.Monad.Trans (lift) import Control.Monad.Trans (lift)
import Control.Monad.Writer (WriterT, execWriterT, tell) import Control.Monad.Writer (WriterT, execWriterT, tell)
import Data.List
import Data.Maybe import Data.Maybe
import Data.Monoid import Data.Monoid
import Data.Foldable (traverse_) import Data.Foldable (traverse_)
@@ -51,13 +55,25 @@ import qualified XMonad.Util.ExtensibleState as XS
-- dynamically responding to screen changes. A startup action, event hook, and -- 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: -- a way to separate PP styles based on the screen's focus are provided:
-- --
-- * The 'dynStatusBarStartup' hook which initializes the status bars. -- * The 'dynStatusBarStartup' hook which initializes the status bars. The
-- first argument is an `ScreenId -> IO Handle` which spawns a status bar on the
-- given screen and returns the pipe which the string should be written to.
-- The second argument is a `IO ()` to shut down all status bars. This should
-- be placed in your `startupHook`.
-- --
-- * The 'dynStatusBarEventHook' hook which respawns status bars when the -- * The 'dynStatusBarEventHook' hook which respawns status bars when the
-- number of screens changes. -- number of screens changes. The arguments are the same as for the
-- `dynStatusBarStartup` function. This should be placed in your
-- `handleEventHook`.
--
-- * Each of the above functions have an alternate form
-- (`dynStatusBarStartup'` and `dynStatusBarEventHook'`) which use a cleanup
-- function which takes an additional `ScreenId` argument which allows for
-- more fine-grained control for shutting down a specific screen's status bar.
-- --
-- * The 'multiPP' function which allows for different output based on whether -- * The 'multiPP' function which allows for different output based on whether
-- the screen for the status bar has focus. -- the screen for the status bar has focus (the first argument) or not (the
-- second argument). This is for use in your `logHook`.
-- --
-- * The 'multiPPFormat' function is the same as the 'multiPP' function, but it -- * The 'multiPPFormat' function is the same as the 'multiPP' function, but it
-- also takes in a function that can customize the output to status bars. -- also takes in a function that can customize the output to status bars.
@@ -69,37 +85,67 @@ import qualified XMonad.Util.ExtensibleState as XS
-- --
data DynStatusBarInfo = DynStatusBarInfo data DynStatusBarInfo = DynStatusBarInfo
{ dsbInfoScreens :: [ScreenId] { dsbInfo :: [(ScreenId, Handle)]
, dsbInfoHandles :: [Handle]
} deriving (Typeable) } deriving (Typeable)
instance ExtensionClass DynStatusBarInfo where instance ExtensionClass DynStatusBarInfo where
initialValue = DynStatusBarInfo [] [] initialValue = DynStatusBarInfo []
type DynamicStatusBar = ScreenId -> IO Handle type DynamicStatusBar = ScreenId -> IO Handle
type DynamicStatusBarCleanup = IO () type DynamicStatusBarCleanup = IO ()
type DynamicStatusBarPartialCleanup = ScreenId -> IO ()
dynStatusBarStartup :: DynamicStatusBar -> DynamicStatusBarCleanup -> X () dynStatusBarSetup :: X ()
dynStatusBarStartup sb cleanup = do dynStatusBarSetup = do
dpy <- asks display dpy <- asks display
root <- asks theRoot root <- asks theRoot
io $ xrrSelectInput dpy root rrScreenChangeNotifyMask io $ xrrSelectInput dpy root rrScreenChangeNotifyMask
dynStatusBarStartup :: DynamicStatusBar -> DynamicStatusBarCleanup -> X ()
dynStatusBarStartup sb cleanup = do
dynStatusBarSetup
updateStatusBars sb cleanup updateStatusBars sb cleanup
dynStatusBarStartup' :: DynamicStatusBar -> DynamicStatusBarPartialCleanup -> X ()
dynStatusBarStartup' sb cleanup = do
dynStatusBarSetup
updateStatusBars' sb cleanup
dynStatusBarEventHook :: DynamicStatusBar -> DynamicStatusBarCleanup -> Event -> X All dynStatusBarEventHook :: DynamicStatusBar -> DynamicStatusBarCleanup -> Event -> X All
dynStatusBarEventHook sb cleanup (RRScreenChangeNotifyEvent {}) = updateStatusBars sb cleanup >> return (All True) dynStatusBarEventHook sb cleanup = dynStatusBarRun (updateStatusBars sb cleanup)
dynStatusBarEventHook _ _ _ = return (All True)
dynStatusBarEventHook' :: DynamicStatusBar -> DynamicStatusBarPartialCleanup -> Event -> X All
dynStatusBarEventHook' sb cleanup = dynStatusBarRun (updateStatusBars' sb cleanup)
dynStatusBarRun :: X () -> Event -> X All
dynStatusBarRun action (RRScreenChangeNotifyEvent {}) = action >> return (All True)
dynStatusBarRun _ _ = return (All True)
updateStatusBars :: DynamicStatusBar -> DynamicStatusBarCleanup -> X () updateStatusBars :: DynamicStatusBar -> DynamicStatusBarCleanup -> X ()
updateStatusBars sb cleanup = do updateStatusBars sb cleanup = do
dsbInfo <- XS.get (dsbInfoScreens, dsbInfoHandles) <- XS.get >>= return . unzip . dsbInfo
screens <- getScreens screens <- getScreens
when (screens /= dsbInfoScreens dsbInfo) $ do when (screens /= dsbInfoScreens) $ do
newHandles <- liftIO $ do newHandles <- liftIO $ do
hClose `mapM_` dsbInfoHandles dsbInfo hClose `mapM_` dsbInfoHandles
cleanup cleanup
mapM sb screens mapM sb screens
XS.put $ DynStatusBarInfo screens newHandles XS.put $ DynStatusBarInfo (zip screens newHandles)
updateStatusBars' :: DynamicStatusBar -> DynamicStatusBarPartialCleanup -> X ()
updateStatusBars' sb cleanup = do
(dsbInfoScreens, dsbInfoHandles) <- XS.get >>= return . unzip . dsbInfo
screens <- getScreens
when (screens /= dsbInfoScreens) $ do
let oldInfo = zip dsbInfoScreens dsbInfoHandles
let (infoToKeep, infoToClose) = partition (flip elem screens . fst) oldInfo
newInfo <- liftIO $ do
mapM_ hClose $ map snd infoToClose
mapM_ cleanup $ map fst infoToClose
let newScreens = screens \\ dsbInfoScreens
newHandles <- mapM sb newScreens
return $ zip newScreens newHandles
XS.put . DynStatusBarInfo $ infoToKeep ++ newInfo
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- The following code is from adamvo's xmonad.hs file. -- The following code is from adamvo's xmonad.hs file.
@@ -112,8 +158,8 @@ multiPP = multiPPFormat dynamicLogString
multiPPFormat :: (PP -> X String) -> PP -> PP -> X () multiPPFormat :: (PP -> X String) -> PP -> PP -> X ()
multiPPFormat dynlStr focusPP unfocusPP = do multiPPFormat dynlStr focusPP unfocusPP = do
dsbInfo <- XS.get (_, dsbInfoHandles) <- XS.get >>= return . unzip . dsbInfo
multiPP' dynlStr focusPP unfocusPP (dsbInfoHandles dsbInfo) multiPP' dynlStr focusPP unfocusPP dsbInfoHandles
multiPP' :: (PP -> X String) -> PP -> PP -> [Handle] -> X () multiPP' :: (PP -> X String) -> PP -> PP -> [Handle] -> X ()
multiPP' dynlStr focusPP unfocusPP handles = do multiPP' dynlStr focusPP unfocusPP handles = do