From 5b28f5ee5461ea9b4245d187d78e99c59eede6e1 Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Sun, 31 Jan 2021 17:05:36 +0000 Subject: [PATCH 1/7] Add X.H.Rescreen module (custom rescreen hooks) --- XMonad/Hooks/Rescreen.hs | 73 ++++++++++++++++++++++++++++++++++++++++ xmonad-contrib.cabal | 1 + 2 files changed, 74 insertions(+) create mode 100644 XMonad/Hooks/Rescreen.hs diff --git a/XMonad/Hooks/Rescreen.hs b/XMonad/Hooks/Rescreen.hs new file mode 100644 index 0000000000..11c3091b0c --- /dev/null +++ b/XMonad/Hooks/Rescreen.hs @@ -0,0 +1,73 @@ +-- | +-- Module : XMonad.Hooks.Rescreen +-- Copyright : (c) 2021 Tomáš Janoušek +-- License : BSD3 +-- Maintainer : Tomáš Janoušek +-- +-- Custom hooks for screen (xrandr) configuration changes. +-- +module XMonad.Hooks.Rescreen ( + -- * Usage + -- $usage + rescreenHook, + rescreenEventHook, + ) where + +import Control.Monad.Fix (fix) +import Control.Monad (when) +import Data.Monoid (All(..)) + +import XMonad + +-- $usage +-- This module provides a replacement for the screen configuration change +-- handling in core that enables attaching a custom hook that can +-- restart/reposition status bars or systray. +-- +-- You can use it by including the following in your @~\/.xmonad\/xmonad.hs@: +-- +-- > import XMonad.Hooks.RescreenHook +-- +-- defining your custom rescreen hook: +-- +-- > myRescreenHook :: X () +-- > myRescreenHook = … +-- +-- and adding 'rescreenHook' to your 'xmonad' config: +-- +-- > main = xmonad $ … . rescreenHook myRescreenHook . … $ def{…} + +-- | Attach a custom hook when the screen configuration changes (due to +-- xrandr). Replaces the built-in rescreen handling of xmonad core with: +-- +-- 1. suppress duplicate change events +-- 2. 'rescreen' +-- 3. invoke specified hook +-- +-- Useful for restarting/repositioning status bars and systray. +rescreenHook :: X () -> XConfig a -> XConfig a +rescreenHook hook xConfig = + xConfig{ handleEventHook = handleEventHook xConfig <> rescreenEventHook hook } + +-- | Event hook with custom rescreen hook. See 'rescreenHook' for more. +rescreenEventHook :: X () -> Event -> X All +rescreenEventHook hook ConfigureEvent{ev_event_type = t, ev_window = w} = do + isRescreen <- isRoot w + if isRescreen + then do + -- Xorg emits several ConfigureEvents after every change, + -- clear them to prevent triggering the hook multiple times + clearTypedWindowEvents w t + rescreen + hook + return (All False) + else mempty +rescreenEventHook _ _ = mempty + +-- | Remove all X events of a given window and type from the event queue. +clearTypedWindowEvents :: Window -> EventType -> X () +clearTypedWindowEvents w t = withDisplay $ \d -> io $ do + sync d False + allocaXEvent $ \e -> fix $ \again -> do + more <- checkTypedWindowEvent d w t e + when more again diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal index 8a4bb55719..5139350323 100644 --- a/xmonad-contrib.cabal +++ b/xmonad-contrib.cabal @@ -184,6 +184,7 @@ library XMonad.Hooks.Place XMonad.Hooks.PositionStoreHooks XMonad.Hooks.RefocusLast + XMonad.Hooks.Rescreen XMonad.Hooks.RestoreMinimized XMonad.Hooks.ScreenCorners XMonad.Hooks.Script From 1ff954b4b65ef35f8f98e19ae8642b833ee4749e Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Mon, 1 Feb 2021 15:24:22 +0000 Subject: [PATCH 2/7] X.H.Rescreen: Add randrHook --- XMonad/Hooks/Rescreen.hs | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/XMonad/Hooks/Rescreen.hs b/XMonad/Hooks/Rescreen.hs index 11c3091b0c..9636cdbad9 100644 --- a/XMonad/Hooks/Rescreen.hs +++ b/XMonad/Hooks/Rescreen.hs @@ -11,12 +11,16 @@ module XMonad.Hooks.Rescreen ( -- $usage rescreenHook, rescreenEventHook, + randrHook, + randrEventHook, + randrStartupHook, ) where import Control.Monad.Fix (fix) import Control.Monad (when) import Data.Monoid (All(..)) +import Graphics.X11.Xrandr import XMonad -- $usage @@ -36,6 +40,9 @@ import XMonad -- and adding 'rescreenHook' to your 'xmonad' config: -- -- > main = xmonad $ … . rescreenHook myRescreenHook . … $ def{…} +-- +-- There is also 'randrHook' which listens for @RRScreenChangeNotify@ events +-- and is useful for reacting to outputs being connected/disconnected. -- | Attach a custom hook when the screen configuration changes (due to -- xrandr). Replaces the built-in rescreen handling of xmonad core with: @@ -64,6 +71,40 @@ rescreenEventHook hook ConfigureEvent{ev_event_type = t, ev_window = w} = do else mempty rescreenEventHook _ _ = mempty +-- | Attach a hook to an @RRScreenChangeNotify@ event which is generated not +-- only when the configuration is changed via xrandr but also when outputs are +-- connected or disconnected. +-- +-- This may be used to automatically trigger xrandr (or perhaps autorandr) +-- when outputs are (dis)connected. Beware: the hook will also run after +-- xrandr makes changes, so care must be taken to not invoke it again. +-- +-- TODO: merge with rescreenHook, do clearTypedWindowEvents for both event +-- types and if there are any ConfigureEvents, do not invoke the randr hook +randrHook :: X () -> XConfig a -> XConfig a +randrHook hook xConfig = + xConfig{ handleEventHook = handleEventHook xConfig <> randrEventHook hook + , startupHook = startupHook xConfig <> randrStartupHook } + +-- | Event hook with custom @RRScreenChangeNotify@ hook. See 'randrHook' +-- for details. +randrEventHook :: X () -> Event -> X All +randrEventHook hook RRScreenChangeNotifyEvent{ev_event_type = t, ev_window = w} = do + whenX (isRoot w) $ do + -- Xorg emits several RRScreenChangeNotifyEvents after every change, + -- clear them to prevent triggering the hook multiple times + clearTypedWindowEvents w t + hook + mempty +randrEventHook _ _ = mempty + +-- | Startup hook to listen for @RRScreenChangeNotify@ events. +randrStartupHook :: X () +randrStartupHook = do + dpy <- asks display + root <- asks theRoot + io $ xrrSelectInput dpy root rrScreenChangeNotifyMask + -- | Remove all X events of a given window and type from the event queue. clearTypedWindowEvents :: Window -> EventType -> X () clearTypedWindowEvents w t = withDisplay $ \d -> io $ do From 90c7621e1f0166e92a6122952b445f2539dd9e09 Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Mon, 1 Feb 2021 16:58:57 +0000 Subject: [PATCH 3/7] X.H.Rescreen: Merge the two hooks together and improve their behaviour Now that randrChangeHook is only invoked for changes that don't result in rescreen, it can actually be used for autorandr. --- XMonad/Hooks/Rescreen.hs | 173 +++++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 72 deletions(-) diff --git a/XMonad/Hooks/Rescreen.hs b/XMonad/Hooks/Rescreen.hs index 9636cdbad9..6d157a94b3 100644 --- a/XMonad/Hooks/Rescreen.hs +++ b/XMonad/Hooks/Rescreen.hs @@ -1,3 +1,6 @@ +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE RecordWildCards #-} -- | -- Module : XMonad.Hooks.Rescreen -- Copyright : (c) 2021 Tomáš Janoušek @@ -9,15 +12,13 @@ module XMonad.Hooks.Rescreen ( -- * Usage -- $usage + RescreenConfig(..), rescreenHook, + rescreenStartupHook, rescreenEventHook, - randrHook, - randrEventHook, - randrStartupHook, ) where -import Control.Monad.Fix (fix) -import Control.Monad (when) +import Control.Monad (void) import Data.Monoid (All(..)) import Graphics.X11.Xrandr @@ -25,90 +26,118 @@ import XMonad -- $usage -- This module provides a replacement for the screen configuration change --- handling in core that enables attaching a custom hook that can --- restart/reposition status bars or systray. +-- handling in core that enables attaching custom hooks to screen (xrandr) +-- configuration change events. These can be used to restart/reposition status +-- bars or systrays automatically after xrandr, as well as to actually invoke +-- xrandr or autorandr when an output is (dis)connected. -- -- You can use it by including the following in your @~\/.xmonad\/xmonad.hs@: -- -- > import XMonad.Hooks.RescreenHook -- --- defining your custom rescreen hook: +-- defining your custom hooks: -- --- > myRescreenHook :: X () --- > myRescreenHook = … +-- > myAfterRescreenHook :: X () +-- > myAfterRescreenHook = … -- --- and adding 'rescreenHook' to your 'xmonad' config: --- --- > main = xmonad $ … . rescreenHook myRescreenHook . … $ def{…} +-- > myRandrChangeHook :: X () +-- > myRandrChangeHook = … -- --- There is also 'randrHook' which listens for @RRScreenChangeNotify@ events --- and is useful for reacting to outputs being connected/disconnected. - --- | Attach a custom hook when the screen configuration changes (due to --- xrandr). Replaces the built-in rescreen handling of xmonad core with: +-- > rescreenCfg = def{ +-- > afterRescreenHook = myAfterRescreenHook, +-- > randrChangeHook = myRandrChangeHook +-- > } -- --- 1. suppress duplicate change events --- 2. 'rescreen' --- 3. invoke specified hook +-- and adding 'rescreenHook' to your 'xmonad' config: -- --- Useful for restarting/repositioning status bars and systray. -rescreenHook :: X () -> XConfig a -> XConfig a -rescreenHook hook xConfig = - xConfig{ handleEventHook = handleEventHook xConfig <> rescreenEventHook hook } +-- > main = xmonad $ … . rescreenHook rescreenCfg . … $ def{…} --- | Event hook with custom rescreen hook. See 'rescreenHook' for more. -rescreenEventHook :: X () -> Event -> X All -rescreenEventHook hook ConfigureEvent{ev_event_type = t, ev_window = w} = do - isRescreen <- isRoot w - if isRescreen - then do - -- Xorg emits several ConfigureEvents after every change, - -- clear them to prevent triggering the hook multiple times - clearTypedWindowEvents w t - rescreen - hook - return (All False) - else mempty -rescreenEventHook _ _ = mempty +-- | Hook configuration for 'rescreenEventHook'. +data RescreenConfig = RescreenConfig + { afterRescreenHook :: X () -- ^ hook to invoke after 'rescreen' + , randrChangeHook :: X () -- ^ hook for other randr changes, e.g. (dis)connects + } + +instance Default RescreenConfig where + def = RescreenConfig + { afterRescreenHook = mempty + , randrChangeHook = mempty + } --- | Attach a hook to an @RRScreenChangeNotify@ event which is generated not --- only when the configuration is changed via xrandr but also when outputs are --- connected or disconnected. +-- | Attach custom hooks to screen (xrandr) configuration change events. +-- Replaces the built-in rescreen handling of xmonad core with: -- --- This may be used to automatically trigger xrandr (or perhaps autorandr) --- when outputs are (dis)connected. Beware: the hook will also run after --- xrandr makes changes, so care must be taken to not invoke it again. +-- 1. listen to 'RRScreenChangeNotifyEvent' in addition to 'ConfigureEvent' on +-- the root window +-- 2. whenever such event is received: +-- 3. clear any other similar events (Xorg server emits them in bunches) +-- 4. if any event was 'ConfigureEvent', 'rescreen' and invoke 'afterRescreenHook' +-- 5. if there was no 'ConfigureEvent', invoke 'randrChangeHook' only -- --- TODO: merge with rescreenHook, do clearTypedWindowEvents for both event --- types and if there are any ConfigureEvents, do not invoke the randr hook -randrHook :: X () -> XConfig a -> XConfig a -randrHook hook xConfig = - xConfig{ handleEventHook = handleEventHook xConfig <> randrEventHook hook - , startupHook = startupHook xConfig <> randrStartupHook } - --- | Event hook with custom @RRScreenChangeNotify@ hook. See 'randrHook' --- for details. -randrEventHook :: X () -> Event -> X All -randrEventHook hook RRScreenChangeNotifyEvent{ev_event_type = t, ev_window = w} = do - whenX (isRoot w) $ do - -- Xorg emits several RRScreenChangeNotifyEvents after every change, - -- clear them to prevent triggering the hook multiple times - clearTypedWindowEvents w t - hook - mempty -randrEventHook _ _ = mempty +-- 'afterRescreenHook' is useful for restarting/repositioning status bars and +-- systray. +-- +-- 'randrChangeHook' may be used to automatically trigger xrandr (or perhaps +-- autorandr) when outputs are (dis)connected. +rescreenHook :: RescreenConfig -> XConfig a -> XConfig a +rescreenHook cfg xConfig = + xConfig{ startupHook = startupHook xConfig <> rescreenStartupHook + , handleEventHook = handleEventHook xConfig <> rescreenEventHook cfg + } -- | Startup hook to listen for @RRScreenChangeNotify@ events. -randrStartupHook :: X () -randrStartupHook = do +rescreenStartupHook :: X () +rescreenStartupHook = do dpy <- asks display root <- asks theRoot io $ xrrSelectInput dpy root rrScreenChangeNotifyMask --- | Remove all X events of a given window and type from the event queue. -clearTypedWindowEvents :: Window -> EventType -> X () -clearTypedWindowEvents w t = withDisplay $ \d -> io $ do - sync d False - allocaXEvent $ \e -> fix $ \again -> do - more <- checkTypedWindowEvent d w t e - when more again +-- | Event hook with custom rescreen/randr hooks. See 'rescreenHook' for more. +rescreenEventHook :: RescreenConfig -> Event -> X All +rescreenEventHook cfg e = do + shouldHandle <- case e of + ConfigureEvent{ ev_window = w } -> isRoot w + RRScreenChangeNotifyEvent{ ev_window = w } -> isRoot w + _ -> pure False + if shouldHandle + then All False <$ handleEvent cfg e + else mempty + +handleEvent :: RescreenConfig -> Event -> X () +handleEvent RescreenConfig{..} e = do + -- Xorg emits several events after every change, clear them to prevent + -- triggering the hook multiple times. + moreConfigureEvents <- clearTypedWindowEvents (ev_window e) configureNotify + _ <- clearTypedWindowRREvents (ev_window e) rrScreenChangeNotify + -- If there were any ConfigureEvents, this is an actual screen + -- configuration change, so rescreen and fire rescreenHook. Otherwise, + -- this is just a connect/disconnect, fire randrChangeHook. + if ev_event_type e == configureNotify || moreConfigureEvents + then rescreen >> afterRescreenHook + else randrChangeHook + +-- | Remove all X events of a given window and type from the event queue, +-- return whether there were any. +clearTypedWindowEvents :: Window -> EventType -> X Bool +clearTypedWindowEvents w t = withDisplay $ \d -> io $ allocaXEvent (go d) + where + go d e' = do + sync d False + gotEvent <- checkTypedWindowEvent d w t e' + e <- if gotEvent then Just <$> getEvent e' else pure Nothing + gotEvent <$ if + | not gotEvent -> mempty + | (ev_window <$> e) == Just w -> void $ go d e' + -- checkTypedWindowEvent checks ev_event instead of ev_window, so + -- we may need to put some events back + | otherwise -> allocaXEvent (go d) >> io (putBackEvent d e') + +clearTypedWindowRREvents :: Window -> EventType -> X Bool +clearTypedWindowRREvents w t = + rrEventBase >>= \case + Just base -> clearTypedWindowEvents w (base + t) + Nothing -> pure False + +rrEventBase :: X (Maybe EventType) +rrEventBase = withDisplay $ \d -> + fmap (fromIntegral . fst) <$> io (xrrQueryExtension d) From 280964b9f5beab0a6cd075c19e06d1501a4f6d1d Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Mon, 1 Feb 2021 18:57:27 +0000 Subject: [PATCH 4/7] Update CHANGES, X.D.Extending: add X.H.Rescreen --- CHANGES.md | 7 +++++++ XMonad/Doc/Extending.hs | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index d5e45c08cf..47193cc076 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -250,6 +250,13 @@ This lets them create custom hooks, ensure they hook into xmonad core only once, and possibly more. + * `XMonad.Hooks.Rescreen` + + Custom hooks for screen (xrandr) configuration changes. These can be used + to restart/reposition status bars or systrays automatically after xrandr, + as well as to actually invoke xrandr or autorandr when an output is + (dis)connected. + ### Bug Fixes and Minor Changes * Add support for GHC 9.0.1. diff --git a/XMonad/Doc/Extending.hs b/XMonad/Doc/Extending.hs index 31e6b93387..1a9b12b596 100644 --- a/XMonad/Doc/Extending.hs +++ b/XMonad/Doc/Extending.hs @@ -543,6 +543,12 @@ Here is a list of the modules found in @XMonad.Hooks@: even if it was opened in a tiled layout initially. The EventHook makes sure that windows are deleted from the PositionStore when they are closed. +* "XMonad.Hooks.Rescreen": + Custom hooks for screen (xrandr) configuration changes. These can be used + to restart/reposition status bars or systrays automatically after xrandr, + as well as to actually invoke xrandr or autorandr when an output is + (dis)connected. + * "XMonad.Hooks.RestoreMinimized": (Deprecated: Use XMonad.Hooks.Minimize) Lets you restore minimized windows (see "XMonad.Layout.Minimize") by selecting them on a From 4a6f21604fddfcf570fd5dc1b5307e0a1d4f995b Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Mon, 17 May 2021 16:51:09 +0100 Subject: [PATCH 5/7] X.H.Rescreen: Port to ExtensibleConf --- XMonad/Hooks/Rescreen.hs | 41 +++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/XMonad/Hooks/Rescreen.hs b/XMonad/Hooks/Rescreen.hs index 6d157a94b3..4f726a96b8 100644 --- a/XMonad/Hooks/Rescreen.hs +++ b/XMonad/Hooks/Rescreen.hs @@ -13,9 +13,9 @@ module XMonad.Hooks.Rescreen ( -- * Usage -- $usage RescreenConfig(..), + addAfterRescreenHook, + addRandrChangeHook, rescreenHook, - rescreenStartupHook, - rescreenEventHook, ) where import Control.Monad (void) @@ -23,6 +23,7 @@ import Data.Monoid (All(..)) import Graphics.X11.Xrandr import XMonad +import qualified XMonad.Util.ExtensibleConf as XC -- $usage -- This module provides a replacement for the screen configuration change @@ -64,6 +65,12 @@ instance Default RescreenConfig where , randrChangeHook = mempty } +instance Semigroup RescreenConfig where + RescreenConfig arh rch <> RescreenConfig arh' rch' = RescreenConfig (arh <> arh') (rch <> rch') + +instance Monoid RescreenConfig where + mempty = def + -- | Attach custom hooks to screen (xrandr) configuration change events. -- Replaces the built-in rescreen handling of xmonad core with: -- @@ -79,11 +86,23 @@ instance Default RescreenConfig where -- -- 'randrChangeHook' may be used to automatically trigger xrandr (or perhaps -- autorandr) when outputs are (dis)connected. +-- +-- Note that 'rescreenHook' is safe to use several times, 'rescreen' is still +-- done just once and hooks are invoked in sequence, also just once. rescreenHook :: RescreenConfig -> XConfig a -> XConfig a -rescreenHook cfg xConfig = - xConfig{ startupHook = startupHook xConfig <> rescreenStartupHook - , handleEventHook = handleEventHook xConfig <> rescreenEventHook cfg - } +rescreenHook = flip XC.once rescreenHook' + +-- | Shortcut for 'rescreenHook'. +addAfterRescreenHook :: X () -> XConfig a -> XConfig a +addAfterRescreenHook h = rescreenHook def{ afterRescreenHook = h } + +-- | Shortcut for 'rescreenHook'. +addRandrChangeHook :: X () -> XConfig a -> XConfig a +addRandrChangeHook h = rescreenHook def{ randrChangeHook = h } + +rescreenHook' :: XConfig a -> XConfig a +rescreenHook' c = c{ startupHook = startupHook c <> rescreenStartupHook + , handleEventHook = handleEventHook c <> rescreenEventHook } -- | Startup hook to listen for @RRScreenChangeNotify@ events. rescreenStartupHook :: X () @@ -93,18 +112,18 @@ rescreenStartupHook = do io $ xrrSelectInput dpy root rrScreenChangeNotifyMask -- | Event hook with custom rescreen/randr hooks. See 'rescreenHook' for more. -rescreenEventHook :: RescreenConfig -> Event -> X All -rescreenEventHook cfg e = do +rescreenEventHook :: Event -> X All +rescreenEventHook e = do shouldHandle <- case e of ConfigureEvent{ ev_window = w } -> isRoot w RRScreenChangeNotifyEvent{ ev_window = w } -> isRoot w _ -> pure False if shouldHandle - then All False <$ handleEvent cfg e + then All False <$ handleEvent e else mempty -handleEvent :: RescreenConfig -> Event -> X () -handleEvent RescreenConfig{..} e = do +handleEvent :: Event -> X () +handleEvent e = XC.with $ \RescreenConfig{..} -> do -- Xorg emits several events after every change, clear them to prevent -- triggering the hook multiple times. moreConfigureEvents <- clearTypedWindowEvents (ev_window e) configureNotify From eab9a3a58e40ee44e7d904287f6ea7ff6fefc2c5 Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Thu, 3 Jun 2021 01:01:12 +0100 Subject: [PATCH 6/7] X.U.ExtensibleConf: Flip arguments of once(M) This appears to be more natural. The function will most often be fixed by the module using `XC.once` and the configuration will often be supplied by users of those modules, so it's better to partially apply the function first. --- XMonad/Hooks/Rescreen.hs | 8 +++----- XMonad/Util/ExtensibleConf.hs | 12 ++++++------ tests/ExtensibleConf.hs | 4 ++-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/XMonad/Hooks/Rescreen.hs b/XMonad/Hooks/Rescreen.hs index 4f726a96b8..8338b84ef0 100644 --- a/XMonad/Hooks/Rescreen.hs +++ b/XMonad/Hooks/Rescreen.hs @@ -90,7 +90,9 @@ instance Monoid RescreenConfig where -- Note that 'rescreenHook' is safe to use several times, 'rescreen' is still -- done just once and hooks are invoked in sequence, also just once. rescreenHook :: RescreenConfig -> XConfig a -> XConfig a -rescreenHook = flip XC.once rescreenHook' +rescreenHook = XC.once $ \c -> c + { startupHook = startupHook c <> rescreenStartupHook + , handleEventHook = handleEventHook c <> rescreenEventHook } -- | Shortcut for 'rescreenHook'. addAfterRescreenHook :: X () -> XConfig a -> XConfig a @@ -100,10 +102,6 @@ addAfterRescreenHook h = rescreenHook def{ afterRescreenHook = h } addRandrChangeHook :: X () -> XConfig a -> XConfig a addRandrChangeHook h = rescreenHook def{ randrChangeHook = h } -rescreenHook' :: XConfig a -> XConfig a -rescreenHook' c = c{ startupHook = startupHook c <> rescreenStartupHook - , handleEventHook = handleEventHook c <> rescreenEventHook } - -- | Startup hook to listen for @RRScreenChangeNotify@ events. rescreenStartupHook :: X () rescreenStartupHook = do diff --git a/XMonad/Util/ExtensibleConf.hs b/XMonad/Util/ExtensibleConf.hs index fac7d8efb3..a4f214d167 100644 --- a/XMonad/Util/ExtensibleConf.hs +++ b/XMonad/Util/ExtensibleConf.hs @@ -113,16 +113,16 @@ add x = alter (<> Just x) -- This can be used to implement a composable interface for modules that must -- only hook into xmonad core once. once :: forall a l. (Semigroup a, Typeable a) - => a -- ^ configuration to add - -> (XConfig l -> XConfig l) -- ^ 'XConfig' modification done only once + => (XConfig l -> XConfig l) -- ^ 'XConfig' modification done only once + -> a -- ^ configuration to add -> XConfig l -> XConfig l -once x f c = add x $ maybe f (const id) (lookup @a c) c +once f x c = add x $ maybe f (const id) (lookup @a c) c -- | Config-time: Applicative (monadic) variant of 'once', useful if the -- 'XConfig' modification needs to do some 'IO' (e.g. create an -- 'Data.IORef.IORef'). onceM :: forall a l m. (Applicative m, Semigroup a, Typeable a) - => a -- ^ configuration to add - -> (XConfig l -> m (XConfig l)) -- ^ 'XConfig' modification done only once + => (XConfig l -> m (XConfig l)) -- ^ 'XConfig' modification done only once + -> a -- ^ configuration to add -> XConfig l -> m (XConfig l) -onceM x f c = add x <$> maybe f (const pure) (lookup @a c) c +onceM f x c = add x <$> maybe f (const pure) (lookup @a c) c diff --git a/tests/ExtensibleConf.hs b/tests/ExtensibleConf.hs index 61404b4c2c..bfb55560c8 100644 --- a/tests/ExtensibleConf.hs +++ b/tests/ExtensibleConf.hs @@ -22,9 +22,9 @@ spec = do XC.lookup (XC.add "a" (XC.add [1 :: Int] def)) `shouldBe` (Nothing :: Maybe ()) specify "once" $ - borderWidth (XC.once "a" incBorderWidth def) `shouldBe` succ (borderWidth def) + borderWidth (XC.once incBorderWidth "a" def) `shouldBe` succ (borderWidth def) specify "once . once" $ - borderWidth (XC.once "b" incBorderWidth (XC.once "a" incBorderWidth def)) + borderWidth (XC.once incBorderWidth "b" (XC.once incBorderWidth "a" def)) `shouldBe` succ (borderWidth def) incBorderWidth :: XConfig l -> XConfig l From 30995c41fffe899626038c06631909ae575c2d21 Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Thu, 3 Jun 2021 00:38:04 +0100 Subject: [PATCH 7/7] X.H.StatusBar: Use addAfterRescreenHook instead of listening for RRScreenChangeNotifyEvent The new X.H.Rescreen provides a simpler composable API for hooking into rescreen. Unfortunately it also breaks other code that listens for RRScreenChangeNotifyEvent, so this conversion isn't really optional. Not that there's any dispute this is nicer, is there? :-) --- XMonad/Hooks/StatusBar.hs | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/XMonad/Hooks/StatusBar.hs b/XMonad/Hooks/StatusBar.hs index 720b43842e..e681d3c9bb 100644 --- a/XMonad/Hooks/StatusBar.hs +++ b/XMonad/Hooks/StatusBar.hs @@ -74,11 +74,10 @@ import qualified XMonad.Util.ExtensibleState as XS import XMonad.Layout.LayoutModifier import XMonad.Hooks.ManageDocks +import XMonad.Hooks.Rescreen import XMonad.Hooks.StatusBar.PP import qualified XMonad.StackSet as W -import Graphics.X11.Xrandr (xrrSelectInput) - -- $usage -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: -- @@ -342,7 +341,7 @@ statusBarPipe cmd xpp = do -- > -- > main = xmonad $ withSB (xmobarTop <> xmobarBottom <> xmobar1) myConfig -- --- And here is an example of the related xmobar configuration for the multiple +-- And here is an example of the related xmobar configuration for the multiple -- status bars mentioned above: -- -- > xmobarrc_top @@ -422,13 +421,9 @@ instance ExtensionClass ActiveSBs where -- -- Heavily inspired by "XMonad.Hooks.DynamicBars" dynamicSBs :: (ScreenId -> IO StatusBarConfig) -> XConfig l -> XConfig l -dynamicSBs f conf = conf - { startupHook = startupHook conf - >> setupEventHandler - >> killAllStatusBars - >> updateSBs f - , logHook = logHook conf >> logSBs - , handleEventHook = eventHookSBs f <> handleEventHook conf +dynamicSBs f conf = addAfterRescreenHook (updateSBs f) $ conf + { startupHook = startupHook conf >> killAllStatusBars >> updateSBs f + , logHook = logHook conf >> logSBs } -- | Like 'dynamicSBs', but applies 'docks' to the @@ -457,23 +452,10 @@ updateSBs f = do traverse_ (sbStartupHook . snd) added XS.put (ASB (toKeep ++ added)) --- | Handles 'RRScreenChangeNotifyEvent' by updating the --- status bars. -eventHookSBs :: (ScreenId -> IO StatusBarConfig) -> Event -> X All -eventHookSBs f RRScreenChangeNotifyEvent{} = updateSBs f >> return (All True) -eventHookSBs _ _ = return (All True) - -- | Run 'sbLogHook' for the saved 'StatusBarConfig's logSBs :: X () logSBs = XS.get >>= traverse_ (sbLogHook . snd) . getASBs --- | Subscribe to the 'RRScreenChangeNotifyEvent' -setupEventHandler :: X () -setupEventHandler = do - dpy <- asks display - root <- asks theRoot - io $ xrrSelectInput dpy root rrScreenChangeNotifyMask - -- | Kill the given 'StatusBarConfig's from the given -- list cleanSBs :: [StatusBarConfig] -> X ()