Detect WM_HINTS popup (yabridge popups fix) (#328)

* Detect WM_HINTS popup (yabridge popups fix)

Many windows popups have:

WM_HINTS(WM_HINTS) Client accepts input or input focus: False

Which is a good indicator window SHOULD be a popup.

This is true for example on yabridge plugins and some other apps.
However toplevel windows have this property True. In order to
differentiate them we check if there are no decorations on the window
(client) and this property is False.
Its applied ONLY to _NET_WM_WINDOW_TYPE_NORMAL windows since its most
generic one that comes up.

This was tested across many apps: Reaper, Ardour, Godot, MaterialMaker,
PixelOver, PixelComposer, Steam, Steam games (both windowed and
fullscreen), Fusion 9, Unity and others I have.

There are no regression seen in any of tested apps and fixes yabridge
popups.
Additionally check if MOTIF has functions that should not be in popup
Combine wmhint popup check with skip_taskbar.
wmhint is only considered to be a popup if application has skip_taskbar
atom.
Fixed edgecases so far:
BattleNet spawning as popupp
PixelComposer spawning as popup
This commit is contained in:
GoranKovac 2026-01-18 17:31:17 +01:00 committed by GitHub
parent 72245e108f
commit 645ca1125b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 128 additions and 17 deletions

View file

@ -346,6 +346,7 @@ xcb::atoms_struct! {
win_type_utility => b"_NET_WM_WINDOW_TYPE_UTILITY",
win_type_dnd => b"_NET_WM_WINDOW_TYPE_DND",
motif_wm_hints => b"_MOTIF_WM_HINTS" only_if_exists = false,
wm_hints => b"WM_HINTS",
mime1 => b"text/plain" only_if_exists = false,
mime2 => b"blah/blah" only_if_exists = false,
incr => b"INCR",
@ -2071,6 +2072,21 @@ fn popup_heuristics() {
);
f.map_as_popup(&mut connection, godot_popup);
let material_maker_popup = connection.new_window(connection.root, 10, 10, 50, 50, false);
connection.set_property(
material_maker_popup,
x::ATOM_ATOM,
connection.atoms.win_type,
&[connection.atoms.win_type_utility],
);
connection.set_property(
material_maker_popup,
connection.atoms.motif_wm_hints,
connection.atoms.motif_wm_hints,
&[0x2_u32, 0, 0, 0, 0],
);
f.map_as_popup(&mut connection, material_maker_popup);
let ardour_toplevel = connection.new_window(connection.root, 10, 10, 50, 50, false);
connection.set_property(
ardour_toplevel,
@ -2079,6 +2095,75 @@ fn popup_heuristics() {
&[connection.atoms.win_type_utility],
);
f.map_as_toplevel(&mut connection, ardour_toplevel);
let yabridge_popup = connection.new_window(connection.root, 10, 10, 50, 50, false);
connection.set_property(
yabridge_popup,
x::ATOM_ATOM,
connection.atoms.win_type,
&[connection.atoms.win_type_normal],
);
connection.set_property(
yabridge_popup,
connection.atoms.motif_wm_hints,
connection.atoms.motif_wm_hints,
&[0x2_u32, 0, 0, 0, 0],
);
connection.set_property(
yabridge_popup,
connection.atoms.wm_hints,
connection.atoms.wm_hints,
&[0x1_u32, 0, 0, 0, 0, 0, 0, 0, 0],
);
connection.set_property(
yabridge_popup,
x::ATOM_ATOM,
connection.atoms.net_wm_state,
&[connection.atoms.skip_taskbar],
);
f.map_as_popup(&mut connection, yabridge_popup);
let steam = connection.new_window(connection.root, 10, 10, 50, 50, false);
connection.set_property(
steam,
x::ATOM_ATOM,
connection.atoms.win_type,
&[connection.atoms.win_type_normal],
);
connection.set_property(
steam,
connection.atoms.motif_wm_hints,
connection.atoms.motif_wm_hints,
&[0x2_u32, 0, 0, 0, 0],
);
connection.set_property(
steam,
connection.atoms.wm_hints,
connection.atoms.wm_hints,
&[0x1_u32, 1, 0, 0, 0, 0, 0, 0, 0],
);
f.map_as_toplevel(&mut connection, steam);
let battle_net = connection.new_window(connection.root, 10, 10, 50, 50, false);
connection.set_property(
battle_net,
x::ATOM_ATOM,
connection.atoms.win_type,
&[connection.atoms.win_type_normal],
);
connection.set_property(
battle_net,
connection.atoms.motif_wm_hints,
connection.atoms.motif_wm_hints,
&[0x3_u32, 0x2c, 0x0, 0x0, 0x0],
);
connection.set_property(
battle_net,
connection.atoms.wm_hints,
connection.atoms.wm_hints,
&[0x1_u32, 0, 0, 0, 0, 0, 0, 0, 0],
);
f.map_as_toplevel(&mut connection, battle_net);
}
#[test]