[PATCH v2 0/1] MR10945: wineandroid: Update Z-order on window activation.
Handle pActivateWindow by forwarding the activation-related Z-order change through ioctl_window_pos_changed(). This fixes activated windows not being visually raised to the front when clicked. <details> <summary>video demonstrating the issue</summary> {width=866 height=600} </details> -- v2: wineandroid: Update Z-order on window activation. https://gitlab.winehq.org/wine/wine/-/merge_requests/10945
From: Twaik Yont <9674930+twaik@users.noreply.github.com> Handle pActivateWindow by forwarding the activation-related Z-order change through ioctl_window_pos_changed(). This fixes activated windows not being visually raised to the front when clicked. Signed-off-by: Twaik Yont <9674930+twaik@users.noreply.github.com> --- dlls/wineandroid.drv/android.h | 1 + dlls/wineandroid.drv/init.c | 1 + dlls/wineandroid.drv/window.c | 36 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h index c79ce31a046..b6246dac213 100644 --- a/dlls/wineandroid.drv/android.h +++ b/dlls/wineandroid.drv/android.h @@ -110,6 +110,7 @@ extern void ANDROID_SetDesktopWindow( HWND hwnd ); extern void ANDROID_DestroyWindow( HWND hwnd ); extern BOOL ANDROID_ProcessEvents( DWORD mask ); extern LRESULT ANDROID_DesktopWindowProc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ); +extern void ANDROID_ActivateWindow( HWND hwnd, HWND previous ); extern void ANDROID_SetParent( HWND hwnd, HWND parent, HWND old_parent ); extern void ANDROID_SetCapture( HWND hwnd, UINT flags ); extern UINT ANDROID_ShowWindow( HWND hwnd, INT cmd, RECT *rect, UINT swp ); diff --git a/dlls/wineandroid.drv/init.c b/dlls/wineandroid.drv/init.c index b5732a12bc1..0742af2e0b0 100644 --- a/dlls/wineandroid.drv/init.c +++ b/dlls/wineandroid.drv/init.c @@ -309,6 +309,7 @@ static const struct user_driver_funcs android_drv_funcs = .pCreateWindow = ANDROID_CreateWindow, .pSetDesktopWindow = ANDROID_SetDesktopWindow, .pDesktopWindowProc = ANDROID_DesktopWindowProc, + .pActivateWindow = ANDROID_ActivateWindow, .pDestroyWindow = ANDROID_DestroyWindow, .pProcessEvents = ANDROID_ProcessEvents, .pSetCapture = ANDROID_SetCapture, diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c index b3f3aeea955..f1ccf99050c 100644 --- a/dlls/wineandroid.drv/window.c +++ b/dlls/wineandroid.drv/window.c @@ -1076,6 +1076,42 @@ void ANDROID_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UI } +/*********************************************************************** + * ANDROID_ActivateWindow + */ +void ANDROID_ActivateWindow( HWND hwnd, HWND previous ) +{ + struct android_win_data *data; + struct window_rects rects; + UINT style, flags; + HWND owner = 0, insert_after; + + if (!hwnd) return; + if (!(data = get_win_data( hwnd ))) return; + + rects = data->rects; + if (!data->parent) owner = NtUserGetWindowRelative( hwnd, GW_OWNER ); + release_win_data( data ); + + style = NtUserGetWindowLongW( hwnd, GWL_STYLE ); + if (!(style & WS_VISIBLE) || (style & WS_CHILD)) return; + + /* A dialog can become active while it is still hidden and before it becomes + * the foreground window. In that path win32u sends WM_NCACTIVATE(FALSE), + * then later calls the driver with hwnd == previous when foreground catches + * up, so the positive non-client activation would otherwise be skipped. + */ + if (hwnd == previous) + NtUserMessageCall( hwnd, WM_NCACTIVATE, TRUE, (LPARAM)previous, 0, NtUserSendMessage, FALSE ); + + insert_after = 0; + flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOCLIENTMOVE | SWP_NOCLIENTSIZE | SWP_NOACTIVATE; + + TRACE( "win %p previous %p owner %p after %p flags %08x\n", hwnd, previous, owner, insert_after, flags ); + ioctl_window_pos_changed( hwnd, &rects, style, flags, insert_after, owner ); +} + + /*********************************************************************** * ANDROID_ShowWindow */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10945
Hi @julliard, any chance you could take a look when you have time? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142162
WM_NCACTIVATE is handled in win32u. If it's not correct that should be fixed there. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142172
In winex11, WM_NCACTIVATE is sent by win32u in response to NtUserSetForegroundWindowInternal called from X11DRV_FocusIn. The X11 WM is an independent source of focus events that can fire before Win32 activation state catches up, so set_active_window sees previous_active != dialog and sends WM_NCACTIVATE(TRUE) through the normal path. On Android there is no window manager — Wine is the sole source of truth for focus state. When set_active_window skips WM_NCACTIVATE via goto done (line 2016, previous == hwnd), there is no external event to re-trigger it. The driver must send WM_NCACTIVATE(TRUE) directly. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142210
X11 in desktop mode doesn't have a window manager. The null driver doesn't have a window manager. In all cases the message sequence needs to be correct, so this cannot be left to the driver. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142212
I'm not sure this approach is correct. On all other platforms — X11, Wayland, macOS — the platform's windowing system is the source of truth for focus, and WM_NCACTIVATE(TRUE) is ultimately triggered by a focus event from that platform. Android does have platform-level focus management, but since we don't use all platform primitives and I plan to replace the View-based windowing with custom GL rendering to avoid unnecessary overhead, relying on it here would not be appropriate. For now the issue only manifests in wineandroid which isn't officially supported. Would it be worth changing the win32u behaviour in a way that could affect other platforms, given that the current code works there? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142217
Yes, it should be fixed at the right place instead of adding workarounds in the driver. That said, I'm not sure what the problem is. You are saying that set_activate_window doesn't send it if the window is already activated, which seems to me to be the correct behavior. What is actually the issue that you are trying to fix? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142222
The issue is that a dialog can receive WM_NCACTIVATE(FALSE) when it becomes the active window while not yet foreground, and then never receive WM_NCACTIVATE(TRUE) when it becomes foreground — because set_active_window exits early since the window is already activated. The result is an inactive-looking title bar on what is actually the foreground window. On all other platforms this is corrected by NtUserSetForegroundWindowInternal being called in response to a focus event from the platform's windowing system (FocusIn in X11, equivalents in Wayland and macOS). For wineandroid a focus event handler in WineView would be the right approach, but I plan to replace View-based windowing with a custom GL renderer, so I'd rather not invest in that direction. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142224
Ok, I created the !11074. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10945#note_142293
participants (3)
-
Alexandre Julliard (@julliard) -
Twaik Yont -
Twaik Yont (@twaik)