diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 2ce2b3c6e54..11fb764e6cd 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -883,6 +883,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 }, @@ -4677,7 +4816,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, @@ -4814,6 +4953,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) @@ -15527,6 +15728,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; @@ -15588,7 +15825,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 072b574f3aa..c6f806f7beb 100644 --- a/dlls/user32/winpos.c +++ b/dlls/user32/winpos.c @@ -1179,7 +1179,13 @@ 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 */ + if (GetAncestor( hwnd, GA_ROOT ) == hwnd && !(swp & SWP_NOACTIVATE)) + SendMessageW( hwnd, WM_ACTIVATE, WA_ACTIVE, 0 ); + } done: SetThreadDpiAwarenessContext( context );