This implements better active window synchronization between Wine and X11 by requesting _NET_ACTIVE_WINDOW change whenever the Win32 active window changes, using source = 2 (pager) which is more often obeyed than other source values by the window managers.
This also implements SWP_NOACTIVATE properly by setting _NET_WM_USER_TIME time to 0 before mapping a window, which has the correct semantics, while later sending _NET_ACTIVE_WINDOW to activate it if it ends up being necessary to match the Win32 logic.
-- v2: winex11: Use _NET_ACTIVE_WINDOW to request/track window activation. win32u: Call ActivateWindow callback when foreground window changes. win32u: Return foreground window changes from GetWindowStateUpdates. winex11: Refactor X11DRV_GetWindowStateUpdates control flow. winex11: Keep track of whether mapped window needs activation. winex11: Only set focus_time_prop for managed windows.
From: Rémi Bernon rbernon@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58038 --- dlls/winex11.drv/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 51b64e8e83c..fe39cf510f8 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1433,7 +1433,7 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) break; }
- if (new_state == NormalState) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)-1 ); + if (new_state == NormalState && data->managed) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)-1 );
data->pending_state.wm_state = new_state; data->wm_state_serial = NextRequest( data->display );
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 45 +++++++++++++++++++++------------------ dlls/winex11.drv/x11drv.h | 1 + 2 files changed, 25 insertions(+), 21 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index fe39cf510f8..b518a4de4cc 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1393,11 +1393,12 @@ static void set_xembed_flags( struct x11drv_win_data *data, unsigned long flags x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, (unsigned char*)info, 2 ); }
-static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) +static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, BOOL activate ) { UINT old_state = data->pending_state.wm_state;
data->desired_state.wm_state = new_state; + data->desired_state.activate = activate; if (!data->whole_window) return; /* no window, nothing to update */ if (data->wm_state_serial && !data->current_state.wm_state != !data->pending_state.wm_state) return; /* another map/unmap WM_STATE update is pending, wait for it to complete */ @@ -1412,8 +1413,8 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) if (data->managed && MAKELONG(old_state, new_state) == MAKELONG(IconicState, NormalState)) { WARN( "window %p/%lx is iconic, remapping to workaround Mutter issues.\n", data->hwnd, data->whole_window ); - window_set_wm_state( data, WithdrawnState ); - window_set_wm_state( data, NormalState ); + window_set_wm_state( data, WithdrawnState, FALSE ); + window_set_wm_state( data, NormalState, activate ); return; }
@@ -1436,9 +1437,10 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) if (new_state == NormalState && data->managed) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)-1 );
data->pending_state.wm_state = new_state; + data->pending_state.activate = activate; data->wm_state_serial = NextRequest( data->display ); - TRACE( "window %p/%lx, requesting WM_STATE %#x -> %#x serial %lu, foreground %p\n", data->hwnd, data->whole_window, - old_state, new_state, data->wm_state_serial, NtUserGetForegroundWindow() ); + TRACE( "window %p/%lx, requesting WM_STATE %#x -> %#x serial %lu, foreground %p, activate %u\n", data->hwnd, data->whole_window, + old_state, new_state, data->wm_state_serial, NtUserGetForegroundWindow(), activate );
switch (MAKELONG(old_state, new_state)) { @@ -1468,7 +1470,7 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) static void window_set_managed( struct x11drv_win_data *data, BOOL new_managed ) { XSetWindowAttributes attr = {.override_redirect = !new_managed}; - UINT wm_state = data->desired_state.wm_state; + UINT wm_state = data->desired_state.wm_state, activate = data->desired_state.activate; BOOL old_managed = data->managed;
if (!data->whole_window) return; /* no window, nothing to update */ @@ -1479,21 +1481,21 @@ static void window_set_managed( struct x11drv_win_data *data, BOOL new_managed ) return; }
- window_set_wm_state( data, WithdrawnState ); /* no WM_STATE is pending, requested immediately */ + window_set_wm_state( data, WithdrawnState, FALSE ); /* no WM_STATE is pending, requested immediately */
data->managed = new_managed; TRACE( "window %p/%lx, requesting override-redirect %u -> %u serial %lu\n", data->hwnd, data->whole_window, !old_managed, !new_managed, NextRequest( data->display ) ); XChangeWindowAttributes( data->display, data->whole_window, CWOverrideRedirect, &attr );
- window_set_wm_state( data, wm_state ); /* queue another WM_STATE request with the desired state */ + window_set_wm_state( data, wm_state, activate ); /* queue another WM_STATE request with the desired state */ }
/*********************************************************************** * map_window */ -static void map_window( HWND hwnd, DWORD new_style ) +static void map_window( HWND hwnd, DWORD new_style, BOOL activate ) { struct x11drv_win_data *data;
@@ -1501,7 +1503,7 @@ static void map_window( HWND hwnd, DWORD new_style )
if (!(data = get_win_data( hwnd ))) return; TRACE( "win %p/%lx\n", data->hwnd, data->whole_window ); - window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState ); + window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, activate ); release_win_data( data ); }
@@ -1661,9 +1663,10 @@ void window_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, if (!handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, current, expected, prefix, received, reason )) return; + data->current_state.activate = data->pending_state.activate;
/* send any pending changes from the desired state */ - window_set_wm_state( data, data->desired_state.wm_state ); + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); window_set_config( data, &data->desired_state.rect, FALSE );
@@ -1686,7 +1689,7 @@ void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long ser return;
/* send any pending changes from the desired state */ - window_set_wm_state( data, data->desired_state.wm_state ); + window_set_wm_state( data, data->desired_state.wm_state, data->desired_state.activate ); window_set_net_wm_state( data, data->desired_state.net_wm_state ); window_set_config( data, &data->desired_state.rect, FALSE ); } @@ -1774,11 +1777,11 @@ BOOL window_should_take_focus( HWND hwnd, Time time ) void make_window_embedded( struct x11drv_win_data *data ) { /* the window cannot be mapped before being embedded */ - window_set_wm_state( data, WithdrawnState ); + window_set_wm_state( data, WithdrawnState, FALSE ); if (data->managed) WARN( "Window is already managed, should wait for WithdrawnState\n" ); else window_set_managed( data, TRUE ); data->embedded = TRUE; - window_set_wm_state( data, NormalState ); + window_set_wm_state( data, NormalState, FALSE ); }
@@ -2587,7 +2590,7 @@ BOOL X11DRV_SystrayDockRemove( HWND hwnd )
if ((data = get_win_data( hwnd ))) { - if ((ret = data->embedded)) window_set_wm_state( data, WithdrawnState ); + if ((ret = data->embedded)) window_set_wm_state( data, WithdrawnState, FALSE ); release_win_data( data ); }
@@ -2932,7 +2935,7 @@ 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; - BOOL was_fullscreen; + BOOL was_fullscreen, activate = !(swp_flags & SWP_NOACTIVATE);
sync_gl_drawable( hwnd, FALSE );
@@ -2967,7 +2970,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN if (((swp_flags & SWP_HIDEWINDOW) && !(new_style & WS_VISIBLE)) || (!(new_style & WS_MINIMIZE) && !is_window_rect_mapped( &new_rects->window ) && is_window_rect_mapped( &old_rects.window ))) { - window_set_wm_state( data, WithdrawnState ); + window_set_wm_state( data, WithdrawnState, FALSE ); release_win_data( data ); if (was_fullscreen) NtUserClipCursor( NULL ); if (!(data = get_win_data( hwnd ))) return; @@ -3007,12 +3010,12 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN needs_map = data->layered || IsRectEmpty( &new_rects->window ); release_win_data( data ); if (needs_icon) fetch_icon_data( hwnd, 0, 0 ); - if (needs_map) map_window( hwnd, new_style ); + if (needs_map) map_window( hwnd, new_style, activate ); return; } else if ((swp_flags & SWP_STATECHANGED) && ((old_style ^ new_style) & WS_MINIMIZE)) { - window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState ); + window_set_wm_state( data, (new_style & WS_MINIMIZE) ? IconicState : NormalState, activate ); update_net_wm_states( data ); } else @@ -3160,7 +3163,7 @@ void X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWO ((style & WS_MINIMIZE) || is_window_rect_mapped( &data->rects.window ))) { release_win_data( data ); - map_window( hwnd, style ); + map_window( hwnd, style, TRUE ); return; } } @@ -3201,7 +3204,7 @@ void X11DRV_UpdateLayeredWindow( HWND hwnd, BYTE alpha, UINT flags ) DWORD style = NtUserGetWindowLongW( hwnd, GWL_STYLE );
if ((style & WS_VISIBLE) && ((style & WS_MINIMIZE) || is_window_rect_mapped( &data->rects.window ))) - map_window( hwnd, style ); + map_window( hwnd, style, TRUE ); } }
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 75dd3c1711a..43c60ab78fd 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -621,6 +621,7 @@ enum x11drv_net_wm_state struct window_state { UINT wm_state; + BOOL activate; UINT net_wm_state; RECT rect; };
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/winex11.drv/window.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index b518a4de4cc..3ea6bb523f4 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1606,16 +1606,20 @@ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, { struct x11drv_win_data *data;
- if (!(data = get_win_data( hwnd ))) return FALSE; - - *state_cmd = window_update_client_state( data ); - *config_cmd = window_update_client_config( data ); - *rect = window_rect_from_visible( &data->rects, data->current_state.rect ); + *state_cmd = *config_cmd = 0;
- release_win_data( data ); + if ((data = get_win_data( hwnd ))) + { + *state_cmd = window_update_client_state( data ); + *config_cmd = window_update_client_config( data ); + *rect = window_rect_from_visible( &data->rects, data->current_state.rect ); + release_win_data( data ); + }
- TRACE( "hwnd %p, returning state_cmd %#x, config_cmd %#x, rect %s\n", hwnd, *state_cmd, *config_cmd, wine_dbgstr_rect(rect) ); - return *state_cmd || *config_cmd; + if (!*state_cmd && !*config_cmd) return FALSE; + TRACE( "hwnd %p, returning state_cmd %#x, config_cmd %#x, rect %s\n", + hwnd, *state_cmd, *config_cmd, wine_dbgstr_rect(rect) ); + return TRUE; }
static BOOL handle_state_change( unsigned long serial, unsigned long *expect_serial, UINT size, const void *value,
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/driver.c | 2 +- dlls/win32u/message.c | 7 +++++-- dlls/winex11.drv/window.c | 16 ++++++++++------ dlls/winex11.drv/x11drv.h | 2 +- include/wine/gdi_driver.h | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 5edac6a0c8a..118fe47357e 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -881,7 +881,7 @@ static BOOL nulldrv_GetWindowStyleMasks( HWND hwnd, UINT style, UINT ex_style, U return FALSE; }
-static BOOL nulldrv_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect ) +static BOOL nulldrv_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect, HWND *foreground ) { return FALSE; } diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 5a5c26d9947..8047e01cb61 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -2177,15 +2177,18 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR { UINT state_cmd, config_cmd; RECT window_rect; + HWND foreground;
- if (!user_driver->pGetWindowStateUpdates( hwnd, &state_cmd, &config_cmd, &window_rect )) return 0; + if (!user_driver->pGetWindowStateUpdates( hwnd, &state_cmd, &config_cmd, &window_rect, &foreground )) return 0; + if (foreground) NtUserSetForegroundWindow( foreground ); if (state_cmd) { if (LOWORD(state_cmd) == SC_RESTORE && HIWORD(state_cmd)) NtUserSetActiveWindow( hwnd ); send_message( hwnd, WM_SYSCOMMAND, LOWORD(state_cmd), 0 );
/* state change might have changed the window config already, check again */ - user_driver->pGetWindowStateUpdates( hwnd, &state_cmd, &config_cmd, &window_rect ); + user_driver->pGetWindowStateUpdates( hwnd, &state_cmd, &config_cmd, &window_rect, &foreground ); + if (foreground) NtUserSetForegroundWindow( foreground ); if (state_cmd) WARN( "window %p state needs another update, ignoring\n", hwnd ); } if (config_cmd) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 3ea6bb523f4..d4222161ba4 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1602,11 +1602,12 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) /*********************************************************************** * GetWindowStateUpdates (X11DRV.@) */ -BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect ) +BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect, HWND *foreground ) { struct x11drv_win_data *data;
*state_cmd = *config_cmd = 0; + *foreground = 0;
if ((data = get_win_data( hwnd ))) { @@ -1616,9 +1617,9 @@ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, release_win_data( data ); }
- if (!*state_cmd && !*config_cmd) return FALSE; - TRACE( "hwnd %p, returning state_cmd %#x, config_cmd %#x, rect %s\n", - hwnd, *state_cmd, *config_cmd, wine_dbgstr_rect(rect) ); + if (!*state_cmd && !*config_cmd && !*foreground) return FALSE; + TRACE( "hwnd %p, returning state_cmd %#x, config_cmd %#x, rect %s, foreground %p\n", + hwnd, *state_cmd, *config_cmd, wine_dbgstr_rect(rect), *foreground ); return TRUE; }
@@ -1725,8 +1726,11 @@ void net_active_window_notify( unsigned long serial, Window value, Time time )
received = wine_dbg_sprintf( "_NET_ACTIVE_WINDOW %p/%lx serial %lu time %lu", current_hwnd, value, serial, time ); expected = *expect_serial ? wine_dbg_sprintf( ", expected %p/%lx serial %lu", pending_hwnd, *pending, *expect_serial ) : ""; - handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, - current, expected, "", received, NULL ); + if (!handle_state_change( serial, expect_serial, sizeof(value), &value, desired, pending, + current, expected, "", received, NULL )) + return; + + NtUserPostMessage( NtUserGetForegroundWindow(), WM_WINE_WINDOW_STATE_CHANGED, 0, 0 ); }
Window get_net_active_window( Display *display ) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 43c60ab78fd..56842c1b94c 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -243,7 +243,7 @@ extern void X11DRV_UpdateLayeredWindow( HWND hwnd, BYTE alpha, UINT flags ); extern LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ); extern BOOL X11DRV_WindowPosChanging( HWND hwnd, UINT swp_flags, BOOL shaped, const struct window_rects *rects ); extern BOOL X11DRV_GetWindowStyleMasks( HWND hwnd, UINT style, UINT ex_style, UINT *style_mask, UINT *ex_style_mask ); -extern BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect ); +extern BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect, HWND *foreground ); extern BOOL X11DRV_CreateWindowSurface( HWND hwnd, BOOL layered, const RECT *surface_rect, struct window_surface **surface ); extern void X11DRV_MoveWindowBits( HWND hwnd, const struct window_rects *old_rects, const struct window_rects *new_rects, const RECT *valid_rects ); diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index efee8d1af4d..226ff0107fe 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -385,7 +385,7 @@ struct user_driver_funcs LRESULT (*pWindowMessage)(HWND,UINT,WPARAM,LPARAM); BOOL (*pWindowPosChanging)(HWND,UINT,BOOL,const struct window_rects *); BOOL (*pGetWindowStyleMasks)(HWND,UINT,UINT,UINT*,UINT*); - BOOL (*pGetWindowStateUpdates)(HWND,UINT*,UINT*,RECT*); + BOOL (*pGetWindowStateUpdates)(HWND,UINT*,UINT*,RECT*,HWND*); BOOL (*pCreateWindowSurface)(HWND,BOOL,const RECT *,struct window_surface**); void (*pMoveWindowBits)(HWND,const struct window_rects *,const struct window_rects *,const RECT *); void (*pWindowPosChanged)(HWND,HWND,HWND,UINT,BOOL,const struct window_rects*,struct window_surface*);
From: Rémi Bernon rbernon@codeweavers.com
Repurposing the SetFocus callback. --- dlls/win32u/driver.c | 6 +++--- dlls/win32u/input.c | 8 +++++--- dlls/winemac.drv/gdi.c | 2 +- dlls/winemac.drv/macdrv.h | 2 +- dlls/winemac.drv/window.c | 6 +++--- dlls/winex11.drv/event.c | 17 +++-------------- dlls/winex11.drv/init.c | 2 +- dlls/winex11.drv/x11drv.h | 2 +- include/wine/gdi_driver.h | 2 +- 9 files changed, 19 insertions(+), 28 deletions(-)
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 118fe47357e..fcf2881d8fa 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -824,7 +824,7 @@ static void nulldrv_SetDesktopWindow( HWND hwnd ) { }
-static void nulldrv_SetFocus( HWND hwnd ) +static void nulldrv_ActivateWindow( HWND hwnd, HWND previous ) { }
@@ -1280,7 +1280,7 @@ static const struct user_driver_funcs lazy_load_driver = nulldrv_ScrollDC, nulldrv_SetCapture, loaderdrv_SetDesktopWindow, - nulldrv_SetFocus, + nulldrv_ActivateWindow, loaderdrv_SetLayeredWindowAttributes, nulldrv_SetParent, loaderdrv_SetWindowRgn, @@ -1377,7 +1377,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(ScrollDC); SET_USER_FUNC(SetCapture); SET_USER_FUNC(SetDesktopWindow); - SET_USER_FUNC(SetFocus); + SET_USER_FUNC(ActivateWindow); SET_USER_FUNC(SetLayeredWindowAttributes); SET_USER_FUNC(SetParent); SET_USER_FUNC(SetWindowRgn); diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 008a8b4943d..a4834b740d8 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1947,8 +1947,6 @@ static HWND set_focus_window( HWND hwnd ) } if (is_window(hwnd)) { - user_driver->pSetFocus(hwnd); - ime_hwnd = get_default_ime_window( hwnd ); if (ime_hwnd) send_message( ime_hwnd, WM_IME_INTERNAL, IME_INTERNAL_ACTIVATE, @@ -2067,7 +2065,11 @@ BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new }
done: - if (hwnd) clip_fullscreen_window( hwnd, FALSE ); + if (hwnd) + { + if (hwnd == NtUserGetForegroundWindow()) user_driver->pActivateWindow( hwnd, previous ); + clip_fullscreen_window( hwnd, FALSE ); + } return TRUE; }
diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index 4921fc2f170..d58e0fd280f 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -286,7 +286,7 @@ static const struct user_driver_funcs macdrv_funcs = .pSetCursor = macdrv_SetCursor, .pSetCursorPos = macdrv_SetCursorPos, .pSetDesktopWindow = macdrv_SetDesktopWindow, - .pSetFocus = macdrv_SetFocus, + .pActivateWindow = macdrv_ActivateWindow, .pSetLayeredWindowAttributes = macdrv_SetLayeredWindowAttributes, .pSetParent = macdrv_SetParent, .pSetWindowRgn = macdrv_SetWindowRgn, diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index 4673d69654a..ba510723e26 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -136,7 +136,7 @@ static inline RECT rect_from_cgrect(CGRect cgrect) extern LRESULT macdrv_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); extern void macdrv_DestroyWindow(HWND hwnd); extern void macdrv_SetDesktopWindow(HWND hwnd); -extern void macdrv_SetFocus(HWND hwnd); +extern void macdrv_ActivateWindow(HWND hwnd, HWND previous); extern void macdrv_SetLayeredWindowAttributes(HWND hwnd, COLORREF key, BYTE alpha, DWORD flags); extern void macdrv_SetParent(HWND hwnd, HWND parent, HWND old_parent); diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 272cdd67912..ca3b0a24050 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -1388,11 +1388,11 @@ void macdrv_DestroyWindow(HWND hwnd)
/***************************************************************** - * SetFocus (MACDRV.@) + * ActivateWindow (MACDRV.@) * - * Set the Mac focus. + * Set the Mac active window. */ -void macdrv_SetFocus(HWND hwnd) +void macdrv_ActivateWindow(HWND hwnd, HWND previous) { struct macdrv_thread_data *thread_data = macdrv_thread_data();
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index a9e9f25b422..7c6bd39884e 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -1297,25 +1297,14 @@ static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev )
/***************************************************************** - * SetFocus (X11DRV.@) + * ActivateWindow (X11DRV.@) * * Set the X focus. */ -void X11DRV_SetFocus( HWND hwnd ) +void X11DRV_ActivateWindow( HWND hwnd, HWND previous ) { struct x11drv_win_data *data; - - HWND parent; - - for (;;) - { - if (!(data = get_win_data( hwnd ))) return; - if (data->embedded) break; - parent = NtUserGetAncestor( hwnd, GA_PARENT ); - if (!parent || parent == NtUserGetDesktopWindow()) break; - release_win_data( data ); - hwnd = parent; - } + if (!(data = get_win_data( hwnd ))) return; if (!data->managed || data->embedder) set_input_focus( data ); release_win_data( data ); } diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index 70da9b8c094..ccafc83a6f2 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -446,7 +446,7 @@ static const struct user_driver_funcs x11drv_funcs = .pScrollDC = X11DRV_ScrollDC, .pSetCapture = X11DRV_SetCapture, .pSetDesktopWindow = X11DRV_SetDesktopWindow, - .pSetFocus = X11DRV_SetFocus, + .pActivateWindow = X11DRV_ActivateWindow, .pSetLayeredWindowAttributes = X11DRV_SetLayeredWindowAttributes, .pSetParent = X11DRV_SetParent, .pSetWindowIcon = X11DRV_SetWindowIcon, diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 56842c1b94c..1fc3dc02611 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -714,7 +714,7 @@ extern XContext winContext; extern XContext cursor_context;
extern BOOL is_current_process_focused(void); -extern void X11DRV_SetFocus( HWND hwnd ); +extern void X11DRV_ActivateWindow( HWND hwnd, HWND previous ); extern void set_window_cursor( Window window, HCURSOR handle ); extern void reapply_cursor_clipping(void); extern void ungrab_clipping_window(void); diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 226ff0107fe..ffb16ba1c3c 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -372,7 +372,7 @@ struct user_driver_funcs BOOL (*pScrollDC)(HDC,INT,INT,HRGN); void (*pSetCapture)(HWND,UINT); void (*pSetDesktopWindow)(HWND); - void (*pSetFocus)(HWND); + void (*pActivateWindow)(HWND,HWND); void (*pSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD); void (*pSetParent)(HWND,HWND,HWND); void (*pSetWindowRgn)(HWND,HRGN,BOOL);
From: Rémi Bernon rbernon@codeweavers.com
When supported, instead of tracking window focus only. --- dlls/user32/tests/win.c | 2 +- dlls/winex11.drv/event.c | 32 ++++++++------ dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/mouse.c | 2 +- dlls/winex11.drv/window.c | 86 ++++++++++++++++++++++++++++++++----- dlls/winex11.drv/x11drv.h | 4 +- 6 files changed, 102 insertions(+), 26 deletions(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 8da4e11b635..b136c6322f5 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -3315,7 +3315,7 @@ static void test_SetWindowPos(HWND hwnd, HWND hwnd2) ret = SetWindowPos(hwnd_child, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_SHOWWINDOW); ok(ret, "Got %d\n", ret); flush_events( TRUE ); - flaky todo_wine check_active_state(hwnd2, hwnd2, hwnd2); + flaky check_active_state(hwnd2, hwnd2, hwnd2); DestroyWindow(hwnd_child); }
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 7c6bd39884e..f9aa4b3f66b 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -650,23 +650,25 @@ static void set_input_focus( struct x11drv_win_data *data ) /********************************************************************** * set_focus */ -static void set_focus( Display *display, HWND hwnd, Time time ) +static void set_focus( Display *display, HWND focus, Time time ) { - HWND focus; Window win; GUITHREADINFO threadinfo;
- TRACE( "setting foreground window to %p\n", hwnd ); - NtUserSetForegroundWindow( hwnd ); + TRACE( "setting foreground window to %p\n", focus );
- threadinfo.cbSize = sizeof(threadinfo); - NtUserGetGUIThreadInfo( 0, &threadinfo ); - focus = threadinfo.hwndFocus; - if (!focus) focus = threadinfo.hwndActive; - if (focus) focus = NtUserGetAncestor( focus, GA_ROOT ); - win = X11DRV_get_whole_window(focus); + if (!is_net_supported( x11drv_atom(_NET_ACTIVE_WINDOW) )) + { + NtUserSetForegroundWindow( focus ); + + threadinfo.cbSize = sizeof(threadinfo); + NtUserGetGUIThreadInfo( 0, &threadinfo ); + focus = threadinfo.hwndFocus; + if (!focus) focus = threadinfo.hwndActive; + if (focus) focus = NtUserGetAncestor( focus, GA_ROOT ); + }
- if (win) + if ((win = X11DRV_get_whole_window( focus ))) { TRACE( "setting focus to %p (%lx) time=%ld\n", focus, win, time ); XSetInputFocus( display, win, RevertToParent, time ); @@ -909,7 +911,7 @@ static void focus_out( Display *display , HWND hwnd ) /* don't reset the foreground window, if the window which is getting the focus is a Wine window */
- if (!is_current_process_focused()) + if (!is_net_supported( x11drv_atom(_NET_ACTIVE_WINDOW) ) && !is_current_process_focused()) { /* Abey : 6-Oct-99. Check again if the focus out window is the Foreground window, because in most cases the messages sent @@ -1222,12 +1224,15 @@ static void handle_wm_state_notify( HWND hwnd, XPropertyEvent *event ) { struct x11drv_win_data *data; UINT value = 0; + BOOL activate;
if (!(data = get_win_data( hwnd ))) return; if (event->state == PropertyNewValue) value = get_window_wm_state( event->display, event->window ); window_wm_state_notify( data, event->serial, value, event->time ); + activate = value == NormalState && !data->wm_state_serial && data->current_state.activate; release_win_data( data );
+ if (hwnd == NtUserGetForegroundWindow() && activate) set_net_active_window( hwnd, 0 ); NtUserPostMessage( hwnd, WM_WINE_WINDOW_STATE_CHANGED, 0, 0 ); }
@@ -1304,6 +1309,9 @@ static BOOL X11DRV_PropertyNotify( HWND hwnd, XEvent *xev ) void X11DRV_ActivateWindow( HWND hwnd, HWND previous ) { struct x11drv_win_data *data; + + set_net_active_window( hwnd, previous ); + if (!(data = get_win_data( hwnd ))) return; if (!data->managed || data->embedder) set_input_focus( data ); release_win_data( data ); diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 8a5ef62f57d..091c507bdc9 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1343,7 +1343,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
if (event->type == KeyPress && (data = get_win_data( hwnd ))) { - window_set_user_time( data, event->time ); + window_set_user_time( data, event->time, FALSE ); release_win_data( data ); }
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index b4693c1fb82..4a74c18672a 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -1550,7 +1550,7 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
if ((data = get_win_data( hwnd ))) { - window_set_user_time( data, event->time ); + window_set_user_time( data, event->time, FALSE ); release_win_data( data ); }
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index d4222161ba4..a733d566d4f 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -266,7 +266,7 @@ static void remove_startup_notification( struct x11drv_win_data *data ) return;
if (!(id = getenv( "DESKTOP_STARTUP_ID" )) || !id[0]) return; - if ((src = strstr( id, "_TIME" ))) window_set_user_time( data, atol( src + 5 ) ); + if ((src = strstr( id, "_TIME" ))) window_set_user_time( data, atol( src + 5 ), FALSE );
pos = snprintf(message, sizeof(message), "remove: ID="); message[pos++] = '"'; @@ -1114,15 +1114,20 @@ Window init_clip_window(void) /*********************************************************************** * window_set_user_time */ -void window_set_user_time( struct x11drv_win_data *data, Time time ) +void window_set_user_time( struct x11drv_win_data *data, Time time, BOOL init ) { - if (data->user_time == time) return; + if (init && data->managed) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)time ); + else if (!init && !time) time = 1; /* time == 0 has reserved semantics */ + + if (init && !data->user_time == !time) return; + if (!init && data->user_time == time) return; data->user_time = time;
TRACE( "window %p/%lx, requesting _NET_WM_USER_TIME %ld serial %lu\n", data->hwnd, data->whole_window, data->user_time, NextRequest( data->display ) ); - XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_USER_TIME), XA_CARDINAL, - 32, PropModeReplace, (unsigned char *)&time, 1 ); + if (init && time) XDeleteProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_USER_TIME) ); + else XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_USER_TIME), XA_CARDINAL, + 32, PropModeReplace, (unsigned char *)&time, 1 ); }
/* Update _NET_WM_FULLSCREEN_MONITORS when _NET_WM_STATE_FULLSCREEN is set to support fullscreen @@ -1396,6 +1401,7 @@ static void set_xembed_flags( struct x11drv_win_data *data, unsigned long flags static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, BOOL activate ) { UINT old_state = data->pending_state.wm_state; + HWND foreground = NtUserGetForegroundWindow();
data->desired_state.wm_state = new_state; data->desired_state.activate = activate; @@ -1434,7 +1440,12 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state, B break; }
- if (new_state == NormalState && data->managed) NtUserSetProp( data->hwnd, focus_time_prop, (HANDLE)-1 ); + if (new_state == NormalState) + { + /* try forcing activation if the window is supposed to be foreground or if it is fullscreen */ + if (data->hwnd == foreground || data->is_fullscreen) activate = TRUE; + window_set_user_time( data, activate ? -1 : 0, TRUE ); + }
data->pending_state.wm_state = new_state; data->pending_state.activate = activate; @@ -1604,11 +1615,22 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) */ BOOL X11DRV_GetWindowStateUpdates( HWND hwnd, UINT *state_cmd, UINT *config_cmd, RECT *rect, HWND *foreground ) { + struct x11drv_thread_data *thread_data = x11drv_thread_data(); struct x11drv_win_data *data; + HWND old_foreground;
*state_cmd = *config_cmd = 0; *foreground = 0;
+ if (!(old_foreground = NtUserGetForegroundWindow())) old_foreground = NtUserGetDesktopWindow(); + if (NtUserGetWindowThread( old_foreground, NULL ) == GetCurrentThreadId() && + !window_has_pending_wm_state( old_foreground, NormalState ) && + !thread_data->net_active_window_serial) + { + *foreground = hwnd_from_window( thread_data->display, thread_data->current_state.net_active_window ); + if (*foreground == old_foreground) *foreground = 0; + } + if ((data = get_win_data( hwnd ))) { *state_cmd = window_update_client_state( data ); @@ -1760,6 +1782,50 @@ void net_active_window_init( struct x11drv_thread_data *data ) data->current_state.net_active_window = window; }
+static BOOL window_set_pending_activate( HWND hwnd ) +{ + struct x11drv_win_data *data; + BOOL pending; + + if (!(data = get_win_data( hwnd ))) return FALSE; + if ((pending = !!data->wm_state_serial)) data->pending_state.activate = TRUE; + release_win_data( data ); + + return pending; +} + +void set_net_active_window( HWND hwnd, HWND previous ) +{ + struct x11drv_thread_data *data = x11drv_thread_data(); + Window window; + XEvent xev; + + if (!is_net_supported( x11drv_atom(_NET_ACTIVE_WINDOW) )) return; + if (!(window = X11DRV_get_whole_window( hwnd ))) return; + if (data->pending_state.net_active_window == window) return; + if (window_set_pending_activate( hwnd )) return; + + xev.xclient.type = ClientMessage; + xev.xclient.window = window; + xev.xclient.message_type = x11drv_atom(_NET_ACTIVE_WINDOW); + xev.xclient.serial = 0; + xev.xclient.display = data->display; + xev.xclient.send_event = True; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 2; /* source: pager */ + xev.xclient.data.l[1] = 0; /* timestamp */ + xev.xclient.data.l[2] = X11DRV_get_whole_window( previous ); /* current active */ + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + data->pending_state.net_active_window = window; + data->net_active_window_serial = NextRequest( data->display ); + TRACE( "requesting _NET_ACTIVE_WINDOW %p/%lx serial %lu\n", hwnd, window, data->net_active_window_serial ); + XSendEvent( data->display, DefaultRootWindow( data->display ), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev ); + XFlush( data->display ); +} + BOOL window_has_pending_wm_state( HWND hwnd, UINT state ) { struct x11drv_win_data *data; @@ -3283,9 +3349,9 @@ LRESULT X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
/*********************************************************************** - * is_netwm_supported + * is_net_supported */ -static BOOL is_netwm_supported( Atom atom ) +BOOL is_net_supported( Atom atom ) { struct x11drv_thread_data *data = x11drv_thread_data(); BOOL supported; @@ -3377,7 +3443,7 @@ LRESULT X11DRV_SysCommand( HWND hwnd, WPARAM wparam, LPARAM lparam, const POINT
if (NtUserGetWindowLongW( hwnd, GWL_STYLE ) & WS_MAXIMIZE) goto failed;
- if (!is_netwm_supported( x11drv_atom(_NET_WM_MOVERESIZE) )) + if (!is_net_supported( x11drv_atom(_NET_WM_MOVERESIZE) )) { TRACE( "_NET_WM_MOVERESIZE not supported\n" ); goto failed; @@ -3434,7 +3500,7 @@ void net_supported_init( struct x11drv_thread_data *data ) for (i = 0; i < NB_NET_WM_STATES; i++) { Atom atom = X11DRV_Atoms[net_wm_state_atoms[i] - FIRST_XATOM]; - if (is_netwm_supported( atom )) data->net_wm_state_mask |= (1 << i); + if (is_net_supported( atom )) data->net_wm_state_mask |= (1 << i); } }
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 1fc3dc02611..c2abf72fae1 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -680,13 +680,15 @@ extern void window_wm_state_notify( struct x11drv_win_data *data, unsigned long extern void window_net_wm_state_notify( struct x11drv_win_data *data, unsigned long serial, UINT value ); extern void window_configure_notify( struct x11drv_win_data *data, unsigned long serial, const RECT *rect );
+extern void set_net_active_window( HWND hwnd, HWND previous ); extern Window get_net_active_window( Display *display ); extern void net_active_window_notify( unsigned long serial, Window window, Time time ); extern void net_active_window_init( struct x11drv_thread_data *data ); extern void net_supported_init( struct x11drv_thread_data *data ); +extern BOOL is_net_supported( Atom atom );
extern Window init_clip_window(void); -extern void window_set_user_time( struct x11drv_win_data *data, Time time ); +extern void window_set_user_time( struct x11drv_win_data *data, Time time, BOOL init ); extern UINT get_window_net_wm_state( Display *display, Window window ); extern void make_window_embedded( struct x11drv_win_data *data ); extern Window create_client_window( HWND hwnd, const XVisualInfo *visual, Colormap colormap );
v2: Remove a todo_wine in user32:win tests that should make it pass, I believe other failures are unrelated:
* ws2_32:sock failures have been introduced in !7634 * d3d8:visual ddraw:ddraw7 failures have been introduced in !7562 * adsldp:ldap seems to have been broken by some remote changes. * mfmediaengine:mfmediaengine has been flaky for a very long time (https://test.winehq.org/data/patterns.html#mfmediaengine:mfmediaengine). * d3d9:device d3d12 creation has been failing and crashing for a very long time on Gitlab (https://test.winehq.org/data/patterns.html#d3d9:device).
To note, this also fixes all the spurious d3d failures that have been plaguing us for a long time.