diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 65f13e12..8bdd7e45 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -302,6 +302,7 @@ patch_enable_all () enable_user32_ShowWindow="$1" enable_user32_msgbox_Support_WM_COPY_mesg="$1" enable_user32_recursive_activation="$1" + enable_user32_window_activation="$1" enable_uxtheme_CloseThemeClass="$1" enable_uxtheme_GTK_Theming="$1" enable_version_VerQueryValue="$1" @@ -1031,6 +1032,9 @@ patch_enable () user32-recursive-activation) enable_user32_recursive_activation="$2" ;; + user32-window-activation) + enable_user32_window_activation="$2" + ;; uxtheme-CloseThemeClass) enable_uxtheme_CloseThemeClass="$2" ;; @@ -1648,6 +1652,13 @@ if test "$enable_uxtheme_GTK_Theming" -eq 1; then enable_uxtheme_CloseThemeClass=1 fi +if test "$enable_user32_window_activation" -eq 1; then + if test "$enable_user32_recursive_activation" -gt 1; then + abort "Patchset user32-recursive-activation disabled, but user32-window-activation depends on that." + fi + enable_user32_recursive_activation=1 +fi + if test "$enable_stdole32_tlb_SLTG_Typelib" -eq 1; then if test "$enable_widl_SLTG_Typelib_Support" -gt 1; then abort "Patchset widl-SLTG_Typelib_Support disabled, but stdole32.tlb-SLTG_Typelib depends on that." @@ -6386,6 +6397,24 @@ if test "$enable_user32_recursive_activation" -eq 1; then ) >> "$patchlist" fi +# Patchset user32-window-activation +# | +# | This patchset has the following (direct or indirect) dependencies: +# | * user32-recursive-activation +# | +# | This patchset fixes the following Wine bugs: +# | * [#47507] Send a WM_ACTIVATE message after restoring a minimized window. +# | +# | Modified files: +# | * dlls/user32/tests/msg.c, dlls/user32/winpos.c +# | +if test "$enable_user32_window_activation" -eq 1; then + patch_apply user32-window-activation/0001-user32-Send-a-WM_ACTIVATE-message-after-restoring-a-.patch + ( + printf '%s\n' '+ { "Zhiyi Zhang", "user32: Send a WM_ACTIVATE message after restoring a minimized window.", 1 },'; + ) >> "$patchlist" +fi + # Patchset uxtheme-CloseThemeClass # | # | This patchset fixes the following Wine bugs: diff --git a/patches/user32-window-activation/0001-user32-Send-a-WM_ACTIVATE-message-after-restoring-a-.patch b/patches/user32-window-activation/0001-user32-Send-a-WM_ACTIVATE-message-after-restoring-a-.patch new file mode 100644 index 00000000..fcbcde37 --- /dev/null +++ b/patches/user32-window-activation/0001-user32-Send-a-WM_ACTIVATE-message-after-restoring-a-.patch @@ -0,0 +1,328 @@ +From c9edb576a00f4af2405b088341136ff149233bb2 Mon Sep 17 00:00:00 2001 +From: Zhiyi Zhang +Date: Tue, 3 Dec 2019 22:54:27 +0800 +Subject: [PATCH] user32: Send a WM_ACTIVATE message after restoring a + minimized window. + +The WmShowRestoreMinimizedOverlappedSeq message sequence in tests +clearly show that there is a WM_ACTIVATE message at the end of +ShowWindow() calls after restoring a minimized window, and it's +not from SetFocus(). + +Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47507 +Signed-off-by: Zhiyi Zhang +--- + dlls/user32/tests/msg.c | 247 +++++++++++++++++++++++++++++++++++++++- + dlls/user32/winpos.c | 9 +- + 2 files changed, 254 insertions(+), 2 deletions(-) + +diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c +index cac989860ff..8cec5798511 100644 +--- a/dlls/user32/tests/msg.c ++++ b/dlls/user32/tests/msg.c +@@ -892,6 +892,145 @@ static const struct message WmShowVisMaxPopupSeq[] = { + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } + }; ++/* ShowWindow(hwnd, SW_RESTORE) to a minimized window */ ++static const struct message WmShowRestoreMinimizedOverlappedSeq[] = ++{ ++ { HCBT_MINMAX, hook }, ++ { WM_QUERYOPEN, sent }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_GETMINMAXINFO, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent }, ++ { HCBT_ACTIVATE, hook }, ++ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, ++ { WM_NCACTIVATE, sent }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_ACTIVATE, sent|wparam, WA_ACTIVE }, ++ { HCBT_SETFOCUS, hook }, ++ { WM_SETFOCUS, sent|defwinproc }, ++ { WM_NCPAINT, sent }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_ERASEBKGND, sent }, ++ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_MOVE, sent|defwinproc }, ++ { WM_SIZE, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_NCPAINT, sent|optional }, ++ { WM_ERASEBKGND, sent|optional }, ++ /* Note this WM_ACTIVATE message even if the window is already active and focused */ ++ { WM_ACTIVATE, sent|wparam|lparam, WA_ACTIVE, 0 }, ++ { WM_SYNCPAINT, sent|optional }, ++ { WM_PAINT, sent }, ++ { WM_GETMINMAXINFO, sent|optional }, ++ { 0 } ++}; ++/* ShowWindow(hwnd, SW_SHOWNOACTIVATE) to a minimized window */ ++static const struct message WmShowNoActivateMinimizedOverlappedSeq[] = ++{ ++ { HCBT_MINMAX, hook }, ++ { WM_QUERYOPEN, sent }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_GETMINMAXINFO, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent }, ++ { WM_NCPAINT, sent }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_ERASEBKGND, sent }, ++ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_MOVE, sent|defwinproc }, ++ { WM_SIZE, sent|defwinproc }, ++ /* Following optional messages are on XP/2003 */ ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_NCPAINT, sent|optional }, ++ { WM_ERASEBKGND, sent|optional }, ++ { HCBT_SETFOCUS, hook|optional }, ++ { WM_SETFOCUS, sent|optional }, ++ { HCBT_ACTIVATE, hook|optional }, ++ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, ++ { WM_NCACTIVATE, sent|optional }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_ACTIVATE, sent|wparam|optional, WA_ACTIVE }, ++ { HCBT_SETFOCUS, hook|optional }, ++ { WM_SETFOCUS, sent|defwinproc|optional }, ++ { WM_KILLFOCUS, sent|optional }, ++ { WM_SETFOCUS, sent|optional }, ++ /* Note this WM_ACTIVATE message on XP even if the window is already active and focused */ ++ { WM_ACTIVATE, sent|wparam|lparam|optional, WA_ACTIVE, 0 }, ++ { WM_SYNCPAINT, sent|optional }, ++ { WM_PAINT, sent }, ++ { WM_GETMINMAXINFO, sent|optional }, ++ { 0 } ++}; ++/* ShowWindow(hwnd, SW_RESTORE) to an active minimized window */ ++static const struct message WmShowRestoreActiveMinimizedOverlappedSeq[] = ++{ ++ { HCBT_MINMAX, hook }, ++ { WM_QUERYOPEN, sent }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_NCACTIVATE, sent }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, ++ { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_MOVE, sent|optional }, ++ { WM_SIZE, sent|optional }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_GETMINMAXINFO, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent }, ++ { WM_NCPAINT, sent }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_ERASEBKGND, sent }, ++ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_MOVE, sent|defwinproc }, ++ { WM_SIZE, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_NCPAINT, sent|optional }, ++ { WM_ERASEBKGND, sent|optional }, ++ { HCBT_SETFOCUS, hook }, ++ { WM_SETFOCUS, sent }, ++ /* Note this WM_ACTIVATE message even if the window is already active */ ++ { WM_ACTIVATE, sent|wparam|lparam, WA_ACTIVE, 0 }, ++ { WM_SYNCPAINT, sent|optional }, ++ { WM_PAINT, sent }, ++ { WM_GETMINMAXINFO, sent|optional }, ++ { 0 } ++}; ++/* ShowWindow(hwnd, SW_SHOWNOACTIVATE) to an active minimized window */ ++static const struct message WmShowNoActivateActiveMinimizedOverlappedSeq[] = ++{ ++ { HCBT_MINMAX, hook }, ++ { WM_QUERYOPEN, sent }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_NCACTIVATE, sent }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, ++ { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_MOVE, sent|optional }, ++ { WM_SIZE, sent|optional }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_GETMINMAXINFO, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent }, ++ { WM_NCPAINT, sent }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_ERASEBKGND, sent }, ++ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_MOVE, sent|defwinproc }, ++ { WM_SIZE, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_NCPAINT, sent|optional }, ++ { WM_ERASEBKGND, sent|optional }, ++ /* Following optional messages are present on XP */ ++ { HCBT_SETFOCUS, hook|optional }, ++ { WM_SETFOCUS, sent|optional }, ++ /* Note this WM_ACTIVATE message even if the window is already active and with flag SW_SHOWNOACTIVATE */ ++ { WM_ACTIVATE, sent|wparam|lparam|optional, WA_ACTIVE, 0 }, ++ { WM_SYNCPAINT, sent|optional }, ++ { WM_PAINT, sent }, ++ { WM_GETMINMAXINFO, sent|optional }, ++ { 0 } ++}; + /* CreateWindow (for a child popup window, not initially visible) */ + static const struct message WmCreateChildPopupSeq[] = { + { HCBT_CREATEWND, hook }, +@@ -4661,7 +4800,7 @@ static void test_scroll_messages(HWND hwnd) + + static void test_showwindow(void) + { +- HWND hwnd, hchild; ++ HWND hwnd, hwnd2, hchild; + RECT rc; + + hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW, +@@ -4798,6 +4937,68 @@ static void test_showwindow(void) + ok_sequence(WmShowVisMaxPopupSeq, "ShowWindow(SW_SHOWMAXIMIZED):popup", FALSE); + DestroyWindow(hwnd); + flush_sequence(); ++ ++ /* Test 5: ++ * 1. Restoring a minimized window. ++ */ ++ hwnd = CreateWindowA("TestWindowClass", "window1", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0); ++ ok(hwnd != NULL, "Failed to create window\n"); ++ ++ hwnd2 = CreateWindowA("static", "window2", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0); ++ ok(hwnd2 != NULL, "Failed to create window\n"); ++ ++ ShowWindow(hwnd, SW_MINIMIZE); ++ SetActiveWindow(hwnd2); ++ ok(GetActiveWindow() == hwnd2, "Unexpected active window\n"); ++ flush_events(); ++ flush_sequence(); ++ ShowWindow(hwnd, SW_RESTORE); ++ flush_events(); ++ ok_sequence(WmShowRestoreMinimizedOverlappedSeq, ++ "ShowWindow(hwnd, SW_RESTORE): minimized overlapped", TRUE); ++ ++ ShowWindow(hwnd, SW_MINIMIZE); ++ SetActiveWindow(hwnd2); ++ ok(GetActiveWindow() == hwnd2, "Unexpected active window\n"); ++ flush_events(); ++ flush_sequence(); ++ ShowWindow(hwnd, SW_SHOWNOACTIVATE); ++ flush_events(); ++ ok_sequence(WmShowNoActivateMinimizedOverlappedSeq, ++ "ShowWindow(hwnd, SW_SHOWNOACTIVATE): minimized overlapped", TRUE); ++ ++ DestroyWindow(hwnd2); ++ DestroyWindow(hwnd); ++ flush_sequence(); ++ ++ /* Test 6: ++ * 1. Restoring a minimized but active window. ++ */ ++ hwnd = CreateWindowA("TestWindowClass", "parent", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0); ++ ok(hwnd != NULL, "Failed to create window\n"); ++ ++ ShowWindow(hwnd, SW_MINIMIZE); ++ SetActiveWindow(hwnd); ++ ok(GetActiveWindow() == hwnd, "Unexpected active window\n"); ++ flush_events(); ++ flush_sequence(); ++ ShowWindow(hwnd, SW_RESTORE); ++ flush_events(); ++ ok_sequence(WmShowRestoreActiveMinimizedOverlappedSeq, ++ "ShowWindow(hwnd, SW_RESTORE): active minimized overlapped", TRUE); ++ ++ ShowWindow(hwnd, SW_MINIMIZE); ++ SetActiveWindow(hwnd); ++ ok(GetActiveWindow() == hwnd, "Unexpected active window\n"); ++ flush_events(); ++ flush_sequence(); ++ ShowWindow(hwnd, SW_SHOWNOACTIVATE); ++ flush_events(); ++ ok_sequence(WmShowNoActivateActiveMinimizedOverlappedSeq, ++ "ShowWindow(hwnd, SW_SHOWNOACTIVATE): active minimized overlapped", TRUE); ++ ++ DestroyWindow(hwnd); ++ flush_sequence(); + } + + static void test_recursive_activation(void) +@@ -15614,6 +15815,42 @@ static const struct message WmRestoreMinimizedOverlappedSeq[] = + { 0 } + }; + ++/* DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0) to an active minimized window */ ++static const struct message WmRestoreActiveMinimizedOverlappedSeq[] = ++{ ++ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_RESTORE, 0 }, ++ { HCBT_MINMAX, hook }, ++ { WM_QUERYOPEN, sent }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_NCACTIVATE, sent }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, ++ { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_MOVE, sent|optional }, ++ { WM_SIZE, sent|optional }, ++ { WM_GETTEXT, sent|optional }, ++ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_GETMINMAXINFO, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent }, ++ { WM_NCPAINT, sent }, ++ { WM_GETTEXT, sent|defwinproc|optional }, ++ { WM_ERASEBKGND, sent }, ++ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, ++ { WM_MOVE, sent|defwinproc }, ++ { WM_SIZE, sent|defwinproc }, ++ { WM_NCCALCSIZE, sent|optional }, ++ { WM_NCPAINT, sent|optional }, ++ { WM_ERASEBKGND, sent|optional }, ++ { HCBT_SETFOCUS, hook }, ++ { WM_SETFOCUS, sent }, ++ /* Note this WM_ACTIVATE messages even if the window is already active */ ++ { WM_ACTIVATE, sent|wparam|lparam, WA_ACTIVE, 0 }, ++ { WM_SYNCPAINT, sent|optional }, ++ { WM_PAINT, sent }, ++ { WM_GETMINMAXINFO, sent|optional }, ++ { 0 } ++}; ++ + struct rbuttonup_thread_data + { + HWND hwnd; +@@ -15675,7 +15912,15 @@ static void test_defwinproc(void) + DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); + flush_events(); + ok_sequence(WmRestoreMinimizedOverlappedSeq, "DefWindowProcA(SC_RESTORE):overlapped", TRUE); ++ ++ ShowWindow(hwnd, SW_MINIMIZE); ++ SetActiveWindow(hwnd); ++ ok(GetActiveWindow() == hwnd, "Unexpected active window\n"); ++ flush_events(); + flush_sequence(); ++ DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); ++ flush_events(); ++ ok_sequence(WmRestoreActiveMinimizedOverlappedSeq, "DefWindowProcA(SC_RESTORE):active minimized overlapped", TRUE); + + GetCursorPos(&pos); + GetWindowRect(hwnd, &rect); +diff --git a/dlls/user32/winpos.c b/dlls/user32/winpos.c +index 968f519478c..66e8dcb15b8 100644 +--- a/dlls/user32/winpos.c ++++ b/dlls/user32/winpos.c +@@ -1182,7 +1182,14 @@ static BOOL show_window( HWND hwnd, INT cmd ) + else WIN_ReleasePtr( wndPtr ); + + /* if previous state was minimized Windows sets focus to the window */ +- if (style & WS_MINIMIZE) SetFocus( hwnd ); ++ if (style & WS_MINIMIZE) ++ { ++ SetFocus( hwnd ); ++ /* Send a WM_ACTIVATE message for a top level window, even if the window is already active */ ++ style = GetWindowLongW( hwnd, GWL_STYLE ); ++ if (!(style & WS_CHILD) && !(swp & SWP_NOACTIVATE)) ++ SendMessageW( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 ); ++ } + + done: + SetThreadDpiAwarenessContext( context ); +-- +2.17.1 + diff --git a/patches/user32-window-activation/definition b/patches/user32-window-activation/definition new file mode 100644 index 00000000..2b3e5b90 --- /dev/null +++ b/patches/user32-window-activation/definition @@ -0,0 +1,2 @@ +Fixes: [47507] Send a WM_ACTIVATE message after restoring a minimized window. +Depends: user32-recursive-activation