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.
dlls/user32/tests/msg.c | 268 +++++++++++++++++++++++++++++++++++++++- dlls/user32/winpos.c | 9 +- 2 files changed, 275 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 2766e7f5fb..70858644be 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -892,6 +892,157 @@ 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, 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_PAINT, sent }, + { 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_PAINT, sent }, + { 0 } +}; +/* ShowWindow(hwnd, SW_RESTORE) to an active minimized window */ +static const struct message WmShowRestoreActiveMinimizedOverlappedSeq[] = +{ + { HCBT_MINMAX, hook }, + { WM_QUERYOPEN, sent }, + { 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_PAINT, sent }, + { 0 } +}; +/* ShowWindow(hwnd, SW_SHOWNOACTIVATE) to an active minimized window */ +static const struct message WmShowNoActivateActiveMinimizedOverlappedSeq[] = +{ + { HCBT_MINMAX, hook }, + { WM_QUERYOPEN, sent }, + { 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_PAINT, sent }, + { 0 } +}; +/* ShowWindow(hwnd, SW_RESTORE) to a minimized child window. Here are messages received by the parent */ +static const struct message WmShowRestoreMinimizedChildSeq[] = +{ + { HCBT_MINMAX, hook }, + { WM_QUERYOPEN, sent }, + { WM_GETTEXT, sent|optional }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, + { WM_NCCALCSIZE, sent }, + { WM_CHILDACTIVATE, sent }, + { WM_ERASEBKGND, sent }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent|optional }, + { WM_SETFOCUS, sent }, + { WM_PAINT, sent }, + { WM_PAINT, sent }, + { WM_NCPAINT, sent|beginpaint }, + { WM_ERASEBKGND, sent|beginpaint }, + { 0 } +}; /* CreateWindow (for a child popup window, not initially visible) */ static const struct message WmCreateChildPopupSeq[] = { { HCBT_CREATEWND, hook }, @@ -4636,7 +4787,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 +4924,80 @@ 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); + + hchild = CreateWindowA("TestWindowClass", "child", WS_VISIBLE | WS_CHILD, 0, 0, 100, 100, hwnd, 0, 0, 0); + ok(hchild != NULL, "Failed to create child window\n"); + ShowWindow(hchild, SW_MINIMIZE); + SetActiveWindow(hwnd); + ok(GetActiveWindow() == hwnd, "Unexpected active window\n"); + flush_events(); + flush_sequence(); + ShowWindow(hchild, SW_RESTORE); + flush_events(); + ok_sequence(WmShowRestoreMinimizedChildSeq, "ShowWindow(hwnd, SW_RESTORE): minimized child", FALSE); + DestroyWindow(hchild); + + DestroyWindow(hwnd); + flush_sequence(); }
static void test_sys_menu(void) @@ -15415,6 +15640,39 @@ 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_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_PAINT, sent }, + { 0 } +}; + struct rbuttonup_thread_data { HWND hwnd; @@ -15476,7 +15734,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 );
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56785
Your paranoid android.
=== wvistau64_zh_CN (32 bit report) ===
user32: msg.c:16463: Test failed: 20: WaitForSingleObject failed
=== wvistau64_he (32 bit report) ===
user32: msg.c:16463: Test failed: 0: WaitForSingleObject failed
=== w7u (32 bit report) ===
user32: msg.c:4945: Test failed: ShowWindow(hwnd, SW_RESTORE): minimized overlapped: 7: the msg 0x0046 was expected, but got msg 0x0086 instead
=== w7pro64 (32 bit report) ===
user32: msg: Timeout
=== w7pro64 (task log) ===
Task errors: The previous 1 run(s) terminated abnormally
=== debian10 (32 bit report) ===
user32: msg.c:5380: Test succeeded inside todo block: ShowWindow(SW_RESTORE):overlapped: marked "todo_wine" but succeeds win.c:3096: Test succeeded inside todo block: Focus should be on child 000900DC, not 000900DC
=== debian10 (32 bit French report) ===
user32: msg.c:5380: Test succeeded inside todo block: ShowWindow(SW_RESTORE):overlapped: marked "todo_wine" but succeeds
=== debian10 (32 bit Japanese:Japan report) ===
user32: msg.c:5380: Test succeeded inside todo block: ShowWindow(SW_RESTORE):overlapped: marked "todo_wine" but succeeds
=== debian10 (32 bit Chinese:China report) ===
user32: msg.c:5380: Test succeeded inside todo block: ShowWindow(SW_RESTORE):overlapped: marked "todo_wine" but succeeds win.c:3096: Test succeeded inside todo block: Focus should be on child 000900DC, not 000900DC
=== debian10 (32 bit WoW report) ===
user32: msg.c:5380: Test succeeded inside todo block: ShowWindow(SW_RESTORE):overlapped: marked "todo_wine" but succeeds win.c:3096: Test succeeded inside todo block: Focus should be on child 000900DC, not 000900DC
=== debian10 (64 bit WoW report) ===
user32: clipboard.c:1630: Test failed: gle 5 clipboard.c:1632: Test failed: expected moveable mem 00000000 clipboard.c:1637: Test failed: expected moveable mem 00000000 clipboard.c:1642: Test failed: expected moveable mem 00000000 clipboard.c:1648: Test failed: expected moveable mem 00000000 clipboard.c:1654: Test failed: expected bitmap 00000000 clipboard.c:1655: Test failed: different bitmap 00000000 / 0046003F clipboard.c:1658: Test failed: expected bitmap 00000000 clipboard.c:1659: Test failed: different bitmap 00000000 / 000B0045 clipboard.c:1662: Test failed: expected palette 00000000 clipboard.c:1663: Test failed: different palette 00000000 / 000C0041 clipboard.c:1666: Test failed: expected fixed mem 00000000 clipboard.c:1668: Test failed: expected fixed mem 00000000 clipboard.c:1670: Test failed: gle 1418 msg.c:5380: Test succeeded inside todo block: ShowWindow(SW_RESTORE):overlapped: marked "todo_wine" but succeeds win.c:3096: Test succeeded inside todo block: Focus should be on child 000900DC, not 000900DC