From f0e7e4d7995867911e47dc54c9087cd7a57b5c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= Date: Mon, 22 Jul 2019 15:29:25 +0300 Subject: [PATCH] user32/focus: Prevent a recursive loop with the activation messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When activating a window and sending activation messages to the window procedure, Windows avoids a recursive loop by not sending more of these messages or hooks while it's still activating the window. Some applications actually depend on this behavior, so it is needed. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46274 Signed-off-by: Gabriel Ivăncescu --- dlls/user32/focus.c | 1 - dlls/user32/tests/msg.c | 2 +- dlls/win32u/input.c | 52 +++++++++++++++++++++++++++--------- dlls/win32u/ntuser_private.h | 1 + 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c index 4998ac2feb6..f68c6080698 100644 --- a/dlls/user32/focus.c +++ b/dlls/user32/focus.c @@ -31,7 +31,6 @@ #include "user_private.h" #include "wine/server.h" - /******************************************************************* * FOCUS_MouseActivate * diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 9164f57fdc2..01cb214acdf 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -5571,7 +5571,7 @@ static void test_messages(void) ShowWindow(hwnd, SW_MINIMIZE); flush_events(); - ok_sequence(WmShowMinOverlappedSeq, "ShowWindow(SW_SHOWMINIMIZED):overlapped", TRUE); + ok_sequence(WmShowMinOverlappedSeq, "ShowWindow(SW_SHOWMINIMIZED):overlapped", FALSE); flush_sequence(); if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_MINIMIZE) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index fff732097ba..cbc6cfd6b0d 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1180,6 +1180,18 @@ static HWND set_focus_window( HWND hwnd ) return previous; } +static UINT win_set_flags( HWND hwnd, UINT set_mask, UINT clear_mask ) +{ + UINT ret; + WND *ptr = get_win_ptr( hwnd ); + + if (!ptr || ptr == WND_OTHER_PROCESS || ptr == WND_DESKTOP) return 0; + ret = ptr->flags; + ptr->flags = (ret & ~clear_mask) | set_mask; + release_win_ptr( ptr ); + return ret; +} + /******************************************************************* * set_active_window */ @@ -1187,7 +1199,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) { HWND previous = get_active_window(); BOOL ret; - DWORD old_thread, new_thread; + DWORD winflags, old_thread, new_thread; CBTACTIVATESTRUCT cbt; if (previous == hwnd) @@ -1196,16 +1208,24 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) return TRUE; } - /* call CBT hook chain */ - cbt.fMouse = mouse; - cbt.hWndActive = previous; - if (call_hooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE; - - if (is_window( previous )) + /* Prevent a recursive activation loop with the activation messages */ + winflags = win_set_flags(hwnd, WIN_IS_IN_ACTIVATION, 0); + if (!(winflags & WIN_IS_IN_ACTIVATION)) { - send_message( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd ); - send_message( previous, WM_ACTIVATE, - MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd ); + ret = FALSE; + + /* call CBT hook chain */ + cbt.fMouse = mouse; + cbt.hWndActive = previous; + if (call_hooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) + goto clear_flags; + + if (is_window(previous)) + { + send_message( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd ); + send_message( previous, WM_ACTIVATE, + MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd ); + } } SERVER_START_REQ( set_active_window ) @@ -1225,7 +1245,11 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) if (send_message( hwnd, WM_QUERYNEWPALETTE, 0, 0 ) && user_callbacks) user_callbacks->pSendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0, SMTO_ABORTIFHUNG, 2000, NULL ); - if (!is_window(hwnd)) return FALSE; + if (!is_window(hwnd)) + { + ret = FALSE; + goto clear_flags; + } } old_thread = previous ? get_window_thread( previous, NULL ) : 0; @@ -1257,7 +1281,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) } } - if (is_window(hwnd)) + if (!(winflags & WIN_IS_IN_ACTIVATION) && is_window(hwnd)) { send_message( hwnd, WM_NCACTIVATE, hwnd == NtUserGetForegroundWindow(), (LPARAM)previous ); send_message( hwnd, WM_ACTIVATE, @@ -1282,7 +1306,9 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) } } - return TRUE; +clear_flags: + win_set_flags(hwnd, 0, WIN_IS_IN_ACTIVATION); + return ret; } /********************************************************************** diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 6c10a3e77e1..b46f0426368 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -109,6 +109,7 @@ typedef struct tagWND #define WIN_NEEDS_SHOW_OWNEDPOPUP 0x0020 /* WM_SHOWWINDOW:SC_SHOW must be sent in the next ShowOwnedPopup call */ #define WIN_CHILDREN_MOVED 0x0040 /* children may have moved, ignore stored positions */ #define WIN_HAS_IME_WIN 0x0080 /* the window has been registered with imm32 */ +#define WIN_IS_IN_ACTIVATION 0x0100 /* the window is in an activation process */ #define WND_OTHER_PROCESS ((WND *)1) /* returned by WIN_GetPtr on unknown window handles */ #define WND_DESKTOP ((WND *)2) /* returned by WIN_GetPtr on the desktop window */ -- 2.35.1