[PATCH v3 0/1] MR9999: winex11.drv: Fix window stacking order with insert_after parameter.
When calling the SetWindowPos function to change the window Z-order with an insert_after parameter as a real window handle, the expected result is that the insert_after window appears above the specified window hwnd. For example, calling SetWindowPos(g_hwndB, g_hwndA, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE) should result in window A appearing above window B. Below is a demo showing test results on Windows versus Wine: The initial z-order of WindowA and WindowB is: WindowB is above WindowA: {width=781 height=399} Next, click the "Place WindowA above WindowB" button on Windows and Wine respectively. The test results are as follows: On Windows, After clicking the button, WindowA is placed above WindowB: {width=801 height=439} \ On wine, After clicking the button, the Z-order of WindowA and WindowB remains unchanged: {width=900 height=444} demo: [demo.cpp](/uploads/6e3a44e8b223e54d28c2d65e66856d40/demo.cpp) Signed-off-by: Zhao Yi zhaoyi@uniontech.com -- v3: winex11.drv: Fix window stacking order with insert_after parameter. https://gitlab.winehq.org/wine/wine/-/merge_requests/9999
From: Zhao Yi <zhaoyi@uniontech.com> Signed-off-by: Zhao Yi <zhaoyi@uniontech.com> --- dlls/winex11.drv/window.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index a9f6dd08276..27717544613 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2122,19 +2122,20 @@ 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, const struct window_rects *old_rects ) +static HWND 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; + HWND prev = 0; - if (data->managed && ((style & WS_MINIMIZE) || data->desired_state.wm_state == IconicState)) return; + if (data->managed && ((style & WS_MINIMIZE) || data->desired_state.wm_state == IconicState)) return prev; if (!(swp_flags & SWP_NOZORDER) || (swp_flags & SWP_SHOWWINDOW)) { /* find window that this one must be after */ - HWND prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); + prev = NtUserGetWindowRelative( data->hwnd, GW_HWNDPREV ); while (prev && !(NtUserGetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE)) prev = NtUserGetWindowRelative( prev, GW_HWNDPREV ); if (!prev) above = TRUE; /* top child */ @@ -2154,6 +2155,7 @@ static void sync_window_position( struct x11drv_win_data *data, UINT swp_flags, window_rect.top - old_rects->window.top ); window_set_config( data, new_rect, above ); + return prev; } @@ -3244,6 +3246,18 @@ static BOOL get_desired_wm_state( DWORD style, const struct window_rects *rects return WithdrawnState; } +static void unset_transient_hint(Display *display, Window window) { + Atom wm_transient_for; + + if (!display || window == None) { + return ; + } + + wm_transient_for = XInternAtom( display, "WM_TRANSIENT_FOR", False ); + if (wm_transient_for != None) { + XDeleteProperty( display, window, wm_transient_for ); + } +} /*********************************************************************** * WindowPosChanged (X11DRV.@) @@ -3255,6 +3269,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN UINT ex_style = NtUserGetWindowLongW( hwnd, GWL_EXSTYLE ), new_style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); struct window_rects old_rects; BOOL is_managed, was_fullscreen, activate = !(swp_flags & SWP_NOACTIVATE), fullscreen = !!(swp_flags & WINE_SWP_FULLSCREEN); + HWND prev = 0; if ((is_managed = is_window_managed( hwnd, swp_flags, fullscreen ))) make_owner_managed( hwnd ); @@ -3297,7 +3312,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, &old_rects ); + prev = 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 ); @@ -3324,6 +3339,16 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN /* if window was fullscreen and is being hidden, release cursor clipping */ was_fullscreen &= data->desired_state.wm_state != NormalState; + if (prev && prev == insert_after) + { + Window prev_window = X11DRV_get_whole_window(prev); + if (prev_window) + { + XSetTransientForHint( data->display, prev_window, data->whole_window ); + unset_transient_hint( data->display, data->whole_window ); + } + } + XFlush( data->display ); /* make sure changes are done before we start painting again */ release_win_data( data ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9999
v2: Use the XSetTransientForHint function to establish the relationship between hwndA and hwndB. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9999#note_128752
Use the following demo to test based on Wine: demo: [demo-1.cpp](/uploads/574fcefc61ce0438aca28d9d0c35cfd6/demo-1.cpp) The initial **Z-order** state of **WindowA**, **WindowB**, and **WindowC** is as follows: {width=900 height=427} Next, click the button **\[Place WindowA above WindowB\]**, **WindowA** is placed above **WindowB**: {width=900 height=427} Next, click the button **\[Place WindowB above WindowA\]**, **WindowB** is placed above **WindowA**: {width=900 height=420} -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9999#note_128753
participants (2)
-
Zhao Yi -
Zhao Yi (@Zhaoyi)