|
| 1 | +{-# LANGUAGE LambdaCase #-} |
| 2 | +-- | |
| 3 | +-- Module : XMonad.Hooks.FloatConfigureReq |
| 4 | +-- Description : Customize handling of floating windows' move\/resize\/restack requests (ConfigureRequest). |
| 5 | +-- Copyright : (c) 2024 Tomáš Janoušek <[email protected]> |
| 6 | +-- License : BSD3 |
| 7 | +-- Maintainer : Tomáš Janoušek <[email protected]> |
| 8 | +-- |
| 9 | +-- xmonad normally honours those requests by doing exactly what the client |
| 10 | +-- application asked, and refreshing. There are some misbehaving clients, |
| 11 | +-- however, that: |
| 12 | +-- |
| 13 | +-- * try to move their window to the last known absolute position regardless |
| 14 | +-- of the current xrandr/xinerama layout |
| 15 | +-- |
| 16 | +-- * move their window to 0, 0 for no particular reason (e.g. rxvt-unicode) |
| 17 | +-- |
| 18 | +-- * issue lots of no-op requests causing flickering (e.g. Steam) |
| 19 | +-- |
| 20 | +-- This module provides a replacement handler for 'ConfigureRequestEvent' to |
| 21 | +-- work around such misbehaviours. |
| 22 | +-- |
| 23 | +module XMonad.Hooks.FloatConfigureReq ( |
| 24 | + -- * Usage |
| 25 | + -- $usage |
| 26 | + MaybeMaybeManageHook, |
| 27 | + floatConfReqHook, |
| 28 | + |
| 29 | + -- * Known workarounds |
| 30 | + fixSteamFlicker, |
| 31 | + fixSteamFlickerMMMH, |
| 32 | + ) where |
| 33 | + |
| 34 | +import qualified Data.Map.Strict as M |
| 35 | +import XMonad |
| 36 | +import XMonad.Hooks.ManageHelpers |
| 37 | +import XMonad.Prelude |
| 38 | +import qualified XMonad.StackSet as W |
| 39 | + |
| 40 | +-- $usage |
| 41 | +-- To use this, include the following in your @xmonad.hs@: |
| 42 | +-- |
| 43 | +-- > import XMonad.Hooks.FloatConfigureReq |
| 44 | +-- > import XMonad.Hooks.ManageHelpers |
| 45 | +-- |
| 46 | +-- > myFloatConfReqHook :: MaybeMaybeManageHook |
| 47 | +-- > myFloatConfReqHook = composeAll |
| 48 | +-- > [ … ] |
| 49 | +-- |
| 50 | +-- > myEventHook :: Event -> X All |
| 51 | +-- > myEventHook = mconcat |
| 52 | +-- > [ … |
| 53 | +-- > , floatConfReqHook myFloatConfReqHook |
| 54 | +-- > , … ] |
| 55 | +-- |
| 56 | +-- > main = xmonad $ … |
| 57 | +-- > $ def{ handleEventHook = myEventHook |
| 58 | +-- > , … } |
| 59 | +-- |
| 60 | +-- Then fill the @myFloatConfReqHook@ with whatever custom rules you need. |
| 61 | +-- |
| 62 | +-- As an example, the following will prevent rxvt-unicode from moving its |
| 63 | +-- (floating) window to 0, 0 after a font change but still ensure its size |
| 64 | +-- increment hints are respected: |
| 65 | +-- |
| 66 | +-- > className =? "URxvt" -?> pure <$> doFloat |
| 67 | +-- |
| 68 | +-- Another example that avoids flickering and xmonad slowdowns caused by the |
| 69 | +-- Steam client (completely ignore all its requests, none of which are |
| 70 | +-- meaningful in the context of a tiling WM): |
| 71 | +-- |
| 72 | +-- > map toLower `fmap` className =? "steam" -?> mempty |
| 73 | +-- |
| 74 | +-- (this example is also available as 'fixSteamFlickerMMMH' to be added to |
| 75 | +-- one's @myFloatConfReqHook@ and also 'fixSteamFlicker' to be added directly |
| 76 | +-- to one's 'handleEventHook') |
| 77 | + |
| 78 | +-- | A variant of 'MaybeManageHook' that additionally may or may not make |
| 79 | +-- changes to the 'WindowSet'. |
| 80 | +type MaybeMaybeManageHook = Query (Maybe (Maybe (Endo WindowSet))) |
| 81 | + |
| 82 | +-- | Customizable handler for a 'ConfigureRequestEvent'. If the event's |
| 83 | +-- 'ev_window' is a managed floating window, the provided |
| 84 | +-- 'MaybeMaybeManageHook' is consulted and its result interpreted as follows: |
| 85 | +-- |
| 86 | +-- * @Nothing@ - no match, fall back to the default handler |
| 87 | +-- |
| 88 | +-- * @Just Nothing@ - match but ignore, no refresh, just send ConfigureNotify |
| 89 | +-- |
| 90 | +-- * @Just (Just a)@ - match, modify 'WindowSet', refresh, send ConfigureNotify |
| 91 | +floatConfReqHook :: MaybeMaybeManageHook -> Event -> X All |
| 92 | +floatConfReqHook mh ConfigureRequestEvent{ev_window = w} = |
| 93 | + runQuery (join <$> (isFloatQ -?> mh)) w >>= \case |
| 94 | + Nothing -> mempty |
| 95 | + Just e -> do |
| 96 | + whenJust e (windows . appEndo) |
| 97 | + sendConfEvent |
| 98 | + pure (All False) |
| 99 | + where |
| 100 | + sendConfEvent = withDisplay $ \dpy -> |
| 101 | + withWindowAttributes dpy w $ \wa -> do |
| 102 | + io . allocaXEvent $ \ev -> do |
| 103 | + -- We may have made no changes to the window size/position |
| 104 | + -- and thus the X server didn't emit any ConfigureNotify, |
| 105 | + -- so we need to send the ConfigureNotify ourselves to make |
| 106 | + -- sure there is a reply to this ConfigureRequestEvent and the |
| 107 | + -- window knows we (possibly) ignored its request. |
| 108 | + setEventType ev configureNotify |
| 109 | + setConfigureEvent ev w w |
| 110 | + (wa_x wa) (wa_y wa) (wa_width wa) |
| 111 | + (wa_height wa) (wa_border_width wa) none (wa_override_redirect wa) |
| 112 | + sendEvent dpy w False 0 ev |
| 113 | +floatConfReqHook _ _ = mempty |
| 114 | + |
| 115 | +-- | A 'Query' to determine if a window is floating. |
| 116 | +isFloatQ :: Query Bool |
| 117 | +isFloatQ = ask >>= \w -> liftX . gets $ M.member w . W.floating . windowset |
| 118 | + |
| 119 | +-- | A pre-packaged 'floatConfReqHook' that fixes flickering of the Steam client by ignoring 'ConfigureRequestEvent's on any of its floating windows. |
| 120 | +-- |
| 121 | +-- To use this, add 'fixSteamFlicker' to your 'handleEventHook'. |
| 122 | +fixSteamFlicker :: Event -> X All |
| 123 | +fixSteamFlicker = floatConfReqHook fixSteamFlickerMMMH |
| 124 | + |
| 125 | +fixSteamFlickerMMMH :: MaybeMaybeManageHook |
| 126 | +fixSteamFlickerMMMH = map toLower `fmap` className =? "steam" -?> mempty |
0 commit comments