Skip to content

Fixes/workarounds for Steam client menus/flickering #886

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand All @@ -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

Expand All @@ -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)
Expand Down
11 changes: 8 additions & 3 deletions XMonad/Hooks/EwmhDesktops.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
126 changes: 126 additions & 0 deletions XMonad/Hooks/FloatConfigureReq.hs
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
-- License : BSD3
-- Maintainer : Tomáš Janoušek <[email protected]>
--
-- 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
4 changes: 4 additions & 0 deletions XMonad/Util/Hacks.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions xmonad-contrib.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down