diff --git a/CHANGES.md b/CHANGES.md index 569daea02..2d3983dbc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,11 +7,11 @@ * `XMonad.Hooks.StatusBars` - Move status bar functions from the `IO` to the `X` monad to - allow them to look up information from `X`, like the screen - width. Existing configurations may need to use `io` from - `XMonad.Core` or `liftIO` from `Control.Monad.IO.Class` in - order to lift any existing `IO StatusBarConfig` values into - `X StatusBarConfig` values. + allow them to look up information from `X`, like the screen + width. Existing configurations may need to use `io` from + `XMonad.Core` or `liftIO` from `Control.Monad.IO.Class` in + order to lift any existing `IO StatusBarConfig` values into + `X StatusBarConfig` values. * `XMonad.Prompt` @@ -22,10 +22,17 @@ ### New Modules - * `XMonad.Actions.Profiles`. + * `XMonad.Actions.Profiles` - Group workspaces by similarity. Useful when one has lots - of workspaces and uses only a couple per unit of work. + of workspaces and uses only a couple per unit of work. + + * `XMonad.Hooks.FloatConfigureReq` + + - Customize handling of floating windows' move/resize/restack requests + (ConfigureRequest). Useful as a workaround for some misbehaving client + applications (Steam, rxvt-unicode, anything that tries to restore + absolute position of floats). ### Bug Fixes and Minor Changes @@ -49,6 +56,15 @@ - The history file is not extraneously read and written anymore if the `historySize` is set to 0. + * `XMonad.Hooks.EwmhDesktops` + + - Requests for unmanaged windows no longer cause a refresh. This avoids + flicker and also fixes disappearing menus in the Steam client and + possibly a few other client applications. + + (See also `XMonad.Hooks.FloatConfigureReq` and/or `XMonad.Util.Hacks` + for additional Steam client workarounds.) + ### Other changes ## 0.18.0 (February 3, 2024) diff --git a/XMonad/Hooks/EwmhDesktops.hs b/XMonad/Hooks/EwmhDesktops.hs index cfb1b91df..337de252c 100644 --- a/XMonad/Hooks/EwmhDesktops.hs +++ b/XMonad/Hooks/EwmhDesktops.hs @@ -459,7 +459,14 @@ ewmhDesktopsEventHook' a_aw <- getAtom "_NET_ACTIVE_WINDOW" a_cw <- getAtom "_NET_CLOSE_WINDOW" - if | mt == a_cd, n : _ <- d, Just ww <- ws !? fi n -> + if | mt == a_cw -> + killWindow w + | not (w `W.member` s) -> + -- do nothing for unmanaged windows; it'd be just a useless + -- refresh which breaks menus/popups of misbehaving apps that + -- send _NET_ACTIVE_WINDOW requests for override-redirect wins + mempty + | mt == a_cd, n : _ <- d, Just ww <- ws !? fi n -> if W.currentTag s == W.tag ww then mempty else windows $ W.view (W.tag ww) | mt == a_cd -> trace $ "Bad _NET_CURRENT_DESKTOP with data=" ++ show d @@ -473,8 +480,6 @@ ewmhDesktopsEventHook' if W.peek s == Just w then mempty else windows $ W.focusWindow w | mt == a_aw -> do if W.peek s == Just w then mempty else windows . appEndo =<< runQuery activateHook w - | mt == a_cw -> - killWindow w | otherwise -> -- The Message is unknown to us, but that is ok, not all are meant -- to be handled by the window manager diff --git a/XMonad/Hooks/FloatConfigureReq.hs b/XMonad/Hooks/FloatConfigureReq.hs new file mode 100644 index 000000000..7b255a7f4 --- /dev/null +++ b/XMonad/Hooks/FloatConfigureReq.hs @@ -0,0 +1,126 @@ +{-# LANGUAGE LambdaCase #-} +-- | +-- Module : XMonad.Hooks.FloatConfigureReq +-- Description : Customize handling of floating windows' move\/resize\/restack requests (ConfigureRequest). +-- Copyright : (c) 2024 Tomáš Janoušek +-- License : BSD3 +-- Maintainer : Tomáš Janoušek +-- +-- xmonad normally honours those requests by doing exactly what the client +-- application asked, and refreshing. There are some misbehaving clients, +-- however, that: +-- +-- * try to move their window to the last known absolute position regardless +-- of the current xrandr/xinerama layout +-- +-- * move their window to 0, 0 for no particular reason (e.g. rxvt-unicode) +-- +-- * issue lots of no-op requests causing flickering (e.g. Steam) +-- +-- This module provides a replacement handler for 'ConfigureRequestEvent' to +-- work around such misbehaviours. +-- +module XMonad.Hooks.FloatConfigureReq ( + -- * Usage + -- $usage + MaybeMaybeManageHook, + floatConfReqHook, + + -- * Known workarounds + fixSteamFlicker, + fixSteamFlickerMMMH, + ) where + +import qualified Data.Map.Strict as M +import XMonad +import XMonad.Hooks.ManageHelpers +import XMonad.Prelude +import qualified XMonad.StackSet as W + +-- $usage +-- To use this, include the following in your @xmonad.hs@: +-- +-- > import XMonad.Hooks.FloatConfigureReq +-- > import XMonad.Hooks.ManageHelpers +-- +-- > myFloatConfReqHook :: MaybeMaybeManageHook +-- > myFloatConfReqHook = composeAll +-- > [ … ] +-- +-- > myEventHook :: Event -> X All +-- > myEventHook = mconcat +-- > [ … +-- > , floatConfReqHook myFloatConfReqHook +-- > , … ] +-- +-- > main = xmonad $ … +-- > $ def{ handleEventHook = myEventHook +-- > , … } +-- +-- Then fill the @myFloatConfReqHook@ with whatever custom rules you need. +-- +-- As an example, the following will prevent rxvt-unicode from moving its +-- (floating) window to 0, 0 after a font change but still ensure its size +-- increment hints are respected: +-- +-- > className =? "URxvt" -?> pure <$> doFloat +-- +-- Another example that avoids flickering and xmonad slowdowns caused by the +-- Steam client (completely ignore all its requests, none of which are +-- meaningful in the context of a tiling WM): +-- +-- > map toLower `fmap` className =? "steam" -?> mempty +-- +-- (this example is also available as 'fixSteamFlickerMMMH' to be added to +-- one's @myFloatConfReqHook@ and also 'fixSteamFlicker' to be added directly +-- to one's 'handleEventHook') + +-- | A variant of 'MaybeManageHook' that additionally may or may not make +-- changes to the 'WindowSet'. +type MaybeMaybeManageHook = Query (Maybe (Maybe (Endo WindowSet))) + +-- | Customizable handler for a 'ConfigureRequestEvent'. If the event's +-- 'ev_window' is a managed floating window, the provided +-- 'MaybeMaybeManageHook' is consulted and its result interpreted as follows: +-- +-- * @Nothing@ - no match, fall back to the default handler +-- +-- * @Just Nothing@ - match but ignore, no refresh, just send ConfigureNotify +-- +-- * @Just (Just a)@ - match, modify 'WindowSet', refresh, send ConfigureNotify +floatConfReqHook :: MaybeMaybeManageHook -> Event -> X All +floatConfReqHook mh ConfigureRequestEvent{ev_window = w} = + runQuery (join <$> (isFloatQ -?> mh)) w >>= \case + Nothing -> mempty + Just e -> do + whenJust e (windows . appEndo) + sendConfEvent + pure (All False) + where + sendConfEvent = withDisplay $ \dpy -> + withWindowAttributes dpy w $ \wa -> do + io . allocaXEvent $ \ev -> do + -- We may have made no changes to the window size/position + -- and thus the X server didn't emit any ConfigureNotify, + -- so we need to send the ConfigureNotify ourselves to make + -- sure there is a reply to this ConfigureRequestEvent and the + -- window knows we (possibly) ignored its request. + setEventType ev configureNotify + setConfigureEvent ev w w + (wa_x wa) (wa_y wa) (wa_width wa) + (wa_height wa) (wa_border_width wa) none (wa_override_redirect wa) + sendEvent dpy w False 0 ev +floatConfReqHook _ _ = mempty + +-- | A 'Query' to determine if a window is floating. +isFloatQ :: Query Bool +isFloatQ = ask >>= \w -> liftX . gets $ M.member w . W.floating . windowset + +-- | A pre-packaged 'floatConfReqHook' that fixes flickering of the Steam client by ignoring 'ConfigureRequestEvent's on any of its floating windows. +-- +-- To use this, add 'fixSteamFlicker' to your 'handleEventHook'. +fixSteamFlicker :: Event -> X All +fixSteamFlicker = floatConfReqHook fixSteamFlickerMMMH + +fixSteamFlickerMMMH :: MaybeMaybeManageHook +fixSteamFlickerMMMH = map toLower `fmap` className =? "steam" -?> mempty diff --git a/XMonad/Util/Hacks.hs b/XMonad/Util/Hacks.hs index 749d81bb6..6b00ead44 100644 --- a/XMonad/Util/Hacks.hs +++ b/XMonad/Util/Hacks.hs @@ -40,10 +40,14 @@ module XMonad.Util.Hacks ( trayerPaddingXmobarEventHook, trayPaddingXmobarEventHook, trayPaddingEventHook, + + -- * Steam flickering fix + fixSteamFlicker, ) where import XMonad +import XMonad.Hooks.FloatConfigureReq (fixSteamFlicker) import XMonad.Hooks.StatusBar (xmonadPropLog') import XMonad.Prelude (All (All), fi, filterM, when) import System.Posix.Env (putEnv) diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal index 354aaca8b..1882eb9db 100644 --- a/xmonad-contrib.cabal +++ b/xmonad-contrib.cabal @@ -192,6 +192,7 @@ library XMonad.Hooks.EwmhDesktops XMonad.Hooks.FadeInactive XMonad.Hooks.FadeWindows + XMonad.Hooks.FloatConfigureReq XMonad.Hooks.FloatNext XMonad.Hooks.Focus XMonad.Hooks.InsertPosition