@@ -15,10 +15,13 @@ module XMonad.Hooks.Rescreen (
15
15
-- $usage
16
16
addAfterRescreenHook ,
17
17
addRandrChangeHook ,
18
+ setRescreenWorkspacesHook ,
19
+ setRescreenDelay ,
18
20
RescreenConfig (.. ),
19
21
rescreenHook ,
20
22
) where
21
23
24
+ import Control.Concurrent (threadDelay )
22
25
import Graphics.X11.Xrandr
23
26
import XMonad
24
27
import XMonad.Prelude
@@ -59,16 +62,21 @@ import qualified XMonad.Util.ExtensibleConf as XC
59
62
data RescreenConfig = RescreenConfig
60
63
{ afterRescreenHook :: X () -- ^ hook to invoke after 'rescreen'
61
64
, randrChangeHook :: X () -- ^ hook for other randr changes, e.g. (dis)connects
65
+ , rescreenWorkspacesHook :: Last (X () ) -- ^ hook to invoke instead of 'rescreen'
66
+ , rescreenDelay :: Last Int -- ^ delay (in microseconds) to wait for events to settle
62
67
}
63
68
64
69
instance Default RescreenConfig where
65
70
def = RescreenConfig
66
71
{ afterRescreenHook = mempty
67
72
, randrChangeHook = mempty
73
+ , rescreenWorkspacesHook = mempty
74
+ , rescreenDelay = mempty
68
75
}
69
76
70
77
instance Semigroup RescreenConfig where
71
- RescreenConfig arh rch <> RescreenConfig arh' rch' = RescreenConfig (arh <> arh') (rch <> rch')
78
+ RescreenConfig arh rch rwh rd <> RescreenConfig arh' rch' rwh' rd' =
79
+ RescreenConfig (arh <> arh') (rch <> rch') (rwh <> rwh') (rd <> rd')
72
80
73
81
instance Monoid RescreenConfig where
74
82
mempty = def
@@ -89,20 +97,45 @@ instance Monoid RescreenConfig where
89
97
-- 'randrChangeHook' may be used to automatically trigger xrandr (or perhaps
90
98
-- autorandr) when outputs are (dis)connected.
91
99
--
100
+ -- 'rescreenWorkspacesHook' allows tweaking the 'rescreen' implementation,
101
+ -- to change the order workspaces are assigned to physical screens for
102
+ -- example.
103
+ --
104
+ -- 'rescreenDelay' makes xmonad wait a bit for events to settle (after the
105
+ -- first event is received) — useful when multiple @xrandr@ invocations are
106
+ -- being used to change the screen layout.
107
+ --
92
108
-- Note that 'rescreenHook' is safe to use several times, 'rescreen' is still
93
- -- done just once and hooks are invoked in sequence, also just once.
109
+ -- done just once and hooks are invoked in sequence (except
110
+ -- 'rescreenWorkspacesHook', which has a replace rather than sequence
111
+ -- semantics), also just once.
94
112
rescreenHook :: RescreenConfig -> XConfig l -> XConfig l
95
- rescreenHook = XC. once $ \ c -> c
96
- { startupHook = startupHook c <> rescreenStartupHook
97
- , handleEventHook = handleEventHook c <> rescreenEventHook }
113
+ rescreenHook = XC. once hook . catchUserCode
114
+ where
115
+ hook c = c
116
+ { startupHook = startupHook c <> rescreenStartupHook
117
+ , handleEventHook = handleEventHook c <> rescreenEventHook }
118
+ catchUserCode rc@ RescreenConfig {.. } = rc
119
+ { afterRescreenHook = userCodeDef () afterRescreenHook
120
+ , randrChangeHook = userCodeDef () randrChangeHook
121
+ , rescreenWorkspacesHook = flip catchX rescreen <$> rescreenWorkspacesHook
122
+ }
98
123
99
124
-- | Shortcut for 'rescreenHook'.
100
125
addAfterRescreenHook :: X () -> XConfig l -> XConfig l
101
- addAfterRescreenHook h = rescreenHook def{ afterRescreenHook = userCodeDef () h }
126
+ addAfterRescreenHook h = rescreenHook def{ afterRescreenHook = h }
102
127
103
128
-- | Shortcut for 'rescreenHook'.
104
129
addRandrChangeHook :: X () -> XConfig l -> XConfig l
105
- addRandrChangeHook h = rescreenHook def{ randrChangeHook = userCodeDef () h }
130
+ addRandrChangeHook h = rescreenHook def{ randrChangeHook = h }
131
+
132
+ -- | Shortcut for 'rescreenHook'.
133
+ setRescreenWorkspacesHook :: X () -> XConfig l -> XConfig l
134
+ setRescreenWorkspacesHook h = rescreenHook def{ rescreenWorkspacesHook = pure h }
135
+
136
+ -- | Shortcut for 'rescreenHook'.
137
+ setRescreenDelay :: Int -> XConfig l -> XConfig l
138
+ setRescreenDelay d = rescreenHook def{ rescreenDelay = pure d }
106
139
107
140
-- | Startup hook to listen for @RRScreenChangeNotify@ events.
108
141
rescreenStartupHook :: X ()
@@ -126,13 +159,14 @@ handleEvent :: Event -> X ()
126
159
handleEvent e = XC. with $ \ RescreenConfig {.. } -> do
127
160
-- Xorg emits several events after every change, clear them to prevent
128
161
-- triggering the hook multiple times.
162
+ whenJust (getLast rescreenDelay) (io . threadDelay)
129
163
moreConfigureEvents <- clearTypedWindowEvents (ev_window e) configureNotify
130
164
_ <- clearTypedWindowRREvents (ev_window e) rrScreenChangeNotify
131
165
-- If there were any ConfigureEvents, this is an actual screen
132
166
-- configuration change, so rescreen and fire rescreenHook. Otherwise,
133
167
-- this is just a connect/disconnect, fire randrChangeHook.
134
168
if ev_event_type e == configureNotify || moreConfigureEvents
135
- then rescreen >> afterRescreenHook
169
+ then fromMaybe rescreen (getLast rescreenWorkspacesHook) >> afterRescreenHook
136
170
else randrChangeHook
137
171
138
172
-- | Remove all X events of a given window and type from the event queue,
0 commit comments