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.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47507 Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- v2: Supersede 169418. Add more tests to show that WM_ACTIVATE is not from SetFocus. v3: Supersede 170219. Fix some tests that occasionally fail on TestBots. v4: Supersede 170290. Fix some more tests. Those optional messages just appear right after I submitted the patch and I ran it multiple times before. I ran it even more times and this time there should be no more failures because of optional messages. v5: Supersede 170327. Fix some more tests :(
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 2766e7f5fb..8819b3a0bc 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 }, @@ -4636,7 +4775,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, @@ -4773,6 +4912,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_sys_menu(void) @@ -15415,6 +15616,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; @@ -15476,7 +15713,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 bbbab60fbe..ebfdf9b6f0 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 );