[PATCH 0/2] MR10949: winex11.drv: Return WithdrawnState when get_window_wm_state() fails.
KWin deletes WM_STATE property from a window when entering WithdrawnState. Please see X11Window::exportMappingState() in the KWin source code. So when get_window_wm_state() fails, -1 is returned before this patch. Since -1 is not a valid WM_STATE, it might cause Wine to expect an invalid value. We also can't simply ignore the invalid -1 because we might be expecting the WM_STATE property notify event to change to the desired state. Treating missing WM_STATE property as WithdrawnState seems like the best way to go. Help fix RiME(493200) sometimes fails to restore from Alt+Tab. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10949
From: Zhiyi Zhang <zzhang@codeweavers.com> KWin deletes WM_STATE property from a window when entering WithdrawnState. Please see X11Window::exportMappingState() in the KWin source code. So when get_window_wm_state() fails, -1 is returned before this patch. Since -1 is not a valid WM_STATE, it might cause Wine to expect an invalid value. We also can't simply ignore the invalid -1 because we might be expecting the WM_STATE property notify event to change to the desired state. Treating missing WM_STATE property as WithdrawnState seems like the best way to go. Help fix RiME(493200) sometimes fails to restore from Alt+Tab. --- dlls/winex11.drv/event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 2983d7bfe8a..c7c97d04527 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1176,7 +1176,7 @@ static int get_window_wm_state( Display *display, Window window ) XID icon; } *state; Atom type; - int format, ret = -1; + int format, ret = WithdrawnState; unsigned long count, remaining; if (!XGetWindowProperty( display, window, x11drv_atom(WM_STATE), 0, -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10949
From: Zhiyi Zhang <zzhang@codeweavers.com> KWin handles UnmapNotify events from XWithdrawWindow() by first setting WM_STATE to WithdrawnState, then deleting the WM_STATE property, then setting it to IconicState. This means there is an unexpected IconicState when we are expecting WithdrawnState. Since 4c07e556, Wine goes through WithdrawnState when transitioning from IconicState to NormalState. The IconicState state can cause an application to stuck at an unexpected state because its overwrites the desired NormalState. While we could not go through WithdrawnState when on KWin, this fix doesn't require a WM check. Fix RiME(493200) sometimes fails to restore from Alt+Tab. --- dlls/winex11.drv/window.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index d2d53a40be3..528126a0f3a 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1865,7 +1865,9 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, received = wine_dbg_sprintf( "WM_STATE %#x/%lu", value, serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %#x/%lu", *pending, *expect_serial ) : ""; /* ignore Metacity/Mutter transient NormalState during WithdrawnState <-> IconicState transitions */ - if (value == NormalState && *current + *pending == IconicState) reason = "transient "; + if (value == NormalState && *current + *pending == IconicState) reason = "transient NormalState "; + /* ignore KWin transient IconicState when entering WithdrawnState */ + if (value == IconicState && *pending == WithdrawnState) reason = "transient IconicState "; if (!handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, current, expected, prefix, received, reason )) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10949
Rémi Bernon (@rbernon) commented about dlls/winex11.drv/window.c:
received = wine_dbg_sprintf( "WM_STATE %#x/%lu", value, serial ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %#x/%lu", *pending, *expect_serial ) : ""; /* ignore Metacity/Mutter transient NormalState during WithdrawnState <-> IconicState transitions */ - if (value == NormalState && *current + *pending == IconicState) reason = "transient "; + if (value == NormalState && *current + *pending == IconicState) reason = "transient NormalState "; + /* ignore KWin transient IconicState when entering WithdrawnState */ + if (value == IconicState && *pending == WithdrawnState) reason = "transient IconicState ";
This seems suspicious, and these transition workarounds are fragile already. If the client requested the window to be unmapped I don't see why the window manager would make it iconic. Do you know where this happens in Kwin source? Could it be fixed there? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10949#note_140592
On Tue May 19 14:22:30 2026 +0000, Rémi Bernon wrote:
This seems suspicious, and these transition workarounds are fragile already. If the client requested the window to be unmapped I don't see why the window manager would make it iconic. Do you know where this happens in Kwin source? Could it be fixed there? Please see https://github.com/KDE/kwin/blob/c6f7a27bb82d136766a62d3a295d57663dc6b206/sr....
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10949#note_140735
On Wed May 20 02:17:24 2026 +0000, Zhiyi Zhang wrote:
Please see https://github.com/KDE/kwin/blob/c6f7a27bb82d136766a62d3a295d57663dc6b206/sr.... And yes, setting IconicState when handling unmap is weird. Maybe they want to support window preview or something.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10949#note_140736
On Wed May 20 02:21:20 2026 +0000, Zhiyi Zhang wrote:
And yes, setting IconicState when handling unmap is weird. Maybe they want to support window preview or something. I do not see this happening with a simple test, WM_STATE only changes to 0 when we request a window to be withdrawn, for instance when calling `ShowWindow( hwnd, SW_HIDE )`. I'm testing kwin 6.6.5, and tried both X11 and Xwayland session. I don't know under which circumstances this `unmap` function gets called but I don't think it's called when client requests the window to be withdrawn.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10949#note_140751
participants (3)
-
Rémi Bernon (@rbernon) -
Zhiyi Zhang -
Zhiyi Zhang (@zhiyi)