When the window manager moves windows offscreen, the Win32 config is explicitly kept out of sync. We need to handle that case when the Win32 config changes, to avoid forcefully moving the windows back on screen.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=57652
Since 895ca2eda64f506b936999cdd8f14e224eef6a7f the window position might be out of sync between Win32 and X if the X window manager has moved it offscreen.
Then b936f7426e33508538f48f7d20e7d7cb47cc1f32 ignored size and position changes, trying to avoid triggering requests that would force the X window position to match the Win32 state.
However, the X visible rect might change on window style adjustments, and not from a application-requested size change, and we need to request these changes to the X server.
The SWP flags should be handled in win32u already and the window rect position and size should not have changed if SWP_NOSIZE | SWP_NOMOVE were set by the application.
What we need to do is to mind that the X window position might be out of sync, and adjust the request to take the eventual offscreen position into account.
From: Rémi Bernon rbernon@codeweavers.com
When the window manager moves windows offscreen, the Win32 config is explicitly kept out of sync. We need to handle that case when the Win32 config changes, to avoid forcefully moving the windows back on screen.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=57652 --- dlls/winex11.drv/window.c | 22 +++++++++------------- include/wine/gdi_driver.h | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 9c92f9e8a7f..194e41e674e 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1773,12 +1773,12 @@ void make_window_embedded( struct x11drv_win_data *data ) * * Synchronize the X window position with the Windows one */ -static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags ) +static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags, const struct window_rects *old_rects ) { DWORD style = NtUserGetWindowLongW( data->hwnd, GWL_STYLE ); DWORD ex_style = NtUserGetWindowLongW( data->hwnd, GWL_EXSTYLE ); + RECT new_rect, window_rect; BOOL above = FALSE; - RECT new_rect;
if (data->managed && data->desired_state.wm_state == IconicState) return;
@@ -1798,16 +1798,12 @@ static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags ) update_net_wm_states( data );
new_rect = data->rects.visible; - if (swp_flags & SWP_NOSIZE) - { - new_rect.right = new_rect.left + data->desired_state.rect.right - data->desired_state.rect.left; - new_rect.bottom = new_rect.top + data->desired_state.rect.bottom - data->desired_state.rect.top; - } - if (swp_flags & SWP_NOMOVE) - { - OffsetRect( &new_rect, data->desired_state.rect.left - new_rect.left, - data->desired_state.rect.top - new_rect.top ); - } + + /* if the window has been moved offscreen by the window manager, we didn't tell the Win32 side about it */ + window_rect = window_rect_from_visible( old_rects, data->desired_state.rect ); + if (!is_window_rect_mapped( &window_rect )) OffsetRect( &new_rect, window_rect.left - old_rects->window.left, + window_rect.top - old_rects->window.top ); + window_set_config( data, &new_rect, above ); }
@@ -2985,7 +2981,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN /* don't change position if we are about to minimize or maximize a managed window */ if (!(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE)))) { - sync_window_position( data, swp_flags ); + sync_window_position( data, swp_flags, &old_rects ); #ifdef HAVE_LIBXSHAPE if (IsRectEmpty( &old_rects.window ) != IsRectEmpty( &new_rects->window )) sync_empty_window_shape( data, surface ); diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 38ce9b81f14..51a095fd2b0 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -59,7 +59,7 @@ static inline const char *debugstr_window_rects( const struct window_rects *rect }
/* convert a visible rect to the corresponding window rect, using the window_rects offsets */ -static inline RECT window_rect_from_visible( struct window_rects *rects, RECT visible_rect ) +static inline RECT window_rect_from_visible( const struct window_rects *rects, RECT visible_rect ) { RECT rect = visible_rect;
This does fix it as well, yes. And it also fixes another long-standing bug with fullscreen apps that have either a menu bar or multiple windows. Thanks!
I'm just curious, but why is it fine/better to ignore swp_flags? (I mean it clearly is, but I don't know why)
This merge request was approved by Gabriel Ivăncescu.
On Mon Jan 13 13:31:11 2025 +0000, Gabriel Ivăncescu wrote:
This does fix it as well, yes. And it also fixes another long-standing bug with fullscreen apps that have either a menu bar or multiple windows. Thanks! Looks good to me. I'm just curious, but why is it fine/better to ignore swp_flags? (I mean it clearly is, but I don't know why)
I believe the flags should already be handled in win32u (for instance see `calc_winpos`), and the win32u window rects should be updated according to them before calling into the driver. Whatever changes is seen to the window/visible rects in the driver, is coming from some internal implementation details (decoration changes, DPI scaling, etc) and should be requested.