[PATCH v2 0/1] MR9793: winex11: Avoid hiding windows between WM_SETREDRAW messages.
See also f323d75e658fcb4f48c0b762001cee1da09d9398. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59098 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59069 This is different from the Splinter Cell case, as the window processes its messages and the window get shown again, but it is still related to WM_SETREDRAW and this fix here would fix the Splinter Cell case too. I originally decided to try a simpler fix, but as this issue is also present with other applications I think it's better now to try fixing it for all of them. -- v2: winex11: Avoid hiding windows between WM_SETREDRAW messages. https://gitlab.winehq.org/wine/wine/-/merge_requests/9793
From: Rémi Bernon <rbernon@codeweavers.com> See also f323d75e658fcb4f48c0b762001cee1da09d9398. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59098 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59069 --- dlls/win32u/defwnd.c | 7 ++++++- dlls/win32u/ntuser_private.h | 1 + dlls/win32u/window.c | 2 ++ dlls/winex11.drv/window.c | 1 + include/wine/gdi_driver.h | 1 + 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 384112fddda..48e3df2ed4c 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -2554,10 +2554,15 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, return 0; case WM_SETREDRAW: - if (wparam) set_window_style_bits( hwnd, WS_VISIBLE, 0 ); + if (wparam) + { + set_window_style_bits( hwnd, WS_VISIBLE, 0 ); + win_set_flags( hwnd, 0, WIN_REDRAW_DISABLED ); + } else { NtUserRedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_VALIDATE ); + if (get_window_long( hwnd, GWL_STYLE ) & WS_VISIBLE) win_set_flags( hwnd, WIN_REDRAW_DISABLED, 0 ); set_window_style_bits( hwnd, 0, WS_VISIBLE ); } return 0; diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index ec6cf02ffb1..159db560730 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -88,6 +88,7 @@ typedef struct tagWND #define WIN_NEEDS_SHOW_OWNEDPOPUP 0x0020 /* WM_SHOWWINDOW:SC_SHOW must be sent in the next ShowOwnedPopup call */ #define WIN_CHILDREN_MOVED 0x0040 /* children may have moved, ignore stored positions */ #define WIN_HAS_IME_WIN 0x0080 /* the window has been registered with imm32 */ +#define WIN_REDRAW_DISABLED 0x0100 /* Window disabled redraw with WM_SETREDRAW */ #define WND_OTHER_PROCESS ((WND *)1) /* returned by get_win_ptr on unknown window handles */ #define WND_DESKTOP ((WND *)2) /* returned by get_win_ptr on the desktop window */ diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 1464d45697e..8c0f5e7db61 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -2277,6 +2277,8 @@ static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags, stru if (is_fullscreen( &monitor_info, &new_rects->window )) swp_flags &= ~WINE_SWP_RESIZABLE; monitor_rects = map_window_rects_virt_to_raw( *new_rects, dpi ); } + + if (win->flags & WIN_REDRAW_DISABLED) swp_flags |= WINE_SWP_SETREDRAW; } release_win_ptr( win ); diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index e5cf1c90080..b98c2d31d96 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3246,6 +3246,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN struct window_rects old_rects; BOOL is_managed, was_fullscreen, activate = !(swp_flags & SWP_NOACTIVATE), fullscreen = !!(swp_flags & WINE_SWP_FULLSCREEN); + if (swp_flags & WINE_SWP_SETREDRAW) new_style |= WS_VISIBLE; if ((is_managed = is_window_managed( hwnd, swp_flags, fullscreen ))) make_owner_managed( hwnd ); if (!(data = get_win_data( hwnd ))) return; diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 81932436963..31c0ec2e9ad 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -356,6 +356,7 @@ struct gdi_device_manager #define WINE_DM_UNSUPPORTED 0x80000000 #define WINE_SWP_FULLSCREEN 0x80000000 #define WINE_SWP_RESIZABLE 0x40000000 +#define WINE_SWP_SETREDRAW 0x20000000 struct vulkan_driver_funcs; struct opengl_driver_funcs; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9793
v2: Be a bit extra careful and only set WIN_REDRAW_DISABLED if the window was visible in the first place (so that the flag doesn't force it become visible if it wasn't). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125789
How adding this kind of fragile hacks is better than explicit handling of SWP_SHOWWINDOW/SWP_HIDEWINDOW? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125790
I don't think there's any reason to assume that SWP_SHOWWINDOW will be set every time a window becomes visible. Adding WS_VISIBLE to the window style triggers a `update_window_state` call which does not apparently set it for instance. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125791
On Thu Dec 18 10:48:46 2025 +0000, Rémi Bernon wrote:
I don't think there's any reason to assume that SWP_SHOWWINDOW will be set every time a window becomes visible. Adding WS_VISIBLE to the window style triggers a `update_window_state` call which does not apparently set it for instance. Do you have a case where a window can be made visible under Windows without SWP_SHOWWINDOW? As far as I'm aware that was/is the only way to show a window.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125792
On Thu Dec 18 10:48:46 2025 +0000, Dmitry Timoshkov wrote:
Do you have a case where a window can be made visible under Windows without SWP_SHOWWINDOW? As far as I'm aware that was/is the only way to show a window. This cannot work based only on the SWP flags signal edges. There are various cases where the host window needs to be kept unmapped until it can actually be shown (offscreen, layered windows, ...) and we then need to check the Win32 window state to decide too. Maybe the whole thing could be changed and maybe tracking SWP flags to latch a visibility flag somewhere could be better, but this doesn't seem like a reasonable change during regression fixes. I also definitely don't know and wouldn't bet on any guarantee of what can actually be done with user32.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125793
On Thu Dec 18 10:57:54 2025 +0000, Rémi Bernon wrote:
This cannot work based only on the SWP flags signal edges. There are various cases where the host window needs to be kept unmapped until it can actually be shown (offscreen, layered windows, ...) and we then need to check the Win32 window state to decide too. Maybe the whole thing could be changed and maybe tracking SWP flags to latch a visibility flag somewhere could be better, but this doesn't seem like a reasonable change during regression fixes. I also definitely don't know and wouldn't bet on any guarantee of what can actually be done with user32. If it's a regression then probably a revert is an appropriate solution, not a pile of other hacks on top.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125794
On Thu Dec 18 11:08:39 2025 +0000, Dmitry Timoshkov wrote:
If it's a regression then probably a revert is an appropriate solution, not a pile of other hacks on top. I don't think so and I don't think these are hacks.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125795
Toggling WS_VISIBLE off then on again is a known trick to temporarily disable redraws. It can be done through WM_SETREDRAW but it can also be done manually, so this would have to be handled directly in `set_window_style_bits`. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793#note_125798
This merge request was closed by Rémi Bernon. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9793
participants (4)
-
Alexandre Julliard (@julliard) -
Dmitry Timoshkov (@dmitry) -
Rémi Bernon -
Rémi Bernon (@rbernon)