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.
-- v2: winex11.drv: Limit fullscreen visible rects to the virtual screen rect.
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/win32u/sysparams.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 463b6b00f68..55f0640dcbe 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2635,9 +2635,9 @@ RECT map_rect_virt_to_raw( RECT rect, UINT dpi_from ) /* map (absolute) window rects from MDT_DEFAULT to MDT_RAW_DPI coordinates */ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UINT dpi_from ) { - RECT rect, monitor_rect, virt_visible_rect = rects.visible; + RECT rect, monitor_rect, raw_monitor_rect, raw_virtual_screen_rect = {0}, virt_visible_rect = rects.visible; + BOOL is_fullscreen, limit_to_virtual_screen = FALSE; struct monitor *monitor; - BOOL is_fullscreen;
if (!lock_display_devices( FALSE )) return rects; if ((monitor = get_monitor_from_rect( rects.window, MONITOR_DEFAULTTONEAREST, dpi_from, MDT_DEFAULT ))) @@ -2652,13 +2652,25 @@ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UIN if (!is_monitor_active( monitor ) || monitor->is_clone) continue;
monitor_rect = monitor_get_rect( monitor, dpi_from, MDT_DEFAULT ); + raw_monitor_rect = monitor_get_rect( monitor, 0, MDT_RAW_DPI ); + union_rect( &raw_virtual_screen_rect, &raw_virtual_screen_rect, &raw_monitor_rect ); is_fullscreen = intersect_rect( &rect, &monitor_rect, &virt_visible_rect ) && EqualRect( &rect, &monitor_rect ); if (is_fullscreen) { - rect = monitor_get_rect( monitor, 0, MDT_RAW_DPI ); - union_rect( &rects.visible, &rects.visible, &rect ); + limit_to_virtual_screen = TRUE; + union_rect( &rects.visible, &rects.visible, &raw_monitor_rect ); } } + + /* 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 (limit_to_virtual_screen) + intersect_rect( &rects.visible, &rects.visible, &raw_virtual_screen_rect ); + unlock_display_devices();
return rects;
On Thu Aug 7 08:50:03 2025 +0000, Zhiyi Zhang wrote:
We already have 4b27077fd47b before. But apparently, the application can still detect the window change with SWP_NOSENDCHANGING.
I've moved the fixup to win32u.
Returning 0 when the window is fullscreen (data->is_fullscreen) in window_update_client_config() doesn't work because some WMs can use keyboard shortcuts to move fullscreen windows to another monitor. We still want the X11 position synced to the Win32 side in this case.
I'm still wondering whether this is the right thing to do. Maybe we're wrong telling the driver and the host that a window is fullscreen as soon as it entirely covers one of the monitors. On a multi-monitor system, if a window covers one monitor and part of another, it probably shouldn't be using _NET_WM_STATE_FULLSCREEN as that would reduce it to only the monitor it covers right?
On Mon Aug 11 07:49:35 2025 +0000, Rémi Bernon wrote:
I'm still wondering whether this is the right thing to do. Maybe we're wrong telling the driver and the host that a window is fullscreen as soon as it entirely covers one of the monitors. On a multi-monitor system, if a window covers one monitor and part of another, it probably shouldn't be using _NET_WM_STATE_FULLSCREEN as that would reduce it to only the monitor it covers right?
Yeah, I had an old patch that doesn't mark the window as fullscreen if the window rect doesn't match the monitor rect exactly, see https://gitlab.winehq.org/zhiyi/wine/-/commit/d236213b, but I didn't get it upstream at the time due to some potential problems. For example, I remember some games use a slightly larger window rect. Not having _NET_WM_STATE_FULLSCREEN will cause the taskbar to be shown on top of the game window. I will revisit d236213b and compare with native and see if a similar fix can be used.
On Mon Aug 11 07:49:35 2025 +0000, Zhiyi Zhang wrote:
Yeah, I had an old patch that doesn't mark the window as fullscreen if the window rect doesn't match the monitor rect exactly, see https://gitlab.winehq.org/zhiyi/wine/-/commit/d236213b, but I didn't get it upstream at the time due to some potential problems. For example, I remember some games use a slightly larger window rect. Not having _NET_WM_STATE_FULLSCREEN will cause the taskbar to be shown on top of the game window. I will revisit d236213b and compare with native and see if a similar fix can be used.
It's also related to the exclusive fullscreen mode support, which we don't have. For example, when using exclusive fullscreen mode, the rendered content covers the exact fullscreen size, regardless of the window size. So using _NET_WM_STATE_FULLSCREEN is a workaround for that.
Implementing exclusive fullscreen seems possible. For example, [0001-dxgi-tests-Test-moving-the-window-for-a-fullscreen-swa.txt](/uploads/9e65af02f7ed1fed7c194dd99024ea02/0001-dxgi-tests-Test-moving-the-window-for-a-fullscreen-swa.txt) [0002-dxgi-Implement-exclusive-screen-mode-for-d3d11.txt](/uploads/b9ebb8e5cfe5f78b175ab3b8618cbd38/0002-dxgi-Implement-exclusive-screen-mode-for-d3d11.txt).
On Mon Aug 11 08:02:59 2025 +0000, Zhiyi Zhang wrote:
It's also related to the exclusive fullscreen mode support, which we don't have. For example, when using exclusive fullscreen mode, the rendered content covers the exact fullscreen size, regardless of the window size. So using _NET_WM_STATE_FULLSCREEN is a workaround for that. Implementing exclusive fullscreen seems possible. For example, [0001-dxgi-tests-Test-moving-the-window-for-a-fullscreen-swa.txt](/uploads/9e65af02f7ed1fed7c194dd99024ea02/0001-dxgi-tests-Test-moving-the-window-for-a-fullscreen-swa.txt) [0002-dxgi-Implement-exclusive-screen-mode-for-d3d11.txt](/uploads/b9ebb8e5cfe5f78b175ab3b8618cbd38/0002-dxgi-Implement-exclusive-screen-mode-for-d3d11.txt).
FWIW even without multiple monitors a window can be larger than screen on Windows (and at least without fullscreen mode it is not getting resized or de-factor shrinked to the fullscreen size). Some games (one I remember offhand which was doing that at least back then is Halo Infinite) do that, make a window with decorations bigger than screen and position in a way so client area matches full screen size.
It is probably not possible to match all the aspects of Windows behaviour for all the cases in the x11 interfacing limitations? IMO if we de facto have a choice between taskbar appearing on top (that is maybe correctable by user clicking the window?) and ending up with wrong window sizes maybe it is better to sacrifice the taskbar but keep sizes correct? And think or ask if it is possible to do something in KDE to support, maybe some property indicating fullscreen request without altering the size?
On Mon Aug 11 16:10:52 2025 +0000, Paul Gofman wrote:
FWIW even without multiple monitors a window can be larger than screen on Windows (and at least without fullscreen mode it is not getting resized or de-factor shrinked to the fullscreen size). Some games (one I remember offhand which was doing that at least back then is Halo Infinite) do that, make a window with decorations bigger than screen and position in a way so client area matches full screen size. It is probably not possible to match all the aspects of Windows behaviour for all the cases in the x11 interfacing limitations? IMO if we de facto have a choice between taskbar appearing on top (that is maybe correctable by user clicking the window?) and ending up with wrong window sizes maybe it is better to sacrifice the taskbar but keep sizes correct? And think or ask if it is possible to do something in KDE to support, maybe some property indicating fullscreen request without altering the size?
What if we decouple X11 window size from Win32 window size instead? X11 window would then show just the part of the Win32 window it covers. It would also help apps with custom decorations that expect a maximized window frame to stick out beyond the screen edges (e.g. apps using Codejock Toolkit Pro). I think Windows does something similar to prevent maximized window borders from showing up on adjacent monitors.