Fix a regression from 31fda1f4, which allowed the visible rect to be larger than the monitor rect. After 31fda1f4, CHRONO TRIGGER (613830) sets a window rect slightly larger than the monitor rect and will change the window rect to the rect it previously set if the game detects a different window rect. Adding __NET_WM_STATE_FULLSCREEN will cause WMs to move the window to cover exactly the monitor rect. So the window rect will be repeatedly changed by the WM and the game, causing a flickering effect. Limit fullscreen visible rects to the virtual screen rect so that the visible rects in winex11.drv are of the same size as the monitor rect. Thus, adding __NET_WM_STATE_FULLSCREEN won't trigger a size change.
From: Zhiyi Zhang zzhang@codeweavers.com
Fix a regression from 31fda1f4, which allowed the visible rect to be larger than the monitor rect. After 31fda1f4, CHRONO TRIGGER (613830) sets a window rect slightly larger than the monitor rect and will change the window rect to the rect it previously set if the game detects a different window rect. Adding __NET_WM_STATE_FULLSCREEN will cause WMs to move the window to cover exactly the monitor rect. So the window rect will be repeatedly changed by the WM and the game, causing a flickering effect. Limit fullscreen visible rects to the virtual screen rect so that the visible rects in winex11.drv are of the same size as the monitor rect. Thus, adding __NET_WM_STATE_FULLSCREEN won't trigger a size change. --- dlls/winex11.drv/window.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index b9055791a60..a22bf699169 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -3158,8 +3158,9 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN { struct x11drv_win_data *data; UINT new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ), old_style; - struct window_rects old_rects; + struct window_rects old_rects, tmp_rects; BOOL was_fullscreen, activate = !(swp_flags & SWP_NOACTIVATE); + RECT virtual;
if (!(data = get_win_data( hwnd ))) return; if (is_window_managed( hwnd, swp_flags, fullscreen )) window_set_managed( data, TRUE ); @@ -3169,6 +3170,20 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN if (data->desired_state.wm_state == IconicState) old_style |= WS_MINIMIZE; if (data->desired_state.net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)) old_style |= WS_MAXIMIZE;
+ /* If the visible rect is fullscreen on any one of the monitors, limit the visible rect to the + * virtual screen rect. This is needed because adding __NET_WM_STATE_FULLSCREEN will make WMs + * move the window to cover exactly the monitor rect. If the application sets a visible rect + * slightly larger than the monitor rect and insists on changing to the rect that it previously + * set when the rect is changed by the WM, then the window rect will be repeatedly changed by + * the WM and the application, causing a flickering effect */ + if (fullscreen) + { + virtual = NtUserGetVirtualScreenRect( MDT_RAW_DPI ); + tmp_rects = *new_rects; + intersect_rect( &tmp_rects.visible, &tmp_rects.visible, &virtual ); + new_rects = &tmp_rects; + } + old_rects = data->rects; was_fullscreen = data->is_fullscreen; data->rects = *new_rects;
Any reason not to do that in win32u? We pass the fullscreen flag from there, I think it would be appropriate to fixup visible rect there too.
On Thu Aug 7 08:28:13 2025 +0000, Rémi Bernon wrote:
Any reason not to do that in win32u? We pass the fullscreen flag from there, I think it would be appropriate to fixup visible rect there too.
It's because the behavior of changing window size when there is a __NET_WM_STATE_FULLSCREEN is limited to winex11.drv. For example, winemac.drv doesn't have a __NET_WM_STATE_FULLSCREEN and thus doesn't need this fixup. My first implementation was in win32u, but later I decided it's better to be in winex11.drv.
On Thu Aug 7 08:28:13 2025 +0000, Zhiyi Zhang wrote:
It's because the behavior of changing window size when there is a __NET_WM_STATE_FULLSCREEN is limited to winex11.drv. For example, winemac.drv doesn't have a __NET_WM_STATE_FULLSCREEN and thus doesn't need this fixup. My first implementation was in win32u, but later I decided it's better to be in winex11.drv.
It has some implications on the consistency of windows rects between win32u and the drivers, I'm not completely sure out of my head what depends on visible_rect but I think some things could get wrong if we make them different.
On Thu Aug 7 08:30:49 2025 +0000, Rémi Bernon wrote:
It has some implications on the consistency of windows rects between win32u and the drivers, I'm not completely sure out of my head what depends on visible_rect but I think some things could get wrong if we make them different.
Another option could be to do this fixup in window_set_config, but I'm also wondering whether we should simply fixup the Win32 rect instead. What does native do here? Do they allow the client rect to extend outside of the virtual rect without causing any window resize?
Do they allow the client rect to extend outside of the virtual rect without causing any window resize?
Yes. It's allowed. Please see https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/user32/tests/win.c?re...
Maybe we could return 0 when the window is fullscreen in window_update_client_config() instead.
On Thu Aug 7 08:42:09 2025 +0000, Zhiyi Zhang wrote:
Do they allow the client rect to extend outside of the virtual rect
without causing any window resize? Yes. It's allowed. Please see https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/user32/tests/win.c?re... Maybe we could return 0 when the window is fullscreen in window_update_client_config() instead.
We already have 4b27077fd47b before. But apparently, the application can still detect the window change without SWP_NOSENDCHANGING.