From ad822b6405419312e6fd55c6ab0a9db10654fd7c Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Mon, 5 Aug 2019 10:27:34 +1000 Subject: [PATCH] Added user32-recursive-activation patchset --- patches/patchinstall.sh | 21 +++ ...vent-a-recursive-loop-with-the-activ.patch | 142 ++++++++++++++++++ ...t-a-recursive-activation-loop-on-WM_.patch | 140 +++++++++++++++++ .../user32-recursive-activation/definition | 2 + 4 files changed, 305 insertions(+) create mode 100644 patches/user32-recursive-activation/0001-user32-focus-Prevent-a-recursive-loop-with-the-activ.patch create mode 100644 patches/user32-recursive-activation/0002-user32-tests-Test-a-recursive-activation-loop-on-WM_.patch create mode 100644 patches/user32-recursive-activation/definition diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 2b4145ed..ba96dcf8 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -303,6 +303,7 @@ patch_enable_all () enable_user32_ScrollWindowEx="$1" enable_user32_ShowWindow="$1" enable_user32_msgbox_Support_WM_COPY_mesg="$1" + enable_user32_recursive_activation="$1" enable_uxtheme_CloseThemeClass="$1" enable_uxtheme_GTK_Theming="$1" enable_version_VerQueryValue="$1" @@ -1039,6 +1040,9 @@ patch_enable () user32-msgbox-Support-WM_COPY-mesg) enable_user32_msgbox_Support_WM_COPY_mesg="$2" ;; + user32-recursive-activation) + enable_user32_recursive_activation="$2" + ;; uxtheme-CloseThemeClass) enable_uxtheme_CloseThemeClass="$2" ;; @@ -6427,6 +6431,23 @@ if test "$enable_user32_msgbox_Support_WM_COPY_mesg" -eq 1; then ) >> "$patchlist" fi +# Patchset user32-recursive-activation +# | +# | This patchset fixes the following Wine bugs: +# | * [#46274] user32: Prevent a recursive loop with the activation messages. +# | +# | Modified files: +# | * dlls/user32/focus.c, dlls/user32/tests/msg.c, dlls/user32/win.h +# | +if test "$enable_user32_recursive_activation" -eq 1; then + patch_apply user32-recursive-activation/0001-user32-focus-Prevent-a-recursive-loop-with-the-activ.patch + patch_apply user32-recursive-activation/0002-user32-tests-Test-a-recursive-activation-loop-on-WM_.patch + ( + printf '%s\n' '+ { "Gabriel Ivăncescu", "user32/focus: Prevent a recursive loop with the activation messages.", 1 },'; + printf '%s\n' '+ { "Gabriel Ivăncescu", "user32/tests: Test a recursive activation loop on WM_ACTIVATE.", 1 },'; + ) >> "$patchlist" +fi + # Patchset uxtheme-CloseThemeClass # | # | This patchset fixes the following Wine bugs: diff --git a/patches/user32-recursive-activation/0001-user32-focus-Prevent-a-recursive-loop-with-the-activ.patch b/patches/user32-recursive-activation/0001-user32-focus-Prevent-a-recursive-loop-with-the-activ.patch new file mode 100644 index 00000000..737c0d86 --- /dev/null +++ b/patches/user32-recursive-activation/0001-user32-focus-Prevent-a-recursive-loop-with-the-activ.patch @@ -0,0 +1,142 @@ +From b41d5d29f777dccdd43f09270858f2ca032db00d 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 1/2] 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 | 44 +++++++++++++++++++++++++++-------------- + dlls/user32/tests/msg.c | 2 +- + dlls/user32/win.h | 1 + + 3 files changed, 31 insertions(+), 16 deletions(-) + +diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c +index f1c883167e..0d32d008a7 100644 +--- a/dlls/user32/focus.c ++++ b/dlls/user32/focus.c +@@ -78,7 +78,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) + { + HWND previous = GetActiveWindow(); + BOOL ret; +- DWORD old_thread, new_thread; ++ DWORD winflags, old_thread, new_thread; + CBTACTIVATESTRUCT cbt; + + if (previous == hwnd) +@@ -87,16 +87,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 (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE; +- +- if (IsWindow(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)) + { +- SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd ); +- SendMessageW( previous, WM_ACTIVATE, +- MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd ); ++ ret = FALSE; ++ ++ /* call CBT hook chain */ ++ cbt.fMouse = mouse; ++ cbt.hWndActive = previous; ++ if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) ++ goto clear_flags; ++ ++ if (IsWindow(previous)) ++ { ++ SendMessageW( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd ); ++ SendMessageW( previous, WM_ACTIVATE, ++ MAKEWPARAM( WA_INACTIVE, IsIconic(previous) ), (LPARAM)hwnd ); ++ } + } + + SERVER_START_REQ( set_active_window ) +@@ -106,9 +114,9 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) + previous = wine_server_ptr_handle( reply->previous ); + } + SERVER_END_REQ; +- if (!ret) return FALSE; ++ if (!ret) goto clear_flags; + if (prev) *prev = previous; +- if (previous == hwnd) return TRUE; ++ if (previous == hwnd) goto clear_flags; + + if (hwnd) + { +@@ -116,7 +124,11 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) + if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 )) + SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0, + SMTO_ABORTIFHUNG, 2000, NULL ); +- if (!IsWindow(hwnd)) return FALSE; ++ if (!IsWindow(hwnd)) ++ { ++ ret = FALSE; ++ goto clear_flags; ++ } + } + + old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0; +@@ -148,7 +160,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) + } + } + +- if (IsWindow(hwnd)) ++ if (!(winflags & WIN_IS_IN_ACTIVATION) && IsWindow(hwnd)) + { + SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), (LPARAM)previous ); + SendMessageW( hwnd, WM_ACTIVATE, +@@ -173,7 +185,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/user32/tests/msg.c b/dlls/user32/tests/msg.c +index a5bc1c5518..64f7eeecf3 100644 +--- a/dlls/user32/tests/msg.c ++++ b/dlls/user32/tests/msg.c +@@ -5142,7 +5142,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/user32/win.h b/dlls/user32/win.h +index 1f51fd6331..a64cc66be5 100644 +--- a/dlls/user32/win.h ++++ b/dlls/user32/win.h +@@ -79,6 +79,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 */ + + /* Window functions */ + extern HWND get_hwnd_message_parent(void) DECLSPEC_HIDDEN; +-- +2.17.1 + diff --git a/patches/user32-recursive-activation/0002-user32-tests-Test-a-recursive-activation-loop-on-WM_.patch b/patches/user32-recursive-activation/0002-user32-tests-Test-a-recursive-activation-loop-on-WM_.patch new file mode 100644 index 00000000..c5a43ee0 --- /dev/null +++ b/patches/user32-recursive-activation/0002-user32-tests-Test-a-recursive-activation-loop-on-WM_.patch @@ -0,0 +1,140 @@ +From 064570eb530a73e822bf19ccb9bc31cd3654c504 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= +Date: Mon, 22 Jul 2019 15:29:26 +0300 +Subject: [PATCH 2/2] user32/tests: Test a recursive activation loop on + WM_ACTIVATE +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Windows only sends the activation messages and hooks once, until the +SetActiveWindow completes for that window. + +Signed-off-by: Gabriel Ivăncescu +--- + dlls/user32/tests/msg.c | 81 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 81 insertions(+) + +diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c +index 64f7eeecf3..191e7bfffb 100644 +--- a/dlls/user32/tests/msg.c ++++ b/dlls/user32/tests/msg.c +@@ -4772,6 +4772,39 @@ static void test_showwindow(void) + flush_sequence(); + } + ++static void test_recursive_activation(void) ++{ ++ static const struct message seq[] = ++ { ++ { HCBT_ACTIVATE, hook }, ++ { WM_NCACTIVATE, sent|wparam, TRUE }, ++ { WM_ACTIVATE, sent|wparam, WA_ACTIVE }, ++ { HCBT_ACTIVATE, hook }, ++ { WM_NCACTIVATE, sent|wparam, FALSE }, ++ { WM_ACTIVATE, sent|wparam, WA_INACTIVE }, ++ { WM_SETFOCUS, sent|optional }, ++ { 0 } ++ }; ++ HWND hwnd, recursive; ++ ++ hwnd = CreateWindowExA(0, "SimpleWindowClass", NULL, WS_OVERLAPPED|WS_VISIBLE, ++ 100, 100, 200, 200, 0, 0, 0, NULL); ++ ok(hwnd != 0, "Failed to create simple window\n"); ++ ++ recursive = CreateWindowExA(0, "RecursiveActivationClass", NULL, WS_OVERLAPPED|WS_VISIBLE, ++ 10, 10, 50, 50, hwnd, 0, 0, NULL); ++ ok(recursive != 0, "Failed to create recursive activation window\n"); ++ SetActiveWindow(hwnd); ++ ++ flush_sequence(); ++ SetActiveWindow(recursive); ++ ok_sequence(seq, "Recursive Activation", FALSE); ++ ++ DestroyWindow(recursive); ++ DestroyWindow(hwnd); ++ flush_sequence(); ++} ++ + static void test_sys_menu(void) + { + HWND hwnd; +@@ -9699,6 +9732,48 @@ static LRESULT WINAPI ShowWindowProcA(HWND hwnd, UINT message, WPARAM wParam, LP + return ret; + } + ++static LRESULT WINAPI recursive_activation_wndprocA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ++{ ++ static LONG defwndproc_counter = 0; ++ struct recvd_message msg; ++ LRESULT ret; ++ ++ switch (message) ++ { ++ /* log only specific messages we are interested in */ ++ case WM_NCACTIVATE: ++ case WM_ACTIVATE: ++ case WM_SETFOCUS: ++ case WM_KILLFOCUS: ++ break; ++ default: ++ return DefWindowProcA(hwnd, message, wParam, lParam); ++ } ++ ++ msg.hwnd = hwnd; ++ msg.message = message; ++ msg.flags = sent|wparam|lparam; ++ if (defwndproc_counter) msg.flags |= defwinproc; ++ msg.wParam = wParam; ++ msg.lParam = lParam; ++ msg.descr = "recursive_activation"; ++ add_message(&msg); ++ ++ /* recursively activate ourselves by first losing activation and changing it back */ ++ if (message == WM_ACTIVATE && LOWORD(wParam) != WA_INACTIVE) ++ { ++ SetActiveWindow((HWND)lParam); ++ SetActiveWindow(hwnd); ++ return 0; ++ } ++ ++ defwndproc_counter++; ++ ret = DefWindowProcA(hwnd, message, wParam, lParam); ++ defwndproc_counter--; ++ ++ return ret; ++} ++ + static LRESULT WINAPI PaintLoopProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) +@@ -9796,6 +9871,10 @@ static BOOL RegisterWindowClasses(void) + cls.lpszClassName = "ShowWindowClass"; + if(!RegisterClassA(&cls)) return FALSE; + ++ cls.lpfnWndProc = recursive_activation_wndprocA; ++ cls.lpszClassName = "RecursiveActivationClass"; ++ if(!RegisterClassA(&cls)) return FALSE; ++ + cls.lpfnWndProc = PopupMsgCheckProcA; + cls.lpszClassName = "TestPopupClass"; + if(!RegisterClassA(&cls)) return FALSE; +@@ -9851,6 +9930,7 @@ static BOOL is_our_logged_class(HWND hwnd) + { + if (!lstrcmpiA(buf, "TestWindowClass") || + !lstrcmpiA(buf, "ShowWindowClass") || ++ !lstrcmpiA(buf, "RecursiveActivationClass") || + !lstrcmpiA(buf, "TestParentClass") || + !lstrcmpiA(buf, "TestPopupClass") || + !lstrcmpiA(buf, "SimpleWindowClass") || +@@ -17756,6 +17836,7 @@ START_TEST(msg) + test_messages(); + test_setwindowpos(); + test_showwindow(); ++ test_recursive_activation(); + invisible_parent_tests(); + test_mdi_messages(); + test_button_messages(); +-- +2.17.1 + diff --git a/patches/user32-recursive-activation/definition b/patches/user32-recursive-activation/definition new file mode 100644 index 00000000..db9ecddb --- /dev/null +++ b/patches/user32-recursive-activation/definition @@ -0,0 +1,2 @@ +Fixes: [46274] user32: Prevent a recursive loop with the activation messages. +