diff --git a/reactos/regtests/winetests/user32/class.c b/reactos/regtests/winetests/user32/class.c new file mode 100755 index 00000000000..250b6b6f407 --- /dev/null +++ b/reactos/regtests/winetests/user32/class.c @@ -0,0 +1,613 @@ +/* Unit test suite for window classes. + * + * Copyright 2002 Mike McCormack + * Copyright 2003 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* To get CS_DROPSHADOW with the MSVC headers */ +#define _WIN32_WINNT 0x0501 + +#include +#include +#include +#include + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "wingdi.h" +#include "winuser.h" + +#define NUMCLASSWORDS 4 + +static LRESULT WINAPI ClassTest_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProcW (hWnd, msg, wParam, lParam); +} + +/*********************************************************************** + */ +static void ClassTest(HINSTANCE hInstance, BOOL global) +{ + WNDCLASSW cls, wc; + static const WCHAR className[] = {'T','e','s','t','C','l','a','s','s',0}; + static const WCHAR winName[] = {'W','i','n','C','l','a','s','s','T','e','s','t',0}; + ATOM test_atom; + HWND hTestWnd; + LONG i; + WCHAR str[20]; + ATOM classatom; + + cls.style = CS_HREDRAW | CS_VREDRAW | (global?CS_GLOBALCLASS:0); + cls.lpfnWndProc = ClassTest_WndProc; + cls.cbClsExtra = NUMCLASSWORDS*sizeof(DWORD); + cls.cbWndExtra = 12; + cls.hInstance = hInstance; + cls.hIcon = LoadIconW (0, (LPWSTR)IDI_APPLICATION); + cls.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject (WHITE_BRUSH); + cls.lpszMenuName = 0; + cls.lpszClassName = className; + + classatom=RegisterClassW(&cls); + if (!classatom && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) + return; + ok(classatom, "failed to register class\n"); + + ok(!RegisterClassW (&cls), + "RegisterClass of the same class should fail for the second time\n"); + + /* Setup windows */ + hTestWnd = CreateWindowW (className, winName, + WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, + 0, hInstance, 0); + + ok(hTestWnd!=0, "Failed to create window\n"); + + /* test initial values of valid classwords */ + for(i=0; iname, class_info->inst, class_info->info_inst, class_info->gcl_inst); + + return 0; +} + +static void check_thread_instance( const char *name, HINSTANCE inst, HINSTANCE info_inst, HINSTANCE gcl_inst ) +{ + HANDLE hThread; + DWORD tid; + struct class_info class_info; + + class_info.name = name; + class_info.inst = inst; + class_info.info_inst = info_inst; + class_info.gcl_inst = gcl_inst; + + hThread = CreateThread(NULL, 0, thread_proc, &class_info, 0, &tid); + ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError()); + ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + CloseHandle(hThread); +} + +/* test various instance parameters */ +static void test_instances(void) +{ + WNDCLASSA cls, wc; + HWND hwnd, hwnd2; + const char *name = "__test__"; + HINSTANCE kernel32 = GetModuleHandleA("kernel32"); + HINSTANCE user32 = GetModuleHandleA("user32"); + HINSTANCE main_module = GetModuleHandleA(NULL); + + memset( &cls, 0, sizeof(cls) ); + cls.style = CS_HREDRAW | CS_VREDRAW; + cls.lpfnWndProc = ClassTest_WndProc; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.lpszClassName = name; + + cls.lpszMenuName = "main_module"; + cls.hInstance = main_module; + + ok( RegisterClassA( &cls ), "Failed to register local class for main module\n" ); + check_class( main_module, name, "main_module" ); + check_instance( name, main_module, main_module, main_module ); + check_thread_instance( name, main_module, main_module, main_module ); + + cls.lpszMenuName = "kernel32"; + cls.hInstance = kernel32; + ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); + check_class( kernel32, name, "kernel32" ); + check_class( main_module, name, "main_module" ); + check_instance( name, kernel32, kernel32, kernel32 ); + check_thread_instance( name, kernel32, kernel32, kernel32 ); + ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); + + /* Bug 2631 - Supplying an invalid number of bytes fails */ + cls.cbClsExtra = 0; + cls.cbWndExtra = -1; + SetLastError(0xdeadbeef); + ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)), + "Failed with invalid number of WndExtra bytes\n"); + + cls.cbClsExtra = -1; + cls.cbWndExtra = 0; + SetLastError(0xdeadbeef); + ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)), + "Failed with invalid number of ClsExtra bytes\n"); + + cls.cbClsExtra = -1; + cls.cbWndExtra = -1; + SetLastError(0xdeadbeef); + ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)), + "Failed with invalid number of ClsExtra and cbWndExtra bytes\n"); + + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + SetLastError(0xdeadbeef); + + /* setting global flag doesn't change status of class */ + hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 ); + SetClassLongA( hwnd, GCL_STYLE, CS_GLOBALCLASS ); + cls.lpszMenuName = "kernel32"; + cls.hInstance = kernel32; + ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); + check_class( kernel32, name, "kernel32" ); + check_class( main_module, name, "main_module" ); + check_instance( name, kernel32, kernel32, kernel32 ); + check_instance( name, main_module, main_module, main_module ); + check_thread_instance( name, kernel32, kernel32, kernel32 ); + check_thread_instance( name, main_module, main_module, main_module ); + ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); + + /* changing the instance doesn't make it global */ + SetClassLongA( hwnd, GCL_HMODULE, 0 ); + ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); + check_class( kernel32, name, "kernel32" ); + check_instance( name, kernel32, kernel32, kernel32 ); + check_thread_instance( name, kernel32, kernel32, kernel32 ); + ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" ); + ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); + + /* GetClassInfo with instance 0 finds user32 instance */ + SetClassLongA( hwnd, GCL_HMODULE, (LONG)user32 ); + ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); + check_class( kernel32, name, "kernel32" ); + check_class( user32, name, "main_module" ); + check_class( 0, name, "main_module" ); + check_instance( name, kernel32, kernel32, kernel32 ); + check_instance( name, user32, 0, user32 ); + check_instance( name, 0, 0, kernel32 ); + check_thread_instance( name, kernel32, kernel32, kernel32 ); + check_thread_instance( name, user32, 0, user32 ); + check_thread_instance( name, 0, 0, kernel32 ); + ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); + + SetClassLongA( hwnd, GCL_HMODULE, 0x12345678 ); + ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); + check_class( kernel32, name, "kernel32" ); + check_class( (HINSTANCE)0x12345678, name, "main_module" ); + check_instance( name, kernel32, kernel32, kernel32 ); + check_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 ); + check_thread_instance( name, kernel32, kernel32, kernel32 ); + check_thread_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 ); + ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" ); + + /* creating a window with instance 0 uses the first class found */ + cls.hInstance = (HINSTANCE)0xdeadbeef; + cls.lpszMenuName = "deadbeef"; + cls.style = 3; + ok( RegisterClassA( &cls ), "Failed to register local class for deadbeef\n" ); + hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 ); + ok( (HINSTANCE)GetClassLong( hwnd2, GCL_HMODULE ) == (HINSTANCE)0xdeadbeef, + "Didn't get deadbeef class for null instance\n" ); + DestroyWindow( hwnd2 ); + ok( UnregisterClassA( name, (HINSTANCE)0xdeadbeef ), "Unregister failed for deadbeef\n" ); + + hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 ); + ok( (HINSTANCE)GetClassLong( hwnd2, GCL_HMODULE ) == kernel32, + "Didn't get kernel32 class for null instance\n" ); + DestroyWindow( hwnd2 ); + + ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); + + hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 ); + ok( GetClassLong( hwnd2, GCL_HMODULE ) == 0x12345678, + "Didn't get 12345678 class for null instance\n" ); + DestroyWindow( hwnd2 ); + + SetClassLongA( hwnd, GCL_HMODULE, (LONG)main_module ); + DestroyWindow( hwnd ); + + /* null handle means the same thing as main module */ + cls.lpszMenuName = "null"; + cls.hInstance = 0; + ok( !RegisterClassA( &cls ), "Succeeded registering local class for null instance\n" ); + ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() ); + ok( UnregisterClassA( name, main_module ), "Unregister failed for main module\n" ); + + ok( RegisterClassA( &cls ), "Failed to register local class for null instance\n" ); + /* must be found with main module handle */ + check_class( main_module, name, "null" ); + check_instance( name, main_module, main_module, main_module ); + check_thread_instance( name, main_module, main_module, main_module ); + ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" ); + ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() ); + ok( UnregisterClassA( name, 0 ), "Unregister failed for null instance\n" ); + + /* registering for user32 always fails */ + cls.lpszMenuName = "user32"; + cls.hInstance = user32; + ok( !RegisterClassA( &cls ), "Succeeded registering local class for user32\n" ); + ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %ld\n", GetLastError() ); + cls.style |= CS_GLOBALCLASS; + ok( !RegisterClassA( &cls ), "Succeeded registering global class for user32\n" ); + ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %ld\n", GetLastError() ); + + /* unregister is OK though */ + cls.hInstance = main_module; + ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" ); + ok( UnregisterClassA( name, user32 ), "Unregister failed for user32\n" ); + + /* instance doesn't matter for global class */ + cls.style |= CS_GLOBALCLASS; + cls.lpszMenuName = "main_module"; + cls.hInstance = main_module; + ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" ); + cls.lpszMenuName = "kernel32"; + cls.hInstance = kernel32; + ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" ); + ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() ); + /* even if global flag is cleared */ + hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 ); + SetClassLongA( hwnd, GCL_STYLE, 0 ); + ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" ); + ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() ); + + check_class( main_module, name, "main_module" ); + check_class( kernel32, name, "main_module" ); + check_class( 0, name, "main_module" ); + check_class( (HINSTANCE)0x12345678, name, "main_module" ); + check_instance( name, main_module, main_module, main_module ); + check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module ); + check_thread_instance( name, main_module, main_module, main_module ); + check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module ); + + /* changing the instance for global class doesn't make much difference */ + SetClassLongA( hwnd, GCL_HMODULE, 0xdeadbeef ); + check_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef ); + check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef ); + check_thread_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef ); + check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef ); + + DestroyWindow( hwnd ); + ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" ); + ok( !UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister succeeded the second time\n" ); + ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() ); + + cls.hInstance = (HINSTANCE)0x12345678; + ok( RegisterClassA( &cls ), "Failed to register global class for dummy instance\n" ); + check_instance( name, main_module, main_module, (HINSTANCE)0x12345678 ); + check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 ); + check_thread_instance( name, main_module, main_module, (HINSTANCE)0x12345678 ); + check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 ); + ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" ); + + /* check system classes */ + + /* we cannot register a global class with the name of a system class */ + cls.style |= CS_GLOBALCLASS; + cls.lpszMenuName = "button_main_module"; + cls.lpszClassName = "BUTTON"; + cls.hInstance = main_module; + ok( !RegisterClassA( &cls ), "Succeeded registering global button class for main module\n" ); + ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() ); + cls.hInstance = kernel32; + ok( !RegisterClassA( &cls ), "Succeeded registering global button class for kernel32\n" ); + ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() ); + + /* local class is OK however */ + cls.style &= ~CS_GLOBALCLASS; + cls.lpszMenuName = "button_main_module"; + cls.hInstance = main_module; + ok( RegisterClassA( &cls ), "Failed to register local button class for main module\n" ); + check_class( main_module, "BUTTON", "button_main_module" ); + cls.lpszMenuName = "button_kernel32"; + cls.hInstance = kernel32; + ok( RegisterClassA( &cls ), "Failed to register local button class for kernel32\n" ); + check_class( kernel32, "BUTTON", "button_kernel32" ); + check_class( main_module, "BUTTON", "button_main_module" ); + ok( UnregisterClassA( "BUTTON", kernel32 ), "Unregister failed for kernel32 button\n" ); + ok( UnregisterClassA( "BUTTON", main_module ), "Unregister failed for main module button\n" ); + /* GetClassInfo sets instance to passed value for global classes */ + check_instance( "BUTTON", 0, 0, user32 ); + check_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); + check_instance( "BUTTON", user32, 0, user32 ); + check_thread_instance( "BUTTON", 0, 0, user32 ); + check_thread_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); + check_thread_instance( "BUTTON", user32, 0, user32 ); + + /* we can unregister system classes */ + ok( GetClassInfo( 0, "BUTTON", &wc ), "Button class not found with null instance\n" ); + ok( GetClassInfo( kernel32, "BUTTON", &wc ), "Button class not found with kernel32\n" ); + ok( UnregisterClass( "BUTTON", (HINSTANCE)0x12345678 ), "Failed to unregister button\n" ); + ok( !UnregisterClass( "BUTTON", (HINSTANCE)0x87654321 ), "Unregistered button a second time\n" ); + ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() ); + ok( !GetClassInfo( 0, "BUTTON", &wc ), "Button still exists\n" ); + ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() ); + + /* we can change the instance of a system class */ + check_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); + check_thread_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); + hwnd = CreateWindowExA( 0, "EDIT", "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 ); + SetClassLongA( hwnd, GCL_HMODULE, 0xdeadbeef ); + check_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef ); + check_thread_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef ); +} + +static LRESULT WINAPI TestDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +static BOOL RegisterTestDialog(HINSTANCE hInstance) +{ + WNDCLASSEX wcx; + ATOM atom = 0; + + ZeroMemory(&wcx, sizeof(WNDCLASSEX)); + wcx.cbSize = sizeof(wcx); + wcx.lpfnWndProc = TestDlgProc; + wcx.cbClsExtra = 0; + wcx.cbWndExtra = DLGWINDOWEXTRA; + wcx.hInstance = hInstance; + wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wcx.hCursor = LoadCursor(NULL, IDC_ARROW); + wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wcx.lpszClassName = "TestDialog"; + wcx.lpszMenuName = "TestDialog"; + wcx.hIconSm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(5), + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_DEFAULTCOLOR); + + atom = RegisterClassEx(&wcx); + ok(atom != 0, "RegisterClassEx returned 0\n"); + + return atom; +} + +/* test registering a dialog box created by using the CLASS directive in a + resource file, then test creating the dialog using CreateDialogParam. */ +static void WINAPI CreateDialogParamTest(HINSTANCE hInstance) +{ + HWND hWndMain; + + if (RegisterTestDialog(hInstance)) + { + hWndMain = CreateDialogParam(hInstance, "CLASS_TEST_DIALOG", NULL, 0, 0); + ok(hWndMain != NULL, "CreateDialogParam returned NULL\n"); + ShowWindow(hWndMain, SW_SHOW); + DestroyWindow(hWndMain); + } +} + +START_TEST(class) +{ + HANDLE hInstance = GetModuleHandleA( NULL ); + + if (!GetModuleHandleW(0)) + { + trace("Class test is incompatible with Win9x implementation, skipping\n"); + return; + } + + ClassTest(hInstance,FALSE); + ClassTest(hInstance,TRUE); + CreateDialogParamTest(hInstance); + test_styles(); + test_instances(); +} diff --git a/reactos/regtests/winetests/user32/clipboard.c b/reactos/regtests/winetests/user32/clipboard.c new file mode 100755 index 00000000000..5f90e7b0ff2 --- /dev/null +++ b/reactos/regtests/winetests/user32/clipboard.c @@ -0,0 +1,198 @@ +/* + * Unit test suite for clipboard functions. + * + * Copyright 2002 Dmitry Timoshkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "wine/test.h" +#include "winbase.h" +#include "winerror.h" +#include "winuser.h" + +static BOOL is_win9x = FALSE; + +#define test_last_error(expected_error) \ + do \ + { \ + if (!is_win9x) \ + ok(GetLastError() == expected_error, \ + "Last error should be set to %d, not %ld\n", \ + expected_error, GetLastError()); \ + } while (0) + +static void test_ClipboardOwner(void) +{ + HWND hWnd1, hWnd2; + BOOL ret; + + SetLastError(0xdeadbeef); + ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef, + "could not perform clipboard test: clipboard already owned\n"); + + hWnd1 = CreateWindowExA(0, "static", NULL, WS_POPUP, + 0, 0, 10, 10, 0, 0, 0, NULL); + ok(hWnd1 != 0, "CreateWindowExA error %ld\n", GetLastError()); + trace("hWnd1 = %p\n", hWnd1); + + hWnd2 = CreateWindowExA(0, "static", NULL, WS_POPUP, + 0, 0, 10, 10, 0, 0, 0, NULL); + ok(hWnd2 != 0, "CreateWindowExA error %ld\n", GetLastError()); + trace("hWnd2 = %p\n", hWnd2); + + SetLastError(0xdeadbeef); + ok(!CloseClipboard(), "CloseClipboard should fail if clipboard wasn't open\n"); + test_last_error(ERROR_CLIPBOARD_NOT_OPEN); + + ok(OpenClipboard(0), "OpenClipboard failed\n"); + ok(!GetClipboardOwner(), "clipboard should still be not owned\n"); + ok(!OpenClipboard(hWnd1), "OpenClipboard should fail since clipboard already opened\n"); + ret = CloseClipboard(); + ok( ret, "CloseClipboard error %ld\n", GetLastError()); + + ok(OpenClipboard(hWnd1), "OpenClipboard failed\n"); + + SetLastError(0xdeadbeef); + ok(!OpenClipboard(hWnd2) && GetLastError() == 0xdeadbeef, + "OpenClipboard should fail without setting last error value\n"); + + SetLastError(0xdeadbeef); + ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef, "clipboard should still be not owned\n"); + ret = EmptyClipboard(); + ok( ret, "EmptyClipboard error %ld\n", GetLastError()); + ok(GetClipboardOwner() == hWnd1, "clipboard should be owned by %p, not by %p\n", hWnd1, GetClipboardOwner()); + + SetLastError(0xdeadbeef); + ok(!OpenClipboard(hWnd2) && GetLastError() == 0xdeadbeef, + "OpenClipboard should fail without setting last error value\n"); + + ret = CloseClipboard(); + ok( ret, "CloseClipboard error %ld\n", GetLastError()); + ok(GetClipboardOwner() == hWnd1, "clipboard should still be owned\n"); + + ret = DestroyWindow(hWnd1); + ok( ret, "DestroyWindow error %ld\n", GetLastError()); + ret = DestroyWindow(hWnd2); + ok( ret, "DestroyWindow error %ld\n", GetLastError()); + SetLastError(0xdeadbeef); + ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef, "clipboard should not be owned\n"); +} + +static void test_RegisterClipboardFormatA(void) +{ + ATOM atom_id; + UINT format_id, format_id2; + char buf[256]; + int len; + BOOL ret; + + format_id = RegisterClipboardFormatA("my_cool_clipboard_format"); + ok(format_id > 0xc000 && format_id < 0xffff, "invalid clipboard format id %04x\n", format_id); + + format_id2 = RegisterClipboardFormatA("MY_COOL_CLIPBOARD_FORMAT"); + ok(format_id2 == format_id, "invalid clipboard format id %04x\n", format_id2); + + len = GetClipboardFormatNameA(format_id, buf, 256); + ok(len == lstrlenA("my_cool_clipboard_format"), "wrong format name length %d\n", len); + ok(!lstrcmpA(buf, "my_cool_clipboard_format"), "wrong format name \"%s\"\n", buf); + + lstrcpyA(buf, "foo"); + SetLastError(0xdeadbeef); + len = GetAtomNameA((ATOM)format_id, buf, 256); + ok(len == 0, "GetAtomNameA should fail\n"); + test_last_error(ERROR_INVALID_HANDLE); + +todo_wine +{ + lstrcpyA(buf, "foo"); + SetLastError(0xdeadbeef); + len = GlobalGetAtomNameA((ATOM)format_id, buf, 256); + ok(len == 0, "GlobalGetAtomNameA should fail\n"); + test_last_error(ERROR_INVALID_HANDLE); +} + + SetLastError(0xdeadbeef); + atom_id = FindAtomA("my_cool_clipboard_format"); + ok(atom_id == 0, "FindAtomA should fail\n"); + test_last_error(ERROR_FILE_NOT_FOUND); + +#if 0 + /* this relies on the clipboard and global atom table being different */ + SetLastError(0xdeadbeef); + atom_id = GlobalFindAtomA("my_cool_clipboard_format"); + ok(atom_id == 0, "GlobalFindAtomA should fail\n"); + test_last_error(ERROR_FILE_NOT_FOUND); + + for (format_id = 0; format_id < 0xffff; format_id++) + { + SetLastError(0xdeadbeef); + len = GetClipboardFormatNameA(format_id, buf, 256); + + if (format_id < 0xc000) + { + ok(!len, "GetClipboardFormatNameA should fail, but it returned %d (%s)\n", len, buf); + test_last_error(ERROR_INVALID_PARAMETER); + } + else + { + if (len) + trace("%04x: %s\n", format_id, len ? buf : ""); + else + test_last_error(ERROR_INVALID_HANDLE); + } + } +#endif + + ret = OpenClipboard(0); + ok( ret, "OpenClipboard error %ld\n", GetLastError()); + + trace("# of formats available: %d\n", CountClipboardFormats()); + + format_id = 0; + while ((format_id = EnumClipboardFormats(format_id))) + { + ok(IsClipboardFormatAvailable(format_id), "format %04x was listed as available\n", format_id); + len = GetClipboardFormatNameA(format_id, buf, 256); + trace("%04x: %s\n", format_id, len ? buf : ""); + } + + ret = EmptyClipboard(); + ok( ret, "EmptyClipboard error %ld\n", GetLastError()); + ret =CloseClipboard(); + ok( ret, "CloseClipboard error %ld\n", GetLastError()); + + if (CountClipboardFormats()) + { + SetLastError(0xdeadbeef); + ok(!EnumClipboardFormats(0), "EnumClipboardFormats should fail if clipboard wasn't open\n"); + ok(GetLastError() == ERROR_CLIPBOARD_NOT_OPEN, + "Last error should be set to ERROR_CLIPBOARD_NOT_OPEN, not %ld\n", GetLastError()); + } + + SetLastError(0xdeadbeef); + ok(!EmptyClipboard(), "EmptyClipboard should fail if clipboard wasn't open\n"); + test_last_error(ERROR_CLIPBOARD_NOT_OPEN); +} + +START_TEST(clipboard) +{ + SetLastError(0xdeadbeef); + FindAtomW(NULL); + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) is_win9x = TRUE; + + test_RegisterClipboardFormatA(); + test_ClipboardOwner(); +} diff --git a/reactos/regtests/winetests/user32/dce.c b/reactos/regtests/winetests/user32/dce.c new file mode 100755 index 00000000000..76beef2702e --- /dev/null +++ b/reactos/regtests/winetests/user32/dce.c @@ -0,0 +1,416 @@ +/* + * Unit tests for DCE support + * + * Copyright 2005 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#include "wine/test.h" + +static HWND hwnd_cache, hwnd_owndc, hwnd_classdc, hwnd_classdc2; + +/* test behavior of DC attributes with various GetDC/ReleaseDC combinations */ +static void test_dc_attributes(void) +{ + HDC hdc, old_hdc; + INT rop, def_rop; + + /* test cache DC */ + + hdc = GetDC( hwnd_cache ); + def_rop = GetROP2( hdc ); + + SetROP2( hdc, R2_WHITE ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop ); + + ReleaseDC( hwnd_cache, hdc ); + hdc = GetDC( hwnd_cache ); + rop = GetROP2( hdc ); + ok( rop == def_rop, "wrong ROP2 %d after release\n", rop ); + SetROP2( hdc, R2_WHITE ); + ReleaseDC( hwnd_cache, hdc ); + + hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS ); + rop = GetROP2( hdc ); + /* Win9x seems to silently ignore DCX_NORESETATTRS */ + ok( rop == def_rop || rop == R2_WHITE, "wrong ROP2 %d\n", rop ); + + SetROP2( hdc, R2_WHITE ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop ); + + ReleaseDC( hwnd_cache, hdc ); + hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS ); + rop = GetROP2( hdc ); + ok( rop == def_rop || rop == R2_WHITE, "wrong ROP2 %d after release\n", rop ); + ReleaseDC( hwnd_cache, hdc ); + + hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE ); + rop = GetROP2( hdc ); + ok( rop == def_rop, "wrong ROP2 %d after release\n", rop ); + ReleaseDC( hwnd_cache, hdc ); + + /* test own DC */ + + hdc = GetDC( hwnd_owndc ); + SetROP2( hdc, R2_WHITE ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop ); + + old_hdc = hdc; + ReleaseDC( hwnd_owndc, hdc ); + hdc = GetDC( hwnd_owndc ); + ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop ); + ReleaseDC( hwnd_owndc, hdc ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop ); + + /* test class DC */ + + hdc = GetDC( hwnd_classdc ); + SetROP2( hdc, R2_WHITE ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop ); + + old_hdc = hdc; + ReleaseDC( hwnd_classdc, hdc ); + hdc = GetDC( hwnd_classdc ); + ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop ); + ReleaseDC( hwnd_classdc, hdc ); + rop = GetROP2( hdc ); + ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop ); + + /* test class DC with 2 windows */ + + old_hdc = GetDC( hwnd_classdc ); + SetROP2( old_hdc, R2_BLACK ); + hdc = GetDC( hwnd_classdc2 ); + ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc ); + rop = GetROP2( hdc ); + ok( rop == R2_BLACK, "wrong ROP2 %d for other window\n", rop ); + ReleaseDC( hwnd_classdc, old_hdc ); + ReleaseDC( hwnd_classdc, hdc ); + rop = GetROP2( hdc ); + ok( rop == R2_BLACK, "wrong ROP2 %d after release\n", rop ); +} + + +/* test behavior with various invalid parameters */ +static void test_parameters(void) +{ + HDC hdc; + + hdc = GetDC( hwnd_cache ); + ok( ReleaseDC( hwnd_owndc, hdc ), "ReleaseDC with wrong window should succeed\n" ); + + hdc = GetDC( hwnd_cache ); + ok( !ReleaseDC( hwnd_cache, 0 ), "ReleaseDC with wrong HDC should fail\n" ); + ok( ReleaseDC( hwnd_cache, hdc ), "correct ReleaseDC should succeed\n" ); + ok( !ReleaseDC( hwnd_cache, hdc ), "second ReleaseDC should fail\n" ); + + hdc = GetDC( hwnd_owndc ); + ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" ); + hdc = GetDC( hwnd_owndc ); + ok( ReleaseDC( hwnd_owndc, hdc ), "correct ReleaseDC should succeed\n" ); + ok( ReleaseDC( hwnd_owndc, hdc ), "second ReleaseDC should succeed\n" ); + + hdc = GetDC( hwnd_classdc ); + ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" ); + hdc = GetDC( hwnd_classdc ); + ok( ReleaseDC( hwnd_classdc, hdc ), "correct ReleaseDC should succeed\n" ); + ok( ReleaseDC( hwnd_classdc, hdc ), "second ReleaseDC should succeed\n" ); +} + + +static void test_dc_visrgn(void) +{ + HDC old_hdc, hdc; + HRGN hrgn, hrgn2; + RECT rect; + + /* cache DC */ + + SetRect( &rect, 10, 10, 20, 20 ); + MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 ); + hrgn = CreateRectRgnIndirect( &rect ); + hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + ReleaseDC( hwnd_cache, hdc ); + ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" ); + + /* cache DC with NORESETATTRS */ + + SetRect( &rect, 10, 10, 20, 20 ); + MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 ); + hrgn = CreateRectRgnIndirect( &rect ); + hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE | DCX_NORESETATTRS ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + ReleaseDC( hwnd_cache, hdc ); + ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" ); + hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20), + "clip box sould have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ReleaseDC( hwnd_cache, hdc ); + + /* window DC */ + + SetRect( &rect, 10, 10, 20, 20 ); + MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 ); + hrgn = CreateRectRgnIndirect( &rect ); + hdc = GetDCEx( hwnd_owndc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + ReleaseDC( hwnd_owndc, hdc ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + hdc = GetDCEx( hwnd_owndc, 0, DCX_USESTYLE ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + ReleaseDC( hwnd_owndc, hdc ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + + SetRect( &rect, 20, 20, 30, 30 ); + MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 ); + hrgn2 = CreateRectRgnIndirect( &rect ); + hdc = GetDCEx( hwnd_owndc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE ); + ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" ); + ReleaseDC( hwnd_owndc, hdc ); + ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" ); + hdc = GetDCEx( hwnd_owndc, 0, DCX_EXCLUDERGN | DCX_USESTYLE ); + ok( GetRgnBox( hrgn2, &rect ) == ERROR, "region must no longer be valid\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30), + "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ReleaseDC( hwnd_owndc, hdc ); + + /* class DC */ + + SetRect( &rect, 10, 10, 20, 20 ); + MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 ); + hrgn = CreateRectRgnIndirect( &rect ); + hdc = GetDCEx( hwnd_classdc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + ReleaseDC( hwnd_classdc, hdc ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + + hdc = GetDCEx( hwnd_classdc, 0, DCX_USESTYLE ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + ReleaseDC( hwnd_classdc, hdc ); + ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" ); + + SetRect( &rect, 20, 20, 30, 30 ); + MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 ); + hrgn2 = CreateRectRgnIndirect( &rect ); + hdc = GetDCEx( hwnd_classdc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE ); + ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" ); + + old_hdc = hdc; + hdc = GetDCEx( hwnd_classdc2, 0, DCX_USESTYLE ); + ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc ); + ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30), + "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ReleaseDC( hwnd_classdc2, hdc ); + ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" ); + hdc = GetDCEx( hwnd_classdc2, 0, DCX_EXCLUDERGN | DCX_USESTYLE ); + ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" ); + ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30), + "clip box must have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ReleaseDC( hwnd_classdc2, hdc ); +} + + +/* test various BeginPaint/EndPaint behaviors */ +static void test_begin_paint(void) +{ + HDC old_hdc, hdc; + RECT rect; + PAINTSTRUCT ps; + + /* cache DC */ + + /* clear update region */ + RedrawWindow( hwnd_cache, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE ); + SetRect( &rect, 10, 10, 20, 20 ); + RedrawWindow( hwnd_cache, &rect, 0, RDW_INVALIDATE ); + hdc = BeginPaint( hwnd_cache, &ps ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + EndPaint( hwnd_cache, &ps ); + + /* window DC */ + + RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE ); + SetRect( &rect, 10, 10, 20, 20 ); + RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE ); + hdc = BeginPaint( hwnd_owndc, &ps ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ReleaseDC( hwnd_owndc, hdc ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + EndPaint( hwnd_owndc, &ps ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20), + "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE ); + SetRect( &rect, 10, 10, 20, 20 ); + RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE|RDW_ERASE ); + ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20), + "clip box should be the whole window %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + RedrawWindow( hwnd_owndc, NULL, 0, RDW_ERASENOW ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20), + "clip box should still be the whole window %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + + /* class DC */ + + RedrawWindow( hwnd_classdc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE ); + SetRect( &rect, 10, 10, 20, 20 ); + RedrawWindow( hwnd_classdc, &rect, 0, RDW_INVALIDATE ); + hdc = BeginPaint( hwnd_classdc, &ps ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20, + "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + + old_hdc = hdc; + hdc = GetDC( hwnd_classdc2 ); + ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc ); + SetRectEmpty( &rect ); + GetClipBox( hdc, &rect ); + ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20), + "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); +} + + +START_TEST(dce) +{ + WNDCLASSA cls; + + cls.style = CS_DBLCLKS; + cls.lpfnWndProc = DefWindowProcA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "cache_class"; + RegisterClassA(&cls); + cls.style = CS_DBLCLKS | CS_OWNDC; + cls.lpszClassName = "owndc_class"; + RegisterClassA(&cls); + cls.style = CS_DBLCLKS | CS_CLASSDC; + cls.lpszClassName = "classdc_class"; + RegisterClassA(&cls); + + hwnd_cache = CreateWindowA("cache_class", NULL, WS_OVERLAPPED | WS_VISIBLE, + 0, 0, 100, 100, + 0, 0, GetModuleHandleA(0), NULL ); + hwnd_owndc = CreateWindowA("owndc_class", NULL, WS_OVERLAPPED | WS_VISIBLE, + 0, 200, 100, 100, + 0, 0, GetModuleHandleA(0), NULL ); + hwnd_classdc = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE, + 200, 0, 100, 100, + 0, 0, GetModuleHandleA(0), NULL ); + hwnd_classdc2 = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE, + 200, 200, 100, 100, + 0, 0, GetModuleHandleA(0), NULL ); + test_dc_attributes(); + test_parameters(); + test_dc_visrgn(); + test_begin_paint(); +} diff --git a/reactos/regtests/winetests/user32/dde.c b/reactos/regtests/winetests/user32/dde.c new file mode 100755 index 00000000000..deacc78a3aa --- /dev/null +++ b/reactos/regtests/winetests/user32/dde.c @@ -0,0 +1,390 @@ +/* + * Unit tests for DDE functions + * + * Copyright (c) 2004 Dmitry Timoshkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "wine/test.h" +#include "winbase.h" +#include "winuser.h" +#include "dde.h" +#include "ddeml.h" +#include "winerror.h" + +static const WCHAR TEST_DDE_SERVICE[] = {'T','e','s','t','D','D','E','S','e','r','v','i','c','e',0}; + +static const char exec_cmdA[] = "ANSI dde command"; +static const WCHAR exec_cmdW[] = {'u','n','i','c','o','d','e',' ','d','d','e',' ','c','o','m','m','a','n','d',0}; + +static WNDPROC old_dde_client_wndproc; + +LRESULT WINAPI hook_dde_client_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + UINT_PTR lo, hi; + + trace("hook_dde_client_wndproc: %p %04x %08x %08lx\n", hwnd, msg, wparam, lparam); + + switch (msg) + { + case WM_DDE_ACK: + UnpackDDElParam(WM_DDE_ACK, lparam, &lo, &hi); + trace("WM_DDE_ACK: status %04x hglobal %p\n", lo, (HGLOBAL)hi); + break; + + default: + break; + } + return CallWindowProcA(old_dde_client_wndproc, hwnd, msg, wparam, lparam); +} + +static LRESULT WINAPI dde_server_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + trace("dde_server_wndproc: %p %04x %08x %08lx\n", hwnd, msg, wparam, lparam); + + switch (msg) + { + case WM_DDE_INITIATE: + { + ATOM aService = GlobalAddAtomW(TEST_DDE_SERVICE); + + trace("server: got WM_DDE_INITIATE from %p with %08lx\n", (HWND)wparam, lparam); + + if (LOWORD(lparam) == aService) + { + ok(!IsWindowUnicode((HWND)wparam), "client should be an ANSI window\n"); + old_dde_client_wndproc = (WNDPROC)SetWindowLongPtrA((HWND)wparam, GWLP_WNDPROC, (ULONG_PTR)hook_dde_client_wndproc); + trace("server: sending WM_DDE_ACK to %p\n", (HWND)wparam); + SendMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, MAKELPARAM(aService, 0)); + } + else + GlobalDeleteAtom(aService); + return 0; + } + + case WM_DDE_EXECUTE: + { + DDEACK ack; + WORD status; + LPCSTR cmd; + UINT_PTR lo, hi; + + trace("server: got WM_DDE_EXECUTE from %p with %08lx\n", (HWND)wparam, lparam); + + UnpackDDElParam(WM_DDE_EXECUTE, lparam, &lo, &hi); + trace("%08lx => lo %04x hi %04x\n", lparam, lo, hi); + + ack.bAppReturnCode = 0; + ack.reserved = 0; + ack.fBusy = 0; + + cmd = GlobalLock((HGLOBAL)hi); + + if (!cmd || (lstrcmpW((LPCWSTR)cmd, exec_cmdW) && lstrcmpA(cmd, exec_cmdA))) + { + trace("ignoring unknown WM_DDE_EXECUTE command\n"); + /* We have to send a negative acknowledge even if we don't + * accept the command, otherwise Windows goes mad and next time + * we send an acknowledge DDEML drops the connection. + * Not sure how to call it: a bug or a feature. + */ + ack.fAck = 0; + } + else + ack.fAck = 1; + GlobalUnlock((HGLOBAL)hi); + + trace("server: posting %s WM_DDE_ACK to %p\n", ack.fAck ? "POSITIVE" : "NEGATIVE", (HWND)wparam); + + status = *((WORD *)&ack); + lparam = ReuseDDElParam(lparam, WM_DDE_EXECUTE, WM_DDE_ACK, status, hi); + + PostMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, lparam); + return 0; + } + + case WM_DDE_TERMINATE: + { + DDEACK ack; + WORD status; + + trace("server: got WM_DDE_TERMINATE from %p with %08lx\n", (HWND)wparam, lparam); + + ack.bAppReturnCode = 0; + ack.reserved = 0; + ack.fBusy = 0; + ack.fAck = 1; + + trace("server: posting %s WM_DDE_ACK to %p\n", ack.fAck ? "POSITIVE" : "NEGATIVE", (HWND)wparam); + + status = *((WORD *)&ack); + lparam = PackDDElParam(WM_DDE_ACK, status, 0); + + PostMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, lparam); + return 0; + } + + default: + break; + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static LRESULT WINAPI dde_client_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static BOOL create_dde_windows(HWND *hwnd_client, HWND *hwnd_server) +{ + WNDCLASSA wcA; + WNDCLASSW wcW; + static const WCHAR server_class_name[] = {'d','d','e','_','s','e','r','v','e','r','_','w','i','n','d','o','w',0}; + static const char client_class_name[] = "dde_client_window"; + + memset(&wcW, 0, sizeof(wcW)); + wcW.lpfnWndProc = dde_server_wndproc; + wcW.lpszClassName = server_class_name; + wcW.hInstance = GetModuleHandleA(0); + if (!RegisterClassW(&wcW)) return FALSE; + + memset(&wcA, 0, sizeof(wcA)); + wcA.lpfnWndProc = dde_client_wndproc; + wcA.lpszClassName = client_class_name; + wcA.hInstance = GetModuleHandleA(0); + assert(RegisterClassA(&wcA)); + + *hwnd_server = CreateWindowExW(0, server_class_name, NULL, + WS_POPUP, + 100, 100, CW_USEDEFAULT, CW_USEDEFAULT, + GetDesktopWindow(), 0, + GetModuleHandleA(0), NULL); + assert(*hwnd_server); + + *hwnd_client = CreateWindowExA(0, client_class_name, NULL, + WS_POPUP, + 100, 100, CW_USEDEFAULT, CW_USEDEFAULT, + GetDesktopWindow(), 0, + GetModuleHandleA(0), NULL); + assert(*hwnd_client); + + trace("server hwnd %p, client hwnd %p\n", *hwnd_server, *hwnd_client); + + ok(IsWindowUnicode(*hwnd_server), "server has to be a unicode window\n"); + ok(!IsWindowUnicode(*hwnd_client), "client has to be an ANSI window\n"); + + return TRUE; +} + +static HDDEDATA CALLBACK client_dde_callback(UINT uType, UINT uFmt, HCONV hconv, + HSZ hsz1, HSZ hsz2, HDDEDATA hdata, + ULONG_PTR dwData1, ULONG_PTR dwData2) +{ + static const char * const cmd_type[15] = { + "XTYP_ERROR", "XTYP_ADVDATA", "XTYP_ADVREQ", "XTYP_ADVSTART", + "XTYP_ADVSTOP", "XTYP_EXECUTE", "XTYP_CONNECT", "XTYP_CONNECT_CONFIRM", + "XTYP_XACT_COMPLETE", "XTYP_POKE", "XTYP_REGISTER", "XTYP_REQUEST", + "XTYP_DISCONNECT", "XTYP_UNREGISTER", "XTYP_WILDCONNECT" }; + UINT type; + const char *cmd_name; + + type = (uType & XTYP_MASK) >> XTYP_SHIFT; + cmd_name = (type >= 0 && type <= 14) ? cmd_type[type] : "unknown"; + + trace("client_dde_callback: %04x (%s) %d %p %p %p %p %08lx %08lx\n", + uType, cmd_name, uFmt, hconv, hsz1, hsz2, hdata, dwData1, dwData2); + return 0; +} + +static void test_dde_transaction(void) +{ + HSZ hsz_server; + DWORD dde_inst, ret, err; + HCONV hconv; + HWND hwnd_client, hwnd_server; + CONVINFO info; + HDDEDATA hdata; + static const char test_cmd[] = "test dde command"; + + /* server: unicode, client: ansi */ + if (!create_dde_windows(&hwnd_client, &hwnd_server)) return; + + dde_inst = 0; + ret = DdeInitializeA(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0); + ok(ret == DMLERR_NO_ERROR, "DdeInitializeW failed with error %04lx (%x)\n", + ret, DdeGetLastError(dde_inst)); + + hsz_server = DdeCreateStringHandleW(dde_inst, TEST_DDE_SERVICE, CP_WINUNICODE); + + hconv = DdeConnect(dde_inst, hsz_server, 0, NULL); + ok(hconv != 0, "DdeConnect error %x\n", DdeGetLastError(dde_inst)); + err = DdeGetLastError(dde_inst); + ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err); + + info.cb = sizeof(info); + ret = DdeQueryConvInfo(hconv, QID_SYNC, &info); + ok(ret, "wrong info size %ld, DdeQueryConvInfo error %x\n", ret, DdeGetLastError(dde_inst)); + /* should be CP_WINANSI since we used DdeInitializeA */ + ok(info.ConvCtxt.iCodePage == CP_WINANSI, "wrong iCodePage %d\n", info.ConvCtxt.iCodePage); + ok(!info.hConvPartner, "unexpected info.hConvPartner: %p\n", info.hConvPartner); +todo_wine { + ok((info.wStatus & DDE_FACK), "unexpected info.wStatus: %04x\n", info.wStatus); +} + ok((info.wStatus & (ST_CONNECTED | ST_CLIENT)) == (ST_CONNECTED | ST_CLIENT), "unexpected info.wStatus: %04x\n", info.wStatus); + ok(info.wConvst == XST_CONNECTED, "unexpected info.wConvst: %04x\n", info.wConvst); + ok(info.wType == 0, "unexpected info.wType: %04x\n", info.wType); + + trace("hwnd %p, hwndPartner %p\n", info.hwnd, info.hwndPartner); + + trace("sending test client transaction command\n"); + ret = 0xdeadbeef; + hdata = DdeClientTransaction((LPBYTE)test_cmd, strlen(test_cmd) + 1, hconv, (HSZ)0xdead, 0xbeef, XTYP_EXECUTE, 1000, &ret); + ok(!hdata, "DdeClientTransaction succeeded\n"); + ok(ret == DDE_FNOTPROCESSED, "wrong status code %04lx\n", ret); + err = DdeGetLastError(dde_inst); + ok(err == DMLERR_NOTPROCESSED, "wrong dde error %lx\n", err); + + trace("sending ANSI client transaction command\n"); + ret = 0xdeadbeef; + hdata = DdeClientTransaction((LPBYTE)exec_cmdA, lstrlenA(exec_cmdA) + 1, hconv, 0, 0, XTYP_EXECUTE, 1000, &ret); + ok(hdata != 0, "DdeClientTransaction returned %p, error %x\n", hdata, DdeGetLastError(dde_inst)); + ok(ret == DDE_FACK, "wrong status code %04lx\n", ret); + + err = DdeGetLastError(dde_inst); + ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err); + + trace("sending unicode client transaction command\n"); + ret = 0xdeadbeef; + hdata = DdeClientTransaction((LPBYTE)exec_cmdW, (lstrlenW(exec_cmdW) + 1) * sizeof(WCHAR), hconv, 0, 0, XTYP_EXECUTE, 1000, &ret); + ok(hdata != 0, "DdeClientTransaction returned %p, error %x\n", hdata, DdeGetLastError(dde_inst)); + ok(ret == DDE_FACK, "wrong status code %04lx\n", ret); + err = DdeGetLastError(dde_inst); + ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err); + + ok(DdeDisconnect(hconv), "DdeDisconnect error %x\n", DdeGetLastError(dde_inst)); + + info.cb = sizeof(info); + ret = DdeQueryConvInfo(hconv, QID_SYNC, &info); + ok(!ret, "DdeQueryConvInfo should fail\n"); + err = DdeGetLastError(dde_inst); +todo_wine { + ok(err == DMLERR_INVALIDPARAMETER, "wrong dde error %lx\n", err); +} + + ok(DdeFreeStringHandle(dde_inst, hsz_server), "DdeFreeStringHandle error %x\n", DdeGetLastError(dde_inst)); + + /* This call hangs on win2k SP4. + DdeUninitialize(dde_inst);*/ + + DestroyWindow(hwnd_client); + DestroyWindow(hwnd_server); + + DdeUninitialize(dde_inst); +} + +static void test_DdeCreateStringHandleW(DWORD dde_inst, int codepage) +{ + static const WCHAR dde_string[] = {'D','D','E',' ','S','t','r','i','n','g',0}; + HSZ str_handle; + WCHAR bufW[256]; + char buf[256]; + int ret; + + str_handle = DdeCreateStringHandleW(dde_inst, dde_string, codepage); + ok(str_handle != 0, "DdeCreateStringHandleW failed with error %08x\n", + DdeGetLastError(dde_inst)); + + ret = DdeQueryStringW(dde_inst, str_handle, NULL, 0, codepage); + if (codepage == CP_WINANSI) + ok(ret == 1, "DdeQueryStringW returned wrong length %d\n", ret); + else + ok(ret == lstrlenW(dde_string), "DdeQueryStringW returned wrong length %d\n", ret); + + ret = DdeQueryStringW(dde_inst, str_handle, bufW, 256, codepage); + if (codepage == CP_WINANSI) + { + ok(ret == 1, "DdeQueryStringW returned wrong length %d\n", ret); + ok(!lstrcmpA("D", (LPCSTR)bufW), "DdeQueryStringW returned wrong string\n"); + } + else + { + ok(ret == lstrlenW(dde_string), "DdeQueryStringW returned wrong length %d\n", ret); + ok(!lstrcmpW(dde_string, bufW), "DdeQueryStringW returned wrong string\n"); + } + + ret = DdeQueryStringA(dde_inst, str_handle, buf, 256, CP_WINANSI); + if (codepage == CP_WINANSI) + { + ok(ret == 1, "DdeQueryStringA returned wrong length %d\n", ret); + ok(!lstrcmpA("D", buf), "DdeQueryStringW returned wrong string\n"); + } + else + { + ok(ret == lstrlenA("DDE String"), "DdeQueryStringA returned wrong length %d\n", ret); + ok(!lstrcmpA("DDE String", buf), "DdeQueryStringA returned wrong string %s\n", buf); + } + + ret = DdeQueryStringA(dde_inst, str_handle, buf, 256, CP_WINUNICODE); + if (codepage == CP_WINANSI) + { + ok(ret == 1, "DdeQueryStringA returned wrong length %d\n", ret); + ok(!lstrcmpA("D", buf), "DdeQueryStringA returned wrong string %s\n", buf); + } + else + { + ok(ret == lstrlenA("DDE String"), "DdeQueryStringA returned wrong length %d\n", ret); + ok(!lstrcmpW(dde_string, (LPCWSTR)buf), "DdeQueryStringW returned wrong string\n"); + } + + ok(DdeFreeStringHandle(dde_inst, str_handle), "DdeFreeStringHandle failed\n"); +} + +static void test_DdeCreateStringHandle(void) +{ + DWORD dde_inst, ret; + + dde_inst = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = DdeInitializeW(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0); + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + trace("Skipping the DDE test on a Win9x platform\n"); + return; + } + + ok(ret == DMLERR_INVALIDPARAMETER, "DdeInitializeW should fail, but got %04lx instead\n", ret); + ok(DdeGetLastError(dde_inst) == DMLERR_INVALIDPARAMETER, "expected DMLERR_INVALIDPARAMETER\n"); + + dde_inst = 0; + ret = DdeInitializeW(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0); + ok(ret == DMLERR_NO_ERROR, "DdeInitializeW failed with error %04lx (%08x)\n", + ret, DdeGetLastError(dde_inst)); + + test_DdeCreateStringHandleW(dde_inst, 0); + test_DdeCreateStringHandleW(dde_inst, CP_WINUNICODE); + test_DdeCreateStringHandleW(dde_inst, CP_WINANSI); + + ok(DdeUninitialize(dde_inst), "DdeUninitialize failed\n"); +} + +START_TEST(dde) +{ + test_DdeCreateStringHandle(); + test_dde_transaction(); +} diff --git a/reactos/regtests/winetests/user32/dialog.c b/reactos/regtests/winetests/user32/dialog.c new file mode 100755 index 00000000000..d55eef4eaed --- /dev/null +++ b/reactos/regtests/winetests/user32/dialog.c @@ -0,0 +1,876 @@ +/* Unit test suite for the dialog functions. + * + * Copyright 2004 Bill Medland + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * + * This test suite currently works by building a quite complex hierarchy of + * objects in a variety of styles and then performs a limited number of tests + * for the previous and next dialog group or tab items. + * + * The test specifically does not test all possibilities at this time since + * there are several cases where the Windows behaviour is rather strange and + * significant work would be required to get the Wine code to duplicate the + * strangeness, especially since most are in situations that would not + * normally be met. + */ + +#include +#include +#include + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" + +#define MAXHWNDS 1024 +static HWND hwnd [MAXHWNDS]; +static unsigned int numwnds=1; /* 0 is reserved for null */ + +/* Global handles */ +static HINSTANCE g_hinst; /* This application's HINSTANCE */ +static HWND g_hwndMain, g_hwndButton1, g_hwndButton2, g_hwndButtonCancel; +static HWND g_hwndTestDlg, g_hwndTestDlgBut1, g_hwndTestDlgBut2, g_hwndTestDlgEdit; +static HWND g_hwndInitialFocusT1, g_hwndInitialFocusT2, g_hwndInitialFocusGroupBox; + +static LONG g_styleInitialFocusT1, g_styleInitialFocusT2; +static BOOL g_bInitialFocusInitDlgResult; + +static int g_terminated; + +typedef struct { + unsigned int id; + int parent; + DWORD style; + DWORD exstyle; +} h_entry; + +static const h_entry hierarchy [] = { + /* 0 is reserved for the null window */ + { 1, 0, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE}, + { 20, 1, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 2, 1, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT}, + { 60, 2, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + /* What happens with groups when the parent is disabled */ + { 8, 2, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, WS_EX_CONTROLPARENT}, + { 85, 8, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP, 0}, + { 9, 8, WS_CHILD, WS_EX_CONTROLPARENT}, + { 86, 9, WS_CHILD | WS_VISIBLE, 0}, + { 87, 9, WS_CHILD | WS_VISIBLE, 0}, + { 31, 8, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 10, 2, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT}, + { 88, 10, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 11, 10, WS_CHILD, WS_EX_CONTROLPARENT}, + { 89, 11, WS_CHILD | WS_VISIBLE, 0}, + { 32, 11, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 90, 11, WS_CHILD | WS_VISIBLE, 0}, + { 33, 10, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 21, 2, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 61, 2, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + { 3, 1, WS_CHILD | WS_VISIBLE | DS_CONTROL, 0}, + { 22, 3, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 62, 3, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + { 7, 3, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT}, + { 4, 7, WS_CHILD | WS_VISIBLE | DS_CONTROL, 0}, + { 83, 4, WS_CHILD | WS_VISIBLE, 0}, + { 5, 4, WS_CHILD | WS_VISIBLE | DS_CONTROL, 0}, + /* A couple of controls around the main dialog */ + { 29, 5, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 81, 5, WS_CHILD | WS_VISIBLE, 0}, + /* The main dialog with lots of controls */ + { 6, 5, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT}, + /* At the start of a dialog */ + /* Disabled controls are skipped */ + { 63, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0}, + /* Invisible controls are skipped */ + { 64, 6, WS_CHILD | WS_TABSTOP, 0}, + /* Invisible disabled controls are skipped */ + { 65, 6, WS_CHILD | WS_DISABLED | WS_TABSTOP, 0}, + /* Non-tabstop controls are skipped for tabs but not for groups */ + { 66, 6, WS_CHILD | WS_VISIBLE, 0}, + /* End of first group, with no tabstops in it */ + { 23, 6, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + /* At last a tabstop */ + { 67, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + /* A group that is totally disabled or invisible */ + { 24, 6, WS_CHILD | WS_DISABLED | WS_GROUP, 0}, + { 68, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0}, + { 69, 6, WS_CHILD | WS_TABSTOP, 0}, + /* A valid group in the middle of the dialog (not the first nor last group*/ + { 25, 6, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + /* A non-tabstop item will be skipped for tabs */ + { 70, 6, WS_CHILD | WS_VISIBLE, 0}, + /* A disabled item will be skipped for tabs and groups */ + { 71, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0}, + /* A valid item will be found for tabs and groups */ + { 72, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + /* A disabled item to skip when looking for the next group item */ + { 73, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0}, + /* The next group begins with an enabled visible label */ + { 26, 6, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + { 74, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + { 75, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + /* That group is terminated by a disabled label */ + { 27, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP, 0}, + { 76, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + { 77, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + /* That group is terminated by an invisible label */ + { 28, 6, WS_CHILD | WS_GROUP, 0}, + /* The end of the dialog with item for loop and recursion testing */ + { 78, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + /* No tabstop so skipped for prev tab, but found for prev group */ + { 79, 6, WS_CHILD | WS_VISIBLE, 0}, + { 80, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0}, + /* A couple of controls after the main dialog */ + { 82, 5, WS_CHILD | WS_VISIBLE, 0}, + { 30, 5, WS_CHILD | WS_VISIBLE | WS_GROUP, 0}, + /* And around them */ + { 84, 4, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0}, + {0, 0, 0, 0} +}; + +static BOOL CreateWindows (HINSTANCE hinst) +{ + const h_entry *p = hierarchy; + + while (p->id != 0) + { + DWORD style, exstyle; + char ctrlname[9]; + + /* Basically assert that the hierarchy is valid and track the + * maximum control number + */ + if (p->id >= numwnds) + { + if (p->id >= sizeof(hwnd)/sizeof(hwnd[0])) + { + trace ("Control %d is out of range\n", p->id); + return FALSE; + } + else + numwnds = p->id+1; + } + if (p->id <= 0) + { + trace ("Control %d is out of range\n", p->id); + return FALSE; + } + if (hwnd[p->id] != 0) + { + trace ("Control %d is used more than once\n", p->id); + return FALSE; + } + + /* Create the control */ + sprintf (ctrlname, "ctrl%4.4d", p->id); + hwnd[p->id] = CreateWindowEx (p->exstyle, TEXT(p->parent ? "static" : "GetNextDlgItemWindowClass"), TEXT(ctrlname), p->style, 10, 10, 10, 10, hwnd[p->parent], p->parent ? (HMENU) (2000 + p->id) : 0, hinst, 0); + if (!hwnd[p->id]) + { + trace ("Failed to create control %d\n", p->id); + return FALSE; + } + + /* Check that the styles are as we specified (except the main one + * which is quite frequently messed up). If this keeps breaking then + * we could mask out the bits that don't concern us. + */ + if (p->parent) + { + style = GetWindowLong (hwnd[p->id], GWL_STYLE); + exstyle = GetWindowLong (hwnd[p->id], GWL_EXSTYLE); + if (style == p->style && exstyle == p->exstyle) + { + trace ("Style mismatch at %d: %8.8lx %8.8lx cf %8.8lx %8.8lx\n", p->id, style, exstyle, p->style, p->exstyle); + } + } + p++; + } + + return TRUE; +} + +/* Form the lParam of a WM_KEYDOWN message */ +static DWORD KeyDownData (int repeat, int scancode, int extended, int wasdown) +{ + return ((repeat & 0x0000FFFF) | ((scancode & 0x00FF) >> 16) | + (extended ? 0x01000000 : 0) | (wasdown ? 0x40000000 : 0)); +} + +/* Form a WM_KEYDOWN VK_TAB message to the specified window */ +static void FormTabMsg (MSG *pMsg, HWND hwnd) +{ + pMsg->hwnd = hwnd; + pMsg->message = WM_KEYDOWN; + pMsg->wParam = VK_TAB; + pMsg->lParam = KeyDownData (1, 0x0F, 0, 0); + /* pMsg->time is not set. It shouldn't be needed */ + /* pMsg->pt is ignored */ +} + +/* Form a WM_KEYDOWN VK_RETURN message to the specified window */ +static void FormEnterMsg (MSG *pMsg, HWND hwnd) +{ + pMsg->hwnd = hwnd; + pMsg->message = WM_KEYDOWN; + pMsg->wParam = VK_RETURN; + pMsg->lParam = KeyDownData (1, 0x1C, 0, 0); + /* pMsg->time is not set. It shouldn't be needed */ + /* pMsg->pt is ignored */ +} + +/*********************************************************************** + * + * The actual tests + */ + +typedef struct +{ + int isok; /* or is it todo */ + int test; + int dlg; + int ctl; + int tab; + int prev; + int res; +} test_record; + +static int id (HWND h) +{ + unsigned int i; + for (i = 0; i < numwnds; i++) + if (hwnd[i] == h) + return i; + return -1; +} + +/* Tests + * + * Tests 1-8 test the hCtl argument of null or the dialog itself. + * + * 1. Prev Group of null is null + * 2. Prev Tab of null is null + * 3. Prev Group of hDlg in hDlg is null + * 4. Prev Tab of hDlg in hDlg is null + * 5. Next Group of null is first visible enabled child + * Check it skips invisible, diabled and both. + * 6. Next Tab of null is first visible enabled tabstop + * Check it skips invisible, disabled, nontabstop, and in combination. + * 7. Next Group of hDlg in hDlg is as of null + * 8. Next Tab of hDlg in hDlg is as of null + * + * Tests 9-14 test descent + * + * 9. DS_CONTROL does not result in descending the hierarchy for Tab Next + * 10. DS_CONTROL does not result in descending the hierarchy for Group Next + * 11. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Next + * 12. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Next + * 13. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Prev + * 14. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Prev + * + * Tests 15-24 are the basic Prev/Next Group tests + * + * 15. Next Group of a visible enabled non-group control is the next visible + * enabled non-group control, if there is one before the next group + * 16. Next Group of a visible enabled non-group control wraps around to the + * beginning of the group on finding a control that starts another group. + * Note that the group is in the middle of the dialog. + * 17. As 16 except note that the next group is started with a disabled + * visible control. + * 18. As 16 except note that the next group is started with an invisible + * enabled control. + * 19. Next Group wraps around the controls of the dialog + * 20. Next Group is the same even if the initial control is disabled. + * 21. Next Group is the same even if the initial control is invisible. + * 22. Next Group is the same even if the initial control has the group style + * 23. Next Group returns the initial control if there is no visible enabled + * control in the group. (Initial control disabled and not group style). + * 24. Prev version of test 16. + * Prev Group of a visible enabled non-group control wraps around to the + * beginning of the group on finding a control that starts the group. + * Note that the group is in the middle of the dialog. + * + * In tests 25 to 28 the control is sitting under dialogs which do not have + * the WS_EX_CONTROLPARENT style and so cannot be reached from the top of + * the dialog. + * + * 25. Next Group of an inaccessible control is as if it were accessible + * 26. Prev Group of an inaccessible control begins searching at the highest + * level ancestor that did not permit recursion down the hierarchy + * 27. Next Tab of an inaccessible control is as if it were accessible + * 28. Prev Tab of an inaccessible control begins searching at the highest + * level ancestor that did not permit recursion down the hierarchy. + * + * Tests 29- are the basic Tab tests + * + * 29. Next Tab of a control is the next visible enabled control with the + * Tabstop style (N.B. skips disabled, invisible and non-tabstop) + * 30. Prev Tab of a control is the previous visible enabled control with the + * Tabstop style (N.B. skips disabled, invisible and non-tabstop) + * 31. Next Tab test with at least two layers of descent and finding the + * result not at the first control. + * 32. Next Tab test with at least two layers of descent with the descent and + * control at the start of each level. + * 33. Prev Tab test with at least two layers of descent and finding the + * result not at the last control. + * 34. Prev Tab test with at least two layers of descent with the descent and + * control at the end of each level. + * + * 35. Passing NULL may result in the first child being the one returned. + * (group test) + * 36. Passing NULL may result in the first child being the one returned. + * (tab test) + */ + +static void GetNextDlgItemTest (void) +{ + static test_record test [] = + { + /* isok test dlg ctl tab prev res */ + + { 1, 1, 6, 0, 0, 1, 0}, + { 1, 2, 6, 0, 1, 1, 0}, + { 1, 3, 6, 6, 0, 1, 0}, + { 1, 4, 6, 6, 1, 1, 0}, + { 1, 5, 6, 0, 0, 0, 66}, + { 1, 6, 6, 0, 1, 0, 67}, + { 1, 7, 6, 6, 0, 0, 66}, + { 1, 8, 6, 6, 1, 0, 67}, + + { 1, 9, 4, 83, 1, 0, 84}, + { 1, 10, 4, 83, 0, 0, 5}, + { 1, 11, 5, 81, 1, 0, 67}, + { 1, 12, 5, 81, 0, 0, 66}, + { 1, 13, 5, 82, 1, 1, 78}, + + { 1, 14, 5, 82, 0, 1, 79}, + { 1, 15, 6, 70, 0, 0, 72}, + { 1, 16, 6, 72, 0, 0, 25}, + { 1, 17, 6, 75, 0, 0, 26}, + { 1, 18, 6, 77, 0, 0, 76}, + { 1, 19, 6, 79, 0, 0, 66}, + { 1, 20, 6, 71, 0, 0, 72}, + { 1, 21, 6, 64, 0, 0, 66}, + + { 1, 22, 6, 25, 0, 0, 70}, + { 1, 23, 6, 68, 0, 0, 68}, + { 1, 24, 6, 25, 0, 1, 72}, + { 1, 25, 1, 70, 0, 0, 72}, + /*{ 0, 26, 1, 70, 0, 1, 3}, Crashes Win95*/ + { 1, 27, 1, 70, 1, 0, 72}, + /*{ 0, 28, 1, 70, 1, 1, 61}, Crashes Win95*/ + + { 1, 29, 6, 67, 1, 0, 72}, + { 1, 30, 6, 72, 1, 1, 67}, + + { 1, 35, 2, 0, 0, 0, 60}, + { 1, 36, 2, 0, 1, 0, 60}, + + { 0, 0, 0, 0, 0, 0, 0} /* End of test */ + }; + const test_record *p = test; + + ok (CreateWindows (g_hinst), "Could not create test windows\n"); + + while (p->dlg) + { + HWND a; + a = (p->tab ? GetNextDlgTabItem : GetNextDlgGroupItem) (hwnd[p->dlg], hwnd[p->ctl], p->prev); + if (p->isok) + { + ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was %d instead of %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res); + } + else + { + todo_wine + { + ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was actually %d matching expected %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res); + } + } + p++; + } +} + +/* + * OnMainWindowCreate + */ +static BOOL OnMainWindowCreate (HWND hwnd, LPCREATESTRUCT lpcs) +{ + g_hwndButton1 = CreateWindow (TEXT("button"), TEXT("Button &1"), + WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | BS_TEXT, + 10, 10, 80, 80, hwnd, (HMENU)100, g_hinst, 0); + if (!g_hwndButton1) return FALSE; + + g_hwndButton2 = CreateWindow (TEXT("button"), TEXT("Button &2"), + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_TEXT, + 110, 10, 80, 80, hwnd, (HMENU)200, g_hinst, 0); + if (!g_hwndButton2) return FALSE; + + g_hwndButtonCancel = CreateWindow (TEXT("button"), TEXT("Cancel"), + WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT, + 210, 10, 80, 80, hwnd, (HMENU)IDCANCEL, g_hinst, 0); + if (!g_hwndButtonCancel) return FALSE; + + return TRUE; +} + + +/* + * OnTestDlgCreate + */ + +static BOOL OnTestDlgCreate (HWND hwnd, LPCREATESTRUCT lpcs) +{ + g_hwndTestDlgEdit = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | + WS_EX_RIGHTSCROLLBAR | WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE, + TEXT("Edit"), TEXT("Edit"), + WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL, + 16,33,184,24, hwnd, (HMENU)101, g_hinst, 0); + if (!g_hwndTestDlgEdit) return FALSE; + + g_hwndTestDlgBut1 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR + | WS_EX_NOPARENTNOTIFY, + TEXT("button"), TEXT("Button &1"), + WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT, + 204,33,30,24, hwnd, (HMENU)201, g_hinst, 0); + if (!g_hwndTestDlgBut1) return FALSE; + + g_hwndTestDlgBut2 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR + | WS_EX_NOPARENTNOTIFY, TEXT("button"), + TEXT("Button &2"), + WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT, + 90,102,80,24, hwnd, (HMENU)IDCANCEL, g_hinst, 0); + if (!g_hwndTestDlgBut2) return FALSE; + + return TRUE; +} + +static LRESULT CALLBACK main_window_procA (HWND hwnd, UINT uiMsg, WPARAM wParam, + LPARAM lParam) +{ + LRESULT result; + switch (uiMsg) + { + /* Add blank case statements for these to ensure we don't use them + * by mistake. + */ + case DM_GETDEFID: break; + case DM_SETDEFID: break; + + case WM_CREATE: + return (OnMainWindowCreate (hwnd, + (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1); + case WM_COMMAND: + if (wParam == IDCANCEL) + { + g_terminated = TRUE; + return 0; + } + break; + } + + result=DefWindowProcA (hwnd, uiMsg, wParam, lParam); + return result; +} + +static LRESULT CALLBACK testDlgWinProc (HWND hwnd, UINT uiMsg, WPARAM wParam, + LPARAM lParam) +{ + LRESULT result; + switch (uiMsg) + { + /* Add blank case statements for these to ensure we don't use them + * by mistake. + */ + case DM_GETDEFID: break; + case DM_SETDEFID: break; + + case WM_CREATE: + return (OnTestDlgCreate (hwnd, + (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1); + } + + result=DefWindowProcA (hwnd, uiMsg, wParam, lParam); + return result; +} + +static BOOL RegisterWindowClasses (void) +{ + WNDCLASSA cls; + + cls.style = 0; + cls.lpfnWndProc = DefWindowProcA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = g_hinst; + cls.hIcon = NULL; + cls.hCursor = LoadCursorA (NULL, IDC_ARROW); + cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + cls.lpszMenuName = NULL; + cls.lpszClassName = "GetNextDlgItemWindowClass"; + + if (!RegisterClassA (&cls)) return FALSE; + + cls.lpfnWndProc = main_window_procA; + cls.lpszClassName = "IsDialogMessageWindowClass"; + + if (!RegisterClassA (&cls)) return FALSE; + + GetClassInfoA(0, "#32770", &cls); + cls.lpfnWndProc = testDlgWinProc; + cls.lpszClassName = "WM_NEXTDLGCTLWndClass"; + if (!RegisterClassA (&cls)) return FALSE; + + return TRUE; +} + +static void WM_NEXTDLGCTLTest(void) +{ + DWORD dwVal; + + g_hwndTestDlg = CreateWindowEx( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR + | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT | WS_EX_APPWINDOW, + "WM_NEXTDLGCTLWndClass", + "WM_NEXTDLGCTL Message test window", + WS_POPUPWINDOW | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_OVERLAPPED | + WS_MINIMIZEBOX | WS_MAXIMIZEBOX | DS_3DLOOK | DS_SETFONT | DS_MODALFRAME, + 0, 0, 235, 135, + NULL, NULL, g_hinst, 0); + + assert (g_hwndTestDlg); + assert (g_hwndTestDlgBut1); + assert (g_hwndTestDlgBut2); + assert (g_hwndTestDlgEdit); + + /* + * Test message DM_SETDEFID + */ + + DefDlgProcA( g_hwndTestDlg, DM_SETDEFID, IDCANCEL, 0 ); + DefDlgProcA( g_hwndTestDlgBut1, BM_SETSTYLE, BS_DEFPUSHBUTTON, FALSE ); + dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0); + + ok ( IDCANCEL == (LOWORD(dwVal)), "Did not set default ID\n" ); + + /* + * Check whether message WM_NEXTDLGCTL is changing the focus to next control and if + * the destination control is a button, style of the button should be changed to + * BS_DEFPUSHBUTTON with out making it default. + */ + + /* + * Keep the focus on Edit control. + */ + + if ( SetFocus( g_hwndTestDlgEdit ) ) + { + ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't set on Edit control\n"); + + /* + * Test message WM_NEXTDLGCTL + */ + DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 ); + ok ((GetFocus() == g_hwndTestDlgBut1), "Focus didn't move to first button\n"); + + /* + * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL" + */ + dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0); + ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n"); + + /* + * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and + * the style of default button changed to BS_PUSHBUTTON. + */ + if ( IDCANCEL == (LOWORD(dwVal)) ) + { + ok ( ((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON), + "Button1 style not set to BS_DEFPUSHBUTTON\n" ); + + ok ( !((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON), + "Button2's style not chaged to BS_PUSHBUTTON\n" ); + } + + /* + * Move focus to Button2 using "WM_NEXTDLGCTL" + */ + DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 ); + ok ((GetFocus() == g_hwndTestDlgBut2), "Focus didn't move to second button\n"); + + /* + * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL" + */ + dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0); + ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n"); + + /* + * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and + * the style of button which lost the focus changed to BS_PUSHBUTTON. + */ + if ( IDCANCEL == (LOWORD(dwVal)) ) + { + ok ( ((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON), + "Button2 style not set to BS_DEFPUSHBUTTON\n" ); + + ok ( !((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON), + "Button1's style not chaged to BS_PUSHBUTTON\n" ); + } + + /* + * Move focus to Edit control using "WM_NEXTDLGCTL" + */ + DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 ); + ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't move to Edit control\n"); + + /* + * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL" + */ + dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0); + ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n"); + } + DestroyWindow(g_hwndTestDlg); +} + +static void IsDialogMessageWTest (void) +{ + MSG msg; + + g_hwndMain = CreateWindow ("IsDialogMessageWindowClass", "IsDialogMessageWindowClass", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, g_hinst, 0); + + assert (g_hwndMain); + assert (g_hwndButton1); + assert (g_hwndButtonCancel); + + /* The focus should initially be nowhere. The first TAB should take it + * to the first button. The second TAB should take it to the Cancel + * button. + */ + FormTabMsg (&msg, g_hwndMain); + ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle first TAB\n"); + ok ((GetFocus() == g_hwndButton1), "Focus did not move to first button\n"); + FormTabMsg (&msg, g_hwndButton1); + ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle second TAB\n"); + ok ((GetFocus() == g_hwndButtonCancel), + "Focus did not move to cancel button\n"); + FormEnterMsg (&msg, g_hwndButtonCancel); + ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle the ENTER\n"); + ok (g_terminated, "ENTER did not terminate\n"); +} + + +static LRESULT CALLBACK delayFocusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam, + LPARAM lParam) +{ + switch (uiMsg) + { + case WM_INITDIALOG: + g_hwndMain = hDlg; + g_hwndInitialFocusGroupBox = GetDlgItem(hDlg,100); + g_hwndButton1 = GetDlgItem(hDlg,200); + g_hwndButton2 = GetDlgItem(hDlg,201); + g_hwndButtonCancel = GetDlgItem(hDlg,IDCANCEL); + g_styleInitialFocusT1 = GetWindowLong(g_hwndInitialFocusGroupBox, GWL_STYLE); + + /* Initially check the second radio button */ + SendMessage(g_hwndButton1, BM_SETCHECK, BST_UNCHECKED, 0); + SendMessage(g_hwndButton2, BM_SETCHECK, BST_CHECKED , 0); + /* Continue testing after dialog initialization */ + PostMessage(hDlg, WM_USER, 0, 0); + return g_bInitialFocusInitDlgResult; + + case WM_COMMAND: + if (LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + return FALSE; + + case WM_USER: + g_styleInitialFocusT2 = GetWindowLong(hDlg, GWL_STYLE); + g_hwndInitialFocusT1 = GetFocus(); + SetFocus(hDlg); + g_hwndInitialFocusT2 = GetFocus(); + PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0); + return TRUE; + } + + return FALSE; +} + +static LRESULT CALLBACK focusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam, + LPARAM lParam) +{ + switch (uiMsg) + { + case WM_INITDIALOG: + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + else if (LOWORD(wParam) == 200) + { + if (HIWORD(wParam) == EN_SETFOCUS) + g_hwndInitialFocusT1 = (HWND)lParam; + } + return FALSE; + } + + return FALSE; +} + +/* Helper for InitialFocusTest */ +static const char * GetHwndString(HWND hw) +{ + if (hw == NULL) + return "a null handle"; + if (hw == g_hwndMain) + return "the dialog handle"; + if (hw == g_hwndInitialFocusGroupBox) + return "the group box control"; + if (hw == g_hwndButton1) + return "the first button"; + if (hw == g_hwndButton2) + return "the second button"; + if (hw == g_hwndButtonCancel) + return "the cancel button"; + + return "unknown handle"; +} + +static void InitialFocusTest (void) +{ + /* Test 1: + * This test intentionally returns FALSE in response to WM_INITDIALOG + * without setting focus to a control. This is not allowed according to + * MSDN, but it is exactly what MFC's CFormView does. + * + * Since the WM_INITDIALOG handler returns FALSE without setting the focus, + * the focus should initially be NULL. Later, when we manually set focus to + * the dialog, the default handler should set focus to the first control that + * is "visible, not disabled, and has the WS_TABSTOP style" (MSDN). Because the + * second radio button has been checked, it should be the first control + * that meets these criteria and should receive the focus. + */ + + g_bInitialFocusInitDlgResult = FALSE; + g_hwndInitialFocusT1 = (HWND) -1; + g_hwndInitialFocusT2 = (HWND) -1; + g_styleInitialFocusT1 = -1; + g_styleInitialFocusT2 = -1; + + DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, (DLGPROC)delayFocusDlgWinProc); + + ok (((g_styleInitialFocusT1 & WS_TABSTOP) == 0), + "Error in wrc - Detected WS_TABSTOP as default style for GROUPBOX\n"); + + ok (((g_styleInitialFocusT2 & WS_VISIBLE) == 0), + "Modal dialogs should not be shown until the message queue first goes empty\n"); + + ok ((g_hwndInitialFocusT1 == NULL), + "Error in initial focus when WM_INITDIALOG returned FALSE: " + "Expected NULL focus, got %s (%p).\n", + GetHwndString(g_hwndInitialFocusT1), g_hwndInitialFocusT1); + + ok ((g_hwndInitialFocusT2 == g_hwndButton2), + "Error after first SetFocus() when WM_INITDIALOG returned FALSE: " + "Expected the second button (%p), got %s (%p).\n", + g_hwndButton2, GetHwndString(g_hwndInitialFocusT2), + g_hwndInitialFocusT2); + + /* Test 2: + * This is the same as above, except WM_INITDIALOG is made to return TRUE. + * This should cause the focus to go to the second radio button right away + * and stay there (until the user indicates otherwise). + */ + + g_bInitialFocusInitDlgResult = TRUE; + g_hwndInitialFocusT1 = (HWND) -1; + g_hwndInitialFocusT2 = (HWND) -1; + g_styleInitialFocusT1 = -1; + g_styleInitialFocusT2 = -1; + + DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, (DLGPROC)delayFocusDlgWinProc); + + ok ((g_hwndInitialFocusT1 == g_hwndButton2), + "Error in initial focus when WM_INITDIALOG returned TRUE: " + "Expected the second button (%p), got %s (%p).\n", + g_hwndButton2, GetHwndString(g_hwndInitialFocusT2), + g_hwndInitialFocusT2); + + ok ((g_hwndInitialFocusT2 == g_hwndButton2), + "Error after first SetFocus() when WM_INITDIALOG returned TRUE: " + "Expected the second button (%p), got %s (%p).\n", + g_hwndButton2, GetHwndString(g_hwndInitialFocusT2), + g_hwndInitialFocusT2); + + /* Test 3: + * If the dialog has DS_CONTROL and it's not visible then we shouldn't change focus */ + { + HWND hDlg; + HRSRC hResource; + HANDLE hTemplate; + DLGTEMPLATE* pTemplate; + + hResource = FindResourceA(g_hinst,"FOCUS_TEST_DIALOG", (LPSTR)RT_DIALOG); + hTemplate = LoadResource(g_hinst, hResource); + pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate); + + g_hwndInitialFocusT1 = 0; + hDlg = CreateDialogIndirectParamW(g_hinst, pTemplate, NULL, (DLGPROC)focusDlgWinProc,0); + ok (hDlg != 0, "Failed to create test dialog.\n"); + + ok ((g_hwndInitialFocusT1 == 0), + "Focus should not be set for an invisible DS_CONTROL dialog %p.\n", g_hwndInitialFocusT1); + + DestroyWindow(hDlg); + } +} + +static void test_GetDlgItemText(void) +{ + char string[64]; + BOOL ret; + + strcpy(string, "Overwrite Me"); + ret = GetDlgItemTextA(NULL, 0, string, sizeof(string)/sizeof(string[0])); + ok(!ret, "GetDlgItemText(NULL) shouldn't have succeeded\n"); + + ok(string[0] == '\0', "string retrieved using GetDlgItemText should have been NULL terminated\n"); +} + + +START_TEST(dialog) +{ + g_hinst = GetModuleHandleA (0); + + if (!RegisterWindowClasses()) assert(0); + + GetNextDlgItemTest(); + IsDialogMessageWTest(); + WM_NEXTDLGCTLTest(); + InitialFocusTest(); + test_GetDlgItemText(); +} diff --git a/reactos/regtests/winetests/user32/edit.c b/reactos/regtests/winetests/user32/edit.c new file mode 100755 index 00000000000..61ab5b74025 --- /dev/null +++ b/reactos/regtests/winetests/user32/edit.c @@ -0,0 +1,988 @@ +/* Unit test suite for edit control. + * + * Copyright 2004 Vitaliy Margolen + * Copyright 2005 C. Scott Ananian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "wine/test.h" + +#ifndef ES_COMBO +#define ES_COMBO 0x200 +#endif + +#define ID_EDITTEST2 99 +#define MAXLEN 200 + +struct edit_notify { + int en_change, en_maxtext, en_update; +}; + +static struct edit_notify notifications; + +static char szEditTest2Name[] = "Edit Test 2 window class"; +static HINSTANCE hinst; +static HWND hwndET2; + +static HWND create_editcontrol (DWORD style, DWORD exstyle) +{ + HWND handle; + + handle = CreateWindowEx(exstyle, + "EDIT", + NULL, + ES_AUTOHSCROLL | ES_AUTOVSCROLL | style, + 10, 10, 300, 300, + NULL, NULL, NULL, NULL); + assert (handle); + if (winetest_interactive) + ShowWindow (handle, SW_SHOW); + return handle; +} + +static LONG get_edit_style (HWND hwnd) +{ + return GetWindowLongA( hwnd, GWL_STYLE ) & ( + ES_LEFT | +/* FIXME: not implemented + ES_CENTER | + ES_RIGHT | + ES_OEMCONVERT | +*/ + ES_MULTILINE | + ES_UPPERCASE | + ES_LOWERCASE | + ES_PASSWORD | + ES_AUTOVSCROLL | + ES_AUTOHSCROLL | + ES_NOHIDESEL | + ES_COMBO | + ES_READONLY | + ES_WANTRETURN | + ES_NUMBER + ); +} + +static void set_client_height(HWND Wnd, unsigned Height) +{ + RECT ClientRect, WindowRect; + + GetWindowRect(Wnd, &WindowRect); + GetClientRect(Wnd, &ClientRect); + SetWindowPos(Wnd, NULL, WindowRect.left, WindowRect.top, + WindowRect.right - WindowRect.left, + Height + (WindowRect.bottom - WindowRect.top) - (ClientRect.bottom - ClientRect.top), + SWP_NOMOVE | SWP_NOZORDER); +} + +#define edit_pos_ok(exp, got, txt) \ + ok(exp == got, "wrong " #txt " expected %d got %ld\n", exp, got); + +#define edit_todo_pos_ok(exp, got, txt, todo) \ + if (todo) todo_wine { edit_pos_ok(exp, got, txt); } \ + else edit_pos_ok(exp, got, txt) + +#define check_pos(hwEdit, set_height, test_top, test_height, test_left, todo_top, todo_height, todo_left) \ +do { \ + RECT format_rect; \ + int left_margin; \ + set_client_height(hwEdit, set_height); \ + SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM) &format_rect); \ + left_margin = LOWORD(SendMessage(hwEdit, EM_GETMARGINS, 0, 0)); \ + edit_todo_pos_ok(test_top, format_rect.top, vertical position, todo_top); \ + edit_todo_pos_ok((int)test_height, format_rect.bottom - format_rect.top, height, todo_height); \ + edit_todo_pos_ok(test_left, format_rect.left - left_margin, left, todo_left); \ +} while(0) + +static void test_edit_control_1(void) +{ + HWND hwEdit; + MSG msMessage; + int i; + LONG r; + HFONT Font, OldFont; + HDC Dc; + TEXTMETRIC Metrics; + + msMessage.message = WM_KEYDOWN; + + trace("EDIT: Single line\n"); + hwEdit = create_editcontrol(0, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL), "Wrong style expected 0xc0 got: 0x%lx\n", r); + for (i=0;i<65535;i++) + { + msMessage.wParam = i; + r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %lx\n", r); + } + DestroyWindow (hwEdit); + + trace("EDIT: Single line want returns\n"); + hwEdit = create_editcontrol(ES_WANTRETURN, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN), "Wrong style expected 0x10c0 got: 0x%lx\n", r); + for (i=0;i<65535;i++) + { + msMessage.wParam = i; + r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %lx\n", r); + } + DestroyWindow (hwEdit); + + trace("EDIT: Multiline line\n"); + hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0xc4 got: 0x%lx\n", r); + for (i=0;i<65535;i++) + { + msMessage.wParam = i; + r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %lx\n", r); + } + DestroyWindow (hwEdit); + + trace("EDIT: Multi line want returns\n"); + hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_WANTRETURN, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0x10c4 got: 0x%lx\n", r); + for (i=0;i<65535;i++) + { + msMessage.wParam = i; + r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %lx\n", r); + } + DestroyWindow (hwEdit); + + /* Get a stock font for which we can determine the metrics */ + Font = GetStockObject(SYSTEM_FONT); + assert(NULL != Font); + Dc = GetDC(NULL); + assert(NULL != Dc); + OldFont = SelectObject(Dc, Font); + assert(NULL != OldFont); + if (! GetTextMetrics(Dc, &Metrics)) + { + assert(FALSE); + } + SelectObject(Dc, OldFont); + ReleaseDC(NULL, Dc); + + trace("EDIT: Text position\n"); + hwEdit = create_editcontrol(0, 0); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, 0); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(0, WS_EX_CLIENTEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, WS_EX_CLIENTEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(0, WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(0, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP, 0); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 0, Metrics.tmHeight , 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 0, Metrics.tmHeight , 0, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, 0); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 0, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 3, 0, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 2, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 2, Metrics.tmHeight , 2, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP, WS_EX_CLIENTEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, WS_EX_CLIENTEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP, WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 0, Metrics.tmHeight , 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 0, Metrics.tmHeight , 0, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 0, Metrics.tmHeight , 0, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 0, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 3, 0, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 2, Metrics.tmHeight , 2, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 2, Metrics.tmHeight , 2, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(0, ES_MULTILINE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(0, ES_MULTILINE | WS_EX_CLIENTEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE | WS_EX_CLIENTEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(0, ES_MULTILINE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 1, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(0, ES_MULTILINE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE); + SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE); + check_pos(hwEdit, Metrics.tmHeight - 1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight , 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 1, 0, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 2, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 4, 1, Metrics.tmHeight , 1, 0, 0, 0); + check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight , 1, 0, 0, 0); + DestroyWindow(hwEdit); +} + +/* WM_SETTEXT is implemented by selecting all text, and then replacing the + * selection. This test checks that the first 'select all' doesn't generate + * an UPDATE message which can escape and (via a handler) change the + * selection, which would cause WM_SETTEXT to break. This old bug + * was fixed 18-Mar-2005; we check here to ensure it doesn't regress. + */ +static void test_edit_control_2(void) +{ + HWND hwndMain; + char szLocalString[MAXLEN]; + + /* Create main and edit windows. */ + hwndMain = CreateWindow(szEditTest2Name, "ET2", WS_OVERLAPPEDWINDOW, + 0, 0, 200, 200, NULL, NULL, hinst, NULL); + assert(hwndMain); + if (winetest_interactive) + ShowWindow (hwndMain, SW_SHOW); + + hwndET2 = CreateWindow("EDIT", NULL, + WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL, + 0, 0, 150, 50, /* important this not be 0 size. */ + hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL); + assert(hwndET2); + if (winetest_interactive) + ShowWindow (hwndET2, SW_SHOW); + + trace("EDIT: SETTEXT atomicity\n"); + /* Send messages to "type" in the word 'foo'. */ + SendMessage(hwndET2, WM_CHAR, 'f', 1); + SendMessage(hwndET2, WM_CHAR, 'o', 1); + SendMessage(hwndET2, WM_CHAR, 'o', 1); + /* 'foo' should have been changed to 'bar' by the UPDATE handler. */ + GetWindowText(hwndET2, szLocalString, MAXLEN); + ok(lstrcmp(szLocalString, "bar")==0, + "Wrong contents of edit: %s\n", szLocalString); + + /* OK, done! */ + DestroyWindow (hwndET2); + DestroyWindow (hwndMain); +} + +static void ET2_check_change(void) { + char szLocalString[MAXLEN]; + /* This EN_UPDATE handler changes any 'foo' to 'bar'. */ + GetWindowText(hwndET2, szLocalString, MAXLEN); + if (lstrcmp(szLocalString, "foo")==0) { + lstrcpy(szLocalString, "bar"); + SendMessage(hwndET2, WM_SETTEXT, 0, (LPARAM) szLocalString); + } + /* always leave the cursor at the end. */ + SendMessage(hwndET2, EM_SETSEL, MAXLEN - 1, MAXLEN - 1); +} +static void ET2_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) +{ + if (id==ID_EDITTEST2 && codeNotify == EN_UPDATE) + ET2_check_change(); +} +static LRESULT CALLBACK ET2_WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + switch (iMsg) { + HANDLE_MSG(hwnd, WM_COMMAND, ET2_OnCommand); + } + return DefWindowProc(hwnd, iMsg, wParam, lParam); +} + +static BOOL RegisterWindowClasses (void) +{ + WNDCLASSA cls; + cls.style = 0; + cls.lpfnWndProc = ET2_WndProc; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = hinst; + cls.hIcon = NULL; + cls.hCursor = LoadCursorA (NULL, IDC_ARROW); + cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + cls.lpszMenuName = NULL; + cls.lpszClassName = szEditTest2Name; + if (!RegisterClassA (&cls)) return FALSE; + + return TRUE; +} + +static void zero_notify(void) +{ + notifications.en_change = 0; + notifications.en_maxtext = 0; + notifications.en_update = 0; +} + +#define test_notify(enchange, enmaxtext, enupdate) \ + ok(notifications.en_change == enchange, "expected %d EN_CHANGE notifications, " \ + "got %d\n", enchange, notifications.en_change); \ + ok(notifications.en_maxtext == enmaxtext, "expected %d EN_MAXTEXT notifications, " \ + "got %d\n", enmaxtext, notifications.en_maxtext); \ + ok(notifications.en_update == enupdate, "expected %d EN_UPDATE notifications, " \ + "got %d\n", enupdate, notifications.en_update) + + +static LRESULT CALLBACK edit3_wnd_procA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_COMMAND: + switch (HIWORD(wParam)) { + case EN_MAXTEXT: + notifications.en_maxtext++; + break; + case EN_UPDATE: + notifications.en_update++; + break; + case EN_CHANGE: + notifications.en_change++; + break; + } + break; + } + return DefWindowProcA(hWnd, msg, wParam, lParam); +} + +/* Test behaviour of WM_SETTEXT, WM_REPLACESEL and notificatisons sent in response + * to these messages. + */ +static void test_edit_control_3(void) +{ + WNDCLASSA cls; + HWND hWnd; + HWND hParent; + int len; + static const char *str = "this is a long string."; + static const char *str2 = "this is a long string.\r\nthis is a long string.\r\nthis is a long string.\r\nthis is a long string."; + + trace("EDIT: Test notifications\n"); + + cls.style = 0; + cls.lpfnWndProc = edit3_wnd_procA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "ParentWindowClass"; + + assert(RegisterClassA(&cls)); + + hParent = CreateWindowExA(0, + "ParentWindowClass", + NULL, + 0, + CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, + NULL, NULL, NULL, NULL); + assert(hParent); + + trace("EDIT: Single line, no ES_AUTOHSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + 0, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) > len, "text should have been truncated\n"); + test_notify(1, 1, 1); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a"); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(1 == len, "wrong text length, expected 1, got %d\n", len); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + DestroyWindow(hWnd); + + trace("EDIT: Single line, ES_AUTOHSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_AUTOHSCROLL, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + DestroyWindow(hWnd); + + trace("EDIT: Multline, no ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_MULTILINE, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(0 == len, "text should have been truncated, expected 0, got %d\n", len); + test_notify(1, 1, 1); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a"); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + DestroyWindow(hWnd); + + trace("EDIT: Multline, ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_MULTILINE | ES_AUTOHSCROLL, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(0 == len, "text should have been truncated, expected 0, got %d\n", len); + test_notify(1, 1, 1); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a"); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + DestroyWindow(hWnd); + + trace("EDIT: Multline, ES_AUTOHSCROLL and ES_AUTOVSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + DestroyWindow(hWnd); +} + +/* Test EM_CHARFROMPOS and EM_POSFROMCHAR + */ +static void test_edit_control_4(void) +{ + HWND hwEdit; + int lo, hi, mid; + int ret; + int i; + + trace("EDIT: Test EM_CHARFROMPOS and EM_POSFROMCHAR\n"); + hwEdit = create_editcontrol(0, 0); + SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa"); + lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2; + + for (i = lo; i < mid; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + for (i = mid; i <= hi; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_RIGHT, 0); + SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa"); + lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2; + + for (i = lo; i < mid; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + for (i = mid; i <= hi; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_CENTER, 0); + SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa"); + lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2; + + for (i = lo; i < mid; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + for (i = mid; i <= hi; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_MULTILINE, 0); + SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa"); + lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2 +1; + + for (i = lo; i < mid; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + for (i = mid; i <= hi; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_MULTILINE | ES_RIGHT, 0); + SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa"); + lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2 +1; + + for (i = lo; i < mid; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + for (i = mid; i <= hi; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_MULTILINE | ES_CENTER, 0); + SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa"); + lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2 +1; + + for (i = lo; i < mid; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + for (i = mid; i <= hi; i++) { + ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); +} + +static void test_margins(void) +{ + HWND hwEdit; + RECT old_rect, new_rect; + INT old_left_margin, old_right_margin; + DWORD old_margins, new_margins; + + hwEdit = create_editcontrol(WS_BORDER, 0); + + old_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0); + old_left_margin = LOWORD(old_margins); + old_right_margin = HIWORD(old_margins); + + /* Check if setting the margins works */ + + SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN, MAKELONG(10, 0)); + new_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins)); + ok(HIWORD(new_margins) == old_right_margin, "Wrong right margin: %d\n", HIWORD(new_margins)); + + SendMessage(hwEdit, EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, 10)); + new_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins)); + ok(HIWORD(new_margins) == 10, "Wrong right margin: %d\n", HIWORD(new_margins)); + + + /* The size of the rectangle must decrease if we increase the margin */ + + SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(5, 5)); + SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect); + SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(15, 20)); + SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(new_rect.left == old_rect.left + 10, "The left border of the rectangle is wrong\n"); + ok(new_rect.right == old_rect.right - 15, "The right border of the rectangle is wrong\n"); + ok(new_rect.top == old_rect.top, "The top border of the rectangle must not change\n"); + ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle must not change\n"); + + + /* If we set the margin to same value as the current margin, + the rectangle must not change */ + + SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10)); + old_rect.left = 1; + old_rect.right = 99; + old_rect.top = 1; + old_rect.bottom = 99; + SendMessage(hwEdit, EM_SETRECT, 0, (LPARAM)&old_rect); + SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect); + SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10)); + SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(new_rect.left == old_rect.left, "The left border of the rectangle has changed\n"); + ok(new_rect.right == old_rect.right, "The right border of the rectangle has changed\n"); + ok(new_rect.top == old_rect.top, "The top border of the rectangle has changed\n"); + ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle has changed\n"); + + DestroyWindow (hwEdit); +} + +START_TEST(edit) +{ + hinst = GetModuleHandleA (NULL); + if (!RegisterWindowClasses()) + assert(0); + + test_edit_control_1(); + test_edit_control_2(); + test_edit_control_3(); + test_edit_control_4(); + test_margins(); +} diff --git a/reactos/regtests/winetests/user32/input.c b/reactos/regtests/winetests/user32/input.c new file mode 100755 index 00000000000..9eca8932bc7 --- /dev/null +++ b/reactos/regtests/winetests/user32/input.c @@ -0,0 +1,366 @@ +/* Test Key event to Key message translation + * + * Copyright 2003 Rein Klazes + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* test whether the right type of messages: + * WM_KEYUP/DOWN vs WM_SYSKEYUP/DOWN are sent in case of combined + * keystrokes. + * + * For instance -X can be accompished by + * the sequence ALT-KEY-DOWN, X-KEY-DOWN, ALT-KEY-UP, X-KEY-UP + * but also X-KEY-DOWN, ALT-KEY-DOWN, X-KEY-UP, ALT-KEY-UP + * Whether a KEY or a SYSKEY message is sent is not always clear, it is + * also not the same in WINNT as in WIN9X */ + +/* NOTE that there will be test failures under WIN9X + * No applications are known to me that rely on this + * so I don't fix it */ + +/* TODO: + * 1. extend it to the wm_command and wm_syscommand notifications + * 2. add some more tests with special cases like dead keys or right (alt) key + * 3. there is some adapted code from input.c in here. Should really + * make that code exactly the same. + * 4. resolve the win9x case when there is a need or the testing frame work + * offers a nice way. + * 5. The test app creates a window, the user should not take the focus + * away during its short existence. I could do something to prevent that + * if it is a problem. + * + */ + +#define _WIN32_WINNT 0x403 + +#include +#include + +#include "windows.h" + +#include "wine/test.h" + +/* globals */ +static HWND hWndTest; +static long timetag = 0x10000000; + +static UINT (WINAPI *ptr_SendInput) (UINT, INPUT*, size_t); + +#define MAXKEYEVENTS 6 +#define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one + and only one message */ + +/* keyboard message names, sorted as their value */ +static const char *MSGNAME[]={"WM_KEYDOWN", "WM_KEYUP", "WM_CHAR","WM_DEADCHAR", + "WM_SYSKEYDOWN", "WM_SYSKEYUP", "WM_SYSCHAR", "WM_SYSDEADCHAR" ,"WM_KEYLAST"}; + +/* keyevents, add more as needed */ +typedef enum KEVtag +{ ALTDOWN = 1, ALTUP, XDOWN, XUP, SHIFTDOWN, SHIFTUP, CTRLDOWN, CTRLUP } KEV; +/* matching VK's */ +static const int GETVKEY[]={0, VK_MENU, VK_MENU, 'X', 'X', VK_SHIFT, VK_SHIFT, VK_CONTROL, VK_CONTROL}; +/* matching scan codes */ +static const int GETSCAN[]={0, 0x38, 0x38, 0x2D, 0x2D, 0x2A, 0x2A, 0x1D, 0x1D }; +/* matching updown events */ +static const int GETUPDOWN[]={0, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP}; +/* matching descripts */ +static const char *getdesc[]={"", "+alt","-alt","+X","-X","+shift","-shift","+ctrl","-ctrl"}; + +/* The MSVC headers ignore our NONAMELESSUNION requests so we have to define our own type */ +typedef struct +{ + DWORD type; + union + { + MOUSEINPUT mi; + KEYBDINPUT ki; + HARDWAREINPUT hi; + } u; +} TEST_INPUT; + +#define ADDTOINPUTS(kev) \ +inputs[evtctr].type = INPUT_KEYBOARD; \ + ((TEST_INPUT*)inputs)[evtctr].u.ki.wVk = GETVKEY[ kev]; \ + ((TEST_INPUT*)inputs)[evtctr].u.ki.wScan = GETSCAN[ kev]; \ + ((TEST_INPUT*)inputs)[evtctr].u.ki.dwFlags = GETUPDOWN[ kev]; \ + ((TEST_INPUT*)inputs)[evtctr].u.ki.dwExtraInfo = 0; \ + ((TEST_INPUT*)inputs)[evtctr].u.ki.time = ++timetag; \ + if( kev) evtctr++; + +typedef struct { + UINT message; + WPARAM wParam; + LPARAM lParam; +} KMSG; + +/******************************************* + * add new test sets here + * the software will make all combinations of the + * keyevent defined here + */ +static const struct { + int nrkev; + KEV keydwn[MAXKEYEVENTS]; + KEV keyup[MAXKEYEVENTS]; +} testkeyset[]= { + { 2, { ALTDOWN, XDOWN }, { ALTUP, XUP}}, + { 3, { ALTDOWN, XDOWN , SHIFTDOWN}, { ALTUP, XUP, SHIFTUP}}, + { 3, { ALTDOWN, XDOWN , CTRLDOWN}, { ALTUP, XUP, CTRLUP}}, + { 3, { SHIFTDOWN, XDOWN , CTRLDOWN}, { SHIFTUP, XUP, CTRLUP}}, + { 0 } /* mark the end */ +}; + +/**********************adapted from input.c **********************************/ + +static BYTE InputKeyStateTable[256]; +static BYTE AsyncKeyStateTable[256]; +static BYTE TrackSysKey = 0; /* determine whether ALT key up will cause a WM_SYSKEYUP + or a WM_KEYUP message */ +typedef union +{ + struct + { + unsigned long count : 16; + unsigned long code : 8; + unsigned long extended : 1; + unsigned long unused : 2; + unsigned long win_internal : 2; + unsigned long context : 1; + unsigned long previous : 1; + unsigned long transition : 1; + } lp1; + unsigned long lp2; +} KEYLP; + +static int KbdMessage( KEV kev, WPARAM *pwParam, LPARAM *plParam ) +{ + UINT message; + int VKey = GETVKEY[kev]; + KEYLP keylp; + + keylp.lp2 = 0; + + keylp.lp1.count = 1; + keylp.lp1.code = GETSCAN[kev]; + keylp.lp1.extended = 0 ;/* FIXME (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; */ + keylp.lp1.win_internal = 0; + + if (GETUPDOWN[kev] & KEYEVENTF_KEYUP ) + { + message = WM_KEYUP; + if( (InputKeyStateTable[VK_MENU] & 0x80) && ( + (VKey == VK_MENU) || (VKey == VK_CONTROL) || + !(InputKeyStateTable[VK_CONTROL] & 0x80))) { + if( TrackSysKey == VK_MENU || /* -down/-up sequence */ + (VKey != VK_MENU)) /* -down...-up */ + message = WM_SYSKEYUP; + TrackSysKey = 0; + } + InputKeyStateTable[VKey] &= ~0x80; + keylp.lp1.previous = 1; + keylp.lp1.transition = 1; + } + else + { + keylp.lp1.previous = (InputKeyStateTable[VKey] & 0x80) != 0; + keylp.lp1.transition = 0; + if (!(InputKeyStateTable[VKey] & 0x80)) InputKeyStateTable[VKey] ^= 0x01; + InputKeyStateTable[VKey] |= 0x80; + AsyncKeyStateTable[VKey] |= 0x80; + + message = WM_KEYDOWN; + if( (InputKeyStateTable[VK_MENU] & 0x80) && + !(InputKeyStateTable[VK_CONTROL] & 0x80)) { + message = WM_SYSKEYDOWN; + TrackSysKey = VKey; + } + } + + keylp.lp1.context = (InputKeyStateTable[VK_MENU] & 0x80) != 0; /* 1 if alt */ + + if( plParam) *plParam = keylp.lp2; + if( pwParam) *pwParam = VKey; + return message; +} + +/****************************** end copy input.c ****************************/ + +/* + * . prepare the keyevents for SendInputs + * . calculate the "expected" messages + * . Send the events to our window + * . retrieve the messages from the input queue + * . verify + */ +static void do_test( HWND hwnd, int seqnr, const KEV td[] ) +{ + HMODULE module; + INPUT inputs[MAXKEYEVENTS]; + KMSG expmsg[MAXKEYEVENTS]; + MSG msg; + char buf[100]; + UINT evtctr=0; + int kmctr, i; + + module = GetModuleHandleA("user32"); + if (!module) return; + ptr_SendInput = (void *)GetProcAddress(module, "SendInput"); + if (!ptr_SendInput) return; + + buf[0]='\0'; + TrackSysKey=0; /* see input.c */ + for( i = 0; i < MAXKEYEVENTS; i++) { + ADDTOINPUTS(td[i]) + strcat(buf, getdesc[td[i]]); + if(td[i]) + expmsg[i].message = KbdMessage(td[i], &(expmsg[i].wParam), &(expmsg[i].lParam)); /* see queue_kbd_event() */ + else + expmsg[i].message = 0; + } + for( kmctr = 0; kmctr < MAXKEYEVENTS && expmsg[kmctr].message; kmctr++) + ; + assert( evtctr <= MAXKEYEVENTS ); + assert( evtctr == ptr_SendInput(evtctr, &inputs[0], sizeof(INPUT))); + i = 0; + trace("======== key stroke sequence #%d: %s =============\n", + seqnr + 1, buf); + while( PeekMessage(&msg,hwnd,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE) ) { + trace("message[%d] %-15s wParam %04x lParam %08lx time %lx\n", i, + MSGNAME[msg.message - WM_KEYFIRST], msg.wParam, msg.lParam, msg.time); + if( i < kmctr ) { + ok( msg.message == expmsg[i].message && + msg.wParam == expmsg[i].wParam && + msg.lParam == expmsg[i].lParam, + "wrong message! expected:\n" + "message[%d] %-15s wParam %04x lParam %08lx\n",i, + MSGNAME[(expmsg[i]).message - WM_KEYFIRST], + expmsg[i].wParam, expmsg[i].lParam ); + } + i++; + } + trace("%d messages retrieved\n", i); + ok( i == kmctr, "message count is wrong: got %d expected: %d\n", i, kmctr); +} + +/* test all combinations of the specified key events */ +static void TestASet( HWND hWnd, int nrkev, const KEV kevdwn[], const KEV kevup[] ) +{ + int i,j,k,l,m,n; + static int count=0; + KEV kbuf[MAXKEYEVENTS]; + assert( nrkev==2 || nrkev==3); + for(i=0;i +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" + +#include "wine/test.h" + +#ifdef VISIBLE +#define WAIT Sleep (1000) +#define REDRAW RedrawWindow (handle, NULL, 0, RDW_UPDATENOW) +#else +#define WAIT +#define REDRAW +#endif + +static const char * const strings[4] = { + "First added", + "Second added", + "Third added", + "Fourth added which is very long because at some time we only had a 256 byte character buffer and that was overflowing in one of those applications that had a common dialog file open box and tried to add a 300 characters long custom filter string which of course the code did not like and crashed. Just make sure this string is longer than 256 characters." +}; + +static HWND +create_listbox (DWORD add_style, HWND parent) +{ + HWND handle=CreateWindow ("LISTBOX", "TestList", + (LBS_STANDARD & ~LBS_SORT) | add_style, + 0, 0, 100, 100, + parent, (HMENU)1, NULL, 0); + + assert (handle); + SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[0]); + SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[1]); + SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[2]); + SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[3]); + +#ifdef VISIBLE + ShowWindow (handle, SW_SHOW); +#endif + REDRAW; + + return handle; +} + +struct listbox_prop { + DWORD add_style; +}; + +struct listbox_stat { + int selected, anchor, caret, selcount; +}; + +struct listbox_test { + struct listbox_prop prop; + struct listbox_stat init, init_todo; + struct listbox_stat click, click_todo; + struct listbox_stat step, step_todo; + struct listbox_stat sel, sel_todo; +}; + +static void +listbox_query (HWND handle, struct listbox_stat *results) +{ + results->selected = SendMessage (handle, LB_GETCURSEL, 0, 0); + results->anchor = SendMessage (handle, LB_GETANCHORINDEX, 0, 0); + results->caret = SendMessage (handle, LB_GETCARETINDEX, 0, 0); + results->selcount = SendMessage (handle, LB_GETSELCOUNT, 0, 0); +} + +static void +buttonpress (HWND handle, WORD x, WORD y) +{ + LPARAM lp=x+(y<<16); + + WAIT; + SendMessage (handle, WM_LBUTTONDOWN, (WPARAM) MK_LBUTTON, lp); + SendMessage (handle, WM_LBUTTONUP , (WPARAM) 0 , lp); + REDRAW; +} + +static void +keypress (HWND handle, WPARAM keycode, BYTE scancode, BOOL extended) +{ + LPARAM lp=1+(scancode<<16)+(extended?KEYEVENTF_EXTENDEDKEY:0); + + WAIT; + SendMessage (handle, WM_KEYDOWN, keycode, lp); + SendMessage (handle, WM_KEYUP , keycode, lp | 0xc000000); + REDRAW; +} + +#define listbox_field_ok(t, s, f, got) \ + ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \ + ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \ + t.s.f, got.f) + +#define listbox_todo_field_ok(t, s, f, got) \ + if (t.s##_todo.f) todo_wine { listbox_field_ok(t, s, f, got); } \ + else listbox_field_ok(t, s, f, got) + +#define listbox_ok(t, s, got) \ + listbox_todo_field_ok(t, s, selected, got); \ + listbox_todo_field_ok(t, s, anchor, got); \ + listbox_todo_field_ok(t, s, caret, got); \ + listbox_todo_field_ok(t, s, selcount, got) + +static void +check (const struct listbox_test test) +{ + struct listbox_stat answer; + HWND hLB=create_listbox (test.prop.add_style, 0); + RECT second_item; + int i; + + listbox_query (hLB, &answer); + listbox_ok (test, init, answer); + + SendMessage (hLB, LB_GETITEMRECT, (WPARAM) 1, (LPARAM) &second_item); + buttonpress(hLB, (WORD)second_item.left, (WORD)second_item.top); + + listbox_query (hLB, &answer); + listbox_ok (test, click, answer); + + keypress (hLB, VK_DOWN, 0x50, TRUE); + + listbox_query (hLB, &answer); + listbox_ok (test, step, answer); + + DestroyWindow (hLB); + hLB=create_listbox (test.prop.add_style, 0); + + SendMessage (hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2)); + listbox_query (hLB, &answer); + listbox_ok (test, sel, answer); + + for (i=0;i<4;i++) { + DWORD size = SendMessage (hLB, LB_GETTEXTLEN, i, 0); + CHAR *txt; + WCHAR *txtw; + + txt = HeapAlloc (GetProcessHeap(), 0, size+1); + SendMessageA(hLB, LB_GETTEXT, i, (LPARAM)txt); + ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]); + + txtw = HeapAlloc (GetProcessHeap(), 0, 2*size+2); + SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw); + WideCharToMultiByte( CP_ACP, 0, txtw, -1, txt, size, NULL, NULL ); + ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]); + + HeapFree (GetProcessHeap(), 0, txtw); + HeapFree (GetProcessHeap(), 0, txt); + } + + WAIT; + DestroyWindow (hLB); +} + +static void check_item_height(void) +{ + HWND hLB; + HDC hdc; + HFONT font; + TEXTMETRIC tm; + INT itemHeight; + + hLB = create_listbox (0, 0); + ok ((hdc = GetDCEx( hLB, 0, DCX_CACHE )) != 0, "Can't get hdc\n"); + ok ((font = GetCurrentObject(hdc, OBJ_FONT)) != 0, "Can't get the current font\n"); + ok (GetTextMetrics( hdc, &tm ), "Can't read font metrics\n"); + ReleaseDC( hLB, hdc); + + ok (SendMessage(hLB, WM_SETFONT, (WPARAM)font, 0) == 0, "Can't set font\n"); + + itemHeight = SendMessage(hLB, LB_GETITEMHEIGHT, 0, 0); + ok (itemHeight == tm.tmHeight, "Item height wrong, got %d, expecting %ld\n", itemHeight, tm.tmHeight); + + DestroyWindow (hLB); +} + +static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_DRAWITEM: + { + RECT rc_item, rc_client, rc_clip; + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam; + + trace("%p WM_DRAWITEM %08x %08lx\n", hwnd, wparam, lparam); + + ok(wparam == 0, "wrong wparam %04x\n", wparam); + ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType); + + GetClientRect(dis->hwndItem, &rc_client); + trace("hwndItem %p client rect (%ld,%ld-%ld,%ld)\n", dis->hwndItem, + rc_client.left, rc_client.top, rc_client.right, rc_client.bottom); + GetClipBox(dis->hDC, &rc_clip); + trace("clip rect (%ld,%ld-%ld,%ld)\n", rc_clip.left, rc_clip.top, rc_clip.right, rc_clip.bottom); + ok(EqualRect(&rc_client, &rc_clip), "client rect of the listbox should be equal to the clip box\n"); + + trace("rcItem (%ld,%ld-%ld,%ld)\n", dis->rcItem.left, dis->rcItem.top, + dis->rcItem.right, dis->rcItem.bottom); + SendMessage(dis->hwndItem, LB_GETITEMRECT, dis->itemID, (LPARAM)&rc_item); + trace("item rect (%ld,%ld-%ld,%ld)\n", rc_item.left, rc_item.top, rc_item.right, rc_item.bottom); + ok(EqualRect(&dis->rcItem, &rc_item), "item rects are not equal\n"); + + break; + } + + default: + break; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +static void test_ownerdraw(void) +{ + WNDCLASS cls; + HWND parent, hLB; + INT ret; + RECT rc; + + cls.style = 0; + cls.lpfnWndProc = main_window_proc; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandle(0); + cls.hIcon = 0; + cls.hCursor = LoadCursor(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "main_window_class"; + assert(RegisterClass(&cls)); + + parent = CreateWindowEx(0, "main_window_class", NULL, + WS_POPUP | WS_VISIBLE, + 100, 100, 400, 400, + GetDesktopWindow(), 0, + GetModuleHandle(0), NULL); + assert(parent); + + hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent); + assert(hLB); + + UpdateWindow(hLB); + + /* make height short enough */ + SendMessage(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, + SWP_NOZORDER | SWP_NOMOVE); + + /* make 0 item invisible */ + SendMessage(hLB, LB_SETTOPINDEX, 1, 0); + ret = SendMessage(hLB, LB_GETTOPINDEX, 0, 0); + ok(ret == 1, "wrong top index %d\n", ret); + + SendMessage(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + trace("item 0 rect (%ld,%ld-%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom); + ok(!IsRectEmpty(&rc), "empty item rect\n"); + ok(rc.top < 0, "rc.top is not negative (%ld)\n", rc.top); + + DestroyWindow(hLB); + DestroyWindow(parent); +} + +START_TEST(listbox) +{ + const struct listbox_test SS = +/* {add_style} */ + {{0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; +/* {selected, anchor, caret, selcount}{TODO fields} */ + const struct listbox_test SS_NS = + {{LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + const struct listbox_test MS = + {{LBS_MULTIPLESEL}, + { 0, LB_ERR, 0, 0}, {0,0,0,0}, + { 1, 1, 1, 1}, {0,0,0,0}, + { 2, 1, 2, 1}, {0,0,0,0}, + { 0, LB_ERR, 0, 2}, {0,0,0,0}}; + const struct listbox_test MS_NS = + {{LBS_MULTIPLESEL | LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + const struct listbox_test ES = + {{LBS_EXTENDEDSEL}, + { 0, LB_ERR, 0, 0}, {0,0,0,0}, + { 1, 1, 1, 1}, {0,0,0,0}, + { 2, 2, 2, 1}, {0,0,0,0}, + { 0, LB_ERR, 0, 2}, {0,0,0,0}}; + const struct listbox_test ES_NS = + {{LBS_EXTENDEDSEL | LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + const struct listbox_test EMS = + {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL}, + { 0, LB_ERR, 0, 0}, {0,0,0,0}, + { 1, 1, 1, 1}, {0,0,0,0}, + { 2, 2, 2, 1}, {0,0,0,0}, + { 0, LB_ERR, 0, 2}, {0,0,0,0}}; + const struct listbox_test EMS_NS = + {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + + trace (" Testing single selection...\n"); + check (SS); + trace (" ... with NOSEL\n"); + check (SS_NS); + trace (" Testing multiple selection...\n"); + check (MS); + trace (" ... with NOSEL\n"); + check (MS_NS); + trace (" Testing extended selection...\n"); + check (ES); + trace (" ... with NOSEL\n"); + check (ES_NS); + trace (" Testing extended and multiple selection...\n"); + check (EMS); + trace (" ... with NOSEL\n"); + check (EMS_NS); + + check_item_height(); + test_ownerdraw(); +} diff --git a/reactos/regtests/winetests/user32/menu.c b/reactos/regtests/winetests/user32/menu.c new file mode 100755 index 00000000000..ef97bf87035 --- /dev/null +++ b/reactos/regtests/winetests/user32/menu.c @@ -0,0 +1,260 @@ +/* + * Unit tests for menus + * + * Copyright 2005 Robert Shearman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#include "wine/test.h" + +static ATOM atomMenuCheckClass; + +static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_ENTERMENULOOP: + /* mark window as having entered menu loop */ + SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE); + /* exit menu modal loop + * ( A SendMessage does not work on NT3.51 here ) */ + return PostMessage(hwnd, WM_CANCELMODE, 0, 0); + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +/* globals to communicate between test and wndproc */ +unsigned int MOD_maxid; +RECT MOD_rc[4]; +int MOD_avec, MOD_hic; +int MOD_odheight; +#define MOD_SIZE 10 +/* wndproc used by test_menu_ownerdraw() */ +static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg, + WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_MEASUREITEM: + { + MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam; + if( winetest_debug) + trace("WM_MEASUREITEM received %d,%d\n", + pmis->itemWidth, pmis->itemHeight); + MOD_odheight = pmis->itemHeight; + pmis->itemWidth = MOD_SIZE; + pmis->itemHeight = MOD_SIZE; + return TRUE; + } + case WM_DRAWITEM: + { + DRAWITEMSTRUCT * pdis; + TEXTMETRIC tm; + HPEN oldpen; + char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + SIZE sz; + pdis = (DRAWITEMSTRUCT *) lparam; + if( winetest_debug) { + trace("WM_DRAWITEM received itemdata %ld item %d rc %ld,%ld-%ld,%ld \n", + pdis->itemData, + pdis->itemID, pdis->rcItem.left, pdis->rcItem.top, + pdis->rcItem.right,pdis->rcItem.bottom ); + oldpen=SelectObject( pdis->hDC, GetStockObject( + pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN)); + Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top, + pdis->rcItem.right,pdis->rcItem.bottom ); + SelectObject( pdis->hDC, oldpen); + } + if( pdis->itemData > MOD_maxid) return TRUE; + /* store the rectangl */ + MOD_rc[pdis->itemData] = pdis->rcItem; + /* calculate average character width */ + GetTextExtentPoint( pdis->hDC, chrs, 52, &sz ); + MOD_avec = (sz.cx + 26)/52; + GetTextMetrics( pdis->hDC, &tm); + MOD_hic = tm.tmHeight; + if( pdis->itemData == MOD_maxid) PostMessage(hwnd, WM_CANCELMODE, 0, 0); + return TRUE; + } + + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +static void register_menu_check_class(void) +{ + WNDCLASS wc = + { + 0, + menu_check_wnd_proc, + 0, + 0, + GetModuleHandle(NULL), + NULL, + LoadCursor(NULL, IDC_ARROW), + (HBRUSH)(COLOR_BTNFACE+1), + NULL, + TEXT("WineMenuCheck"), + }; + + atomMenuCheckClass = RegisterClass(&wc); +} + +/* demonstrates that windows lock the menu object so that it is still valid + * even after a client calls DestroyMenu on it */ +static void test_menu_locked_by_window(void) +{ + BOOL ret; + HMENU hmenu; + HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL, + WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError()); + hmenu = CreateMenu(); + ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError()); + ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test")); + ok(ret, "InsertMenu failed with error %ld\n", GetLastError()); + ret = SetMenu(hwnd, hmenu); + ok(ret, "SetMenu failed with error %ld\n", GetLastError()); + ret = DestroyMenu(hmenu); + ok(ret, "DestroyMenu failed with error %ld\n", GetLastError()); + + ret = DrawMenuBar(hwnd); + todo_wine { + ok(ret, "DrawMenuBar failed with error %ld\n", GetLastError()); + } + ret = IsMenu(GetMenu(hwnd)); + ok(!ret, "Menu handle should have been destroyed\n"); + + SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); + /* did we process the WM_INITMENU message? */ + ret = GetWindowLongPtr(hwnd, GWLP_USERDATA); + todo_wine { + ok(ret, "WM_INITMENU should have been sent\n"); + } + + DestroyWindow(hwnd); +} + +static void test_menu_ownerdraw(void) +{ + int i,j,k; + BOOL ret; + HMENU hmenu; + LONG leftcol; + HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL, + WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError()); + if( !hwnd) return; + SetWindowLong( hwnd, GWL_WNDPROC, (LONG)menu_ownerdraw_wnd_proc); + hmenu = CreatePopupMenu(); + ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError()); + if( !hmenu) { DestroyWindow(hwnd);return;} + k=0; + for( j=0;j<2;j++) /* create columns */ + for(i=0;i<2;i++) { /* create rows */ + ret = AppendMenu( hmenu, MF_OWNERDRAW | + (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k); + k++; + ok( ret, "AppendMenu failed for %d\n", k-1); + } + MOD_maxid = k-1; + assert( k <= sizeof(MOD_rc)/sizeof(RECT)); + /* display the menu */ + ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL); + + /* columns have a 4 pixel gap between them */ + ok( MOD_rc[0].right + 4 == MOD_rc[2].left, + "item rectangles are not separated by 4 pixels space\n"); + /* height should be what the MEASUREITEM message has returned */ + ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE, + "menu item has wrong height: %ld should be %d\n", + MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE); + /* no gaps between the rows */ + ok( MOD_rc[0].bottom - MOD_rc[1].top == 0, + "There should not be a space between the rows, gap is %ld\n", + MOD_rc[0].bottom - MOD_rc[1].top); + /* test the correct value of the item height that was sent + * by the WM_MEASUREITEM message */ + ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */ + MOD_odheight == MOD_hic, /* Win95,98,ME */ + "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n", + HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight); + /* test what MF_MENUBREAK did at the first position. Also show + * that an MF_SEPARATOR is ignored in the height calculation. */ + leftcol= MOD_rc[0].left; + ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0); + /* display the menu */ + ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL); + /* left should be 4 pixels less now */ + ok( leftcol == MOD_rc[0].left + 4, + "columns should be 4 pixels to the left (actual %ld).\n", + leftcol - MOD_rc[0].left); + /* test width */ + ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE, + "width of owner drawn menu item is wrong. Got %ld expected %d\n", + MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE); + /* and height */ + ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE, + "Height is incorrect. Got %ld expected %d\n", + MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE); + + /* test width/height of a OD menu bar as well */ + ret = DestroyMenu(hmenu); + ok(ret, "DestroyMenu failed with error %ld\n", GetLastError()); + hmenu = CreateMenu(); + ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError()); + if( !hmenu) { DestroyWindow(hwnd);return;} + MOD_maxid=1; + for(i=0;i<2;i++) { + ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0); + ok( ret, "AppendMenu failed for %d\n", i); + } + SetMenu( hwnd, hmenu); + UpdateWindow( hwnd); /* hack for wine to draw the window + menu */ + ok(ret, "SetMenu failed with error %ld\n", GetLastError()); + /* test width */ + ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE, + "width of owner drawn menu item is wrong. Got %ld expected %d\n", + MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE); + /* test hight */ + ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1, + "Height of owner drawn menu item is wrong. Got %ld expected %d\n", + MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1); + /* clean up */ + DestroyWindow(hwnd); +} + +START_TEST(menu) +{ + register_menu_check_class(); + + test_menu_locked_by_window(); + test_menu_ownerdraw(); +} diff --git a/reactos/regtests/winetests/user32/msg.c b/reactos/regtests/winetests/user32/msg.c new file mode 100755 index 00000000000..615fd15236e --- /dev/null +++ b/reactos/regtests/winetests/user32/msg.c @@ -0,0 +1,5858 @@ +/* + * Unit tests for window message handling + * + * Copyright 1999 Ove Kaaven + * Copyright 2003 Dimitrie O. Paun + * Copyright 2004, 2005 Dmitry Timoshkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#define WINVER 0x0500 +#define _WIN32_WINNT 0x0500 /* For WM_CHANGEUISTATE */ + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#include "wine/test.h" + +#define MDI_FIRST_CHILD_ID 2004 +#define OBJID_QUERYCLASSNAMEIDX -12 // Fixme w32api +#define OBJID_NATIVEOM -16 // ditto + +/* undocumented SWP flags - from SDK 3.1 */ +#define SWP_NOCLIENTSIZE 0x0800 +#define SWP_NOCLIENTMOVE 0x1000 + +#define WND_PARENT_ID 1 +#define WND_POPUP_ID 2 +#define WND_CHILD_ID 3 + +static BOOL test_DestroyWindow_flag; +static HWINEVENTHOOK hEvent_hook; + +static HWND (WINAPI *pGetAncestor)(HWND,UINT); + +/* +FIXME: add tests for these +Window Edge Styles (Win31/Win95/98 look), in order of precedence: + WS_EX_DLGMODALFRAME: double border, WS_CAPTION allowed + WS_THICKFRAME: thick border + WS_DLGFRAME: double border, WS_CAPTION not allowed (but possibly shown anyway) + WS_BORDER (default for overlapped windows): single black border + none (default for child (and popup?) windows): no border +*/ + +typedef enum { + sent=0x1, + posted=0x2, + parent=0x4, + wparam=0x8, + lparam=0x10, + defwinproc=0x20, + beginpaint=0x40, + optional=0x80, + hook=0x100, + winevent_hook=0x200 +} msg_flags_t; + +struct message { + UINT message; /* the WM_* code */ + msg_flags_t flags; /* message props */ + WPARAM wParam; /* expected value of wParam */ + LPARAM lParam; /* expected value of lParam */ +}; + +/* Empty message sequence */ +static const struct message WmEmptySeq[] = +{ + { 0 } +}; +/* CreateWindow (for overlapped window, not initially visible) (16/32) */ +static const struct message WmCreateOverlappedSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_GETMINMAXINFO, sent }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; +/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE) + * for a not visible overlapped window. + */ +static const struct message WmSWP_ShowOverlappedSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { HCBT_ACTIVATE, hook }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, /* Win9x: SWP_NOSENDCHANGING */ + { WM_ACTIVATEAPP, sent|wparam, 1 }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ACTIVATE, sent|wparam, 1 }, + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { WM_IME_NOTIFY, sent|defwinproc|optional }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|wparam|defwinproc, 0 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + /* Win9x adds SWP_NOZORDER below */ + { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ }, + { WM_NCCALCSIZE, sent|wparam|optional, 1 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_ERASEBKGND, sent|optional }, + { 0 } +}; +/* SetWindowPos(SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE) + * for a visible overlapped window. + */ +static const struct message WmSWP_HideOverlappedSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; + +/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE) + * for a visible overlapped window. + */ +static const struct message WmSWP_ResizeSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE }, + { WM_GETMINMAXINFO, sent|defwinproc }, + { WM_NCCALCSIZE, sent|wparam, TRUE }, + { WM_NCPAINT, sent|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED }, + { WM_NCCALCSIZE, sent|wparam|optional, TRUE }, + { WM_NCPAINT, sent|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; + +/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE) + * for a visible popup window. + */ +static const struct message WmSWP_ResizePopupSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE }, + { WM_NCCALCSIZE, sent|wparam, TRUE }, + { WM_NCPAINT, sent|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED }, + { WM_NCCALCSIZE, sent|wparam|optional, TRUE }, + { WM_NCPAINT, sent|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; + +/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE) + * for a visible overlapped window. + */ +static const struct message WmSWP_MoveSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE }, + { WM_NCPAINT, sent|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOCLIENTSIZE }, + { WM_MOVE, sent|defwinproc|wparam, 0 }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; + +/* ShowWindow(SW_SHOW) for a not visible overlapped window */ +static const struct message WmShowOverlappedSeq[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { HCBT_ACTIVATE, hook }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, + { WM_ACTIVATEAPP, sent|wparam, 1 }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ACTIVATE, sent|wparam, 1 }, + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { WM_IME_NOTIFY, sent|defwinproc|optional }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|wparam|defwinproc, 0 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + /* Win9x adds SWP_NOZORDER below */ + { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ }, + { WM_NCCALCSIZE, sent|optional }, + { WM_NCPAINT, sent|optional }, + { WM_ERASEBKGND, sent|optional }, +#if 0 /* CreateWindow/ShowWindow(SW_SHOW) also generates WM_SIZE/WM_MOVE + * messages. Does that mean that CreateWindow doesn't set initial + * window dimensions for overlapped windows? + */ + { WM_SIZE, sent }, + { WM_MOVE, sent }, +#endif + { 0 } +}; +/* ShowWindow(SW_HIDE) for a visible overlapped window */ +static const struct message WmHideOverlappedSeq[] = { + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { WM_NCACTIVATE, sent|wparam, 0 }, + { WM_ACTIVATE, sent|wparam, 0 }, + { WM_ACTIVATEAPP, sent|wparam, 0 }, + { WM_KILLFOCUS, sent|wparam, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_NOTIFY, sent|optional|defwinproc }, + { 0 } +}; +/* DestroyWindow for a visible overlapped window */ +static const struct message WmDestroyOverlappedSeq[] = { + { HCBT_DESTROYWND, hook }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_NCACTIVATE, sent|wparam, 0 }, + { WM_ACTIVATE, sent|wparam, 0 }, + { WM_ACTIVATEAPP, sent|wparam, 0 }, + { WM_KILLFOCUS, sent|wparam, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_NOTIFY, sent|optional|defwinproc }, + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent }, + { WM_NCDESTROY, sent }, + { 0 } +}; +/* CreateWindow (for a child popup window, not initially visible) */ +static const struct message WmCreateChildPopupSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { 0 } +}; +/* CreateWindow (for a popup window, not initially visible, + * which sets WS_VISIBLE in WM_CREATE handler) + */ +static const struct message WmCreateInvisiblePopupSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { WM_STYLECHANGING, sent }, + { WM_STYLECHANGED, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { 0 } +}; +/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER) + * for a popup window with WS_VISIBLE style set + */ +static const struct message WmShowVisiblePopupSeq_2[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, + { 0 } +}; +/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE) + * for a popup window with WS_VISIBLE style set + */ +static const struct message WmShowVisiblePopupSeq_3[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, + { HCBT_ACTIVATE, hook }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { WM_ACTIVATE, sent|wparam, 1 }, + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent|parent }, + { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { WM_IME_NOTIFY, sent|defwinproc|optional }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|defwinproc }, + { 0 } +}; +/* CreateWindow (for child window, not initially visible) */ +static const struct message WmCreateChildSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + /* child is inserted into parent's child list after WM_NCCREATE returns */ + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE }, + { 0 } +}; +/* CreateWindow (for maximized child window, not initially visible) */ +static const struct message WmCreateMaximizedChildSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE }, + { WM_GETMINMAXINFO, sent }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE }, + { 0 } +}; +/* CreateWindow (for a child window, initially visible) */ +static const struct message WmCreateVisibleChildSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + /* child is inserted into parent's child list after WM_NCCREATE returns */ + { WM_NCCALCSIZE, sent|wparam, 0 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE }, + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_NCCALCSIZE, sent|wparam|optional, 1 }, /* WinXP */ + { 0 } +}; +/* ShowWindow(SW_SHOW) for a not visible child window */ +static const struct message WmShowChildSeq[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* ShowWindow(SW_HIDE) for a visible child window */ +static const struct message WmHideChildSeq[] = { + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE) + * for a not visible child window + */ +static const struct message WmShowChildSeq_2[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CHILDACTIVATE, sent }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE) + * for a not visible child window + */ +static const struct message WmShowChildSeq_3[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE) + * for a visible child window with a caption + */ +static const struct message WmShowChildSeq_4[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { WM_CHILDACTIVATE, sent }, + { 0 } +}; +/* ShowWindow(SW_SHOW) for child with invisible parent */ +static const struct message WmShowChildInvisibleParentSeq[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { 0 } +}; +/* ShowWindow(SW_HIDE) for child with invisible parent */ +static const struct message WmHideChildInvisibleParentSeq[] = { + { WM_SHOWWINDOW, sent|wparam, 0 }, + { 0 } +}; +/* SetWindowPos(SWP_SHOWWINDOW) for child with invisible parent */ +static const struct message WmShowChildInvisibleParentSeq_2[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* SetWindowPos(SWP_HIDEWINDOW) for child with invisible parent */ +static const struct message WmHideChildInvisibleParentSeq_2[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* DestroyWindow for a visible child window */ +static const struct message WmDestroyChildSeq[] = { + { HCBT_DESTROYWND, hook }, + { WM_PARENTNOTIFY, sent|parent|wparam, WM_DESTROY }, + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { HCBT_SETFOCUS, hook }, /* set focus to a parent */ + { WM_KILLFOCUS, sent }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|parent|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|parent }, + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent }, + { WM_DESTROY, sent|optional }, /* some other (IME?) window */ + { WM_NCDESTROY, sent|optional }, /* some other (IME?) window */ + { WM_NCDESTROY, sent }, + { 0 } +}; +/* Moving the mouse in nonclient area */ +static const struct message WmMouseMoveInNonClientAreaSeq[] = { /* FIXME: add */ + { WM_NCHITTEST, sent }, + { WM_SETCURSOR, sent }, + { WM_NCMOUSEMOVE, posted }, + { 0 } +}; +/* Moving the mouse in client area */ +static const struct message WmMouseMoveInClientAreaSeq[] = { /* FIXME: add */ + { WM_NCHITTEST, sent }, + { WM_SETCURSOR, sent }, + { WM_MOUSEMOVE, posted }, + { 0 } +}; +/* Moving by dragging the title bar (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */ +static const struct message WmDragTitleBarSeq[] = { /* FIXME: add */ + { WM_NCLBUTTONDOWN, sent|wparam, HTCAPTION }, + { WM_SYSCOMMAND, sent|defwinproc|wparam, SC_MOVE+2 }, + { WM_GETMINMAXINFO, sent|defwinproc }, + { WM_ENTERSIZEMOVE, sent|defwinproc }, + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 }, + { WM_MOVE, sent|defwinproc }, + { WM_EXITSIZEMOVE, sent|defwinproc }, + { 0 } +}; +/* Sizing by dragging the thick borders (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */ +static const struct message WmDragThickBordersBarSeq[] = { /* FIXME: add */ + { WM_NCLBUTTONDOWN, sent|wparam, 0xd }, + { WM_SYSCOMMAND, sent|defwinproc|wparam, 0xf004 }, + { WM_GETMINMAXINFO, sent|defwinproc }, + { WM_ENTERSIZEMOVE, sent|defwinproc }, + { WM_SIZING, sent|defwinproc|wparam, 4}, /* one for each mouse movement */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 }, + { WM_GETMINMAXINFO, sent|defwinproc }, + { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 }, + { WM_NCPAINT, sent|defwinproc|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc }, + { WM_ERASEBKGND, sent|defwinproc }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + { WM_EXITSIZEMOVE, sent|defwinproc }, + { 0 } +}; +/* Resizing child window with MoveWindow (32) */ +static const struct message WmResizingChildWithMoveWindowSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; +/* Clicking on inactive button */ +static const struct message WmClickInactiveButtonSeq[] = { /* FIXME: add */ + { WM_NCHITTEST, sent }, + { WM_PARENTNOTIFY, sent|parent|wparam, WM_LBUTTONDOWN }, + { WM_MOUSEACTIVATE, sent }, + { WM_MOUSEACTIVATE, sent|parent|defwinproc }, + { WM_SETCURSOR, sent }, + { WM_SETCURSOR, sent|parent|defwinproc }, + { WM_LBUTTONDOWN, posted }, + { WM_KILLFOCUS, posted|parent }, + { WM_SETFOCUS, posted }, + { WM_CTLCOLORBTN, posted|parent }, + { BM_SETSTATE, posted }, + { WM_CTLCOLORBTN, posted|parent }, + { WM_LBUTTONUP, posted }, + { BM_SETSTATE, posted }, + { WM_CTLCOLORBTN, posted|parent }, + { WM_COMMAND, posted|parent }, + { 0 } +}; +/* Reparenting a button (16/32) */ +/* The last child (button) reparented gets topmost for its new parent. */ +static const struct message WmReparentButtonSeq[] = { /* FIXME: add */ + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOZORDER }, + { WM_CHILDACTIVATE, sent }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER }, + { WM_MOVE, sent|defwinproc }, + { WM_SHOWWINDOW, sent|wparam, 1 }, + { 0 } +}; +/* Creation of a custom dialog (32) */ +static const struct message WmCreateCustomDialogSeq[] = { + { HCBT_CREATEWND, hook }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_GETMINMAXINFO, sent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_NCCREATE, sent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SHOWWINDOW, sent|wparam, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { HCBT_ACTIVATE, hook }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + + { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, + + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_ACTIVATE, sent|wparam, 1 }, + { WM_KILLFOCUS, sent|parent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_IME_NOTIFY, sent|optional|defwinproc }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_GETDLGCODE, sent|defwinproc|wparam, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_NCPAINT, sent|wparam, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional|defwinproc }, + { WM_ERASEBKGND, sent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_CTLCOLORDLG, sent|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_NCCALCSIZE, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_NCPAINT, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETICON, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_GETTEXT, sent|optional|defwinproc }, + { WM_ERASEBKGND, sent|optional }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_CTLCOLORDLG, sent|optional|defwinproc }, + { WM_SIZE, sent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_MOVE, sent }, + { 0 } +}; +/* Calling EndDialog for a custom dialog (32) */ +static const struct message WmEndCustomDialogSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_GETTEXT, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { HCBT_ACTIVATE, hook }, + { WM_NCACTIVATE, sent|wparam, 0 }, + { WM_GETTEXT, sent|optional|defwinproc }, + { WM_GETICON, sent|optional|defwinproc }, + { WM_GETICON, sent|optional|defwinproc }, + { WM_GETICON, sent|optional|defwinproc }, + { WM_GETTEXT, sent|optional|defwinproc }, + { WM_ACTIVATE, sent|wparam, 0 }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|parent|wparam|defwinproc|optional, 1 }, + { WM_IME_NOTIFY, sent|optional }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|parent|defwinproc }, + { 0 } +}; +/* ShowWindow(SW_SHOW) for a custom dialog (initially invisible) */ +static const struct message WmShowCustomDialogSeq[] = { + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SHOWWINDOW, sent|wparam, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { HCBT_ACTIVATE, hook }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 }, + + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_ACTIVATEAPP, sent|wparam|optional, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_ACTIVATE, sent|wparam, 1 }, + + { WM_KILLFOCUS, sent|parent }, + { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { WM_IME_NOTIFY, sent|optional|defwinproc }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_GETDLGCODE, sent|defwinproc|wparam, 0 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_NCPAINT, sent|wparam, 1 }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_ERASEBKGND, sent }, + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_CTLCOLORDLG, sent|defwinproc }, + + { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* Creation and destruction of a modal dialog (32) */ +static const struct message WmModalDialogSeq[] = { + { WM_CANCELMODE, sent|parent }, + { HCBT_SETFOCUS, hook }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_KILLFOCUS, sent|parent }, + { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 }, + { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ENABLE, sent|parent|wparam, 0 }, + { HCBT_CREATEWND, hook }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SETFONT, sent }, + { WM_INITDIALOG, sent }, + { WM_CHANGEUISTATE, sent|optional }, + { WM_SHOWWINDOW, sent }, + { HCBT_ACTIVATE, hook }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_ACTIVATE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_NCPAINT, sent }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_ERASEBKGND, sent }, + { WM_CTLCOLORDLG, sent }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_NCCALCSIZE, sent|optional }, + { WM_NCPAINT, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_CTLCOLORDLG, sent|optional }, + { WM_PAINT, sent|optional }, + { WM_CTLCOLORBTN, sent }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_ENTERIDLE, sent|parent|optional }, + { WM_GETICON, sent|parent|optional }, + { WM_GETICON, sent|parent|optional }, + { WM_GETICON, sent|parent|optional }, + { WM_TIMER, sent }, + { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ENABLE, sent|parent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { HCBT_ACTIVATE, hook }, + { WM_NCACTIVATE, sent|wparam, 0 }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_ACTIVATE, sent|wparam, 0 }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|optional }, + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|parent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|parent|defwinproc }, + { EVENT_SYSTEM_DIALOGEND, winevent_hook|wparam|lparam, 0, 0 }, + { HCBT_DESTROYWND, hook }, + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent }, + { WM_NCDESTROY, sent }, + { 0 } +}; +/* Creation of a modal dialog that is resized inside WM_INITDIALOG (32) */ +static const struct message WmCreateModalDialogResizeSeq[] = { /* FIXME: add */ + /* (inside dialog proc, handling WM_INITDIALOG) */ + { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, + { WM_NCCALCSIZE, sent }, + { WM_NCACTIVATE, sent|parent|wparam, 0 }, + { WM_GETTEXT, sent|defwinproc }, + { WM_ACTIVATE, sent|parent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|parent }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { WM_ACTIVATE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, 0 }, + { WM_SIZE, sent|defwinproc }, + /* (setting focus) */ + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, 0 }, + { WM_NCPAINT, sent }, + { WM_GETTEXT, sent|defwinproc }, + { WM_ERASEBKGND, sent }, + { WM_CTLCOLORDLG, sent|defwinproc }, + { WM_WINDOWPOSCHANGED, sent|wparam, 0 }, + { WM_PAINT, sent }, + /* (bunch of WM_CTLCOLOR* for each control) */ + { WM_PAINT, sent|parent }, + { WM_ENTERIDLE, sent|parent|wparam, 0 }, + { WM_SETCURSOR, sent|parent }, + { 0 } +}; +/* SetMenu for NonVisible windows with size change*/ +static const struct message WmSetMenuNonVisibleSizeChangeSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_NCCALCSIZE, sent|wparam|optional, 1 }, + { 0 } +}; +/* SetMenu for NonVisible windows with no size change */ +static const struct message WmSetMenuNonVisibleNoSizeChangeSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; +/* SetMenu for Visible windows with size change */ +static const struct message WmSetMenuVisibleSizeChangeSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { WM_NCPAINT, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_ACTIVATE, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_NCCALCSIZE, sent|wparam|optional, 1 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_ERASEBKGND, sent|optional }, + { 0 } +}; +/* SetMenu for Visible windows with no size change */ +static const struct message WmSetMenuVisibleNoSizeChangeSeq[] = { + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_NCPAINT, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_ACTIVATE, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; +/* DrawMenuBar for a visible window */ +static const struct message WmDrawMenuBarSeq[] = +{ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_NCPAINT, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; + +static const struct message WmSetRedrawFalseSeq[] = +{ + { WM_SETREDRAW, sent|wparam, 0 }, + { 0 } +}; + +static const struct message WmSetRedrawTrueSeq[] = +{ + { WM_SETREDRAW, sent|wparam, 1 }, + { 0 } +}; + +static const struct message WmEnableWindowSeq_1[] = +{ + { WM_CANCELMODE, sent|wparam|lparam, 0, 0 }, + { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ENABLE, sent|wparam|lparam, FALSE, 0 }, + { 0 } +}; + +static const struct message WmEnableWindowSeq_2[] = +{ + { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ENABLE, sent|wparam|lparam, TRUE, 0 }, + { 0 } +}; + +static const struct message WmGetScrollRangeSeq[] = +{ + { SBM_GETRANGE, sent }, + { 0 } +}; +static const struct message WmGetScrollInfoSeq[] = +{ + { SBM_GETSCROLLINFO, sent }, + { 0 } +}; +static const struct message WmSetScrollRangeSeq[] = +{ + /* MSDN claims that Windows sends SBM_SETRANGE message, but win2k SP4 + sends SBM_SETSCROLLINFO. + */ + { SBM_SETSCROLLINFO, sent }, + { 0 } +}; +/* SetScrollRange for a window without a non-client area */ +static const struct message WmSetScrollRangeHSeq_empty[] = +{ + { EVENT_OBJECT_VALUECHANGE, winevent_hook|wparam|lparam, OBJID_HSCROLL, 0 }, + { 0 } +}; +static const struct message WmSetScrollRangeVSeq_empty[] = +{ + { EVENT_OBJECT_VALUECHANGE, winevent_hook|wparam|lparam, OBJID_VSCROLL, 0 }, + { 0 } +}; +static const struct message WmSetScrollRangeHVSeq[] = +{ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { 0 } +}; +/* SetScrollRange for a window with a non-client area */ +static const struct message WmSetScrollRangeHV_NC_Seq[] = +{ + { WM_WINDOWPOSCHANGING, sent, /*|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER*/ }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { WM_NCPAINT, sent|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_GETICON, sent|optional|defwinproc }, + { WM_GETICON, sent|optional|defwinproc }, + { WM_GETICON, sent|optional|defwinproc }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_CTLCOLORDLG, sent|defwinproc|optional }, /* sent to a parent of the dialog */ + { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|0x1000*/ }, + { WM_SIZE, sent|defwinproc }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_GETTEXT, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|optional }, + { 0 } +}; +/* test if we receive the right sequence of messages */ +/* after calling ShowWindow( SW_SHOWNA) */ +static const struct message WmSHOWNAChildInvisParInvis[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { 0 } +}; +static const struct message WmSHOWNAChildVisParInvis[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { 0 } +}; +static const struct message WmSHOWNAChildVisParVis[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER }, + { 0 } +}; +static const struct message WmSHOWNAChildInvisParVis[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER}, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { 0 } +}; +static const struct message WmSHOWNATopVisible[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE }, + { 0 } +}; +static const struct message WmSHOWNATopInvisible[] = { + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_NCPAINT, sent|wparam, 1 }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETICON, sent|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_NCCALCSIZE, sent|wparam|optional, 1 }, + { WM_NCPAINT, sent|wparam|optional, 1 }, + { WM_ERASEBKGND, sent|optional }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { 0 } +}; + +static int after_end_dialog; +static int sequence_cnt, sequence_size; +static struct message* sequence; +static int log_all_parent_messages; + +static void add_message(const struct message *msg) +{ + if (!sequence) + { + sequence_size = 10; + sequence = HeapAlloc( GetProcessHeap(), 0, sequence_size * sizeof (struct message) ); + } + if (sequence_cnt == sequence_size) + { + sequence_size *= 2; + sequence = HeapReAlloc( GetProcessHeap(), 0, sequence, sequence_size * sizeof (struct message) ); + } + assert(sequence); + + sequence[sequence_cnt].message = msg->message; + sequence[sequence_cnt].flags = msg->flags; + sequence[sequence_cnt].wParam = msg->wParam; + sequence[sequence_cnt].lParam = msg->lParam; + + sequence_cnt++; +} + +static void flush_sequence(void) +{ + HeapFree(GetProcessHeap(), 0, sequence); + sequence = 0; + sequence_cnt = sequence_size = 0; +} + +#define ok_sequence( exp, contx, todo) \ + ok_sequence_( (exp), (contx), (todo), __FILE__, __LINE__) + + +static void ok_sequence_(const struct message *expected, const char *context, int todo, + const char *file, int line) +{ + static const struct message end_of_sequence = { 0, 0, 0, 0 }; + const struct message *actual; + int failcount = 0; + + add_message(&end_of_sequence); + + actual = sequence; + + while (expected->message && actual->message) + { + trace_( file, line)("expected %04x - actual %04x\n", expected->message, actual->message); + + if (expected->message == actual->message) + { + if (expected->flags & wparam) + { + if (expected->wParam != actual->wParam && todo) + { + todo_wine { + failcount ++; + ok_( file, line) (FALSE, + "%s: in msg 0x%04x expecting wParam 0x%x got 0x%x\n", + context, expected->message, expected->wParam, actual->wParam); + } + } + else + ok_( file, line) (expected->wParam == actual->wParam, + "%s: in msg 0x%04x expecting wParam 0x%x got 0x%x\n", + context, expected->message, expected->wParam, actual->wParam); + } + if (expected->flags & lparam) + ok_( file, line) (expected->lParam == actual->lParam, + "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n", + context, expected->message, expected->lParam, actual->lParam); + ok_( file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc), + "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n", + context, expected->message, (expected->flags & defwinproc) ? "" : "NOT "); + ok_( file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint), + "%s: the msg 0x%04x should %shave been sent by BeginPaint\n", + context, expected->message, (expected->flags & beginpaint) ? "" : "NOT "); + ok_( file, line) ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)), + "%s: the msg 0x%04x should have been %s\n", + context, expected->message, (expected->flags & posted) ? "posted" : "sent"); + ok_( file, line) ((expected->flags & parent) == (actual->flags & parent), + "%s: the msg 0x%04x was expected in %s\n", + context, expected->message, (expected->flags & parent) ? "parent" : "child"); + ok_( file, line) ((expected->flags & hook) == (actual->flags & hook), + "%s: the msg 0x%04x should have been sent by a hook\n", + context, expected->message); + ok_( file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook), + "%s: the msg 0x%04x should have been sent by a winevent hook\n", + context, expected->message); + expected++; + actual++; + } + /* silently drop winevent messages if there is no support for them */ + else if ((expected->flags & optional) || ((expected->flags & winevent_hook) && !hEvent_hook)) + expected++; + else if (todo) + { + failcount++; + todo_wine { + ok_( file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n", + context, expected->message, actual->message); + } + flush_sequence(); + return; + } + else + { + ok_( file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n", + context, expected->message, actual->message); + expected++; + actual++; + } + } + + /* skip all optional trailing messages */ + while (expected->message && ((expected->flags & optional) || + ((expected->flags & winevent_hook) && !hEvent_hook))) + expected++; + + if (todo) + { + todo_wine { + if (expected->message || actual->message) { + failcount++; + ok_( file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n", + context, expected->message, actual->message); + } + } + } + else + { + if (expected->message || actual->message) + ok_( file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n", + context, expected->message, actual->message); + } + if( todo && !failcount) /* succeeded yet marked todo */ + todo_wine { + ok_( file, line)( TRUE, "%s: marked \"todo_wine\" but succeeds\n", context); + } + + flush_sequence(); +} + +/******************************** MDI test **********************************/ + +/* CreateWindow for MDI frame window, initially visible */ +static const struct message WmCreateMDIframeSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_GETMINMAXINFO, sent }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { HCBT_ACTIVATE, hook }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE }, + { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, /* Win9x */ + { WM_ACTIVATEAPP, sent|wparam, 1 }, + { WM_NCACTIVATE, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_GETICON, sent|defwinproc|optional }, + { WM_GETICON, sent|defwinproc|optional }, + { WM_ACTIVATE, sent|wparam, 1 }, + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|wparam|defwinproc, 0 }, + /* Win9x adds SWP_NOZORDER below */ + { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { 0 } +}; +/* DestroyWindow for MDI frame window, initially visible */ +static const struct message WmDestroyMDIframeSeq[] = { + { HCBT_DESTROYWND, hook }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_NCACTIVATE, sent|wparam, 0 }, + { WM_ACTIVATE, sent|wparam|optional, 0 }, /* Win9x */ + { WM_ACTIVATEAPP, sent|wparam|optional, 0 }, /* Win9x */ + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent }, + { WM_NCDESTROY, sent }, + { 0 } +}; +/* CreateWindow for MDI client window, initially visible */ +static const struct message WmCreateMDIclientSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { WM_PARENTNOTIFY, sent|wparam, WM_CREATE }, /* in MDI frame */ + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { 0 } +}; +/* DestroyWindow for MDI client window, initially visible */ +static const struct message WmDestroyMDIclientSeq[] = { + { HCBT_DESTROYWND, hook }, + { WM_PARENTNOTIFY, sent|wparam, WM_DESTROY }, /* in MDI frame */ + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent }, + { WM_NCDESTROY, sent }, + { 0 } +}; +/* CreateWindow for MDI child window, initially visible */ +static const struct message WmCreateMDIchildVisibleSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + /* Win2k sends wparam set to + * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */ + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + + /* Win9x: message sequence terminates here. */ + + { WM_NCACTIVATE, sent|wparam|defwinproc, 1 }, + { HCBT_SETFOCUS, hook }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|defwinproc }, + { WM_MDIACTIVATE, sent|defwinproc }, + { 0 } +}; +/* DestroyWindow for MDI child window, initially visible */ +static const struct message WmDestroyMDIchildVisibleSeq[] = { + { HCBT_DESTROYWND, hook }, + /* Win2k sends wparam set to + * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */ + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + + /* { WM_DESTROY, sent } + * Win9x: message sequence terminates here. + */ + + { HCBT_SETFOCUS, hook }, /* set focus to MDI client */ + { WM_KILLFOCUS, sent }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + + { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */ + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + + { HCBT_SETFOCUS, hook }, /* set focus to MDI client */ + { WM_KILLFOCUS, sent }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + + { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */ + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + + { WM_DESTROY, sent }, + + { HCBT_SETFOCUS, hook }, /* set focus to MDI client */ + { WM_KILLFOCUS, sent }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + + { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */ + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + + { WM_NCDESTROY, sent }, + { 0 } +}; +/* CreateWindow for MDI child window, initially invisible */ +static const struct message WmCreateMDIchildInvisibleSeq[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + /* Win2k sends wparam set to + * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */ + { 0 } +}; +/* DestroyWindow for MDI child window, initially invisible */ +static const struct message WmDestroyMDIchildInvisibleSeq[] = { + { HCBT_DESTROYWND, hook }, + /* Win2k sends wparam set to + * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */ + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent }, + { WM_NCDESTROY, sent }, + { 0 } +}; +/* CreateWindow for the 1st MDI child window, initially visible and maximized */ +static const struct message WmCreateMDIchildVisibleMaxSeq1[] = { + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE }, + { WM_GETMINMAXINFO, sent }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + /* Win2k sends wparam set to + * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */ + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + + /* Win9x: message sequence terminates here. */ + + { WM_NCACTIVATE, sent|wparam|defwinproc, 1 }, + { HCBT_SETFOCUS, hook }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|defwinproc }, + { WM_MDIACTIVATE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { 0 } +}; +/* CreateWindow for the 2nd MDI child window, initially visible and maximized */ +static const struct message WmCreateMDIchildVisibleMaxSeq2[] = { + /* restore the 1st MDI child */ + { WM_SETREDRAW, sent|wparam, 0 }, + { HCBT_MINMAX, hook }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { WM_SETREDRAW, sent|wparam, 1 }, /* in the 1st MDI child */ + /* create the 2nd MDI child */ + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE }, + { WM_GETMINMAXINFO, sent }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + /* Win2k sends wparam set to + * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */ + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + + { WM_NCACTIVATE, sent|wparam|defwinproc, 0 }, /* in the 1st MDI child */ + { WM_MDIACTIVATE, sent|defwinproc }, /* in the 1st MDI child */ + + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + + /* Win9x: message sequence terminates here. */ + + { WM_NCACTIVATE, sent|wparam|defwinproc, 1 }, + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent|defwinproc }, /* in the 1st MDI child */ + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 0 }, /* in the 1st MDI child */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|defwinproc }, + + { WM_MDIACTIVATE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { 0 } +}; +/* WM_MDICREATE MDI child window, initially visible and maximized */ +static const struct message WmCreateMDIchildVisibleMaxSeq3[] = { + { WM_MDICREATE, sent }, + { HCBT_CREATEWND, hook }, + { WM_NCCREATE, sent }, + { WM_NCCALCSIZE, sent|wparam, 0 }, + { WM_CREATE, sent }, + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_SIZE, sent }, + { WM_MOVE, sent }, + { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE }, + { WM_GETMINMAXINFO, sent }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + + /* Win2k sends wparam set to + * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */ + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE }, + + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + + /* Win9x: message sequence terminates here. */ + + { WM_NCACTIVATE, sent|wparam|defwinproc, 1 }, + { HCBT_SETFOCUS, hook }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|defwinproc }, + + { WM_MDIACTIVATE, sent|defwinproc }, + + /* in MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, + + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + + /* in MDI client */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent }, + + /* in MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent|defwinproc }, + + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + + { 0 } +}; +/* WM_SYSCOMMAND/SC_CLOSE for the 2nd MDI child window, initially visible and maximized */ +static const struct message WmDestroyMDIchildVisibleMaxSeq2[] = { + { WM_SYSCOMMAND, sent|wparam, SC_CLOSE }, + { HCBT_SYSCOMMAND, hook }, + { WM_CLOSE, sent|defwinproc }, + { WM_MDIDESTROY, sent }, /* in MDI client */ + + /* bring the 1st MDI child to top */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOSIZE|SWP_NOMOVE }, /* in the 1st MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, /* in the 2nd MDI child */ + + { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + + { WM_CHILDACTIVATE, sent|defwinproc|wparam|lparam, 0, 0 }, /* in the 1st MDI child */ + { WM_NCACTIVATE, sent|wparam|defwinproc, 0 }, /* in the 1st MDI child */ + { WM_MDIACTIVATE, sent|defwinproc }, /* in the 1st MDI child */ + + /* maximize the 1st MDI child */ + { HCBT_MINMAX, hook }, + { WM_GETMINMAXINFO, sent|defwinproc }, + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|0x8000 }, + { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 }, + { WM_CHILDACTIVATE, sent|defwinproc|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + + /* restore the 2nd MDI child */ + { WM_SETREDRAW, sent|defwinproc|wparam, 0 }, + { HCBT_MINMAX, hook }, + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOZORDER|0x8000 }, + { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 }, + + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + + { WM_SETREDRAW, sent|defwinproc|wparam, 1 }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + + /* bring the 1st MDI child to top */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + { WM_NCACTIVATE, sent|wparam|defwinproc, 1 }, + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent|defwinproc }, + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|defwinproc }, + { WM_MDIACTIVATE, sent|defwinproc }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + + /* apparently ShowWindow(SW_SHOW) on an MDI client */ + { WM_SHOWWINDOW, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { WM_MDIREFRESHMENU, sent }, + + { HCBT_DESTROYWND, hook }, + /* Win2k sends wparam set to + * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */ + { WM_SHOWWINDOW, sent|defwinproc|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent|defwinproc }, + { WM_NCDESTROY, sent|defwinproc }, + { 0 } +}; +/* WM_MDIDESTROY for the single MDI child window, initially visible and maximized */ +static const struct message WmDestroyMDIchildVisibleMaxSeq1[] = { + { WM_MDIDESTROY, sent }, /* in MDI client */ + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, + + /* in MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + + /* in MDI client */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent }, + + /* in MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent|defwinproc }, + + /* in MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + + /* in MDI client */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent }, + + /* in MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent|defwinproc }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + + { WM_NCACTIVATE, sent|wparam, 0 }, + { WM_MDIACTIVATE, sent }, + + { HCBT_MINMAX, hook }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_SHOWWINDOW|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + + /* in MDI child */ + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_MOVE, sent|defwinproc }, + { WM_SIZE, sent|defwinproc }, + + /* in MDI client */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE }, + { WM_SIZE, sent }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + + { WM_MDIREFRESHMENU, sent }, /* in MDI client */ + + { HCBT_DESTROYWND, hook }, + /* Win2k sends wparam set to + * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated), + * while Win9x doesn't bother to set child window id according to + * CLIENTCREATESTRUCT.idFirstChild + */ + { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */ + + { WM_SHOWWINDOW, sent|wparam, 0 }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 }, + { WM_ERASEBKGND, sent|parent|optional }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, + { WM_DESTROY, sent }, + { WM_NCDESTROY, sent }, + { 0 } +}; +/* ShowWindow(SW_MAXIMIZE) for a not visible MDI child window */ +static const struct message WmMaximizeMDIchildInvisibleSeq[] = { + { HCBT_MINMAX, hook }, + { WM_GETMINMAXINFO, sent }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + + { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, + { WM_NCACTIVATE, sent|wparam|defwinproc, 1 }, + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */ + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent }, /* in MDI client */ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */ + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|defwinproc }, + { WM_MDIACTIVATE, sent|defwinproc }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { 0 } +}; +/* ShowWindow(SW_MAXIMIZE) for a visible MDI child window */ +static const struct message WmMaximizeMDIchildVisibleSeq[] = { + { HCBT_MINMAX, hook }, + { WM_GETMINMAXINFO, sent }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { 0 } +}; +/* ShowWindow(SW_RESTORE) for a visible MDI child window */ +static const struct message WmRestoreMDIchildVisibleSeq[] = { + { HCBT_MINMAX, hook }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { 0 } +}; +/* ShowWindow(SW_RESTORE) for a not visible MDI child window */ +static const struct message WmRestoreMDIchildInisibleSeq[] = { + { HCBT_MINMAX, hook }, + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|0x8000 }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 }, + { WM_SIZE, sent|defwinproc }, + /* in MDI frame */ + { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER }, + { WM_NCCALCSIZE, sent|wparam, 1 }, + { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */ + { 0 } +}; + +static HWND mdi_client; +static WNDPROC old_mdi_client_proc; + +static LRESULT WINAPI mdi_client_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct message msg; + + /* do not log painting messages */ + if (message != WM_PAINT && + message != WM_ERASEBKGND && + message != WM_NCPAINT && + message != WM_NCHITTEST && + message != WM_GETTEXT && + message != WM_MDIGETACTIVE && + message != WM_DEVICECHANGE) + { + trace("mdi client: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + switch (message) + { + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lParam; + + trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + /* Log only documented flags, win2k uses 0x1000 and 0x2000 + * in the high word for internal purposes + */ + wParam = winpos->flags & 0xffff; + break; + } + } + + msg.message = message; + msg.flags = sent|wparam|lparam; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + } + + return CallWindowProcA(old_mdi_client_proc, hwnd, message, wParam, lParam); +} + +static LRESULT WINAPI mdi_child_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + LRESULT ret; + struct message msg; + + /* do not log painting messages */ + if (message != WM_PAINT && + message != WM_ERASEBKGND && + message != WM_NCPAINT && + message != WM_NCHITTEST && + message != WM_GETTEXT && + message != WM_DEVICECHANGE) + { + trace("mdi child: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + switch (message) + { + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lParam; + + trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + /* Log only documented flags, win2k uses 0x1000 and 0x2000 + * in the high word for internal purposes + */ + wParam = winpos->flags & 0xffff; + break; + } + + case WM_MDIACTIVATE: + { + HWND active, client = GetParent(hwnd); + + active = (HWND)SendMessageA(client, WM_MDIGETACTIVE, 0, 0); + + if (hwnd == (HWND)lParam) /* if we are being activated */ + ok (active == (HWND)lParam, "new active %p != active %p\n", (HWND)lParam, active); + else + ok (active == (HWND)wParam, "old active %p != active %p\n", (HWND)wParam, active); + break; + } + } + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + } + + defwndproc_counter++; + ret = DefMDIChildProcA(hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static LRESULT WINAPI mdi_frame_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + LRESULT ret; + struct message msg; + + /* do not log painting messages */ + if (message != WM_PAINT && + message != WM_ERASEBKGND && + message != WM_NCPAINT && + message != WM_NCHITTEST && + message != WM_GETTEXT && + message != WM_DEVICECHANGE) + { + trace("mdi frame: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + switch (message) + { + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lParam; + + trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + /* Log only documented flags, win2k uses 0x1000 and 0x2000 + * in the high word for internal purposes + */ + wParam = winpos->flags & 0xffff; + break; + } + } + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + } + + defwndproc_counter++; + ret = DefFrameProcA(hwnd, mdi_client, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static BOOL mdi_RegisterWindowClasses(void) +{ + WNDCLASSA cls; + + cls.style = 0; + cls.lpfnWndProc = mdi_frame_wnd_proc; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "MDI_frame_class"; + if (!RegisterClassA(&cls)) return FALSE; + + cls.lpfnWndProc = mdi_child_wnd_proc; + cls.lpszClassName = "MDI_child_class"; + if (!RegisterClassA(&cls)) return FALSE; + + if (!GetClassInfoA(0, "MDIClient", &cls)) assert(0); + old_mdi_client_proc = cls.lpfnWndProc; + cls.hInstance = GetModuleHandleA(0); + cls.lpfnWndProc = mdi_client_hook_proc; + cls.lpszClassName = "MDI_client_class"; + if (!RegisterClassA(&cls)) assert(0); + + return TRUE; +} + +static void test_mdi_messages(void) +{ + MDICREATESTRUCTA mdi_cs; + CLIENTCREATESTRUCT client_cs; + HWND mdi_frame, mdi_child, mdi_child2, active_child; + BOOL zoomed; + HMENU hMenu = CreateMenu(); + + assert(mdi_RegisterWindowClasses()); + + flush_sequence(); + + trace("creating MDI frame window\n"); + mdi_frame = CreateWindowExA(0, "MDI_frame_class", "MDI frame window", + WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_MAXIMIZEBOX | WS_VISIBLE, + 100, 100, CW_USEDEFAULT, CW_USEDEFAULT, + GetDesktopWindow(), hMenu, + GetModuleHandleA(0), NULL); + assert(mdi_frame); + ok_sequence(WmCreateMDIframeSeq, "Create MDI frame window", TRUE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_frame, "wrong focus window %p\n", GetFocus()); + + trace("creating MDI client window\n"); + client_cs.hWindowMenu = 0; + client_cs.idFirstChild = MDI_FIRST_CHILD_ID; + mdi_client = CreateWindowExA(0, "MDI_client_class", + NULL, + WS_CHILD | WS_VISIBLE | MDIS_ALLCHILDSTYLES, + 0, 0, 0, 0, + mdi_frame, 0, GetModuleHandleA(0), &client_cs); + assert(mdi_client); + ok_sequence(WmCreateMDIclientSeq, "Create visible MDI client window", FALSE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_frame, "input focus should be on MDI frame not on %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(!active_child, "wrong active MDI child %p\n", active_child); + ok(!zoomed, "wrong zoomed state %d\n", zoomed); + + SetFocus(0); + flush_sequence(); + + trace("creating invisible MDI child window\n"); + mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child", + WS_CHILD, + 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandleA(0), NULL); + assert(mdi_child); + + flush_sequence(); + ShowWindow(mdi_child, SW_SHOWNORMAL); + ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOWNORMAL) MDI child window", FALSE); + + ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n"); + ok(IsWindowVisible(mdi_child), "MDI child should be visible\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(!active_child, "wrong active MDI child %p\n", active_child); + ok(!zoomed, "wrong zoomed state %d\n", zoomed); + + ShowWindow(mdi_child, SW_HIDE); + ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE) MDI child window", FALSE); + flush_sequence(); + + ShowWindow(mdi_child, SW_SHOW); + ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW) MDI child window", FALSE); + + ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n"); + ok(IsWindowVisible(mdi_child), "MDI child should be visible\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(!active_child, "wrong active MDI child %p\n", active_child); + ok(!zoomed, "wrong zoomed state %d\n", zoomed); + + DestroyWindow(mdi_child); + flush_sequence(); + + trace("creating visible MDI child window\n"); + mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child", + WS_CHILD | WS_VISIBLE, + 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandleA(0), NULL); + assert(mdi_child); + ok_sequence(WmCreateMDIchildVisibleSeq, "Create visible MDI child window", FALSE); + + ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n"); + ok(IsWindowVisible(mdi_child), "MDI child should be visible\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child); + ok(!zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + DestroyWindow(mdi_child); + ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + /* Win2k: MDI client still returns a just destroyed child as active + * Win9x: MDI client returns 0 + */ + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child || /* win2k */ + !active_child, /* win9x */ + "wrong active MDI child %p\n", active_child); + ok(!zoomed, "wrong zoomed state %d\n", zoomed); + + flush_sequence(); + + trace("creating invisible MDI child window\n"); + mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child", + WS_CHILD, + 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandleA(0), NULL); + assert(mdi_child2); + ok_sequence(WmCreateMDIchildInvisibleSeq, "Create invisible MDI child window", FALSE); + + ok(!(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE), "MDI child should not be visible\n"); + ok(!IsWindowVisible(mdi_child2), "MDI child should not be visible\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + /* Win2k: MDI client still returns a just destroyed child as active + * Win9x: MDI client returns mdi_child2 + */ + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child || /* win2k */ + active_child == mdi_child2, /* win9x */ + "wrong active MDI child %p\n", active_child); + ok(!zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + ShowWindow(mdi_child2, SW_MAXIMIZE); + ok_sequence(WmMaximizeMDIchildInvisibleSeq, "ShowWindow(SW_MAXIMIZE):invisible MDI child", TRUE); + + ok(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n"); + ok(IsWindowVisible(mdi_child2), "MDI child should be visible\n"); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child); + ok(zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child2 || /* win2k */ + GetFocus() == 0, /* win9x */ + "wrong focus window %p\n", GetFocus()); + + SetFocus(0); + flush_sequence(); + + ShowWindow(mdi_child2, SW_HIDE); + ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE); + + ShowWindow(mdi_child2, SW_RESTORE); + ok_sequence(WmRestoreMDIchildInisibleSeq, "ShowWindow(SW_RESTORE):invisible MDI child", TRUE); + flush_sequence(); + + ok(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n"); + ok(IsWindowVisible(mdi_child2), "MDI child should be visible\n"); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child); + ok(!zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + SetFocus(0); + flush_sequence(); + + ShowWindow(mdi_child2, SW_HIDE); + ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE); + + ShowWindow(mdi_child2, SW_SHOW); + ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):MDI child", FALSE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + ShowWindow(mdi_child2, SW_MAXIMIZE); + ok_sequence(WmMaximizeMDIchildVisibleSeq, "ShowWindow(SW_MAXIMIZE):MDI child", TRUE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + ShowWindow(mdi_child2, SW_RESTORE); + ok_sequence(WmRestoreMDIchildVisibleSeq, "ShowWindow(SW_RESTORE):MDI child", TRUE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + SetFocus(0); + flush_sequence(); + + ShowWindow(mdi_child2, SW_HIDE); + ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + DestroyWindow(mdi_child2); + ok_sequence(WmDestroyMDIchildInvisibleSeq, "Destroy invisible MDI child window", FALSE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + /* test for maximized MDI children */ + trace("creating maximized visible MDI child window 1\n"); + mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child", + WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE, + 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandleA(0), NULL); + assert(mdi_child); + ok_sequence(WmCreateMDIchildVisibleMaxSeq1, "Create maximized visible 1st MDI child window", TRUE); + ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child || /* win2k */ + GetFocus() == 0, /* win9x */ + "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child); + ok(zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + trace("creating maximized visible MDI child window 2\n"); + mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child", + WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE, + 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandleA(0), NULL); + assert(mdi_child2); + ok_sequence(WmCreateMDIchildVisibleMaxSeq2, "Create maximized visible 2nd MDI child 2 window", TRUE); + ok(IsZoomed(mdi_child2), "2nd MDI child should be maximized\n"); + ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child2, "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child); + ok(zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + trace("destroying maximized visible MDI child window 2\n"); + DestroyWindow(mdi_child2); + ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE); + + ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + /* Win2k: MDI client still returns a just destroyed child as active + * Win9x: MDI client returns 0 + */ + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child2 || /* win2k */ + !active_child, /* win9x */ + "wrong active MDI child %p\n", active_child); + flush_sequence(); + + ShowWindow(mdi_child, SW_MAXIMIZE); + ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n"); + flush_sequence(); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus()); + + trace("re-creating maximized visible MDI child window 2\n"); + mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child", + WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE, + 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandleA(0), NULL); + assert(mdi_child2); + ok_sequence(WmCreateMDIchildVisibleMaxSeq2, "Create maximized visible 2nd MDI child 2 window", TRUE); + ok(IsZoomed(mdi_child2), "2nd MDI child should be maximized\n"); + ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n"); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child2, "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child); + ok(zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + SendMessageA(mdi_child2, WM_SYSCOMMAND, SC_CLOSE, 0); + ok_sequence(WmDestroyMDIchildVisibleMaxSeq2, "WM_SYSCOMMAND/SC_CLOSE on a visible maximized MDI child window", TRUE); + ok(!IsWindow(mdi_child2), "MDI child 2 should be destroyed\n"); + + ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n"); + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child); + ok(zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + DestroyWindow(mdi_child); + ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE); + + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + /* Win2k: MDI client still returns a just destroyed child as active + * Win9x: MDI client returns 0 + */ + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child || /* win2k */ + !active_child, /* win9x */ + "wrong active MDI child %p\n", active_child); + flush_sequence(); + /* end of test for maximized MDI children */ + + mdi_cs.szClass = "MDI_child_Class"; + mdi_cs.szTitle = "MDI child"; + mdi_cs.hOwner = GetModuleHandleA(0); + mdi_cs.x = 0; + mdi_cs.y = 0; + mdi_cs.cx = CW_USEDEFAULT; + mdi_cs.cy = CW_USEDEFAULT; + mdi_cs.style = WS_CHILD | WS_SYSMENU | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE; + mdi_cs.lParam = 0; + mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok_sequence(WmCreateMDIchildVisibleMaxSeq3, "WM_MDICREATE for maximized visible MDI child window", TRUE); + + ok(GetMenuItemID(hMenu, GetMenuItemCount(hMenu) - 1) == SC_CLOSE, "SC_CLOSE menu item not found\n"); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child); + + ok(IsZoomed(mdi_child), "MDI child should be maximized\n"); + ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow()); + ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus()); + + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child); + ok(zoomed, "wrong zoomed state %d\n", zoomed); + flush_sequence(); + + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok_sequence(WmDestroyMDIchildVisibleMaxSeq1, "Destroy visible maximized MDI child window", TRUE); + + ok(!IsWindow(mdi_child), "MDI child should be destroyed\n"); + active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed); + ok(!active_child, "wrong active MDI child %p\n", active_child); + + SetFocus(0); + flush_sequence(); + + DestroyWindow(mdi_client); + ok_sequence(WmDestroyMDIclientSeq, "Destroy MDI client window", FALSE); + + DestroyWindow(mdi_frame); + ok_sequence(WmDestroyMDIframeSeq, "Destroy MDI frame window", FALSE); +} +/************************* End of MDI test **********************************/ + +static void test_WM_SETREDRAW(HWND hwnd) +{ + DWORD style = GetWindowLongA(hwnd, GWL_STYLE); + + flush_sequence(); + + SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0); + ok_sequence(WmSetRedrawFalseSeq, "SetRedraw:FALSE", FALSE); + + ok(!(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should NOT be set\n"); + ok(!IsWindowVisible(hwnd), "IsWindowVisible() should return FALSE\n"); + + flush_sequence(); + SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0); + ok_sequence(WmSetRedrawTrueSeq, "SetRedraw:TRUE", FALSE); + + ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n"); + ok(IsWindowVisible(hwnd), "IsWindowVisible() should return TRUE\n"); + + /* restore original WS_VISIBLE state */ + SetWindowLongA(hwnd, GWL_STYLE, style); + + flush_sequence(); +} + +static INT_PTR CALLBACK TestModalDlgProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct message msg; + + trace("dialog: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + switch (message) + { + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lParam; + + trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + /* Log only documented flags, win2k uses 0x1000 and 0x2000 + * in the high word for internal purposes + */ + wParam = winpos->flags & 0xffff; + break; + } + } + + msg.message = message; + msg.flags = sent|wparam|lparam; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + + if (message == WM_INITDIALOG) SetTimer( hwnd, 1, 100, NULL ); + if (message == WM_TIMER) EndDialog( hwnd, 0 ); + return 0; +} + +static void test_hv_scroll_1(HWND hwnd, INT ctl, DWORD clear, DWORD set, INT min, INT max) +{ + DWORD style, exstyle; + INT xmin, xmax; + BOOL ret; + + exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE); + style = GetWindowLongA(hwnd, GWL_STYLE); + /* do not be confused by WS_DLGFRAME set */ + if ((style & WS_CAPTION) == WS_CAPTION) style &= ~WS_CAPTION; + + if (clear) ok(style & clear, "style %08lx should be set\n", clear); + if (set) ok(!(style & set), "style %08lx should not be set\n", set); + + ret = SetScrollRange(hwnd, ctl, min, max, FALSE); + ok( ret, "SetScrollRange(%d) error %ld\n", ctl, GetLastError()); + if ((style & (WS_DLGFRAME | WS_BORDER | WS_THICKFRAME)) || (exstyle & WS_EX_DLGMODALFRAME)) + ok_sequence(WmSetScrollRangeHV_NC_Seq, "SetScrollRange(SB_HORZ/SB_VERT) NC", FALSE); + else + ok_sequence(WmSetScrollRangeHVSeq, "SetScrollRange(SB_HORZ/SB_VERT)", FALSE); + + style = GetWindowLongA(hwnd, GWL_STYLE); + if (set) ok(style & set, "style %08lx should be set\n", set); + if (clear) ok(!(style & clear), "style %08lx should not be set\n", clear); + + /* a subsequent call should do nothing */ + ret = SetScrollRange(hwnd, ctl, min, max, FALSE); + ok( ret, "SetScrollRange(%d) error %ld\n", ctl, GetLastError()); + ok_sequence(WmEmptySeq, "SetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE); + + xmin = 0xdeadbeef; + xmax = 0xdeadbeef; + trace("Ignore GetScrollRange error below if you are on Win9x\n"); + ret = GetScrollRange(hwnd, ctl, &xmin, &xmax); + ok( ret, "GetScrollRange(%d) error %ld\n", ctl, GetLastError()); + ok_sequence(WmEmptySeq, "GetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE); + ok(xmin == min, "unexpected min scroll value %d\n", xmin); + ok(xmax == max, "unexpected max scroll value %d\n", xmax); +} + +static void test_hv_scroll_2(HWND hwnd, INT ctl, DWORD clear, DWORD set, INT min, INT max) +{ + DWORD style, exstyle; + SCROLLINFO si; + BOOL ret; + + exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE); + style = GetWindowLongA(hwnd, GWL_STYLE); + /* do not be confused by WS_DLGFRAME set */ + if ((style & WS_CAPTION) == WS_CAPTION) style &= ~WS_CAPTION; + + if (clear) ok(style & clear, "style %08lx should be set\n", clear); + if (set) ok(!(style & set), "style %08lx should not be set\n", set); + + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE; + si.nMin = min; + si.nMax = max; + SetScrollInfo(hwnd, ctl, &si, TRUE); + if ((style & (WS_DLGFRAME | WS_BORDER | WS_THICKFRAME)) || (exstyle & WS_EX_DLGMODALFRAME)) + ok_sequence(WmSetScrollRangeHV_NC_Seq, "SetScrollInfo(SB_HORZ/SB_VERT) NC", FALSE); + else + ok_sequence(WmSetScrollRangeHVSeq, "SetScrollInfo(SB_HORZ/SB_VERT)", FALSE); + + style = GetWindowLongA(hwnd, GWL_STYLE); + if (set) ok(style & set, "style %08lx should be set\n", set); + if (clear) ok(!(style & clear), "style %08lx should not be set\n", clear); + + /* a subsequent call should do nothing */ + SetScrollInfo(hwnd, ctl, &si, TRUE); + if (style & WS_HSCROLL) + ok_sequence(WmSetScrollRangeHSeq_empty, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE); + else if (style & WS_VSCROLL) + ok_sequence(WmSetScrollRangeVSeq_empty, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE); + else + ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE); + + si.fMask = SIF_PAGE; + si.nPage = 5; + SetScrollInfo(hwnd, ctl, &si, FALSE); + ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE); + + si.fMask = SIF_POS; + si.nPos = max - 1; + SetScrollInfo(hwnd, ctl, &si, FALSE); + ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE); + + si.fMask = SIF_RANGE; + si.nMin = 0xdeadbeef; + si.nMax = 0xdeadbeef; + ret = GetScrollInfo(hwnd, ctl, &si); + ok( ret, "GetScrollInfo error %ld\n", GetLastError()); + ok_sequence(WmEmptySeq, "GetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE); + ok(si.nMin == min, "unexpected min scroll value %d\n", si.nMin); + ok(si.nMax == max, "unexpected max scroll value %d\n", si.nMax); +} + +/* Win9x sends WM_USER+xxx while and NT versions send SBM_xxx messages */ +static void test_scroll_messages(HWND hwnd) +{ + SCROLLINFO si; + INT min, max; + BOOL ret; + + min = 0xdeadbeef; + max = 0xdeadbeef; + ret = GetScrollRange(hwnd, SB_CTL, &min, &max); + ok( ret, "GetScrollRange error %ld\n", GetLastError()); + if (sequence->message != WmGetScrollRangeSeq[0].message) + trace("GetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message); + /* values of min and max are undefined */ + flush_sequence(); + + ret = SetScrollRange(hwnd, SB_CTL, 10, 150, FALSE); + ok( ret, "SetScrollRange error %ld\n", GetLastError()); + if (sequence->message != WmSetScrollRangeSeq[0].message) + trace("SetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message); + flush_sequence(); + + min = 0xdeadbeef; + max = 0xdeadbeef; + ret = GetScrollRange(hwnd, SB_CTL, &min, &max); + ok( ret, "GetScrollRange error %ld\n", GetLastError()); + if (sequence->message != WmGetScrollRangeSeq[0].message) + trace("GetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message); + /* values of min and max are undefined */ + flush_sequence(); + + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE; + si.nMin = 20; + si.nMax = 160; + SetScrollInfo(hwnd, SB_CTL, &si, FALSE); + if (sequence->message != WmSetScrollRangeSeq[0].message) + trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message); + flush_sequence(); + + si.fMask = SIF_PAGE; + si.nPage = 10; + SetScrollInfo(hwnd, SB_CTL, &si, FALSE); + if (sequence->message != WmSetScrollRangeSeq[0].message) + trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message); + flush_sequence(); + + si.fMask = SIF_POS; + si.nPos = 20; + SetScrollInfo(hwnd, SB_CTL, &si, FALSE); + if (sequence->message != WmSetScrollRangeSeq[0].message) + trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message); + flush_sequence(); + + si.fMask = SIF_RANGE; + si.nMin = 0xdeadbeef; + si.nMax = 0xdeadbeef; + ret = GetScrollInfo(hwnd, SB_CTL, &si); + ok( ret, "GetScrollInfo error %ld\n", GetLastError()); + if (sequence->message != WmGetScrollInfoSeq[0].message) + trace("GetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message); + /* values of min and max are undefined */ + flush_sequence(); + + /* set WS_HSCROLL */ + test_hv_scroll_1(hwnd, SB_HORZ, 0, WS_HSCROLL, 10, 150); + /* clear WS_HSCROLL */ + test_hv_scroll_1(hwnd, SB_HORZ, WS_HSCROLL, 0, 0, 0); + + /* set WS_HSCROLL */ + test_hv_scroll_2(hwnd, SB_HORZ, 0, WS_HSCROLL, 10, 150); + /* clear WS_HSCROLL */ + test_hv_scroll_2(hwnd, SB_HORZ, WS_HSCROLL, 0, 0, 0); + + /* set WS_VSCROLL */ + test_hv_scroll_1(hwnd, SB_VERT, 0, WS_VSCROLL, 10, 150); + /* clear WS_VSCROLL */ + test_hv_scroll_1(hwnd, SB_VERT, WS_VSCROLL, 0, 0, 0); + + /* set WS_VSCROLL */ + test_hv_scroll_2(hwnd, SB_VERT, 0, WS_VSCROLL, 10, 150); + /* clear WS_VSCROLL */ + test_hv_scroll_2(hwnd, SB_VERT, WS_VSCROLL, 0, 0, 0); +} + +static void test_showwindow(void) +{ + HWND hwnd, hchild; + + hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hwnd != 0, "Failed to create overlapped window\n"); + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD, + 0, 0, 10, 10, hwnd, 0, 0, NULL); + ok (hchild != 0, "Failed to create child\n"); + flush_sequence(); + + /* ShowWindow( SW_SHOWNA) for invisible top level window */ + trace("calling ShowWindow( SW_SHOWNA) for invisible top level window\n"); + ok( ShowWindow(hwnd, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" ); + ok_sequence(WmSHOWNATopInvisible, "ShowWindow(SW_SHOWNA) on invisible top level window", TRUE); + trace("done\n"); + + /* ShowWindow( SW_SHOWNA) for now visible top level window */ + trace("calling ShowWindow( SW_SHOWNA) for now visible top level window\n"); + ok( ShowWindow(hwnd, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" ); + ok_sequence(WmSHOWNATopVisible, "ShowWindow(SW_SHOWNA) on visible top level window", FALSE); + trace("done\n"); + /* back to invisible */ + ShowWindow(hchild, SW_HIDE); + ShowWindow(hwnd, SW_HIDE); + flush_sequence(); + /* ShowWindow(SW_SHOWNA) with child and parent invisible */ + trace("calling ShowWindow( SW_SHOWNA) for invisible child with invisible parent\n"); + ok( ShowWindow(hchild, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" ); + ok_sequence(WmSHOWNAChildInvisParInvis, "ShowWindow(SW_SHOWNA) invisible child and parent", TRUE); + trace("done\n"); + /* ShowWindow(SW_SHOWNA) with child visible and parent invisible */ + ok( ShowWindow(hchild, SW_SHOW) != FALSE, "ShowWindow: window was invisible\n" ); + flush_sequence(); + trace("calling ShowWindow( SW_SHOWNA) for the visible child and invisible parent\n"); + ok( ShowWindow(hchild, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" ); + ok_sequence(WmSHOWNAChildVisParInvis, "ShowWindow(SW_SHOWNA) visible child and invisible parent", TRUE); + trace("done\n"); + /* ShowWindow(SW_SHOWNA) with child visible and parent visible */ + ShowWindow( hwnd, SW_SHOW); + flush_sequence(); + trace("calling ShowWindow( SW_SHOWNA) for the visible child and parent\n"); + ok( ShowWindow(hchild, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" ); + ok_sequence(WmSHOWNAChildVisParVis, "ShowWindow(SW_SHOWNA) for the visible child and parent", FALSE); + trace("done\n"); + + /* ShowWindow(SW_SHOWNA) with child invisible and parent visible */ + ShowWindow( hchild, SW_HIDE); + flush_sequence(); + trace("calling ShowWindow( SW_SHOWNA) for the invisible child and visible parent\n"); + ok( ShowWindow(hchild, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" ); + ok_sequence(WmSHOWNAChildInvisParVis, "ShowWindow(SW_SHOWNA) for the invisible child and visible parent", FALSE); + trace("done\n"); + + SetCapture(hchild); + ok(GetCapture() == hchild, "wrong capture window %p\n", GetCapture()); + DestroyWindow(hchild); + ok(!GetCapture(), "wrong capture window %p\n", GetCapture()); + + DestroyWindow(hwnd); + flush_sequence(); +} + +static void test_sys_menu(HWND hwnd) +{ + HMENU hmenu; + UINT state; + + /* test existing window without CS_NOCLOSE style */ + hmenu = GetSystemMenu(hwnd, FALSE); + ok(hmenu != 0, "GetSystemMenu error %ld\n", GetLastError()); + + state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND); + ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state); + ok(!(state & (MF_DISABLED | MF_GRAYED)), "wrong SC_CLOSE state %x\n", state); + + EnableMenuItem(hmenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED); + ok_sequence(WmEmptySeq, "WmEnableMenuItem", FALSE); + + state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND); + ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state); + ok((state & (MF_DISABLED | MF_GRAYED)) == MF_GRAYED, "wrong SC_CLOSE state %x\n", state); + + EnableMenuItem(hmenu, SC_CLOSE, 0); + ok_sequence(WmEmptySeq, "WmEnableMenuItem", FALSE); + + state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND); + ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state); + ok(!(state & (MF_DISABLED | MF_GRAYED)), "wrong SC_CLOSE state %x\n", state); + + /* test new window with CS_NOCLOSE style */ + hwnd = CreateWindowExA(0, "NoCloseWindowClass", NULL, WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hwnd != 0, "Failed to create overlapped window\n"); + + hmenu = GetSystemMenu(hwnd, FALSE); + ok(hmenu != 0, "GetSystemMenu error %ld\n", GetLastError()); + + state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND); + ok(state == 0xffffffff, "wrong SC_CLOSE state %x\n", state); + + DestroyWindow(hwnd); +} + +/* test if we receive the right sequence of messages */ +static void test_messages(void) +{ + HWND hwnd, hparent, hchild; + HWND hchild2, hbutton; + HMENU hmenu; + MSG msg; + DWORD ret; + + flush_sequence(); + + hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hwnd != 0, "Failed to create overlapped window\n"); + ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped", FALSE); + + /* test ShowWindow(SW_HIDE) on a newly created invisible window */ + ok( ShowWindow(hwnd, SW_HIDE) == FALSE, "ShowWindow: window was visible\n" ); + ok_sequence(WmEmptySeq, "ShowWindow(SW_HIDE):overlapped, invisible", FALSE); + + /* test WM_SETREDRAW on a not visible top level window */ + test_WM_SETREDRAW(hwnd); + + SetWindowPos(hwnd, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE); + ok_sequence(WmSWP_ShowOverlappedSeq, "SetWindowPos:SWP_SHOWWINDOW:overlapped", FALSE); + ok(IsWindowVisible(hwnd), "window should be visible at this point\n"); + + ok(GetActiveWindow() == hwnd, "window should be active\n"); + ok(GetFocus() == hwnd, "window should have input focus\n"); + ShowWindow(hwnd, SW_HIDE); + ok_sequence(WmHideOverlappedSeq, "ShowWindow(SW_HIDE):overlapped", TRUE); + + ShowWindow(hwnd, SW_SHOW); + ok_sequence(WmShowOverlappedSeq, "ShowWindow(SW_SHOW):overlapped", TRUE); + + ShowWindow(hwnd, SW_SHOW); + ok_sequence(WmEmptySeq, "ShowWindow(SW_SHOW):overlapped already visible", FALSE); + + ok(GetActiveWindow() == hwnd, "window should be active\n"); + ok(GetFocus() == hwnd, "window should have input focus\n"); + SetWindowPos(hwnd, 0,0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE); + ok_sequence(WmSWP_HideOverlappedSeq, "SetWindowPos:SWP_HIDEWINDOW:overlapped", FALSE); + ok(!IsWindowVisible(hwnd), "window should not be visible at this point\n"); + ok(GetActiveWindow() == hwnd, "window should still be active\n"); + + /* test WM_SETREDRAW on a visible top level window */ + ShowWindow(hwnd, SW_SHOW); + test_WM_SETREDRAW(hwnd); + + trace("testing scroll APIs on a visible top level window %p\n", hwnd); + test_scroll_messages(hwnd); + + /* test resizing and moving */ + SetWindowPos( hwnd, 0, 0, 0, 300, 300, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE ); + ok_sequence(WmSWP_ResizeSeq, "SetWindowPos:Resize", FALSE ); + SetWindowPos( hwnd, 0, 200, 200, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE ); + ok_sequence(WmSWP_MoveSeq, "SetWindowPos:Move", FALSE ); + + /* popups don't get WM_GETMINMAXINFO */ + SetWindowLongW( hwnd, GWL_STYLE, WS_VISIBLE|WS_POPUP ); + SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED); + flush_sequence(); + SetWindowPos( hwnd, 0, 0, 0, 200, 200, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE ); + ok_sequence(WmSWP_ResizePopupSeq, "SetWindowPos:ResizePopup", FALSE ); + + test_sys_menu(hwnd); + + flush_sequence(); + DestroyWindow(hwnd); + ok_sequence(WmDestroyOverlappedSeq, "DestroyWindow:overlapped", FALSE); + + hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hparent != 0, "Failed to create parent window\n"); + flush_sequence(); + + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_MAXIMIZE, + 0, 0, 10, 10, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create child window\n"); + ok_sequence(WmCreateMaximizedChildSeq, "CreateWindow:maximized child", TRUE); + DestroyWindow(hchild); + flush_sequence(); + + /* visible child window with a caption */ + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", + WS_CHILD | WS_VISIBLE | WS_CAPTION, + 0, 0, 10, 10, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create child window\n"); + ok_sequence(WmCreateVisibleChildSeq, "CreateWindow:visible child", FALSE); + + trace("testing scroll APIs on a visible child window %p\n", hchild); + test_scroll_messages(hchild); + + SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE); + ok_sequence(WmShowChildSeq_4, "SetWindowPos(SWP_SHOWWINDOW):child with a caption", FALSE); + + DestroyWindow(hchild); + flush_sequence(); + + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD, + 0, 0, 10, 10, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create child window\n"); + ok_sequence(WmCreateChildSeq, "CreateWindow:child", FALSE); + + hchild2 = CreateWindowExA(0, "SimpleWindowClass", "Test child2", WS_CHILD, + 100, 100, 50, 50, hparent, 0, 0, NULL); + ok (hchild2 != 0, "Failed to create child2 window\n"); + flush_sequence(); + + hbutton = CreateWindowExA(0, "TestWindowClass", "Test button", WS_CHILD, + 0, 100, 50, 50, hchild, 0, 0, NULL); + ok (hbutton != 0, "Failed to create button window\n"); + + /* test WM_SETREDRAW on a not visible child window */ + test_WM_SETREDRAW(hchild); + + ShowWindow(hchild, SW_SHOW); + ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):child", FALSE); + + ShowWindow(hchild, SW_HIDE); + ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):child", FALSE); + + ShowWindow(hchild, SW_SHOW); + ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):child", FALSE); + + /* test WM_SETREDRAW on a visible child window */ + test_WM_SETREDRAW(hchild); + + MoveWindow(hchild, 10, 10, 20, 20, TRUE); + ok_sequence(WmResizingChildWithMoveWindowSeq, "MoveWindow:child", FALSE); + + ShowWindow(hchild, SW_HIDE); + flush_sequence(); + SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE); + ok_sequence(WmShowChildSeq_2, "SetWindowPos:show_child_2", FALSE); + + ShowWindow(hchild, SW_HIDE); + flush_sequence(); + SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE); + ok_sequence(WmShowChildSeq_3, "SetWindowPos:show_child_3", FALSE); + + /* DestroyWindow sequence below expects that a child has focus */ + SetFocus(hchild); + flush_sequence(); + + DestroyWindow(hchild); + ok_sequence(WmDestroyChildSeq, "DestroyWindow:child", FALSE); + DestroyWindow(hchild2); + DestroyWindow(hbutton); + + flush_sequence(); + hchild = CreateWindowExA(0, "TestWindowClass", "Test Child Popup", WS_CHILD | WS_POPUP, + 0, 0, 100, 100, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create child popup window\n"); + ok_sequence(WmCreateChildPopupSeq, "CreateWindow:child_popup", FALSE); + DestroyWindow(hchild); + + /* test what happens to a window which sets WS_VISIBLE in WM_CREATE */ + flush_sequence(); + hchild = CreateWindowExA(0, "TestPopupClass", "Test Popup", WS_POPUP, + 0, 0, 100, 100, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create popup window\n"); + ok_sequence(WmCreateInvisiblePopupSeq, "CreateWindow:invisible_popup", FALSE); + ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n"); + ok(IsWindowVisible(hchild), "IsWindowVisible() should return TRUE\n"); + flush_sequence(); + ShowWindow(hchild, SW_SHOW); + ok_sequence(WmEmptySeq, "ShowWindow:show_visible_popup", FALSE); + flush_sequence(); + SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); + ok_sequence(WmShowVisiblePopupSeq_2, "SetWindowPos:show_visible_popup_2", FALSE); + flush_sequence(); + SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE); + ok_sequence(WmShowVisiblePopupSeq_3, "SetWindowPos:show_visible_popup_3", FALSE); + DestroyWindow(hchild); + + /* this time add WS_VISIBLE for CreateWindowEx, but this fact actually + * changes nothing in message sequences. + */ + flush_sequence(); + hchild = CreateWindowExA(0, "TestPopupClass", "Test Popup", WS_POPUP | WS_VISIBLE, + 0, 0, 100, 100, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create popup window\n"); + ok_sequence(WmCreateInvisiblePopupSeq, "CreateWindow:invisible_popup", FALSE); + ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n"); + ok(IsWindowVisible(hchild), "IsWindowVisible() should return TRUE\n"); + flush_sequence(); + ShowWindow(hchild, SW_SHOW); + ok_sequence(WmEmptySeq, "ShowWindow:show_visible_popup", FALSE); + flush_sequence(); + SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); + ok_sequence(WmShowVisiblePopupSeq_2, "SetWindowPos:show_visible_popup_2", FALSE); + DestroyWindow(hchild); + + flush_sequence(); + hwnd = CreateWindowExA(WS_EX_DLGMODALFRAME, "TestDialogClass", NULL, WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_DLGFRAME, + 0, 0, 100, 100, hparent, 0, 0, NULL); + ok(hwnd != 0, "Failed to create custom dialog window\n"); + ok_sequence(WmCreateCustomDialogSeq, "CreateCustomDialog", TRUE); + + /* + trace("testing scroll APIs on a visible dialog %p\n", hwnd); + test_scroll_messages(hwnd); + */ + + flush_sequence(); + after_end_dialog = 1; + EndDialog( hwnd, 0 ); + ok_sequence(WmEndCustomDialogSeq, "EndCustomDialog", FALSE); + + DestroyWindow(hwnd); + after_end_dialog = 0; + + hwnd = CreateWindowExA(0, "TestDialogClass", NULL, WS_POPUP, + 0, 0, 100, 100, 0, 0, GetModuleHandleA(0), NULL); + ok(hwnd != 0, "Failed to create custom dialog window\n"); + flush_sequence(); + trace("call ShowWindow(%p, SW_SHOW)\n", hwnd); + ShowWindow(hwnd, SW_SHOW); + ok_sequence(WmShowCustomDialogSeq, "ShowCustomDialog", TRUE); + DestroyWindow(hwnd); + + flush_sequence(); + DialogBoxA( 0, "TEST_DIALOG", hparent, TestModalDlgProcA ); + ok_sequence(WmModalDialogSeq, "ModalDialog", TRUE); + + /* test showing child with hidden parent */ + ShowWindow( hparent, SW_HIDE ); + flush_sequence(); + + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD, + 0, 0, 10, 10, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create child window\n"); + ok_sequence(WmCreateChildSeq, "CreateWindow:child", FALSE); + + ShowWindow( hchild, SW_SHOW ); + ok_sequence(WmShowChildInvisibleParentSeq, "ShowWindow:show child with invisible parent", TRUE); + ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n"); + ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n"); + + ShowWindow( hchild, SW_HIDE ); + ok_sequence(WmHideChildInvisibleParentSeq, "ShowWindow:hide child with invisible parent", TRUE); + ok(!(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should be not set\n"); + ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n"); + + SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); + ok_sequence(WmShowChildInvisibleParentSeq_2, "SetWindowPos:show child with invisible parent", FALSE); + ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n"); + ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n"); + + SetWindowPos(hchild, 0,0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER); + ok_sequence(WmHideChildInvisibleParentSeq_2, "SetWindowPos:hide child with invisible parent", FALSE); + ok(!(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should not be set\n"); + ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n"); + + DestroyWindow(hchild); + DestroyWindow(hparent); + flush_sequence(); + + /* Message sequence for SetMenu */ + ok(!DrawMenuBar(hwnd), "DrawMenuBar should return FALSE for a window without a menu\n"); + ok_sequence(WmEmptySeq, "DrawMenuBar for a window without a menu", FALSE); + + hmenu = CreateMenu(); + ok (hmenu != 0, "Failed to create menu\n"); + ok (InsertMenuA(hmenu, -1, MF_BYPOSITION, 0x1000, "foo"), "InsertMenu failed\n"); + hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, hmenu, 0, NULL); + ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped", FALSE); + ok (SetMenu(hwnd, 0), "SetMenu\n"); + ok_sequence(WmSetMenuNonVisibleSizeChangeSeq, "SetMenu:NonVisibleSizeChange", FALSE); + ok (SetMenu(hwnd, 0), "SetMenu\n"); + ok_sequence(WmSetMenuNonVisibleNoSizeChangeSeq, "SetMenu:NonVisibleNoSizeChange", FALSE); + ShowWindow(hwnd, SW_SHOW); + flush_sequence(); + ok (SetMenu(hwnd, 0), "SetMenu\n"); + ok_sequence(WmSetMenuVisibleNoSizeChangeSeq, "SetMenu:VisibleNoSizeChange", TRUE); + ok (SetMenu(hwnd, hmenu), "SetMenu\n"); + ok_sequence(WmSetMenuVisibleSizeChangeSeq, "SetMenu:VisibleSizeChange", TRUE); + + ok(DrawMenuBar(hwnd), "DrawMenuBar\n"); + ok_sequence(WmDrawMenuBarSeq, "DrawMenuBar", TRUE); + + DestroyWindow(hwnd); + flush_sequence(); + + /* Message sequence for EnableWindow */ + hparent = CreateWindowExA(0, "TestWindowClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hparent != 0, "Failed to create parent window\n"); + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_VISIBLE, + 0, 0, 10, 10, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create child window\n"); + + SetFocus(hchild); + flush_sequence(); + + EnableWindow(hparent, FALSE); + ok_sequence(WmEnableWindowSeq_1, "EnableWindow(FALSE)", FALSE); + + EnableWindow(hparent, TRUE); + ok_sequence(WmEnableWindowSeq_2, "EnableWindow(TRUE)", FALSE); + + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + flush_sequence(); + + /* MsgWaitForMultipleObjects test */ + ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE); + ok(ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %lx\n", ret); + + PostMessageA(hparent, WM_USER, 0, 0); + + ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE); + ok(ret == WAIT_OBJECT_0, "MsgWaitForMultipleObjects returned %lx\n", ret); + + ok(PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n"); + ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message); + + ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE); + ok(ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %lx\n", ret); + /* end of MsgWaitForMultipleObjects test */ + + /* the following test causes an exception in user.exe under win9x */ + if (!PostMessageW( hparent, WM_USER, 0, 0 )) return; + PostMessageW( hparent, WM_USER+1, 0, 0 ); + /* PeekMessage(NULL) fails, but still removes the message */ + SetLastError(0xdeadbeef); + ok( !PeekMessageW( NULL, 0, 0, 0, PM_REMOVE ), "PeekMessage(NULL) should fail\n" ); + ok( GetLastError() == ERROR_NOACCESS || /* Win2k */ + GetLastError() == 0xdeadbeef, /* NT4 */ + "last error is %ld\n", GetLastError() ); + ok( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n" ); + ok( msg.message == WM_USER+1, "got %x instead of WM_USER+1\n", msg.message ); + + DestroyWindow(hchild); + DestroyWindow(hparent); + flush_sequence(); + + test_showwindow(); +} + +/****************** button message test *************************/ +static const struct message WmSetFocusButtonSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORBTN, sent|defwinproc }, + { 0 } +}; +static const struct message WmKillFocusButtonSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_KILLFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORBTN, sent|defwinproc }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { 0 } +}; +static const struct message WmSetFocusStaticSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORSTATIC, sent|defwinproc }, + { 0 } +}; +static const struct message WmKillFocusStaticSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_KILLFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORSTATIC, sent|defwinproc }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { 0 } +}; +static const struct message WmLButtonDownSeq[] = +{ + { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 }, + { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 }, + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { WM_SETFOCUS, sent|wparam|defwinproc, 0 }, + { WM_CTLCOLORBTN, sent|defwinproc }, + { BM_SETSTATE, sent|wparam|defwinproc, TRUE }, + { WM_CTLCOLORBTN, sent|defwinproc }, + { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { 0 } +}; +static const struct message WmLButtonUpSeq[] = +{ + { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 }, + { BM_SETSTATE, sent|wparam|defwinproc, FALSE }, + { WM_CTLCOLORBTN, sent|defwinproc }, + { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 }, + { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CAPTURECHANGED, sent|wparam|defwinproc, 0 }, + { 0 } +}; + +static WNDPROC old_button_proc; + +static LRESULT CALLBACK button_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + LRESULT ret; + struct message msg; + + trace("button: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + + if (message == BM_SETSTATE) + ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture()); + + defwndproc_counter++; + ret = CallWindowProcA(old_button_proc, hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static void subclass_button(void) +{ + WNDCLASSA cls; + + if (!GetClassInfoA(0, "button", &cls)) assert(0); + + old_button_proc = cls.lpfnWndProc; + + cls.hInstance = GetModuleHandle(0); + cls.lpfnWndProc = button_hook_proc; + cls.lpszClassName = "my_button_class"; + if (!RegisterClassA(&cls)) assert(0); +} + +static void test_button_messages(void) +{ + static const struct + { + DWORD style; + DWORD dlg_code; + const struct message *setfocus; + const struct message *killfocus; + } button[] = { + { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, + WmSetFocusButtonSeq, WmKillFocusButtonSeq }, + { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON, + WmSetFocusButtonSeq, WmKillFocusButtonSeq }, + { BS_CHECKBOX, DLGC_BUTTON, + WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_AUTOCHECKBOX, DLGC_BUTTON, + WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, + WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_3STATE, DLGC_BUTTON, + WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_AUTO3STATE, DLGC_BUTTON, + WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_GROUPBOX, DLGC_STATIC, + WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, + WmSetFocusButtonSeq, WmKillFocusButtonSeq }, + { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, + WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_OWNERDRAW, DLGC_BUTTON, + WmSetFocusButtonSeq, WmKillFocusButtonSeq } + }; + unsigned int i; + HWND hwnd; + DWORD dlg_code; + + subclass_button(); + + for (i = 0; i < sizeof(button)/sizeof(button[0]); i++) + { + hwnd = CreateWindowExA(0, "my_button_class", "test", button[i].style | WS_POPUP, + 0, 0, 50, 14, 0, 0, 0, NULL); + ok(hwnd != 0, "Failed to create button window\n"); + + dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0); + ok(dlg_code == button[i].dlg_code, "%d: wrong dlg_code %08lx\n", i, dlg_code); + + ShowWindow(hwnd, SW_SHOW); + UpdateWindow(hwnd); + SetFocus(0); + flush_sequence(); + + trace("button style %08lx\n", button[i].style); + SetFocus(hwnd); + ok_sequence(button[i].setfocus, "SetFocus(hwnd) on a button", FALSE); + + SetFocus(0); + ok_sequence(button[i].killfocus, "SetFocus(0) on a button", FALSE); + + DestroyWindow(hwnd); + } + + hwnd = CreateWindowExA(0, "my_button_class", "test", BS_PUSHBUTTON | WS_POPUP | WS_VISIBLE, + 0, 0, 50, 14, 0, 0, 0, NULL); + ok(hwnd != 0, "Failed to create button window\n"); + + SetFocus(0); + flush_sequence(); + + SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0); + ok_sequence(WmLButtonDownSeq, "WM_LBUTTONDOWN on a button", FALSE); + + SendMessageA(hwnd, WM_LBUTTONUP, 0, 0); + ok_sequence(WmLButtonUpSeq, "WM_LBUTTONUP on a button", FALSE); + DestroyWindow(hwnd); +} + +/************* painting message test ********************/ + +void dump_region(HRGN hrgn) +{ + DWORD i, size; + RGNDATA *data = NULL; + RECT *rect; + + if (!hrgn) + { + printf( "null region\n" ); + return; + } + if (!(size = GetRegionData( hrgn, 0, NULL ))) return; + if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return; + GetRegionData( hrgn, size, data ); + printf("%ld rects:", data->rdh.nCount ); + for (i = 0, rect = (RECT *)data->Buffer; i < data->rdh.nCount; i++, rect++) + printf( " (%ld,%ld)-(%ld,%ld)", rect->left, rect->top, rect->right, rect->bottom ); + printf("\n"); + HeapFree( GetProcessHeap(), 0, data ); +} + +static void check_update_rgn( HWND hwnd, HRGN hrgn ) +{ + INT ret; + RECT r1, r2; + HRGN tmp = CreateRectRgn( 0, 0, 0, 0 ); + HRGN update = CreateRectRgn( 0, 0, 0, 0 ); + + ret = GetUpdateRgn( hwnd, update, FALSE ); + ok( ret != ERROR, "GetUpdateRgn failed\n" ); + if (ret == NULLREGION) + { + ok( !hrgn, "Update region shouldn't be empty\n" ); + } + else + { + if (CombineRgn( tmp, hrgn, update, RGN_XOR ) != NULLREGION) + { + ok( 0, "Regions are different\n" ); + if (winetest_debug > 0) + { + printf( "Update region: " ); + dump_region( update ); + printf( "Wanted region: " ); + dump_region( hrgn ); + } + } + } + GetRgnBox( update, &r1 ); + GetUpdateRect( hwnd, &r2, FALSE ); + ok( r1.left == r2.left && r1.top == r2.top && r1.right == r2.right && r1.bottom == r2.bottom, + "Rectangles are different: %ld,%ld-%ld,%ld / %ld,%ld-%ld,%ld\n", + r1.left, r1.top, r1.right, r1.bottom, r2.left, r2.top, r2.right, r2.bottom ); + + DeleteObject( tmp ); + DeleteObject( update ); +} + +static const struct message WmInvalidateRgn[] = { + { WM_NCPAINT, sent }, + { WM_GETTEXT, sent|defwinproc|optional }, + { 0 } +}; + +static const struct message WmGetUpdateRect[] = { + { WM_NCPAINT, sent }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_PAINT, sent }, + { 0 } +}; + +static const struct message WmInvalidateFull[] = { + { WM_NCPAINT, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { 0 } +}; + +static const struct message WmInvalidateErase[] = { + { WM_NCPAINT, sent|wparam, 1 }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent }, + { 0 } +}; + +static const struct message WmInvalidatePaint[] = { + { WM_PAINT, sent }, + { WM_NCPAINT, sent|wparam|beginpaint, 1 }, + { WM_GETTEXT, sent|beginpaint|defwinproc|optional }, + { 0 } +}; + +static const struct message WmInvalidateErasePaint[] = { + { WM_PAINT, sent }, + { WM_NCPAINT, sent|wparam|beginpaint, 1 }, + { WM_GETTEXT, sent|beginpaint|defwinproc|optional }, + { WM_ERASEBKGND, sent|beginpaint }, + { 0 } +}; + +static const struct message WmInvalidateErasePaint2[] = { + { WM_PAINT, sent }, + { WM_NCPAINT, sent|beginpaint }, + { WM_GETTEXT, sent|beginpaint|defwinproc|optional }, + { WM_ERASEBKGND, sent|beginpaint }, + { 0 } +}; + +static const struct message WmErase[] = { + { WM_ERASEBKGND, sent }, + { 0 } +}; + +static const struct message WmPaint[] = { + { WM_PAINT, sent }, + { 0 } +}; + +static const struct message WmParentOnlyPaint[] = { + { WM_PAINT, sent|parent }, + { 0 } +}; + +static const struct message WmInvalidateParent[] = { + { WM_NCPAINT, sent|parent }, + { WM_GETTEXT, sent|defwinproc|parent|optional }, + { WM_ERASEBKGND, sent|parent }, + { 0 } +}; + +static const struct message WmInvalidateParentChild[] = { + { WM_NCPAINT, sent|parent }, + { WM_GETTEXT, sent|defwinproc|parent|optional }, + { WM_ERASEBKGND, sent|parent }, + { WM_NCPAINT, sent }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent }, + { 0 } +}; + +static const struct message WmInvalidateParentChild2[] = { + { WM_ERASEBKGND, sent|parent }, + { WM_NCPAINT, sent }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent }, + { 0 } +}; + +static const struct message WmParentPaint[] = { + { WM_PAINT, sent|parent }, + { WM_PAINT, sent }, + { 0 } +}; + +static const struct message WmParentPaintNc[] = { + { WM_PAINT, sent|parent }, + { WM_PAINT, sent }, + { WM_NCPAINT, sent|beginpaint }, + { WM_GETTEXT, sent|beginpaint|defwinproc|optional }, + { WM_ERASEBKGND, sent|beginpaint }, + { 0 } +}; + +static const struct message WmChildPaintNc[] = { + { WM_PAINT, sent }, + { WM_NCPAINT, sent|beginpaint }, + { WM_GETTEXT, sent|beginpaint|defwinproc|optional }, + { WM_ERASEBKGND, sent|beginpaint }, + { 0 } +}; + +static const struct message WmParentErasePaint[] = { + { WM_PAINT, sent|parent }, + { WM_NCPAINT, sent|parent|beginpaint }, + { WM_GETTEXT, sent|parent|beginpaint|defwinproc|optional }, + { WM_ERASEBKGND, sent|parent|beginpaint }, + { WM_PAINT, sent }, + { WM_NCPAINT, sent|beginpaint }, + { WM_GETTEXT, sent|beginpaint|defwinproc|optional }, + { WM_ERASEBKGND, sent|beginpaint }, + { 0 } +}; + +static const struct message WmParentOnlyNcPaint[] = { + { WM_PAINT, sent|parent }, + { WM_NCPAINT, sent|parent|beginpaint }, + { WM_GETTEXT, sent|parent|beginpaint|defwinproc|optional }, + { 0 } +}; + +static const struct message WmSetParentStyle[] = { + { WM_STYLECHANGING, sent|parent }, + { WM_STYLECHANGED, sent|parent }, + { 0 } +}; + +static void test_paint_messages(void) +{ + RECT rect; + POINT pt; + MSG msg; + HWND hparent, hchild; + HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 ); + HRGN hrgn2 = CreateRectRgn( 0, 0, 0, 0 ); + HWND hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hwnd != 0, "Failed to create overlapped window\n"); + + ShowWindow( hwnd, SW_SHOW ); + UpdateWindow( hwnd ); + + /* try to flush pending X expose events */ + MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + + check_update_rgn( hwnd, 0 ); + SetRectRgn( hrgn, 10, 10, 20, 20 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE ); + check_update_rgn( hwnd, hrgn ); + SetRectRgn( hrgn2, 20, 20, 30, 30 ); + RedrawWindow( hwnd, NULL, hrgn2, RDW_INVALIDATE ); + CombineRgn( hrgn, hrgn, hrgn2, RGN_OR ); + check_update_rgn( hwnd, hrgn ); + /* validate everything */ + RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE ); + check_update_rgn( hwnd, 0 ); + /* now with frame */ + SetRectRgn( hrgn, -5, -5, 20, 20 ); + + /* flush pending messages */ + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + + flush_sequence(); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME ); + ok_sequence( WmEmptySeq, "EmptySeq", FALSE ); + + SetRectRgn( hrgn, 0, 0, 20, 20 ); /* GetUpdateRgn clips to client area */ + check_update_rgn( hwnd, hrgn ); + + flush_sequence(); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW ); + ok_sequence( WmInvalidateRgn, "InvalidateRgn", FALSE ); + + flush_sequence(); + RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW ); + ok_sequence( WmInvalidateFull, "InvalidateFull", FALSE ); + + GetClientRect( hwnd, &rect ); + SetRectRgn( hrgn, rect.left, rect.top, rect.right, rect.bottom ); + check_update_rgn( hwnd, hrgn ); + + flush_sequence(); + RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ); + ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE ); + + flush_sequence(); + RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW | RDW_UPDATENOW ); + ok_sequence( WmInvalidatePaint, "InvalidatePaint", FALSE ); + check_update_rgn( hwnd, 0 ); + + flush_sequence(); + RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_UPDATENOW ); + ok_sequence( WmInvalidateErasePaint, "InvalidateErasePaint", FALSE ); + check_update_rgn( hwnd, 0 ); + + flush_sequence(); + SetRectRgn( hrgn, 0, 0, 100, 100 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE ); + SetRectRgn( hrgn, 0, 0, 50, 100 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE ); + SetRectRgn( hrgn, 50, 0, 100, 100 ); + check_update_rgn( hwnd, hrgn ); + RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_ERASENOW ); + ok_sequence( WmEmptySeq, "EmptySeq", FALSE ); /* must not generate messages, everything is valid */ + check_update_rgn( hwnd, 0 ); + + flush_sequence(); + SetRectRgn( hrgn, 0, 0, 100, 100 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE ); + SetRectRgn( hrgn, 0, 0, 100, 50 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_ERASENOW ); + ok_sequence( WmErase, "Erase", FALSE ); + SetRectRgn( hrgn, 0, 50, 100, 100 ); + check_update_rgn( hwnd, hrgn ); + + flush_sequence(); + SetRectRgn( hrgn, 0, 0, 100, 100 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE ); + SetRectRgn( hrgn, 0, 0, 50, 50 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOERASE | RDW_UPDATENOW ); + ok_sequence( WmPaint, "Paint", FALSE ); + + flush_sequence(); + SetRectRgn( hrgn, -4, -4, -2, -2 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME ); + SetRectRgn( hrgn, -200, -200, -198, -198 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOFRAME | RDW_ERASENOW ); + ok_sequence( WmEmptySeq, "EmptySeq", FALSE ); + + flush_sequence(); + SetRectRgn( hrgn, -4, -4, -2, -2 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME ); + SetRectRgn( hrgn, -4, -4, -3, -3 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOFRAME ); + SetRectRgn( hrgn, 0, 0, 1, 1 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_UPDATENOW ); + ok_sequence( WmPaint, "Paint", FALSE ); + + flush_sequence(); + SetRectRgn( hrgn, -4, -4, -1, -1 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME ); + RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW ); + /* make sure no WM_PAINT was generated */ + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmInvalidateRgn, "InvalidateRgn", FALSE ); + + flush_sequence(); + SetRectRgn( hrgn, -4, -4, -1, -1 ); + RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) + { + if (msg.hwnd == hwnd && msg.message == WM_PAINT) + { + /* GetUpdateRgn must return empty region since only nonclient area is invalidated */ + INT ret = GetUpdateRgn( hwnd, hrgn, FALSE ); + ok( ret == NULLREGION, "Invalid GetUpdateRgn result %d\n", ret ); + ret = GetUpdateRect( hwnd, &rect, FALSE ); + ok( ret, "Invalid GetUpdateRect result %d\n", ret ); + /* this will send WM_NCPAINT and validate the non client area */ + ret = GetUpdateRect( hwnd, &rect, TRUE ); + ok( !ret, "Invalid GetUpdateRect result %d\n", ret ); + } + DispatchMessage( &msg ); + } + ok_sequence( WmGetUpdateRect, "GetUpdateRect", FALSE ); + + DestroyWindow( hwnd ); + + /* now test with a child window */ + + hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hparent != 0, "Failed to create parent window\n"); + + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_VISIBLE | WS_BORDER, + 10, 10, 100, 100, hparent, 0, 0, NULL); + ok (hchild != 0, "Failed to create child window\n"); + + ShowWindow( hparent, SW_SHOW ); + UpdateWindow( hparent ); + UpdateWindow( hchild ); + /* try to flush pending X expose events */ + MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + + flush_sequence(); + log_all_parent_messages++; + + SetRect( &rect, 0, 0, 50, 50 ); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME ); + RedrawWindow( hparent, NULL, 0, RDW_ERASENOW | RDW_ALLCHILDREN ); + ok_sequence( WmInvalidateParentChild, "InvalidateParentChild", FALSE ); + + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME ); + pt.x = pt.y = 0; + MapWindowPoints( hchild, hparent, &pt, 1 ); + SetRectRgn( hrgn, 0, 0, 50 - pt.x, 50 - pt.y ); + check_update_rgn( hchild, hrgn ); + SetRectRgn( hrgn, 0, 0, 50, 50 ); + check_update_rgn( hparent, hrgn ); + RedrawWindow( hparent, NULL, 0, RDW_ERASENOW ); + ok_sequence( WmInvalidateParent, "InvalidateParent", FALSE ); + RedrawWindow( hchild, NULL, 0, RDW_ERASENOW ); + ok_sequence( WmEmptySeq, "EraseNow child", FALSE ); + + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentPaintNc, "WmParentPaintNc", FALSE ); + + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN ); + RedrawWindow( hparent, NULL, 0, RDW_ERASENOW ); + ok_sequence( WmInvalidateParent, "InvalidateParent2", FALSE ); + RedrawWindow( hchild, NULL, 0, RDW_ERASENOW ); + ok_sequence( WmEmptySeq, "EraseNow child", FALSE ); + + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE ); + RedrawWindow( hparent, NULL, 0, RDW_ERASENOW | RDW_ALLCHILDREN ); + ok_sequence( WmInvalidateParentChild2, "InvalidateParentChild2", FALSE ); + + SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) | WS_CLIPCHILDREN ); + flush_sequence(); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN ); + RedrawWindow( hparent, NULL, 0, RDW_ERASENOW ); + ok_sequence( WmInvalidateParentChild, "InvalidateParentChild3", FALSE ); + + /* flush all paint messages */ + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + flush_sequence(); + + /* RDW_UPDATENOW on child with WS_CLIPCHILDREN doesn't change corresponding parent area */ + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN ); + SetRectRgn( hrgn, 0, 0, 50, 50 ); + check_update_rgn( hparent, hrgn ); + RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW ); + ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE ); + SetRectRgn( hrgn, 0, 0, 50, 50 ); + check_update_rgn( hparent, hrgn ); + + /* flush all paint messages */ + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) & ~WS_CLIPCHILDREN ); + flush_sequence(); + + /* RDW_UPDATENOW on child without WS_CLIPCHILDREN will validate corresponding parent area */ + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME ); + SetRectRgn( hrgn, 0, 0, 50, 50 ); + check_update_rgn( hparent, hrgn ); + RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW ); + ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE ); + SetRectRgn( hrgn2, 10, 10, 50, 50 ); + CombineRgn( hrgn, hrgn, hrgn2, RGN_DIFF ); + check_update_rgn( hparent, hrgn ); + /* flush all paint messages */ + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + flush_sequence(); + + /* same as above but parent gets completely validated */ + SetRect( &rect, 20, 20, 30, 30 ); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME ); + SetRectRgn( hrgn, 20, 20, 30, 30 ); + check_update_rgn( hparent, hrgn ); + RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW ); + ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE ); + check_update_rgn( hparent, 0 ); /* no update region */ + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmEmptySeq, "WmEmpty", FALSE ); /* and no paint messages */ + + /* make sure RDW_VALIDATE on child doesn't have the same effect */ + flush_sequence(); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME ); + SetRectRgn( hrgn, 20, 20, 30, 30 ); + check_update_rgn( hparent, hrgn ); + RedrawWindow( hchild, NULL, 0, RDW_VALIDATE | RDW_NOERASE ); + SetRectRgn( hrgn, 20, 20, 30, 30 ); + check_update_rgn( hparent, hrgn ); + + /* same as above but normal WM_PAINT doesn't validate parent */ + flush_sequence(); + SetRect( &rect, 20, 20, 30, 30 ); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME ); + SetRectRgn( hrgn, 20, 20, 30, 30 ); + check_update_rgn( hparent, hrgn ); + /* no WM_PAINT in child while parent still pending */ + while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE ); + while (PeekMessage( &msg, hparent, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentErasePaint, "WmParentErasePaint", FALSE ); + + flush_sequence(); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME ); + /* no WM_PAINT in child while parent still pending */ + while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE ); + RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_NOERASE | RDW_NOCHILDREN ); + /* now that parent is valid child should get WM_PAINT */ + while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmEmptySeq, "No other message", FALSE ); + + /* same thing with WS_CLIPCHILDREN in parent */ + flush_sequence(); + SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) | WS_CLIPCHILDREN ); + ok_sequence( WmSetParentStyle, "WmSetParentStyle", FALSE ); + /* changing style invalidates non client area, but we need to invalidate something else to see it */ + RedrawWindow( hparent, &rect, 0, RDW_UPDATENOW ); + ok_sequence( WmEmptySeq, "No message", FALSE ); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_UPDATENOW ); + ok_sequence( WmParentOnlyNcPaint, "WmParentOnlyNcPaint", FALSE ); + + flush_sequence(); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN ); + SetRectRgn( hrgn, 20, 20, 30, 30 ); + check_update_rgn( hparent, hrgn ); + /* no WM_PAINT in child while parent still pending */ + while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE ); + /* WM_PAINT in parent first */ + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentPaintNc, "WmParentPaintNc2", FALSE ); + + /* no RDW_ERASE in parent still causes RDW_ERASE and RDW_FRAME in child */ + flush_sequence(); + SetRect( &rect, 0, 0, 30, 30 ); + RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ALLCHILDREN ); + SetRectRgn( hrgn, 0, 0, 30, 30 ); + check_update_rgn( hparent, hrgn ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentPaintNc, "WmParentPaintNc3", FALSE ); + + /* validate doesn't cause RDW_NOERASE or RDW_NOFRAME in child */ + flush_sequence(); + SetRect( &rect, -10, 0, 30, 30 ); + RedrawWindow( hchild, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE ); + SetRect( &rect, 0, 0, 20, 20 ); + RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_ALLCHILDREN ); + RedrawWindow( hparent, NULL, 0, RDW_UPDATENOW ); + ok_sequence( WmChildPaintNc, "WmChildPaintNc", FALSE ); + + /* validate doesn't cause RDW_NOERASE or RDW_NOFRAME in child */ + flush_sequence(); + SetRect( &rect, -10, 0, 30, 30 ); + RedrawWindow( hchild, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE ); + SetRect( &rect, 0, 0, 100, 100 ); + RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_ALLCHILDREN ); + RedrawWindow( hparent, NULL, 0, RDW_UPDATENOW ); + ok_sequence( WmEmptySeq, "WmChildPaintNc2", FALSE ); + RedrawWindow( hparent, NULL, 0, RDW_ERASENOW ); + ok_sequence( WmEmptySeq, "WmChildPaintNc3", FALSE ); + + /* test RDW_INTERNALPAINT behavior */ + + flush_sequence(); + RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT | RDW_NOCHILDREN ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentOnlyPaint, "WmParentOnlyPaint", FALSE ); + + RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT | RDW_ALLCHILDREN ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentPaint, "WmParentPaint", FALSE ); + + RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentOnlyPaint, "WmParentOnlyPaint", FALSE ); + + SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) & ~WS_CLIPCHILDREN ); + ok_sequence( WmSetParentStyle, "WmSetParentStyle", FALSE ); + RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence( WmParentPaint, "WmParentPaint", FALSE ); + + log_all_parent_messages--; + DestroyWindow( hparent ); + ok(!IsWindow(hchild), "child must be destroyed with its parent\n"); + + DeleteObject( hrgn ); + DeleteObject( hrgn2 ); +} + +struct wnd_event +{ + HWND hwnd; + HANDLE event; +}; + +static DWORD WINAPI thread_proc(void *param) +{ + MSG msg; + struct wnd_event *wnd_event = (struct wnd_event *)param; + + wnd_event->hwnd = CreateWindowExA(0, "TestWindowClass", "window caption text", WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok(wnd_event->hwnd != 0, "Failed to create overlapped window\n"); + + SetEvent(wnd_event->event); + + while (GetMessage(&msg, 0, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + ok(IsWindow(wnd_event->hwnd), "window should still exist\n"); + + return 0; +} + +static void test_interthread_messages(void) +{ + HANDLE hThread; + DWORD tid; + WNDPROC proc; + MSG msg; + char buf[256]; + int len, expected_len; + struct wnd_event wnd_event; + BOOL ret; + + wnd_event.event = CreateEventW(NULL, 0, 0, NULL); + if (!wnd_event.event) + { + trace("skipping interthread message test under win9x\n"); + return; + } + + hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid); + ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError()); + + ok(WaitForSingleObject(wnd_event.event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + + CloseHandle(wnd_event.event); + + SetLastError(0xdeadbeef); + ok(!DestroyWindow(wnd_event.hwnd), "DestroyWindow succeded\n"); + ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error code %ld\n", GetLastError()); + + proc = (WNDPROC)GetWindowLongPtrA(wnd_event.hwnd, GWLP_WNDPROC); + ok(proc != NULL, "GetWindowLongPtrA(GWLP_WNDPROC) error %ld\n", GetLastError()); + + expected_len = lstrlenA("window caption text"); + memset(buf, 0, sizeof(buf)); + SetLastError(0xdeadbeef); + len = CallWindowProcA(proc, wnd_event.hwnd, WM_GETTEXT, sizeof(buf), (LPARAM)buf); + ok(len == expected_len, "CallWindowProcA(WM_GETTEXT) error %ld, len %d, expected len %d\n", GetLastError(), len, expected_len); + ok(!lstrcmpA(buf, "window caption text"), "window text mismatch\n"); + + msg.hwnd = wnd_event.hwnd; + msg.message = WM_GETTEXT; + msg.wParam = sizeof(buf); + msg.lParam = (LPARAM)buf; + memset(buf, 0, sizeof(buf)); + SetLastError(0xdeadbeef); + len = DispatchMessageA(&msg); + ok(!len && GetLastError() == ERROR_MESSAGE_SYNC_ONLY, + "DispatchMessageA(WM_GETTEXT) succeded on another thread window: ret %d, error %ld\n", len, GetLastError()); + + /* the following test causes an exception in user.exe under win9x */ + msg.hwnd = wnd_event.hwnd; + msg.message = WM_TIMER; + msg.wParam = 0; + msg.lParam = GetWindowLongPtrA(wnd_event.hwnd, GWLP_WNDPROC); + SetLastError(0xdeadbeef); + len = DispatchMessageA(&msg); + ok(!len && GetLastError() == 0xdeadbeef, + "DispatchMessageA(WM_TIMER) failed on another thread window: ret %d, error %ld\n", len, GetLastError()); + + ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0); + ok( ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError()); + + ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + CloseHandle(hThread); + + ok(!IsWindow(wnd_event.hwnd), "window should be destroyed on thread exit\n"); +} + + +static const struct message WmVkN[] = { + { WM_KEYDOWN, wparam|lparam, 'N', 1 }, + { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 }, + { WM_CHAR, wparam|lparam, 'n', 1 }, + { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1002,1), 0 }, + { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 }, + { 0 } +}; +static const struct message WmShiftVkN[] = { + { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 1 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 1 }, + { WM_KEYDOWN, wparam|lparam, 'N', 1 }, + { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 }, + { WM_CHAR, wparam|lparam, 'N', 1 }, + { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1001,1), 0 }, + { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xc0000001 }, + { 0 } +}; +static const struct message WmCtrlVkN[] = { + { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, wparam|lparam, 'N', 1 }, + { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 }, + { WM_CHAR, wparam|lparam, 0x000e, 1 }, + { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1000,1), 0 }, + { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 }, + { 0 } +}; +static const struct message WmCtrlVkN_2[] = { + { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, wparam|lparam, 'N', 1 }, + { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1000,1), 0 }, + { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 }, + { 0 } +}; +static const struct message WmAltVkN[] = { + { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 }, + { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 }, + { WM_SYSKEYDOWN, wparam|lparam, 'N', 0x20000001 }, + { WM_SYSKEYDOWN, sent|wparam|lparam, 'N', 0x20000001 }, + { WM_SYSCHAR, wparam|lparam, 'n', 0x20000001 }, + { WM_SYSCHAR, sent|wparam|lparam, 'n', 0x20000001 }, + { WM_SYSCOMMAND, sent|defwinproc|wparam|lparam, SC_KEYMENU, 'n' }, + { HCBT_SYSCOMMAND, hook }, + { WM_ENTERMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 }, + { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 }, + { WM_INITMENU, sent|defwinproc }, + { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 }, + { WM_MENUCHAR, sent|defwinproc|wparam, MAKEWPARAM('n',MF_SYSMENU) }, + { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 }, + { WM_CAPTURECHANGED, sent|defwinproc }, + { WM_MENUSELECT, sent|defwinproc|wparam, MAKEWPARAM(0,0xffff) }, + { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 }, + { WM_EXITMENULOOP, sent|defwinproc }, + { WM_MENUSELECT, sent|defwinproc|wparam|optional, MAKEWPARAM(0,0xffff) }, /* Win95 bug */ + { WM_EXITMENULOOP, sent|defwinproc|optional }, /* Win95 bug */ + { WM_SYSKEYUP, wparam|lparam, 'N', 0xe0000001 }, + { WM_SYSKEYUP, sent|wparam|lparam, 'N', 0xe0000001 }, + { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 }, + { 0 } +}; +static const struct message WmAltVkN_2[] = { + { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 }, + { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 }, + { WM_SYSKEYDOWN, wparam|lparam, 'N', 0x20000001 }, + { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1003,1), 0 }, + { WM_SYSKEYUP, wparam|lparam, 'N', 0xe0000001 }, + { WM_SYSKEYUP, sent|wparam|lparam, 'N', 0xe0000001 }, + { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 }, + { 0 } +}; +static const struct message WmCtrlAltVkN[] = { + { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, wparam|lparam, VK_MENU, 0x20000001 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 }, + { WM_KEYDOWN, wparam|lparam, 'N', 0x20000001 }, + { WM_KEYDOWN, sent|wparam|lparam, 'N', 0x20000001 }, + { WM_KEYUP, wparam|lparam, 'N', 0xe0000001 }, + { WM_KEYUP, sent|wparam|lparam, 'N', 0xe0000001 }, + { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 }, + { 0 } +}; +static const struct message WmCtrlShiftVkN[] = { + { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 1 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 1 }, + { WM_KEYDOWN, wparam|lparam, 'N', 1 }, + { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1004,1), 0 }, + { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 }, + { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xc0000001 }, + { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 }, + { 0 } +}; +static const struct message WmCtrlAltShiftVkN[] = { + { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 }, + { WM_KEYDOWN, wparam|lparam, VK_MENU, 0x20000001 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 }, + { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0x20000001 }, + { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 0x20000001 }, + { WM_KEYDOWN, wparam|lparam, 'N', 0x20000001 }, + { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1005,1), 0 }, + { WM_KEYUP, wparam|lparam, 'N', 0xe0000001 }, + { WM_KEYUP, sent|wparam|lparam, 'N', 0xe0000001 }, + { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xe0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xe0000001 }, + { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 }, + { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 }, + { 0 } +}; +static const struct message WmAltPressRelease[] = { + { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 }, + { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 }, + { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_SYSCOMMAND, sent|defwinproc|wparam|lparam, SC_KEYMENU, 0 }, + { HCBT_SYSCOMMAND, hook }, + { WM_ENTERMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 }, + { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 }, + { WM_INITMENU, sent|defwinproc }, + { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 }, + { WM_MENUSELECT, sent|defwinproc|wparam, MAKEWPARAM(0,MF_SYSMENU|MF_POPUP|MF_HILITE) }, + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_SYSMENU, 1 }, + + { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 }, + { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0, }, + { WM_CAPTURECHANGED, sent|defwinproc }, + { WM_MENUSELECT, sent|defwinproc|wparam|optional, MAKEWPARAM(0,0xffff) }, + { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 }, + { WM_EXITMENULOOP, sent|defwinproc }, + { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 }, + { 0 } +}; +static const struct message WmAltMouseButton[] = { + { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 }, + { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 }, + { WM_MOUSEMOVE, wparam|optional, 0, 0 }, + { WM_MOUSEMOVE, sent|wparam|optional, 0, 0 }, + { WM_LBUTTONDOWN, wparam, MK_LBUTTON, 0 }, + { WM_LBUTTONDOWN, sent|wparam, MK_LBUTTON, 0 }, + { WM_LBUTTONUP, wparam, 0, 0 }, + { WM_LBUTTONUP, sent|wparam, 0, 0 }, + { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 }, + { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 }, + { 0 } +}; + +static void pump_msg_loop(HWND hwnd, HACCEL hAccel) +{ + MSG msg; + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + { + struct message log_msg; + + trace("accel: %p, %04x, %08x, %08lx\n", msg.hwnd, msg.message, msg.wParam, msg.lParam); + + /* ignore some unwanted messages */ + if (msg.message == WM_MOUSEMOVE || + msg.message == WM_DEVICECHANGE) + continue; + + log_msg.message = msg.message; + log_msg.flags = wparam|lparam; + log_msg.wParam = msg.wParam; + log_msg.lParam = msg.lParam; + add_message(&log_msg); + + if (!hAccel || !TranslateAccelerator(hwnd, hAccel, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +static void test_accelerators(void) +{ + RECT rc; + SHORT state; + HACCEL hAccel; + HWND hwnd = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 200, 200, 0, 0, 0, NULL); + BOOL ret; + + assert(hwnd != 0); + UpdateWindow(hwnd); + SetFocus(hwnd); + ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus()); + + state = GetKeyState(VK_SHIFT); + ok(!(state & 0x8000), "wrong Shift state %04x\n", state); + state = GetKeyState(VK_CAPITAL); + ok(state == 0, "wrong CapsLock state %04x\n", state); + + hAccel = LoadAccelerators(GetModuleHandleA(0), MAKEINTRESOURCE(1)); + assert(hAccel != 0); + + pump_msg_loop(hwnd, 0); + flush_sequence(); + + trace("testing VK_N press/release\n"); + flush_sequence(); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmVkN, "VK_N press/release", FALSE); + + trace("testing Shift+VK_N press/release\n"); + flush_sequence(); + keybd_event(VK_SHIFT, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmShiftVkN, "Shift+VK_N press/release", FALSE); + + trace("testing Ctrl+VK_N press/release\n"); + flush_sequence(); + keybd_event(VK_CONTROL, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmCtrlVkN, "Ctrl+VK_N press/release", FALSE); + + trace("testing Alt+VK_N press/release\n"); + flush_sequence(); + keybd_event(VK_MENU, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmAltVkN, "Alt+VK_N press/release", FALSE); + + trace("testing Ctrl+Alt+VK_N press/release 1\n"); + flush_sequence(); + keybd_event(VK_CONTROL, 0, 0, 0); + keybd_event(VK_MENU, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmCtrlAltVkN, "Ctrl+Alt+VK_N press/release 1", FALSE); + + ret = DestroyAcceleratorTable(hAccel); + ok( ret, "DestroyAcceleratorTable error %ld\n", GetLastError()); + + hAccel = LoadAccelerators(GetModuleHandleA(0), MAKEINTRESOURCE(2)); + assert(hAccel != 0); + + trace("testing VK_N press/release\n"); + flush_sequence(); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmVkN, "VK_N press/release", FALSE); + + trace("testing Shift+VK_N press/release\n"); + flush_sequence(); + keybd_event(VK_SHIFT, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmShiftVkN, "Shift+VK_N press/release", FALSE); + + trace("testing Ctrl+VK_N press/release 2\n"); + flush_sequence(); + keybd_event(VK_CONTROL, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmCtrlVkN_2, "Ctrl+VK_N press/release 2", FALSE); + + trace("testing Alt+VK_N press/release 2\n"); + flush_sequence(); + keybd_event(VK_MENU, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmAltVkN_2, "Alt+VK_N press/release 2", FALSE); + + trace("testing Ctrl+Alt+VK_N press/release 2\n"); + flush_sequence(); + keybd_event(VK_CONTROL, 0, 0, 0); + keybd_event(VK_MENU, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmCtrlAltVkN, "Ctrl+Alt+VK_N press/release 2", FALSE); + + trace("testing Ctrl+Shift+VK_N press/release\n"); + flush_sequence(); + keybd_event(VK_CONTROL, 0, 0, 0); + keybd_event(VK_SHIFT, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmCtrlShiftVkN, "Ctrl+Shift+VK_N press/release", FALSE); + + trace("testing Ctrl+Alt+Shift+VK_N press/release\n"); + flush_sequence(); + keybd_event(VK_CONTROL, 0, 0, 0); + keybd_event(VK_MENU, 0, 0, 0); + keybd_event(VK_SHIFT, 0, 0, 0); + keybd_event('N', 0, 0, 0); + keybd_event('N', 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, hAccel); + ok_sequence(WmCtrlAltShiftVkN, "Ctrl+Alt+Shift+VK_N press/release", FALSE); + + ret = DestroyAcceleratorTable(hAccel); + ok( ret, "DestroyAcceleratorTable error %ld\n", GetLastError()); + + trace("testing Alt press/release\n"); + flush_sequence(); + keybd_event(VK_MENU, 0, 0, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + keybd_event(VK_MENU, 0, 0, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, 0); + /* this test doesn't pass in Wine for managed windows */ + ok_sequence(WmAltPressRelease, "Alt press/release", TRUE); + + trace("testing Alt+MouseButton press/release\n"); + /* first, move mouse pointer inside of the window client area */ + GetClientRect(hwnd, &rc); + MapWindowPoints(hwnd, 0, (LPPOINT)&rc, 2); + rc.left += (rc.right - rc.left)/2; + rc.top += (rc.bottom - rc.top)/2; + SetCursorPos(rc.left, rc.top); + + pump_msg_loop(hwnd, 0); + flush_sequence(); + keybd_event(VK_MENU, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0); + pump_msg_loop(hwnd, 0); + ok_sequence(WmAltMouseButton, "Alt+MouseButton press/release", FALSE); + + DestroyWindow(hwnd); +} + +/************* window procedures ********************/ + +static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + static long beginpaint_counter = 0; + LRESULT ret; + struct message msg; + + trace("%p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + switch (message) + { + case WM_ENABLE: + { + LONG style = GetWindowLongA(hwnd, GWL_STYLE); + ok((BOOL)wParam == !(style & WS_DISABLED), + "wrong WS_DISABLED state: %d != %d\n", wParam, !(style & WS_DISABLED)); + break; + } + + case WM_CAPTURECHANGED: + if (test_DestroyWindow_flag) + { + DWORD style = GetWindowLongA(hwnd, GWL_STYLE); + if (style & WS_CHILD) + lParam = GetWindowLongA(hwnd, GWL_ID); + else if (style & WS_POPUP) + lParam = WND_POPUP_ID; + else + lParam = WND_PARENT_ID; + } + break; + + case WM_NCDESTROY: + { + HWND capture; + + ok(!GetWindow(hwnd, GW_CHILD), "children should be unlinked at this point\n"); + capture = GetCapture(); + if (capture) + { + ok(capture == hwnd, "capture should NOT be released at this point (capture %p)\n", capture); + trace("current capture %p, releasing...\n", capture); + ReleaseCapture(); + } + } + /* fall through */ + case WM_DESTROY: + if (pGetAncestor) + ok(pGetAncestor(hwnd, GA_PARENT) != 0, "parent should NOT be unlinked at this point\n"); + if (test_DestroyWindow_flag) + { + DWORD style = GetWindowLongA(hwnd, GWL_STYLE); + if (style & WS_CHILD) + lParam = GetWindowLongA(hwnd, GWL_ID); + else if (style & WS_POPUP) + lParam = WND_POPUP_ID; + else + lParam = WND_PARENT_ID; + } + break; + + /* test_accelerators() depends on this */ + case WM_NCHITTEST: + return HTCLIENT; + + /* ignore */ + case WM_MOUSEMOVE: + case WM_SETCURSOR: + case WM_DEVICECHANGE: + return 0; + + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lParam; + + trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + /* Log only documented flags, win2k uses 0x1000 and 0x2000 + * in the high word for internal purposes + */ + wParam = winpos->flags & 0xffff; + break; + } + } + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + if (beginpaint_counter) msg.flags |= beginpaint; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + + if (message == WM_GETMINMAXINFO && (GetWindowLongA(hwnd, GWL_STYLE) & WS_CHILD)) + { + HWND parent = GetParent(hwnd); + RECT rc; + MINMAXINFO *minmax = (MINMAXINFO *)lParam; + + GetClientRect(parent, &rc); + trace("parent %p client size = (%ld x %ld)\n", parent, rc.right, rc.bottom); + + trace("ptReserved = (%ld,%ld)\n" + "ptMaxSize = (%ld,%ld)\n" + "ptMaxPosition = (%ld,%ld)\n" + "ptMinTrackSize = (%ld,%ld)\n" + "ptMaxTrackSize = (%ld,%ld)\n", + minmax->ptReserved.x, minmax->ptReserved.y, + minmax->ptMaxSize.x, minmax->ptMaxSize.y, + minmax->ptMaxPosition.x, minmax->ptMaxPosition.y, + minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y, + minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y); + + ok(minmax->ptMaxSize.x == rc.right, "default width of maximized child %ld != %ld\n", + minmax->ptMaxSize.x, rc.right); + ok(minmax->ptMaxSize.y == rc.bottom, "default height of maximized child %ld != %ld\n", + minmax->ptMaxSize.y, rc.bottom); + } + + if (message == WM_PAINT) + { + PAINTSTRUCT ps; + beginpaint_counter++; + BeginPaint( hwnd, &ps ); + beginpaint_counter--; + EndPaint( hwnd, &ps ); + return 0; + } + + defwndproc_counter++; + ret = DefWindowProcA(hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static LRESULT WINAPI PopupMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + LRESULT ret; + struct message msg; + + trace("popup: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + + if (message == WM_CREATE) + { + DWORD style = GetWindowLongA(hwnd, GWL_STYLE) | WS_VISIBLE; + SetWindowLongA(hwnd, GWL_STYLE, style); + } + + defwndproc_counter++; + ret = DefWindowProcA(hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + static long beginpaint_counter = 0; + LRESULT ret; + struct message msg; + + trace("parent: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + if (log_all_parent_messages || + message == WM_PARENTNOTIFY || message == WM_CANCELMODE || + message == WM_SETFOCUS || message == WM_KILLFOCUS || + message == WM_ENABLE || message == WM_ENTERIDLE || + message == WM_IME_SETCONTEXT) + { + msg.message = message; + msg.flags = sent|parent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + if (beginpaint_counter) msg.flags |= beginpaint; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + } + + if (message == WM_PAINT) + { + PAINTSTRUCT ps; + beginpaint_counter++; + BeginPaint( hwnd, &ps ); + beginpaint_counter--; + EndPaint( hwnd, &ps ); + return 0; + } + + defwndproc_counter++; + ret = DefWindowProcA(hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static LRESULT WINAPI TestDlgProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + LRESULT ret; + struct message msg; + + trace("dialog: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + DefDlgProcA(hwnd, DM_SETDEFID, 1, 0); + ret = DefDlgProcA(hwnd, DM_GETDEFID, 0, 0); + if (after_end_dialog) + ok( ret == 0, "DM_GETDEFID should return 0 after EndDialog, got %lx\n", ret ); + else + ok(HIWORD(ret) == DC_HASDEFID, "DM_GETDEFID should return DC_HASDEFID, got %lx\n", ret); + + switch (message) + { + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lParam; + + trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + /* Log only documented flags, win2k uses 0x1000 and 0x2000 + * in the high word for internal purposes + */ + wParam = winpos->flags & 0xffff; + break; + } + } + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + + defwndproc_counter++; + ret = DefDlgProcA(hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static BOOL RegisterWindowClasses(void) +{ + WNDCLASSA cls; + + cls.style = 0; + cls.lpfnWndProc = MsgCheckProcA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "TestWindowClass"; + if(!RegisterClassA(&cls)) return FALSE; + + cls.lpfnWndProc = PopupMsgCheckProcA; + cls.lpszClassName = "TestPopupClass"; + if(!RegisterClassA(&cls)) return FALSE; + + cls.lpfnWndProc = ParentMsgCheckProcA; + cls.lpszClassName = "TestParentClass"; + if(!RegisterClassA(&cls)) return FALSE; + + cls.lpfnWndProc = DefWindowProcA; + cls.lpszClassName = "SimpleWindowClass"; + if(!RegisterClassA(&cls)) return FALSE; + + cls.style = CS_NOCLOSE; + cls.lpszClassName = "NoCloseWindowClass"; + if(!RegisterClassA(&cls)) return FALSE; + + ok(GetClassInfoA(0, "#32770", &cls), "GetClassInfo failed\n"); + cls.style = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hbrBackground = 0; + cls.lpfnWndProc = TestDlgProcA; + cls.lpszClassName = "TestDialogClass"; + if(!RegisterClassA(&cls)) return FALSE; + + return TRUE; +} + +static HHOOK hCBT_hook; +static DWORD cbt_hook_thread_id; + +static LRESULT CALLBACK cbt_hook_proc(int nCode, WPARAM wParam, LPARAM lParam) +{ + static const char *CBT_code_name[10] = { + "HCBT_MOVESIZE", + "HCBT_MINMAX", + "HCBT_QS", + "HCBT_CREATEWND", + "HCBT_DESTROYWND", + "HCBT_ACTIVATE", + "HCBT_CLICKSKIPPED", + "HCBT_KEYSKIPPED", + "HCBT_SYSCOMMAND", + "HCBT_SETFOCUS" }; + const char *code_name = (nCode >= 0 && nCode <= HCBT_SETFOCUS) ? CBT_code_name[nCode] : "Unknown"; + HWND hwnd; + char buf[256]; + + trace("CBT: %d (%s), %08x, %08lx\n", nCode, code_name, wParam, lParam); + + ok(cbt_hook_thread_id == GetCurrentThreadId(), "we didn't ask for events from other threads\n"); + + if (nCode == HCBT_SYSCOMMAND) + { + struct message msg; + + msg.message = nCode; + msg.flags = hook|wparam|lparam; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + + return CallNextHookEx(hCBT_hook, nCode, wParam, lParam); + } + + if (nCode == HCBT_DESTROYWND) + { + if (test_DestroyWindow_flag) + { + DWORD style = GetWindowLongA((HWND)wParam, GWL_STYLE); + if (style & WS_CHILD) + lParam = GetWindowLongA((HWND)wParam, GWL_ID); + else if (style & WS_POPUP) + lParam = WND_POPUP_ID; + else + lParam = WND_PARENT_ID; + } + } + + /* Log also SetFocus(0) calls */ + hwnd = wParam ? (HWND)wParam : (HWND)lParam; + + if (GetClassNameA(hwnd, buf, sizeof(buf))) + { + if (!lstrcmpiA(buf, "TestWindowClass") || + !lstrcmpiA(buf, "TestParentClass") || + !lstrcmpiA(buf, "TestPopupClass") || + !lstrcmpiA(buf, "SimpleWindowClass") || + !lstrcmpiA(buf, "TestDialogClass") || + !lstrcmpiA(buf, "MDI_frame_class") || + !lstrcmpiA(buf, "MDI_client_class") || + !lstrcmpiA(buf, "MDI_child_class") || + !lstrcmpiA(buf, "my_button_class") || + !lstrcmpiA(buf, "static") || + !lstrcmpiA(buf, "#32770")) + { + struct message msg; + + msg.message = nCode; + msg.flags = hook|wparam|lparam; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + } + } + return CallNextHookEx(hCBT_hook, nCode, wParam, lParam); +} + +static void CALLBACK win_event_proc(HWINEVENTHOOK hevent, + DWORD event, + HWND hwnd, + LONG object_id, + LONG child_id, + DWORD thread_id, + DWORD event_time) +{ + char buf[256]; + + trace("WEH:%p,event %08lx,hwnd %p,obj %08lx,id %08lx,thread %08lx,time %08lx\n", + hevent, event, hwnd, object_id, child_id, thread_id, event_time); + + ok(thread_id == GetCurrentThreadId(), "we didn't ask for events from other threads\n"); + + /* ignore mouse cursor events */ + if (object_id == OBJID_CURSOR) return; + + if (!hwnd || GetClassNameA(hwnd, buf, sizeof(buf))) + { + if (!hwnd || + !lstrcmpiA(buf, "TestWindowClass") || + !lstrcmpiA(buf, "TestParentClass") || + !lstrcmpiA(buf, "TestPopupClass") || + !lstrcmpiA(buf, "SimpleWindowClass") || + !lstrcmpiA(buf, "TestDialogClass") || + !lstrcmpiA(buf, "MDI_frame_class") || + !lstrcmpiA(buf, "MDI_client_class") || + !lstrcmpiA(buf, "MDI_child_class") || + !lstrcmpiA(buf, "my_button_class") || + !lstrcmpiA(buf, "static") || + !lstrcmpiA(buf, "#32770")) + { + struct message msg; + + msg.message = event; + msg.flags = winevent_hook|wparam|lparam; + msg.wParam = object_id; + msg.lParam = child_id; + add_message(&msg); + } + } +} + +static const WCHAR wszUnicode[] = {'U','n','i','c','o','d','e',0}; +static const WCHAR wszAnsi[] = {'U',0}; + +static LRESULT CALLBACK MsgConversionProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case CB_FINDSTRINGEXACT: + trace("String: %p\n", (LPCWSTR)lParam); + if (!lstrcmpW((LPCWSTR)lParam, wszUnicode)) + return 1; + if (!lstrcmpW((LPCWSTR)lParam, wszAnsi)) + return 0; + return -1; + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +static void test_message_conversion(void) +{ + static const WCHAR wszMsgConversionClass[] = + {'M','s','g','C','o','n','v','e','r','s','i','o','n','C','l','a','s','s',0}; + WNDCLASSW cls; + LRESULT lRes; + HWND hwnd; + WNDPROC wndproc; + + cls.style = 0; + cls.lpfnWndProc = MsgConversionProcW; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleW(NULL); + cls.hIcon = NULL; + cls.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW); + cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); + cls.lpszMenuName = NULL; + cls.lpszClassName = wszMsgConversionClass; + /* this call will fail on Win9x, but that doesn't matter as this test is + * meaningless on those platforms */ + if(!RegisterClassW(&cls)) return; + + hwnd = CreateWindowExW(0, wszMsgConversionClass, NULL, WS_OVERLAPPED, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok(hwnd != NULL, "Window creation failed\n"); + + /* {W, A} -> A */ + + wndproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC); + lRes = CallWindowProcA(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0, "String should have been converted\n"); + lRes = CallWindowProcW(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 1, "String shouldn't have been converted\n"); + + /* {W, A} -> W */ + + wndproc = (WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC); + lRes = CallWindowProcA(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 1, "String shouldn't have been converted\n"); + lRes = CallWindowProcW(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 1, "String shouldn't have been converted\n"); + + /* Synchronous messages */ + + lRes = SendMessageA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0, "String should have been converted\n"); + lRes = SendMessageW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 1, "String shouldn't have been converted\n"); + + /* Asynchronous messages */ + + SetLastError(0); + lRes = PostMessageA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "PostMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); + SetLastError(0); + lRes = PostMessageW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "PostMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); + SetLastError(0); + lRes = PostThreadMessageA(GetCurrentThreadId(), CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "PosThreadtMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); + SetLastError(0); + lRes = PostThreadMessageW(GetCurrentThreadId(), CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "PosThreadtMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); + SetLastError(0); + lRes = SendNotifyMessageA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "SendNotifyMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); + SetLastError(0); + lRes = SendNotifyMessageW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "SendNotifyMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); + SetLastError(0); + lRes = SendMessageCallbackA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode, NULL, 0); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "SendMessageCallback on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); + SetLastError(0); + lRes = SendMessageCallbackW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode, NULL, 0); + ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER), + "SendMessageCallback on sync only message returned %ld, last error %ld\n", lRes, GetLastError()); +} + +typedef struct _thread_info +{ + HWND hWnd; + HANDLE handles[2]; + DWORD id; +} thread_info; + +static VOID CALLBACK tfunc(HWND hwnd, UINT uMsg, UINT id, DWORD dwTime) +{ +} + +#define TIMER_ID 0x19 + +static DWORD WINAPI timer_thread_proc(LPVOID x) +{ + thread_info *info = x; + DWORD r; + + r = KillTimer(info->hWnd, 0x19); + ok(r,"KillTimer failed in thread\n"); + r = SetTimer(info->hWnd,TIMER_ID,10000,tfunc); + ok(r,"SetTimer failed in thread\n"); + ok(r==TIMER_ID,"SetTimer id different\n"); + r = SetEvent(info->handles[0]); + ok(r,"SetEvent failed in thread\n"); + return 0; +} + +static void test_timers(void) +{ + thread_info info; + DWORD id; + + info.hWnd = CreateWindow ("TestWindowClass", NULL, + WS_OVERLAPPEDWINDOW , + CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, 0, + NULL, NULL, 0); + + info.id = SetTimer(info.hWnd,TIMER_ID,10000,tfunc); + ok(info.id, "SetTimer failed\n"); + ok(info.id==TIMER_ID, "SetTimer timer ID different\n"); + info.handles[0] = CreateEvent(NULL,0,0,NULL); + info.handles[1] = CreateThread(NULL,0,timer_thread_proc,&info,0,&id); + + WaitForMultipleObjects(2, info.handles, FALSE, INFINITE); + + WaitForSingleObject(info.handles[1], INFINITE); + + CloseHandle(info.handles[0]); + CloseHandle(info.handles[1]); + + ok( KillTimer(info.hWnd, TIMER_ID), "KillTimer failed\n"); + + ok(DestroyWindow(info.hWnd), "failed to destroy window\n"); +} + +/* Various win events with arbitrary parameters */ +static const struct message WmWinEventsSeq[] = { + { EVENT_SYSTEM_SOUND, winevent_hook|wparam|lparam, OBJID_WINDOW, 0 }, + { EVENT_SYSTEM_ALERT, winevent_hook|wparam|lparam, OBJID_SYSMENU, 1 }, + { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, OBJID_TITLEBAR, 2 }, + { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_MENU, 3 }, + { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_CLIENT, 4 }, + { EVENT_SYSTEM_MENUPOPUPSTART, winevent_hook|wparam|lparam, OBJID_VSCROLL, 5 }, + { EVENT_SYSTEM_MENUPOPUPEND, winevent_hook|wparam|lparam, OBJID_HSCROLL, 6 }, + { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, OBJID_SIZEGRIP, 7 }, + { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, OBJID_CARET, 8 }, + /* our win event hook ignores OBJID_CURSOR events */ + /*{ EVENT_SYSTEM_MOVESIZESTART, winevent_hook|wparam|lparam, OBJID_CURSOR, 9 },*/ + { EVENT_SYSTEM_MOVESIZEEND, winevent_hook|wparam|lparam, OBJID_ALERT, 10 }, + { EVENT_SYSTEM_CONTEXTHELPSTART, winevent_hook|wparam|lparam, OBJID_SOUND, 11 }, + { EVENT_SYSTEM_CONTEXTHELPEND, winevent_hook|wparam|lparam, OBJID_QUERYCLASSNAMEIDX, 12 }, + { EVENT_SYSTEM_DRAGDROPSTART, winevent_hook|wparam|lparam, OBJID_NATIVEOM, 13 }, + { EVENT_SYSTEM_DRAGDROPEND, winevent_hook|wparam|lparam, OBJID_WINDOW, 0 }, + { EVENT_SYSTEM_DIALOGSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 1 }, + { EVENT_SYSTEM_DIALOGEND, winevent_hook|wparam|lparam, OBJID_TITLEBAR, 2 }, + { EVENT_SYSTEM_SCROLLINGSTART, winevent_hook|wparam|lparam, OBJID_MENU, 3 }, + { EVENT_SYSTEM_SCROLLINGEND, winevent_hook|wparam|lparam, OBJID_CLIENT, 4 }, + { EVENT_SYSTEM_SWITCHSTART, winevent_hook|wparam|lparam, OBJID_VSCROLL, 5 }, + { EVENT_SYSTEM_SWITCHEND, winevent_hook|wparam|lparam, OBJID_HSCROLL, 6 }, + { EVENT_SYSTEM_MINIMIZESTART, winevent_hook|wparam|lparam, OBJID_SIZEGRIP, 7 }, + { EVENT_SYSTEM_MINIMIZEEND, winevent_hook|wparam|lparam, OBJID_CARET, 8 }, + { 0 } +}; +static const struct message WmWinEventCaretSeq[] = { + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1 */ + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1 */ + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 2 */ + { EVENT_OBJECT_NAMECHANGE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1 */ + { 0 } +}; +static const struct message WmWinEventCaretSeq_2[] = { + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1/2 */ + { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1/2 */ + { EVENT_OBJECT_NAMECHANGE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1/2 */ + { 0 } +}; +static const struct message WmWinEventAlertSeq[] = { + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, OBJID_ALERT, 0 }, + { 0 } +}; +static const struct message WmWinEventAlertSeq_2[] = { + /* create window in the thread proc */ + { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_WINDOW, 2 }, + /* our test event */ + { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, OBJID_ALERT, 2 }, + { 0 } +}; +static const struct message WmGlobalHookSeq_1[] = { + /* create window in the thread proc */ + { HCBT_CREATEWND, hook|lparam, 0, 2 }, + /* our test events */ + { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_PREVWINDOW, 2 }, + { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_NEXTWINDOW, 2 }, + { 0 } +}; +static const struct message WmGlobalHookSeq_2[] = { + { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_NEXTWINDOW, 0 }, /* old local hook */ + { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_NEXTWINDOW, 2 }, /* new global hook */ + { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_PREVWINDOW, 0 }, /* old local hook */ + { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_PREVWINDOW, 2 }, /* new global hook */ + { 0 } +}; + +static void CALLBACK win_event_global_hook_proc(HWINEVENTHOOK hevent, + DWORD event, + HWND hwnd, + LONG object_id, + LONG child_id, + DWORD thread_id, + DWORD event_time) +{ + char buf[256]; + + trace("WEH_2:%p,event %08lx,hwnd %p,obj %08lx,id %08lx,thread %08lx,time %08lx\n", + hevent, event, hwnd, object_id, child_id, thread_id, event_time); + + if (GetClassNameA(hwnd, buf, sizeof(buf))) + { + if (!lstrcmpiA(buf, "TestWindowClass") || + !lstrcmpiA(buf, "static")) + { + struct message msg; + + msg.message = event; + msg.flags = winevent_hook|wparam|lparam; + msg.wParam = object_id; + msg.lParam = (thread_id == GetCurrentThreadId()) ? child_id : (child_id + 2); + add_message(&msg); + } + } +} + +static HHOOK hCBT_global_hook; +static DWORD cbt_global_hook_thread_id; + +static LRESULT CALLBACK cbt_global_hook_proc(int nCode, WPARAM wParam, LPARAM lParam) +{ + HWND hwnd; + char buf[256]; + + trace("CBT_2: %d, %08x, %08lx\n", nCode, wParam, lParam); + + if (nCode == HCBT_SYSCOMMAND) + { + struct message msg; + + msg.message = nCode; + msg.flags = hook|wparam|lparam; + msg.wParam = wParam; + msg.lParam = (cbt_global_hook_thread_id == GetCurrentThreadId()) ? 1 : 2; + add_message(&msg); + + return CallNextHookEx(hCBT_global_hook, nCode, wParam, lParam); + } + + /* Log also SetFocus(0) calls */ + hwnd = wParam ? (HWND)wParam : (HWND)lParam; + + if (GetClassNameA(hwnd, buf, sizeof(buf))) + { + if (!lstrcmpiA(buf, "TestWindowClass") || + !lstrcmpiA(buf, "static")) + { + struct message msg; + + msg.message = nCode; + msg.flags = hook|wparam|lparam; + msg.wParam = wParam; + msg.lParam = (cbt_global_hook_thread_id == GetCurrentThreadId()) ? 1 : 2; + add_message(&msg); + } + } + return CallNextHookEx(hCBT_global_hook, nCode, wParam, lParam); +} + +static DWORD WINAPI win_event_global_thread_proc(void *param) +{ + HWND hwnd; + MSG msg; + HANDLE hevent = *(HANDLE *)param; + HMODULE user32 = GetModuleHandleA("user32.dll"); + FARPROC pNotifyWinEvent = GetProcAddress(user32, "NotifyWinEvent"); + + assert(pNotifyWinEvent); + + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0,0,0,0,0,0,0, NULL); + assert(hwnd); + trace("created thread window %p\n", hwnd); + + *(HWND *)param = hwnd; + + flush_sequence(); + /* this event should be received only by our new hook proc, + * an old one does not expect an event from another thread. + */ + pNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, hwnd, OBJID_ALERT, 0); + SetEvent(hevent); + + while (GetMessage(&msg, 0, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +} + +static DWORD WINAPI cbt_global_hook_thread_proc(void *param) +{ + HWND hwnd; + MSG msg; + HANDLE hevent = *(HANDLE *)param; + + flush_sequence(); + /* these events should be received only by our new hook proc, + * an old one does not expect an event from another thread. + */ + + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0,0,0,0,0,0,0, NULL); + assert(hwnd); + trace("created thread window %p\n", hwnd); + + *(HWND *)param = hwnd; + + /* Windows doesn't like when a thread plays games with the focus, + that leads to all kinds of misbehaviours and failures to activate + a window. So, better keep next lines commented out. + SetFocus(0); + SetFocus(hwnd);*/ + + DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_PREVWINDOW, 0); + DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_NEXTWINDOW, 0); + + SetEvent(hevent); + + while (GetMessage(&msg, 0, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +} + +static void test_winevents(void) +{ + BOOL ret; + MSG msg; + HWND hwnd, hwnd2; + UINT i; + HANDLE hthread, hevent; + DWORD tid; + HWINEVENTHOOK hhook; + const struct message *events = WmWinEventsSeq; + HMODULE user32 = GetModuleHandleA("user32.dll"); + FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook"); + FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent"); + FARPROC pNotifyWinEvent = GetProcAddress(user32, "NotifyWinEvent"); + + hwnd = CreateWindowExA(0, "TestWindowClass", NULL, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, 0, + NULL, NULL, 0); + assert(hwnd); + + /****** start of global hook test *************/ + hCBT_global_hook = SetWindowsHookExA(WH_CBT, cbt_global_hook_proc, GetModuleHandleA(0), 0); + assert(hCBT_global_hook); + + hevent = CreateEventA(NULL, 0, 0, NULL); + assert(hevent); + hwnd2 = (HWND)hevent; + + hthread = CreateThread(NULL, 0, cbt_global_hook_thread_proc, &hwnd2, 0, &tid); + ok(hthread != NULL, "CreateThread failed, error %ld\n", GetLastError()); + + ok(WaitForSingleObject(hevent, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + + ok_sequence(WmGlobalHookSeq_1, "global hook 1", FALSE); + + flush_sequence(); + /* this one should be received only by old hook proc */ + DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_NEXTWINDOW, 0); + /* this one should be received only by old hook proc */ + DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_PREVWINDOW, 0); + + ok_sequence(WmGlobalHookSeq_2, "global hook 2", FALSE); + + ret = UnhookWindowsHookEx(hCBT_global_hook); + ok( ret, "UnhookWindowsHookEx error %ld\n", GetLastError()); + + PostThreadMessageA(tid, WM_QUIT, 0, 0); + ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + CloseHandle(hthread); + CloseHandle(hevent); + ok(!IsWindow(hwnd2), "window should be destroyed on thread exit\n"); + /****** end of global hook test *************/ + + if (!pSetWinEventHook || !pNotifyWinEvent || !pUnhookWinEvent) + { + ok(DestroyWindow(hwnd), "failed to destroy window\n"); + return; + } + + flush_sequence(); + +#if 0 /* this test doesn't pass under Win9x */ + /* win2k ignores events with hwnd == 0 */ + SetLastError(0xdeadbeef); + pNotifyWinEvent(events[0].message, 0, events[0].wParam, events[0].lParam); + ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE || /* Win2k */ + GetLastError() == 0xdeadbeef, /* Win9x */ + "unexpected error %ld\n", GetLastError()); + ok_sequence(WmEmptySeq, "empty notify winevents", FALSE); +#endif + + for (i = 0; i < sizeof(WmWinEventsSeq)/sizeof(WmWinEventsSeq[0]); i++) + pNotifyWinEvent(events[i].message, hwnd, events[i].wParam, events[i].lParam); + + ok_sequence(WmWinEventsSeq, "notify winevents", FALSE); + + /****** start of event filtering test *************/ + hhook = (HWINEVENTHOOK)pSetWinEventHook( + EVENT_OBJECT_SHOW, /* 0x8002 */ + EVENT_OBJECT_LOCATIONCHANGE, /* 0x800B */ + GetModuleHandleA(0), win_event_global_hook_proc, + GetCurrentProcessId(), 0, + WINEVENT_INCONTEXT); + ok(hhook != 0, "SetWinEventHook error %ld\n", GetLastError()); + + hevent = CreateEventA(NULL, 0, 0, NULL); + assert(hevent); + hwnd2 = (HWND)hevent; + + hthread = CreateThread(NULL, 0, win_event_global_thread_proc, &hwnd2, 0, &tid); + ok(hthread != NULL, "CreateThread failed, error %ld\n", GetLastError()); + + ok(WaitForSingleObject(hevent, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + + ok_sequence(WmWinEventAlertSeq, "alert winevent", FALSE); + + flush_sequence(); + /* this one should be received only by old hook proc */ + pNotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_CARET, 0); /* 0x8000 */ + pNotifyWinEvent(EVENT_OBJECT_SHOW, hwnd, OBJID_CARET, 0); /* 0x8002 */ + /* this one should be received only by old hook proc */ + pNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_CARET, 0); /* 0x800C */ + + ok_sequence(WmWinEventCaretSeq, "caret winevent", FALSE); + + ret = pUnhookWinEvent(hhook); + ok( ret, "UnhookWinEvent error %ld\n", GetLastError()); + + PostThreadMessageA(tid, WM_QUIT, 0, 0); + ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + CloseHandle(hthread); + CloseHandle(hevent); + ok(!IsWindow(hwnd2), "window should be destroyed on thread exit\n"); + /****** end of event filtering test *************/ + + /****** start of out of context event test *************/ + hhook = (HWINEVENTHOOK)pSetWinEventHook( + EVENT_MIN, EVENT_MAX, + 0, win_event_global_hook_proc, + GetCurrentProcessId(), 0, + WINEVENT_OUTOFCONTEXT); + ok(hhook != 0, "SetWinEventHook error %ld\n", GetLastError()); + + hevent = CreateEventA(NULL, 0, 0, NULL); + assert(hevent); + hwnd2 = (HWND)hevent; + + flush_sequence(); + + hthread = CreateThread(NULL, 0, win_event_global_thread_proc, &hwnd2, 0, &tid); + ok(hthread != NULL, "CreateThread failed, error %ld\n", GetLastError()); + + ok(WaitForSingleObject(hevent, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + + ok_sequence(WmEmptySeq, "empty notify winevents", FALSE); + /* process pending winevent messages */ + ok(!PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE), "msg queue should be empty\n"); + ok_sequence(WmWinEventAlertSeq_2, "alert winevent for out of context proc", FALSE); + + flush_sequence(); + /* this one should be received only by old hook proc */ + pNotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_CARET, 0); /* 0x8000 */ + pNotifyWinEvent(EVENT_OBJECT_SHOW, hwnd, OBJID_CARET, 0); /* 0x8002 */ + /* this one should be received only by old hook proc */ + pNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_CARET, 0); /* 0x800C */ + + ok_sequence(WmWinEventCaretSeq_2, "caret winevent for incontext proc", FALSE); + /* process pending winevent messages */ + ok(!PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE), "msg queue should be empty\n"); + ok_sequence(WmWinEventCaretSeq_2, "caret winevent for out of context proc", FALSE); + + ret = pUnhookWinEvent(hhook); + ok( ret, "UnhookWinEvent error %ld\n", GetLastError()); + + PostThreadMessageA(tid, WM_QUIT, 0, 0); + ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); + CloseHandle(hthread); + CloseHandle(hevent); + ok(!IsWindow(hwnd2), "window should be destroyed on thread exit\n"); + /****** end of out of context event test *************/ + + ok(DestroyWindow(hwnd), "failed to destroy window\n"); +} + +static void test_set_hook(void) +{ + BOOL ret; + HHOOK hhook; + HWINEVENTHOOK hwinevent_hook; + HMODULE user32 = GetModuleHandleA("user32.dll"); + FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook"); + FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent"); + + hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, GetModuleHandleA(0), GetCurrentThreadId()); + ok(hhook != 0, "local hook does not require hModule set to 0\n"); + UnhookWindowsHookEx(hhook); + +#if 0 /* this test doesn't pass under Win9x: BUG! */ + SetLastError(0xdeadbeef); + hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, 0); + ok(!hhook, "global hook requires hModule != 0\n"); + ok(GetLastError() == ERROR_HOOK_NEEDS_HMOD, "unexpected error %ld\n", GetLastError()); +#endif + + SetLastError(0xdeadbeef); + hhook = SetWindowsHookExA(WH_CBT, 0, GetModuleHandleA(0), GetCurrentThreadId()); + ok(!hhook, "SetWinEventHook with invalid proc should fail\n"); + ok(GetLastError() == ERROR_INVALID_FILTER_PROC || /* Win2k */ + GetLastError() == 0xdeadbeef, /* Win9x */ + "unexpected error %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + ok(!UnhookWindowsHookEx((HHOOK)0xdeadbeef), "UnhookWindowsHookEx succeeded\n"); + ok(GetLastError() == ERROR_INVALID_HOOK_HANDLE || /* Win2k */ + GetLastError() == 0xdeadbeef, /* Win9x */ + "unexpected error %ld\n", GetLastError()); + + if (!pSetWinEventHook || !pUnhookWinEvent) return; + + /* even process local incontext hooks require hmodule */ + SetLastError(0xdeadbeef); + hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX, + 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT); + ok(!hwinevent_hook, "WINEVENT_INCONTEXT requires hModule != 0\n"); + ok(GetLastError() == ERROR_HOOK_NEEDS_HMOD || /* Win2k */ + GetLastError() == 0xdeadbeef, /* Win9x */ + "unexpected error %ld\n", GetLastError()); + + /* even thread local incontext hooks require hmodule */ + SetLastError(0xdeadbeef); + hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX, + 0, win_event_proc, GetCurrentProcessId(), GetCurrentThreadId(), WINEVENT_INCONTEXT); + ok(!hwinevent_hook, "WINEVENT_INCONTEXT requires hModule != 0\n"); + ok(GetLastError() == ERROR_HOOK_NEEDS_HMOD || /* Win2k */ + GetLastError() == 0xdeadbeef, /* Win9x */ + "unexpected error %ld\n", GetLastError()); + +#if 0 /* these 3 tests don't pass under Win9x */ + SetLastError(0xdeadbeef); + hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(1, 0, + 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_OUTOFCONTEXT); + ok(!hwinevent_hook, "SetWinEventHook with invalid event range should fail\n"); + ok(GetLastError() == ERROR_INVALID_HOOK_FILTER, "unexpected error %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(-1, 1, + 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_OUTOFCONTEXT); + ok(!hwinevent_hook, "SetWinEventHook with invalid event range should fail\n"); + ok(GetLastError() == ERROR_INVALID_HOOK_FILTER, "unexpected error %ld\n", GetLastError()); + + SetLastError(0xdeadbeef); + hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX, + 0, win_event_proc, 0, 0xdeadbeef, WINEVENT_OUTOFCONTEXT); + ok(!hwinevent_hook, "SetWinEventHook with invalid tid should fail\n"); + ok(GetLastError() == ERROR_INVALID_THREAD_ID, "unexpected error %ld\n", GetLastError()); +#endif + + SetLastError(0xdeadbeef); + hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(0, 0, + 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_OUTOFCONTEXT); + ok(hwinevent_hook != 0, "SetWinEventHook error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "unexpected error %ld\n", GetLastError()); + ret = pUnhookWinEvent(hwinevent_hook); + ok( ret, "UnhookWinEvent error %ld\n", GetLastError()); + +todo_wine { + /* This call succeeds under win2k SP4, but fails under Wine. + Does win2k test/use passed process id? */ + SetLastError(0xdeadbeef); + hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX, + 0, win_event_proc, 0xdeadbeef, 0, WINEVENT_OUTOFCONTEXT); + ok(hwinevent_hook != 0, "SetWinEventHook error %ld\n", GetLastError()); + ok(GetLastError() == 0xdeadbeef, "unexpected error %ld\n", GetLastError()); + ret = pUnhookWinEvent(hwinevent_hook); + ok( ret, "UnhookWinEvent error %ld\n", GetLastError()); +} + + SetLastError(0xdeadbeef); + ok(!pUnhookWinEvent((HWINEVENTHOOK)0xdeadbeef), "UnhookWinEvent succeeded\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE || /* Win2k */ + GetLastError() == 0xdeadbeef, /* Win9x */ + "unexpected error %ld\n", GetLastError()); +} + +static const struct message ScrollWindowPaint1[] = { + { WM_PAINT, sent }, + { WM_ERASEBKGND, sent|beginpaint }, + { 0 } +}; + +static const struct message ScrollWindowPaint2[] = { + { WM_PAINT, sent }, + { 0 } +}; + +static void test_scrollwindowex(void) +{ + HWND hwnd, hchild; + RECT rect={0,0,130,130}; + MSG msg; + + hwnd = CreateWindowExA(0, "TestWindowClass", "Test Scroll", + WS_VISIBLE|WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok (hwnd != 0, "Failed to create overlapped window\n"); + hchild = CreateWindowExA(0, "TestWindowClass", "Test child", + WS_VISIBLE|WS_CAPTION|WS_CHILD, + 10, 10, 150, 150, hwnd, 0, 0, NULL); + ok (hchild != 0, "Failed to create child\n"); + UpdateWindow(hwnd); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + flush_sequence(); + + /* scroll without the child window */ + trace("start scroll\n"); + ScrollWindowEx( hwnd, 10, 10, &rect, NULL, NULL, NULL, + SW_ERASE|SW_INVALIDATE); + ok_sequence(WmEmptySeq, "ScrollWindowEx", 0); + trace("end scroll\n"); + flush_sequence(); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence(ScrollWindowPaint1, "ScrollWindowEx", 0); + + /* Now without the SW_ERASE flag */ + trace("start scroll\n"); + ScrollWindowEx( hwnd, 10, 10, &rect, NULL, NULL, NULL, SW_INVALIDATE); + ok_sequence(WmEmptySeq, "ScrollWindowEx", 0); + trace("end scroll\n"); + flush_sequence(); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence(ScrollWindowPaint2, "ScrollWindowEx", 0); + + /* now scroll the child window as well */ + trace("start scroll\n"); + ScrollWindowEx( hwnd, 10, 10, &rect, NULL, NULL, NULL, + SW_SCROLLCHILDREN|SW_ERASE|SW_INVALIDATE); + todo_wine { /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */ + /* windows sometimes a WM_MOVE */ + ok_sequence(WmEmptySeq, "ScrollWindowEx", 0); + } + trace("end scroll\n"); + flush_sequence(); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence(ScrollWindowPaint1, "ScrollWindowEx", 0); + + /* now scroll with ScrollWindow() */ + trace("start scroll with ScrollWindow\n"); + ScrollWindow( hwnd, 5, 5, NULL, NULL); + trace("end scroll\n"); + flush_sequence(); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + ok_sequence(ScrollWindowPaint1, "ScrollWindow", 0); + + ok(DestroyWindow(hchild), "failed to destroy window\n"); + ok(DestroyWindow(hwnd), "failed to destroy window\n"); + flush_sequence(); +} + +static const struct message destroy_window_with_children[] = { + { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 }, /* popup */ + { HCBT_DESTROYWND, hook|lparam, 0, WND_PARENT_ID }, /* parent */ + { HCBT_DESTROYWND, hook|lparam, 0, WND_POPUP_ID }, /* popup */ + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, /* popup */ + { WM_DESTROY, sent|wparam|lparam, 0, WND_POPUP_ID }, /* popup */ + { WM_CAPTURECHANGED, sent|wparam|lparam, 0, WND_POPUP_ID }, /* popup */ + { WM_NCDESTROY, sent|wparam|lparam, 0, WND_POPUP_ID }, /* popup */ + { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, /* parent */ + { WM_DESTROY, sent|wparam|lparam, 0, WND_PARENT_ID }, /* parent */ + { WM_DESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 2 }, /* child2 */ + { WM_DESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 1 }, /* child1 */ + { WM_DESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 3 }, /* child3 */ + { WM_NCDESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 2 }, /* child2 */ + { WM_NCDESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 3 }, /* child3 */ + { WM_NCDESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 1 }, /* child1 */ + { WM_NCDESTROY, sent|wparam|lparam, 0, WND_PARENT_ID }, /* parent */ + { 0 } +}; + +static void test_DestroyWindow(void) +{ + BOOL ret; + HWND parent, child1, child2, child3, child4, test; + UINT child_id = WND_CHILD_ID + 1; + + parent = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + assert(parent != 0); + child1 = CreateWindowExA(0, "TestWindowClass", NULL, WS_CHILD, + 0, 0, 50, 50, parent, (HMENU)child_id++, 0, NULL); + assert(child1 != 0); + child2 = CreateWindowExA(0, "TestWindowClass", NULL, WS_CHILD, + 0, 0, 50, 50, GetDesktopWindow(), (HMENU)child_id++, 0, NULL); + assert(child2 != 0); + child3 = CreateWindowExA(0, "TestWindowClass", NULL, WS_CHILD, + 0, 0, 50, 50, child1, (HMENU)child_id++, 0, NULL); + assert(child3 != 0); + child4 = CreateWindowExA(0, "TestWindowClass", NULL, WS_POPUP, + 0, 0, 50, 50, parent, 0, 0, NULL); + assert(child4 != 0); + + /* test owner/parent of child2 */ + test = GetParent(child2); + ok(test == GetDesktopWindow(), "wrong parent %p\n", test); + ok(!IsChild(parent, child2), "wrong parent/child %p/%p\n", parent, child2); + if(pGetAncestor) { + test = pGetAncestor(child2, GA_PARENT); + ok(test == GetDesktopWindow(), "wrong parent %p\n", test); + } + test = GetWindow(child2, GW_OWNER); + ok(!test, "wrong owner %p\n", test); + + test = SetParent(child2, parent); + ok(test == GetDesktopWindow(), "wrong old parent %p\n", test); + + /* test owner/parent of the parent */ + test = GetParent(parent); + ok(!test, "wrong parent %p\n", test); +todo_wine { + ok(!IsChild(GetDesktopWindow(), parent), "wrong parent/child %p/%p\n", GetDesktopWindow(), parent); +} + if(pGetAncestor) { + test = pGetAncestor(parent, GA_PARENT); + ok(test == GetDesktopWindow(), "wrong parent %p\n", test); + } + test = GetWindow(parent, GW_OWNER); + ok(!test, "wrong owner %p\n", test); + + /* test owner/parent of child1 */ + test = GetParent(child1); + ok(test == parent, "wrong parent %p\n", test); + ok(IsChild(parent, child1), "wrong parent/child %p/%p\n", parent, child1); + if(pGetAncestor) { + test = pGetAncestor(child1, GA_PARENT); + ok(test == parent, "wrong parent %p\n", test); + } + test = GetWindow(child1, GW_OWNER); + ok(!test, "wrong owner %p\n", test); + + /* test owner/parent of child2 */ + test = GetParent(child2); + ok(test == parent, "wrong parent %p\n", test); + ok(IsChild(parent, child2), "wrong parent/child %p/%p\n", parent, child2); + if(pGetAncestor) { + test = pGetAncestor(child2, GA_PARENT); + ok(test == parent, "wrong parent %p\n", test); + } + test = GetWindow(child2, GW_OWNER); + ok(!test, "wrong owner %p\n", test); + + /* test owner/parent of child3 */ + test = GetParent(child3); + ok(test == child1, "wrong parent %p\n", test); + ok(IsChild(parent, child3), "wrong parent/child %p/%p\n", parent, child3); + if(pGetAncestor) { + test = pGetAncestor(child3, GA_PARENT); + ok(test == child1, "wrong parent %p\n", test); + } + test = GetWindow(child3, GW_OWNER); + ok(!test, "wrong owner %p\n", test); + + /* test owner/parent of child4 */ + test = GetParent(child4); + ok(test == parent, "wrong parent %p\n", test); + ok(!IsChild(parent, child4), "wrong parent/child %p/%p\n", parent, child4); + if(pGetAncestor) { + test = pGetAncestor(child4, GA_PARENT); + ok(test == GetDesktopWindow(), "wrong parent %p\n", test); + } + test = GetWindow(child4, GW_OWNER); + ok(test == parent, "wrong owner %p\n", test); + + flush_sequence(); + + trace("parent %p, child1 %p, child2 %p, child3 %p, child4 %p\n", + parent, child1, child2, child3, child4); + + SetCapture(child4); + test = GetCapture(); + ok(test == child4, "wrong capture window %p\n", test); + + test_DestroyWindow_flag = TRUE; + ret = DestroyWindow(parent); + ok( ret, "DestroyWindow() error %ld\n", GetLastError()); + test_DestroyWindow_flag = FALSE; + ok_sequence(destroy_window_with_children, "destroy window with children", 0); + + ok(!IsWindow(parent), "parent still exists\n"); + ok(!IsWindow(child1), "child1 still exists\n"); + ok(!IsWindow(child2), "child2 still exists\n"); + ok(!IsWindow(child3), "child3 still exists\n"); + ok(!IsWindow(child4), "child4 still exists\n"); + + test = GetCapture(); + ok(!test, "wrong capture window %p\n", test); +} + + +static const struct message WmDispatchPaint[] = { + { WM_NCPAINT, sent }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_GETTEXT, sent|defwinproc|optional }, + { WM_ERASEBKGND, sent }, + { 0 } +}; + +static LRESULT WINAPI DispatchMessageCheckProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_PAINT) + { + trace( "Got WM_PAINT, ignoring\n" ); + return 0; + } + return MsgCheckProcA( hwnd, message, wParam, lParam ); +} + +static void test_DispatchMessage(void) +{ + RECT rect; + MSG msg; + int count; + HWND hwnd = CreateWindowA( "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + ShowWindow( hwnd, SW_SHOW ); + UpdateWindow( hwnd ); + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg ); + flush_sequence(); + SetWindowLongPtrA( hwnd, GWL_WNDPROC, (LONG_PTR)DispatchMessageCheckProc ); + + SetRect( &rect, -5, -5, 5, 5 ); + RedrawWindow( hwnd, &rect, 0, RDW_INVALIDATE|RDW_ERASE|RDW_FRAME ); + count = 0; + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) + { + if (msg.message != WM_PAINT) DispatchMessage( &msg ); + else + { + flush_sequence(); + DispatchMessage( &msg ); + /* DispatchMessage will send WM_NCPAINT if non client area is still invalid after WM_PAINT */ + if (!count) ok_sequence( WmDispatchPaint, "WmDispatchPaint", FALSE ); + else ok_sequence( WmEmptySeq, "WmEmpty", FALSE ); + if (++count > 10) break; + } + } + ok( msg.message == WM_PAINT && count > 10, "WM_PAINT messages stopped\n" ); + + trace("now without DispatchMessage\n"); + flush_sequence(); + RedrawWindow( hwnd, &rect, 0, RDW_INVALIDATE|RDW_ERASE|RDW_FRAME ); + count = 0; + while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) + { + if (msg.message != WM_PAINT) DispatchMessage( &msg ); + else + { + HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 ); + flush_sequence(); + /* this will send WM_NCCPAINT just like DispatchMessage does */ + GetUpdateRgn( hwnd, hrgn, TRUE ); + ok_sequence( WmDispatchPaint, "WmDispatchPaint", FALSE ); + DeleteObject( hrgn ); + GetClientRect( hwnd, &rect ); + ValidateRect( hwnd, &rect ); /* this will stop WM_PAINTs */ + ok( !count, "Got multiple WM_PAINTs\n" ); + if (++count > 10) break; + } + } +} + + +START_TEST(msg) +{ + BOOL ret; + HMODULE user32 = GetModuleHandleA("user32.dll"); + FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook"); + FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent"); + FARPROC pIsWinEventHookInstalled = 0;/*GetProcAddress(user32, "IsWinEventHookInstalled");*/ + pGetAncestor = (void*) GetProcAddress(user32, "GetAncestor"); + + if (!RegisterWindowClasses()) assert(0); + + if (pSetWinEventHook) + { + hEvent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX, + GetModuleHandleA(0), + win_event_proc, + 0, + GetCurrentThreadId(), + WINEVENT_INCONTEXT); + assert(hEvent_hook); + + if (pIsWinEventHookInstalled) + { + UINT event; + for (event = EVENT_MIN; event <= EVENT_MAX; event++) + ok(pIsWinEventHookInstalled(event), "IsWinEventHookInstalled(%u) failed\n", event); + } + } + + cbt_hook_thread_id = GetCurrentThreadId(); + hCBT_hook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId()); + assert(hCBT_hook); + + test_winevents(); + + /* Fix message sequences before removing 3 lines below */ + ret = pUnhookWinEvent(hEvent_hook); + ok( ret, "UnhookWinEvent error %ld\n", GetLastError()); + pUnhookWinEvent = 0; + hEvent_hook = 0; + + test_scrollwindowex(); + test_messages(); + test_mdi_messages(); + test_button_messages(); + test_paint_messages(); + test_interthread_messages(); + test_message_conversion(); + test_accelerators(); + test_timers(); + test_set_hook(); + test_DestroyWindow(); + test_DispatchMessage(); + + UnhookWindowsHookEx(hCBT_hook); + if (pUnhookWinEvent) + { + ret = pUnhookWinEvent(hEvent_hook); + ok( ret, "UnhookWinEvent error %ld\n", GetLastError()); + SetLastError(0xdeadbeef); + ok(!pUnhookWinEvent(hEvent_hook), "UnhookWinEvent succeeded\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE || /* Win2k */ + GetLastError() == 0xdeadbeef, /* Win9x */ + "unexpected error %ld\n", GetLastError()); + } +} diff --git a/reactos/regtests/winetests/user32/resource.c b/reactos/regtests/winetests/user32/resource.c new file mode 100755 index 00000000000..acb24ece46e --- /dev/null +++ b/reactos/regtests/winetests/user32/resource.c @@ -0,0 +1,255 @@ +/* Unit test suite for resources. + * + * Copyright 2004 Ferenc Wagner + * Copyright 2003, 2004 Mike McCormack + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "wine/test.h" + +static void test_LoadStringA (void) +{ + HINSTANCE hInst = GetModuleHandle (NULL); + static const char str[] = "String resource"; /* same in resource.rc */ + char buf[128]; + struct string_test { + int bufsiz; + int expected; + }; + struct string_test tests[] = {{sizeof buf, sizeof str - 1}, + {sizeof str, sizeof str - 1}, + {sizeof str - 1, sizeof str - 2}}; + unsigned int i; + + assert (sizeof str < sizeof buf); + for (i = 0; i < sizeof tests / sizeof tests[0]; i++) { + const int bufsiz = tests[i].bufsiz; + const int expected = tests[i].expected; + const int len = LoadStringA (hInst, 0, buf, bufsiz); + + ok (len == expected, "bufsiz=%d: got %d, expected %d\n", + bufsiz, len, expected); + ok (!memcmp (buf, str, len), + "bufsiz=%d: got '%s', expected '%.*s'\n", + bufsiz, buf, len, str); + ok (buf[len] == 0, "bufsiz=%d: NUL termination missing\n", + bufsiz); + } +} + +static void test_accel1(void) +{ + UINT r, n; + HACCEL hAccel; + ACCEL ac[10]; + + /* now create our own valid accelerator table */ + n = 0; + ac[n].cmd = 1000; + ac[n].key = 'A'; + ac[n++].fVirt = FVIRTKEY | FNOINVERT; + + ac[n].cmd = 1001; + ac[n].key = 'B'; + ac[n++].fVirt = FNOINVERT; + + ac[n].cmd = 0; + ac[n].key = 0; + ac[n++].fVirt = 0; + + hAccel = CreateAcceleratorTable( &ac[0], n ); + ok( hAccel != NULL, "create accelerator table\n"); + + r = DestroyAcceleratorTable( hAccel ); + ok( r, "destroy accelerator table\n"); + + /* now try create an invalid one */ + n = 0; + ac[n].cmd = 1000; + ac[n].key = 'A'; + ac[n++].fVirt = FVIRTKEY | FNOINVERT; + + ac[n].cmd = 0xffff; + ac[n].key = 0xffff; + ac[n++].fVirt = (SHORT) 0xffff; + + ac[n].cmd = 0xfff0; + ac[n].key = 0xffff; + ac[n++].fVirt = (SHORT) 0xfff0; + + ac[n].cmd = 0xfff0; + ac[n].key = 0xffff; + ac[n++].fVirt = (SHORT) 0x0000; + + ac[n].cmd = 0xfff0; + ac[n].key = 0xffff; + ac[n++].fVirt = (SHORT) 0x0001; + + hAccel = CreateAcceleratorTable( &ac[0], n ); + ok( hAccel != NULL, "create accelerator table\n"); + + r = CopyAcceleratorTable( hAccel, NULL, 0 ); + ok( r == n, "two entries in table\n"); + + r = CopyAcceleratorTable( hAccel, &ac[0], r ); + ok( r == n, "still should be two entries in table\n"); + + n=0; + ok( ac[n].cmd == 1000, "cmd 0 not preserved\n"); + ok( ac[n].key == 'A', "key 0 not preserved\n"); + ok( ac[n].fVirt == (FVIRTKEY | FNOINVERT), "fVirt 0 not preserved\n"); + + n++; + ok( ac[n].cmd == 0xffff, "cmd 1 not preserved\n"); + ok( ac[n].key == 0xffff, "key 1 not preserved\n"); + ok( ac[n].fVirt == 0x007f, "fVirt 1 not changed\n"); + + n++; + ok( ac[n].cmd == 0xfff0, "cmd 2 not preserved\n"); + ok( ac[n].key == 0x00ff, "key 2 not preserved\n"); + ok( ac[n].fVirt == 0x0070, "fVirt 2 not changed\n"); + + n++; + ok( ac[n].cmd == 0xfff0, "cmd 3 not preserved\n"); + ok( ac[n].key == 0x00ff, "key 3 not preserved\n"); + ok( ac[n].fVirt == 0x0000, "fVirt 3 not changed\n"); + + n++; + ok( ac[n].cmd == 0xfff0, "cmd 4 not preserved\n"); + ok( ac[n].key == 0xffff, "key 4 not preserved\n"); + ok( ac[n].fVirt == 0x0001, "fVirt 4 not changed\n"); + + r = DestroyAcceleratorTable( hAccel ); + ok( r, "destroy accelerator table\n"); + + hAccel = CreateAcceleratorTable( &ac[0], 0 ); + ok( !hAccel, "zero elements should fail\n"); + + /* these will on crash win2k + hAccel = CreateAcceleratorTable( NULL, 1 ); + hAccel = CreateAcceleratorTable( &ac[0], -1 ); + */ +} + +/* + * memcmp on the tables works in Windows, but does not work in wine, as + * there is an extra undefined and unused byte between fVirt and the key + */ +static void test_accel2(void) +{ + ACCEL ac[2], out[2]; + HACCEL hac; + + ac[0].cmd = 0; + ac[0].fVirt = 0; + ac[0].key = 0; + + ac[1].cmd = 0; + ac[1].fVirt = 0; + ac[1].key = 0; + + /* + * crashes on win2k + * hac = CreateAcceleratorTable( NULL, 1 ); + */ + + /* try a zero count */ + hac = CreateAcceleratorTable( &ac[0], 0 ); + ok( !hac , "fail\n"); + ok( !DestroyAcceleratorTable( hac ), "destroy failed\n"); + + /* creating one accelerator should work */ + hac = CreateAcceleratorTable( &ac[0], 1 ); + ok( hac != NULL , "fail\n"); + ok( 1 == CopyAcceleratorTable( hac, out, 1 ), "copy failed\n"); + ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); + + /* how about two of the same type? */ + hac = CreateAcceleratorTable( &ac[0], 2); + ok( hac != NULL , "fail\n"); + ok( 2 == CopyAcceleratorTable( hac, NULL, 100 ), "copy null failed\n"); + ok( 2 == CopyAcceleratorTable( hac, NULL, 0 ), "copy null failed\n"); + ok( 2 == CopyAcceleratorTable( hac, NULL, 1 ), "copy null failed\n"); + ok( 1 == CopyAcceleratorTable( hac, out, 1 ), "copy 1 failed\n"); + ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n"); + ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); + /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */ + + /* how about two of the same type with a non-zero key? */ + ac[0].key = 0x20; + ac[1].key = 0x20; + hac = CreateAcceleratorTable( &ac[0], 2); + ok( hac != NULL , "fail\n"); + ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n"); + ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); + /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */ + + /* how about two of the same type with a non-zero virtual key? */ + ac[0].fVirt = FVIRTKEY; + ac[0].key = 0x40; + ac[1].fVirt = FVIRTKEY; + ac[1].key = 0x40; + hac = CreateAcceleratorTable( &ac[0], 2); + ok( hac != NULL , "fail\n"); + ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n"); + /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */ + ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); + + /* how virtual key codes */ + ac[0].fVirt = FVIRTKEY; + hac = CreateAcceleratorTable( &ac[0], 1); + ok( hac != NULL , "fail\n"); + ok( 1 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n"); + /* ok( !memcmp( ac, out, sizeof ac/2 ), "tables different\n"); */ + ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); + + /* how turning on all bits? */ + ac[0].cmd = 0xffff; + ac[0].fVirt = 0xff; + ac[0].key = 0xffff; + hac = CreateAcceleratorTable( &ac[0], 1); + ok( hac != NULL , "fail\n"); + ok( 1 == CopyAcceleratorTable( hac, out, 1 ), "copy 1 failed\n"); + /* ok( memcmp( ac, out, sizeof ac/2 ), "tables not different\n"); */ + ok( out[0].cmd == ac[0].cmd, "cmd modified\n"); + ok( out[0].fVirt == (ac[0].fVirt&0x7f), "fVirt not modified\n"); + ok( out[0].key == ac[0].key, "key modified\n"); + ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); + + /* how turning on all bits? */ + memset( ac, 0xff, sizeof ac ); + hac = CreateAcceleratorTable( &ac[0], 2); + ok( hac != NULL , "fail\n"); + ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n"); + /* ok( memcmp( ac, out, sizeof ac ), "tables not different\n"); */ + ok( out[0].cmd == ac[0].cmd, "cmd modified\n"); + ok( out[0].fVirt == (ac[0].fVirt&0x7f), "fVirt not modified\n"); + ok( out[0].key == ac[0].key, "key modified\n"); + ok( out[1].cmd == ac[1].cmd, "cmd modified\n"); + ok( out[1].fVirt == (ac[1].fVirt&0x7f), "fVirt not modified\n"); + ok( out[1].key == ac[1].key, "key modified\n"); + ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); +} + +START_TEST(resource) +{ + test_LoadStringA (); + test_accel1(); + test_accel2(); +} diff --git a/reactos/regtests/winetests/user32/resource.rc b/reactos/regtests/winetests/user32/resource.rc new file mode 100755 index 00000000000..13ab48bde80 --- /dev/null +++ b/reactos/regtests/winetests/user32/resource.rc @@ -0,0 +1,79 @@ +/* Unit test suite for resources. + * + * Copyright 2004 Ferenc Wagner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "windef.h" +#include "winuser.h" + +1 ACCELERATORS +{ + "^N", 1000 /* Ctrl+'N' */ + "N", 1001 /* Shift+'n' */ + "n", 1002 /* 'n' */ +} + +2 ACCELERATORS +{ + 78, 1000, VIRTKEY, CONTROL /* Ctrl+'N' */ + 78, 1001, ASCII /* 'N' */ + 110, 1002, ASCII /* 'n' */ + 78, 1003, VIRTKEY, ALT /* Alt+'N' */ + 78, 1004, VIRTKEY, CONTROL, SHIFT /* Ctrl+Shift+'N' */ + 78, 1005, VIRTKEY, CONTROL, ALT, SHIFT /* Ctrl+Alt+Shift+'N' */ +} + +STRINGTABLE +{ + 0 "String resource" +} + +TEST_DIALOG DIALOG DISCARDABLE 0, 0, 60, 30 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE +CAPTION "Test dialog" +FONT 8, "MS Shell Dlg" +{ + DEFPUSHBUTTON "OK", IDOK,4,4,50,14, WS_TABSTOP | WS_GROUP +} + +RADIO_TEST_DIALOG DIALOGEX 0, 0, 160, 80 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "Radio Button Test Dialog" +FONT 8, "MS Shell Dlg" +{ + GROUPBOX "Static", 100,6,5,92,70 + CONTROL "Radio1", 200,"Button",BS_AUTORADIOBUTTON | + WS_GROUP | WS_TABSTOP,17,27,39,10 + CONTROL "Radio2", 201,"Button",BS_AUTORADIOBUTTON,17,40,39,10 + PUSHBUTTON "Cancel", IDCANCEL,109,20,50,14, WS_TABSTOP | WS_GROUP +} + +CLASS_TEST_DIALOG DIALOG DISCARDABLE 0, 0, 91, 28 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "CreateDialogParams Test" +CLASS "TestDialog" +FONT 8, "MS Shell Dlg" +{ +} + +FOCUS_TEST_DIALOG DIALOG DISCARDABLE 0, 0, 60, 30 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_CONTROL +CAPTION "Test dialog" +FONT 8, "MS Shell Dlg" +{ + EDITTEXT 200,4,4,50,14 +} diff --git a/reactos/regtests/winetests/user32/sysparams.c b/reactos/regtests/winetests/user32/sysparams.c new file mode 100755 index 00000000000..a3d694de3a0 --- /dev/null +++ b/reactos/regtests/winetests/user32/sysparams.c @@ -0,0 +1,1737 @@ +/* Unit test suite for functions SystemParametersInfo and GetSystemMetrics. + * + * Copyright 2002 Andriy Palamarchuk + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#define _WIN32_WINNT 0x0500 /* For SPI_GETMOUSEHOVERWIDTH and more */ + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winreg.h" +#include "winuser.h" + +#ifndef SPI_GETDESKWALLPAPER +# define SPI_GETDESKWALLPAPER 0x0073 +#endif + +static int strict; + +#define eq(received, expected, label, type) \ + ok((received) == (expected), "%s: got " type " instead of " type "\n", (label),(received),(expected)) + + +#define SPI_SETBEEP_REGKEY "Control Panel\\Sound" +#define SPI_SETBEEP_VALNAME "Beep" +#define SPI_SETMOUSE_REGKEY "Control Panel\\Mouse" +#define SPI_SETMOUSE_VALNAME1 "MouseThreshold1" +#define SPI_SETMOUSE_VALNAME2 "MouseThreshold2" +#define SPI_SETMOUSE_VALNAME3 "MouseSpeed" +#define SPI_SETBORDER_REGKEY "Control Panel\\Desktop\\WindowMetrics" +#define SPI_SETBORDER_VALNAME "BorderWidth" +#define SPI_SETKEYBOARDSPEED_REGKEY "Control Panel\\Keyboard" +#define SPI_SETKEYBOARDSPEED_VALNAME "KeyboardSpeed" +#define SPI_SETSCREENSAVETIMEOUT_REGKEY "Control Panel\\Desktop" +#define SPI_SETSCREENSAVETIMEOUT_VALNAME "ScreenSaveTimeOut" +#define SPI_SETSCREENSAVEACTIVE_REGKEY "Control Panel\\Desktop" +#define SPI_SETSCREENSAVEACTIVE_VALNAME "ScreenSaveActive" +#define SPI_SETGRIDGRANULARITY_REGKEY "Control Panel\\Desktop" +#define SPI_SETGRIDGRANULARITY_VALNAME "GridGranularity" +#define SPI_SETKEYBOARDDELAY_REGKEY "Control Panel\\Keyboard" +#define SPI_SETKEYBOARDDELAY_VALNAME "KeyboardDelay" +#define SPI_SETICONTITLEWRAP_REGKEY1 "Control Panel\\Desktop\\WindowMetrics" +#define SPI_SETICONTITLEWRAP_REGKEY2 "Control Panel\\Desktop" +#define SPI_SETICONTITLEWRAP_VALNAME "IconTitleWrap" +#define SPI_SETMENUDROPALIGNMENT_REGKEY1 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows" +#define SPI_SETMENUDROPALIGNMENT_REGKEY2 "Control Panel\\Desktop" +#define SPI_SETMENUDROPALIGNMENT_VALNAME "MenuDropAlignment" +#define SPI_SETDOUBLECLKWIDTH_REGKEY1 "Control Panel\\Mouse" +#define SPI_SETDOUBLECLKWIDTH_REGKEY2 "Control Panel\\Desktop" +#define SPI_SETDOUBLECLKWIDTH_VALNAME "DoubleClickWidth" +#define SPI_SETDOUBLECLKHEIGHT_REGKEY1 "Control Panel\\Mouse" +#define SPI_SETDOUBLECLKHEIGHT_REGKEY2 "Control Panel\\Desktop" +#define SPI_SETDOUBLECLKHEIGHT_VALNAME "DoubleClickHeight" +#define SPI_SETDOUBLECLICKTIME_REGKEY "Control Panel\\Mouse" +#define SPI_SETDOUBLECLICKTIME_VALNAME "DoubleClickSpeed" +#define SPI_SETMOUSEBUTTONSWAP_REGKEY "Control Panel\\Mouse" +#define SPI_SETMOUSEBUTTONSWAP_VALNAME "SwapMouseButtons" +#define SPI_SETWORKAREA_REGKEY "Control Panel\\Desktop" +#define SPI_SETWORKAREA_VALNAME "WINE_WorkArea" +#define SPI_SETSHOWSOUNDS_REGKEY "Control Panel\\Accessibility\\ShowSounds" +#define SPI_SETSHOWSOUNDS_VALNAME "On" +#define SPI_SETKEYBOARDPREF_REGKEY "Control Panel\\Accessibility\\Keyboard Preference" +#define SPI_SETKEYBOARDPREF_VALNAME "On" +#define SPI_SETKEYBOARDPREF_REGKEY_LEGACY "Control Panel\\Accessibility" +#define SPI_SETKEYBOARDPREF_VALNAME_LEGACY "Keyboard Preference" +#define SPI_SETSCREENREADER_REGKEY "Control Panel\\Accessibility\\Blind Access" +#define SPI_SETSCREENREADER_VALNAME "On" +#define SPI_SETSCREENREADER_REGKEY_LEGACY "Control Panel\\Accessibility" +#define SPI_SETSCREENREADER_VALNAME_LEGACY "Blind Access" +#define SPI_SETLOWPOWERACTIVE_REGKEY "Control Panel\\Desktop" +#define SPI_SETLOWPOWERACTIVE_VALNAME "LowPowerActive" +#define SPI_SETPOWEROFFACTIVE_REGKEY "Control Panel\\Desktop" +#define SPI_SETPOWEROFFACTIVE_VALNAME "PowerOffActive" +#define SPI_SETDRAGFULLWINDOWS_REGKEY "Control Panel\\Desktop" +#define SPI_SETDRAGFULLWINDOWS_VALNAME "DragFullWindows" +#define SPI_SETMOUSEHOVERWIDTH_REGKEY "Control Panel\\Mouse" +#define SPI_SETMOUSEHOVERWIDTH_VALNAME "MouseHoverWidth" +#define SPI_SETMOUSEHOVERHEIGHT_REGKEY "Control Panel\\Mouse" +#define SPI_SETMOUSEHOVERHEIGHT_VALNAME "MouseHoverHeight" +#define SPI_SETMOUSEHOVERTIME_REGKEY "Control Panel\\Mouse" +#define SPI_SETMOUSEHOVERTIME_VALNAME "MouseHoverTime" +#define SPI_SETMOUSESCROLLLINES_REGKEY "Control Panel\\Desktop" +#define SPI_SETMOUSESCROLLLINES_VALNAME "WheelScrollLines" +#define SPI_SETMENUSHOWDELAY_REGKEY "Control Panel\\Desktop" +#define SPI_SETMENUSHOWDELAY_VALNAME "MenuShowDelay" +#define SPI_SETDESKWALLPAPER_REGKEY "Control Panel\\Desktop" +#define SPI_SETDESKWALLPAPER_VALNAME "Wallpaper" + +/* volatile registry branch under CURRENT_USER_REGKEY for temporary values storage */ +#define WINE_CURRENT_USER_REGKEY "Wine" + +static HWND ghTestWnd; + +static DWORD WINAPI SysParamsThreadFunc( LPVOID lpParam ); +static LRESULT CALLBACK SysParamsTestWndProc( HWND hWnd, UINT msg, WPARAM wParam, + LPARAM lParam ); +static int change_counter; +static int change_last_param; + +static LRESULT CALLBACK SysParamsTestWndProc( HWND hWnd, UINT msg, WPARAM wParam, + LPARAM lParam ) +{ + switch (msg) { + + case WM_SETTINGCHANGE: + if (change_counter>0) { + ok(0,"too many changes counter=%d last change=%d\n", + change_counter,change_last_param); + } + change_counter++; + change_last_param = wParam; + break; + + case WM_DESTROY: + PostQuitMessage( 0 ); + break; + + default: + return( DefWindowProcA( hWnd, msg, wParam, lParam ) ); + } + + return 0; +} + +/* +Performs testing for system parameters messages +params: + - system parameter id + - supposed value of the registry key +*/ +static void test_change_message( int action, int optional ) +{ + if (change_counter==0 && optional==1) + return; + ok( 1 == change_counter, + "Missed a message: change_counter=%d\n", change_counter ); + change_counter = 0; + ok( action == change_last_param, + "Wrong action got %d expected %d\n", change_last_param, action ); + change_last_param = 0; +} + +static BOOL test_error_msg ( int rc, const char *name ) +{ + DWORD last_error = GetLastError(); + + if (rc==0) + { + if (last_error==0xdeadbeef || last_error==ERROR_INVALID_SPI_VALUE) + { + trace("%s not supported on this platform. Skipping test\n", name); + } + else if (last_error==ERROR_ACCESS_DENIED) + { + trace("%s does not have privileges to run. Skipping test\n", name); + } + else + { + trace("%s failed for reason: %ld. Indicating test failure and skipping remainder of test\n",name,last_error); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,last_error); + } + return FALSE; + } + else + { + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,last_error); + return TRUE; + } +} + +/* + * Tests the HKEY_CURRENT_USER subkey value. + * The value should contain string value. + * + * Params: + * lpsSubKey - subkey name + * lpsRegName - registry entry name + * lpsTestValue - value to test + */ +static void _test_reg_key( LPCSTR subKey1, LPCSTR subKey2, LPCSTR valName1, LPCSTR valName2, LPCSTR testValue ) +{ + CHAR value[MAX_PATH]; + DWORD valueLen; + DWORD type; + HKEY hKey; + LONG rc; + int found=0; + + *value='\0'; + valueLen=sizeof(value); + RegOpenKeyA( HKEY_CURRENT_USER, subKey1, &hKey ); + rc=RegQueryValueExA( hKey, valName1, NULL, &type, (LPBYTE)value, &valueLen ); + RegCloseKey( hKey ); + if (rc==ERROR_SUCCESS) + { + ok( !strcmp( testValue, value ), + "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n", + subKey1, valName1, testValue, value ); + found++; + } + else if (strict) + { + ok(0,"Missing registry entry: subKey=%s, valName=%s\n", + subKey1, valName1); + } + if (valName2) + { + *value='\0'; + valueLen=sizeof(value); + RegOpenKeyA( HKEY_CURRENT_USER, subKey1, &hKey ); + rc=RegQueryValueExA( hKey, valName2, NULL, &type, (LPBYTE)value, &valueLen ); + RegCloseKey( hKey ); + if (rc==ERROR_SUCCESS) + { + ok( !strcmp( testValue, value ), + "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n", + subKey1, valName2, testValue, value ); + found++; + } + else if (strict) + { + ok( 0,"Missing registry entry: subKey=%s, valName=%s\n", + subKey1, valName2 ); + } + } + if (subKey2 && !strict) + { + *value='\0'; + valueLen=sizeof(value); + RegOpenKeyA( HKEY_CURRENT_USER, subKey2, &hKey ); + rc=RegQueryValueExA( hKey, valName1, NULL, &type, (LPBYTE)value, &valueLen ); + RegCloseKey( hKey ); + if (rc==ERROR_SUCCESS) + { + ok( !strcmp( testValue, value ), + "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n", + subKey2, valName1, testValue, value ); + found++; + } + else if (strict) + { + ok( 0,"Missing registry entry: subKey=%s, valName=%s\n", + subKey2, valName1 ); + } + if (valName2) + { + *value='\0'; + valueLen=sizeof(value); + RegOpenKeyA( HKEY_CURRENT_USER, subKey2, &hKey ); + rc=RegQueryValueExA( hKey, valName2, NULL, &type, (LPBYTE)value, &valueLen ); + RegCloseKey( hKey ); + if (rc==ERROR_SUCCESS) + { + ok( !strcmp( testValue, value ), + "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n", + subKey2, valName2, testValue, value ); + found++; + } + else if (strict) + { + ok( 0,"Missing registry entry: subKey=%s, valName=%s\n", + subKey2, valName2 ); + } + } + } + ok(found,"Missing registry values: %s or %s in keys: %s or %s\n", + valName1, (valName2?valName2:""), subKey1, (subKey2?subKey2:"") ); +} + +#define test_reg_key( subKey, valName, testValue ) \ + _test_reg_key( subKey, NULL, valName, NULL, testValue ) +#define test_reg_key_ex( subKey1, subKey2, valName, testValue ) \ + _test_reg_key( subKey1, subKey2, valName, NULL, testValue ) +#define test_reg_key_ex2( subKey1, subKey2, valName1, valName2, testValue ) \ + _test_reg_key( subKey1, subKey2, valName1, valName2, testValue ) + +static void test_SPI_SETBEEP( void ) /* 2 */ +{ + BOOL rc; + BOOL old_b; + BOOL b; + BOOL curr_val; + + trace("testing SPI_{GET,SET}BEEP\n"); + SetLastError(0xdeadbeef); + rc=SystemParametersInfoA( SPI_GETBEEP, 0, &old_b, 0 ); + if (!test_error_msg(rc,"SPI_{GET,SET}BEEP")) + return; + + curr_val = TRUE; + rc=SystemParametersInfoA( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + test_change_message( SPI_SETBEEP, 0 ); + test_reg_key( SPI_SETBEEP_REGKEY, + SPI_SETBEEP_VALNAME, + curr_val ? "Yes" : "No" ); + rc=SystemParametersInfoA( SPI_GETBEEP, 0, &b, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + eq( b, curr_val, "SPI_{GET,SET}BEEP", "%d" ); + rc=SystemParametersInfoW( SPI_GETBEEP, 0, &b, 0 ); + if (rc!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) + { + ok(rc!=0,"SystemParametersInfoW: rc=%d err=%ld\n",rc,GetLastError()); + eq( b, curr_val, "SystemParametersInfoW", "%d" ); + } + + /* is a message sent for the second change? */ + rc=SystemParametersInfoA( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + test_change_message( SPI_SETBEEP, 0 ); + + curr_val = FALSE; + rc=SystemParametersInfoW( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) + rc=SystemParametersInfoA( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + ok(rc!=0,"SystemParametersInfo: rc=%d err=%ld\n",rc,GetLastError()); + test_change_message( SPI_SETBEEP, 0 ); + test_reg_key( SPI_SETBEEP_REGKEY, + SPI_SETBEEP_VALNAME, + curr_val ? "Yes" : "No" ); + rc=SystemParametersInfoA( SPI_GETBEEP, 0, &b, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + eq( b, curr_val, "SPI_{GET,SET}BEEP", "%d" ); + rc=SystemParametersInfoW( SPI_GETBEEP, 0, &b, 0 ); + if (rc!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) + { + ok(rc!=0,"SystemParametersInfoW: rc=%d err=%ld\n",rc,GetLastError()); + eq( b, curr_val, "SystemParametersInfoW", "%d" ); + } + ok( MessageBeep( MB_OK ), "Return value of MessageBeep when sound is disabled\n" ); + + rc=SystemParametersInfoA( SPI_SETBEEP, old_b, 0, SPIF_UPDATEINIFILE ); + ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError()); +} + +static const char *setmouse_valuenames[3] = { + SPI_SETMOUSE_VALNAME1, + SPI_SETMOUSE_VALNAME2, + SPI_SETMOUSE_VALNAME3 +}; + +/* + * Runs check for one setting of spi_setmouse. + */ +static void run_spi_setmouse_test( int curr_val[], POINT *req_change, POINT *proj_change, + int nchange ) +{ + BOOL rc; + INT mi[3]; + static int aw_turn = 0; + static BOOL w_implemented = 1; + + char buf[20]; + int i; + + aw_turn++; + rc=0; + if ((aw_turn % 2!=0) && (w_implemented)) + { + /* call unicode on odd (non even) calls */ + SetLastError(0xdeadbeef); + rc=SystemParametersInfoW( SPI_SETMOUSE, 0, curr_val, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) + { + w_implemented = 0; + trace("SystemParametersInfoW not supported on this platform\n"); + } + } + + if ((aw_turn % 2==0) || (!w_implemented)) + { + /* call ascii version on even calls or if unicode is not available */ + rc=SystemParametersInfoA( SPI_SETMOUSE, 0, curr_val, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + } + + ok(rc!=0,"SystemParametersInfo: rc=%d err=%ld\n",rc,GetLastError()); + test_change_message( SPI_SETMOUSE, 0 ); + for (i = 0; i < 3; i++) + { + sprintf( buf, "%d", curr_val[i] ); + test_reg_key( SPI_SETMOUSE_REGKEY, setmouse_valuenames[i], buf ); + } + + rc=SystemParametersInfoA( SPI_GETMOUSE, 0, mi, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + for (i = 0; i < 3; i++) + { + ok(mi[i] == curr_val[i], + "incorrect value for %d: %d != %d\n", i, mi[i], curr_val[i]); + } + + if (w_implemented) + { + rc=SystemParametersInfoW( SPI_GETMOUSE, 0, mi, 0 ); + ok(rc!=0,"SystemParametersInfoW: rc=%d err=%ld\n",rc,GetLastError()); + for (i = 0; i < 3; i++) + { + ok(mi[i] == curr_val[i], + "incorrect value for %d: %d != %d\n", i, mi[i], curr_val[i]); + } + } + +#if 0 /* FIXME: this always fails for me - AJ */ + for (i = 0; i < nchange; i++) + { + POINT mv; + mouse_event( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, 0, 0, 0, 0 ); + mouse_event( MOUSEEVENTF_MOVE, req_change[i].x, req_change[i].y, 0, 0 ); + GetCursorPos( &mv ); + ok( proj_change[i].x == mv.x, "Projected dx and real dx comparison. May fail under high load.\n" ); + ok( proj_change[i].y == mv.y, "Projected dy equals real dy. May fail under high load.\n" ); + } +#endif +} + +static void test_SPI_SETMOUSE( void ) /* 4 */ +{ + BOOL rc; + INT old_mi[3]; + + /* win nt default values - 6, 10, 1 */ + INT curr_val[3] = {6, 10, 1}; + + /* requested and projected mouse movements */ + POINT req_change[] = { {6, 6}, { 7, 6}, { 8, 6}, {10, 10}, {11, 10}, {100, 100} }; + POINT proj_change1[] = { {6, 6}, {14, 6}, {16, 6}, {20, 20}, {22, 20}, {200, 200} }; + POINT proj_change2[] = { {6, 6}, {14, 6}, {16, 6}, {20, 20}, {44, 20}, {400, 400} }; + POINT proj_change3[] = { {6, 6}, {14, 6}, {16, 6}, {20, 20}, {22, 20}, {200, 200} }; + POINT proj_change4[] = { {6, 6}, { 7, 6}, { 8, 6}, {10, 10}, {11, 10}, {100, 100} }; + POINT proj_change5[] = { {6, 6}, { 7, 6}, {16, 6}, {20, 20}, {22, 20}, {200, 200} }; + POINT proj_change6[] = { {6, 6}, {28, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} }; + POINT proj_change7[] = { {6, 6}, {14, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} }; + POINT proj_change8[] = { {6, 6}, {28, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} }; + + int nchange = sizeof( req_change ) / sizeof( POINT ); + + trace("testing SPI_{GET,SET}MOUSE\n"); + SetLastError(0xdeadbeef); + rc=SystemParametersInfoA( SPI_GETMOUSE, 0, old_mi, 0 ); + if (!test_error_msg(rc,"SPI_{GET,SET}MOUSE")) + return; + + run_spi_setmouse_test( curr_val, req_change, proj_change1, nchange ); + + /* acceleration change */ + curr_val[2] = 2; + run_spi_setmouse_test( curr_val, req_change, proj_change2, nchange ); + + /* acceleration change */ + curr_val[2] = 3; + run_spi_setmouse_test( curr_val, req_change, proj_change3, nchange ); + + /* acceleration change */ + curr_val[2] = 0; + run_spi_setmouse_test( curr_val, req_change, proj_change4, nchange ); + + /* threshold change */ + curr_val[2] = 1; + curr_val[0] = 7; + run_spi_setmouse_test( curr_val, req_change, proj_change5, nchange ); + + /* threshold change */ + curr_val[2] = 2; + curr_val[0] = 6; + curr_val[1] = 6; + run_spi_setmouse_test( curr_val, req_change, proj_change6, nchange ); + + /* threshold change */ + curr_val[1] = 7; + run_spi_setmouse_test( curr_val, req_change, proj_change7, nchange ); + + /* threshold change */ + curr_val[1] = 5; + run_spi_setmouse_test( curr_val, req_change, proj_change8, nchange ); + + rc=SystemParametersInfoA( SPI_SETMOUSE, 0, old_mi, SPIF_UPDATEINIFILE ); + ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError()); +} + +#if 0 +static void test_setborder(UINT curr_val) +{ + BOOL rc; + UINT border; + INT frame; + char buf[10]; + + rc=SystemParametersInfoA( SPI_SETBORDER, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + test_change_message( SPI_SETBORDER, 1 ); + sprintf( buf, "%d", curr_val ); + test_reg_key( SPI_SETBORDER_REGKEY, SPI_SETBORDER_VALNAME, buf ); + + if (curr_val == 0) + curr_val = 1; + rc=SystemParametersInfoA( SPI_GETBORDER, 0, &border, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + eq( border, curr_val, "SPI_{GET,SET}BORDER", "%d"); + + frame = curr_val + GetSystemMetrics( SM_CXDLGFRAME ); + eq( frame, GetSystemMetrics( SM_CXFRAME ), "SM_CXFRAME", "%d" ); + eq( frame, GetSystemMetrics( SM_CYFRAME ), "SM_CYFRAME", "%d" ); + eq( frame, GetSystemMetrics( SM_CXSIZEFRAME ), "SM_CXSIZEFRAME", "%d" ); + eq( frame, GetSystemMetrics( SM_CYSIZEFRAME ), "SM_CYSIZEFRAME", "%d" ); +} + +static void test_SPI_SETBORDER( void ) /* 6 */ +{ + BOOL rc; + UINT old_border; + + /* These tests hang when XFree86 4.0 for Windows is running (tested on + * WinNT, SP2, Cygwin/XFree 4.1.0. Skip the test when XFree86 is + * running. + */ + if (FindWindowA( NULL, "Cygwin/XFree86" )) + return; + + trace("testing SPI_{GET,SET}BORDER\n"); + SetLastError(0xdeadbeef); + rc=SystemParametersInfoA( SPI_GETBORDER, 0, &old_border, 0 ); + if (!test_error_msg(rc,"SPI_{GET,SET}BORDER")) + return; + + test_setborder(1); + test_setborder(0); + test_setborder(7); + test_setborder(20); + + /* This will restore sane values if the test hang previous run. */ + if ( old_border == 7 || old_border == 20 ) + old_border = -15; + + rc=SystemParametersInfoA( SPI_SETBORDER, old_border, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ); + ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError()); +} +#endif + +static void test_SPI_SETKEYBOARDSPEED( void ) /* 10 */ +{ + BOOL rc; + UINT old_speed; + const UINT vals[]={0,20,31}; + unsigned int i; + + trace("testing SPI_{GET,SET}KEYBOARDSPEED\n"); + SetLastError(0xdeadbeef); + rc=SystemParametersInfoA( SPI_GETKEYBOARDSPEED, 0, &old_speed, 0 ); + if (!test_error_msg(rc,"SPI_{GET,SET}KEYBOARDSPEED")) + return; + + for (i=0;i 32 ? old_spacing-1 : 32); + rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, curr_val, 0, + SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + test_change_message( SPI_ICONHORIZONTALSPACING, 0 ); + /* The registry keys depend on the Windows version and the values too + * => don't test them + */ + + rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, 0, &spacing, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + eq( spacing, curr_val, "ICONHORIZONTALSPACING", "%d"); + eq( GetSystemMetrics( SM_CXICONSPACING ), curr_val, "SM_CXICONSPACING", "%d" ); + + curr_val = 10; + rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, curr_val, 0, + SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + curr_val = 32; /*min value*/ + test_change_message( SPI_ICONHORIZONTALSPACING, 0 ); + + rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, 0, &spacing, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + eq( spacing, curr_val, "ICONHORIZONTALSPACING", "%d" ); + eq( GetSystemMetrics( SM_CXICONSPACING ), curr_val, "SM_CXICONSPACING", "%d" ); + + rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, old_spacing, 0, SPIF_UPDATEINIFILE ); + ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError()); +} + +static void test_SPI_SETSCREENSAVETIMEOUT( void ) /* 14 */ +{ + BOOL rc; + UINT old_timeout; + const UINT vals[]={0,32767}; + unsigned int i; + + trace("testing SPI_{GET,SET}SCREENSAVETIMEOUT\n"); + SetLastError(0xdeadbeef); + rc=SystemParametersInfoA( SPI_GETSCREENSAVETIMEOUT, 0, &old_timeout, 0 ); + if (!test_error_msg(rc,"SPI_{GET,SET}SCREENSAVETIMEOUT")) + return; + + for (i=0;i don't test them + */ + + rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, 0, &spacing, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + eq( spacing, curr_val, "ICONVERTICALSPACING", "%d" ); + eq( GetSystemMetrics( SM_CYICONSPACING ), curr_val, "SM_CYICONSPACING", "%d" ); + + curr_val = 10; + rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, curr_val, 0, + SPIF_UPDATEINIFILE | SPIF_SENDCHANGE); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + curr_val = 32; /*min value*/ + test_change_message( SPI_ICONVERTICALSPACING, 0 ); + + rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, 0, &spacing, 0 ); + ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError()); + eq( spacing, curr_val, "ICONVERTICALSPACING", "%d" ); + eq( GetSystemMetrics( SM_CYICONSPACING ), curr_val, "SM_CYICONSPACING", "%d" ); + + rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, old_spacing, 0, + SPIF_UPDATEINIFILE ); + ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError()); +} + +static void test_SPI_SETICONTITLEWRAP( void ) /* 26 */ +{ + BOOL rc; + BOOL old_b; + const UINT vals[]={TRUE,FALSE}; + unsigned int i; + + /* These tests hang when XFree86 4.0 for Windows is running (tested on + * WinNT, SP2, Cygwin/XFree 4.1.0. Skip the test when XFree86 is + * running. + */ + if (FindWindowA( NULL, "Cygwin/XFree86" )) + return; + + trace("testing SPI_{GET,SET}ICONTITLEWRAP\n"); + SetLastError(0xdeadbeef); + rc=SystemParametersInfoA( SPI_GETICONTITLEWRAP, 0, &old_b, 0 ); + if (!test_error_msg(rc,"SPI_{GET,SET}ICONTITLEWRAP")) + return; + + for (i=0;i= 3 && strcmp(argv[2],"strict")==0); + trace("strict=%d\n",strict); + + change_counter = 0; + change_last_param = 0; + + wc.lpszClassName = "SysParamsTestClass"; + wc.lpfnWndProc = SysParamsTestWndProc; + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + wc.hInstance = hInstance; + wc.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION ); + wc.hCursor = LoadCursorA( 0, (LPSTR)IDC_ARROW ); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1 ); + wc.lpszMenuName = 0; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + RegisterClassA( &wc ); + + ghTestWnd = CreateWindowA( "SysParamsTestClass", "Test System Parameters Application", + WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, hInstance, NULL ); + + hThread = CreateThread( NULL, 0, SysParamsThreadFunc, 0, 0, &dwThreadId ); + assert( hThread ); + CloseHandle( hThread ); + + while( GetMessageA( &msg, 0, 0, 0 )) { + TranslateMessage( &msg ); + DispatchMessageA( &msg ); + } +} diff --git a/reactos/regtests/winetests/user32/testlist.c b/reactos/regtests/winetests/user32/testlist.c new file mode 100644 index 00000000000..389eab71b6b --- /dev/null +++ b/reactos/regtests/winetests/user32/testlist.c @@ -0,0 +1,55 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +/* stdarg.h is needed for Winelib */ +#include +#include +#include +#include "windef.h" +#include "winbase.h" + +struct test +{ + const char *name; + void (*func)(void); +}; + +extern void func_class(void); +extern void func_clipboard(void); +extern void func_dce(void); +extern void func_dde(void); +extern void func_dialog(void); +extern void func_edit(void); +extern void func_input(void); +extern void func_listbox(void); +extern void func_menu(void); +extern void func_msg(void); +extern void func_resource(void); +extern void func_sysparams(void); +extern void func_text(void); +extern void func_win(void); +extern void func_winstation(void); +extern void func_wsprintf(void); + +const struct test winetest_testlist[] = +{ + { "class", func_class }, + { "clipboard", func_clipboard }, + { "dce", func_dce }, + { "dde", func_dde }, + { "dialog", func_dialog }, + { "edit", func_edit }, + { "input", func_input }, + { "listbox", func_listbox }, + { "menu", func_menu }, + { "msg", func_msg }, + { "resource", func_resource }, + { "sysparams", func_sysparams }, + { "text", func_text }, + { "win", func_win }, + { "winstation", func_winstation }, + { "wsprintf", func_wsprintf }, + { 0, 0 } +}; + +#define WINETEST_WANT_MAIN +#include "wine/test.h" diff --git a/reactos/regtests/winetests/user32/text.c b/reactos/regtests/winetests/user32/text.c new file mode 100755 index 00000000000..b2ba93ca3aa --- /dev/null +++ b/reactos/regtests/winetests/user32/text.c @@ -0,0 +1,117 @@ +/* + * DrawText tests + * + * Copyright (c) 2004 Zach Gorman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include + +#include "wine/test.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winerror.h" + + +static void test_DrawTextCalcRect(void) +{ + HWND hwnd; + HDC hdc; + HFONT hFont, hOldFont; + LOGFONTA lf; + const char text[] = "Example text for testing DrawText in " + "MM_HIENGLISH mode"; + INT textlen,textheight; + RECT rect = { 0, 0, 100, 0 }; + BOOL ret; + + /* Initialization */ + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, + 0, 0, 200, 200, 0, 0, 0, NULL); + ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError()); + hdc = GetDC(hwnd); + ok(hdc != 0, "GetDC error %lu\n", GetLastError()); + trace("hdc %p\n", hdc); + textlen = lstrlenA(text); + + /* LOGFONT initialization */ + memset(&lf, 0, sizeof(lf)); + lf.lfCharSet = ANSI_CHARSET; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfWeight = FW_DONTCARE; + lf.lfHeight = 0; /* mapping mode dependent */ + lf.lfQuality = DEFAULT_QUALITY; + lstrcpyA(lf.lfFaceName, "Arial"); + + /* DrawText in MM_HIENGLISH with DT_CALCRECT */ + SetMapMode(hdc, MM_HIENGLISH); + lf.lfHeight = 100 * 9 / 72; /* 9 point */ + hFont = CreateFontIndirectA(&lf); + ok(hFont != 0, "CreateFontIndirectA error %lu\n", + GetLastError()); + hOldFont = SelectObject(hdc, hFont); + + textheight = DrawTextA(hdc, text, textlen, &rect, DT_CALCRECT | + DT_EXTERNALLEADING | DT_WORDBREAK | DT_NOCLIP | DT_LEFT | + DT_NOPREFIX); + ok( textheight, "DrawTextA error %lu\n", GetLastError()); + + trace("MM_HIENGLISH rect.bottom %ld\n", rect.bottom); + todo_wine ok(rect.bottom < 0, "In MM_HIENGLISH, DrawText with " + "DT_CALCRECT should return a negative rectangle bottom. " + "(bot=%ld)\n", rect.bottom); + + SelectObject(hdc, hOldFont); + ret = DeleteObject(hFont); + ok( ret, "DeleteObject error %lu\n", GetLastError()); + + + /* DrawText in MM_TEXT with DT_CALCRECT */ + SetMapMode(hdc, MM_TEXT); + lf.lfHeight = -MulDiv(9, GetDeviceCaps(hdc, + LOGPIXELSY), 72); /* 9 point */ + hFont = CreateFontIndirectA(&lf); + ok(hFont != 0, "CreateFontIndirectA error %lu\n", + GetLastError()); + hOldFont = SelectObject(hdc, hFont); + + textheight = DrawTextA(hdc, text, textlen, &rect, DT_CALCRECT | + DT_EXTERNALLEADING | DT_WORDBREAK | DT_NOCLIP | DT_LEFT | + DT_NOPREFIX); + ok( textheight, "DrawTextA error %lu\n", GetLastError()); + + trace("MM_TEXT rect.bottom %ld\n", rect.bottom); + ok(rect.bottom > 0, "In MM_TEXT, DrawText with DT_CALCRECT " + "should return a positive rectangle bottom. (bot=%ld)\n", + rect.bottom); + + SelectObject(hdc, hOldFont); + ret = DeleteObject(hFont); + ok( ret, "DeleteObject error %lu\n", GetLastError()); + + /* Clean up */ + ret = ReleaseDC(hwnd, hdc); + ok( ret, "ReleaseDC error %lu\n", GetLastError()); + ret = DestroyWindow(hwnd); + ok( ret, "DestroyWindow error %lu\n", GetLastError()); +} + +START_TEST(text) +{ + test_DrawTextCalcRect(); +} diff --git a/reactos/regtests/winetests/user32/user32_test.xml b/reactos/regtests/winetests/user32/user32_test.xml new file mode 100644 index 00000000000..5d4963e8f40 --- /dev/null +++ b/reactos/regtests/winetests/user32/user32_test.xml @@ -0,0 +1,25 @@ + + . + + ntdll + user32 + gdi32 + class.c + clipboard.c + dce.c + dde.c + dialog.c + edit.c + input.c + listbox.c + menu.c + msg.c + resource.c + sysparams.c + text.c + win.c + winstation.c + wsprintf.c + testlist.c + resource.rc + diff --git a/reactos/regtests/winetests/user32/win.c b/reactos/regtests/winetests/user32/win.c new file mode 100644 index 00000000000..54d0bf615f1 --- /dev/null +++ b/reactos/regtests/winetests/user32/win.c @@ -0,0 +1,3412 @@ +/* + * Unit tests for window handling + * + * Copyright 2002 Bill Medland + * Copyright 2002 Alexandre Julliard + * Copyright 2003 Dmitry Timoshkov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* To get ICON_SMALL2 with the MSVC headers */ +#define _WIN32_WINNT 0x0501 + +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#include "wine/test.h" + +#ifndef SPI_GETDESKWALLPAPER +#define SPI_GETDESKWALLPAPER 0x0073 +#endif + +#define LONG_PTR INT_PTR +#define ULONG_PTR UINT_PTR + +void dump_region(HRGN hrgn); + +static HWND (WINAPI *pGetAncestor)(HWND,UINT); +static BOOL (WINAPI *pGetWindowInfo)(HWND,WINDOWINFO*); + +static BOOL test_lbuttondown_flag; +static HWND hwndMessage; +static HWND hwndMain, hwndMain2; +static HHOOK hhook; + +static const char* szAWRClass = "Winsize"; +static HMENU hmenu; + +#define COUNTOF(arr) (sizeof(arr)/sizeof(arr[0])) + +/* check the values returned by the various parent/owner functions on a given window */ +static void check_parents( HWND hwnd, HWND ga_parent, HWND gwl_parent, HWND get_parent, + HWND gw_owner, HWND ga_root, HWND ga_root_owner ) +{ + HWND res; + + if (pGetAncestor) + { + res = pGetAncestor( hwnd, GA_PARENT ); + ok( res == ga_parent, "Wrong result for GA_PARENT %p expected %p\n", res, ga_parent ); + } + res = (HWND)GetWindowLongA( hwnd, GWL_HWNDPARENT ); + ok( res == gwl_parent, "Wrong result for GWL_HWNDPARENT %p expected %p\n", res, gwl_parent ); + res = GetParent( hwnd ); + ok( res == get_parent, "Wrong result for GetParent %p expected %p\n", res, get_parent ); + res = GetWindow( hwnd, GW_OWNER ); + ok( res == gw_owner, "Wrong result for GW_OWNER %p expected %p\n", res, gw_owner ); + if (pGetAncestor) + { + res = pGetAncestor( hwnd, GA_ROOT ); + ok( res == ga_root, "Wrong result for GA_ROOT %p expected %p\n", res, ga_root ); + res = pGetAncestor( hwnd, GA_ROOTOWNER ); + ok( res == ga_root_owner, "Wrong result for GA_ROOTOWNER %p expected %p\n", res, ga_root_owner ); + } +} + + +static HWND create_tool_window( LONG style, HWND parent ) +{ + HWND ret = CreateWindowExA(0, "ToolWindowClass", "Tool window 1", style, + 0, 0, 100, 100, parent, 0, 0, NULL ); + ok( ret != 0, "Creation failed\n" ); + return ret; +} + +/* test parent and owner values for various combinations */ +static void test_parent_owner(void) +{ + LONG style; + HWND test, owner, ret; + HWND desktop = GetDesktopWindow(); + HWND child = create_tool_window( WS_CHILD, hwndMain ); + + trace( "main window %p main2 %p desktop %p child %p\n", hwndMain, hwndMain2, desktop, child ); + + /* child without parent, should fail */ + test = CreateWindowExA(0, "ToolWindowClass", "Tool window 1", + WS_CHILD, 0, 0, 100, 100, 0, 0, 0, NULL ); + ok( !test, "WS_CHILD without parent created\n" ); + + /* desktop window */ + check_parents( desktop, 0, 0, 0, 0, 0, 0 ); + style = GetWindowLongA( desktop, GWL_STYLE ); + ok( !SetWindowLongA( desktop, GWL_STYLE, WS_POPUP ), "Set GWL_STYLE on desktop succeeded\n" ); + ok( !SetWindowLongA( desktop, GWL_STYLE, 0 ), "Set GWL_STYLE on desktop succeeded\n" ); + ok( GetWindowLongA( desktop, GWL_STYLE ) == style, "Desktop style changed\n" ); + + /* normal child window */ + test = create_tool_window( WS_CHILD, hwndMain ); + trace( "created child %p\n", test ); + check_parents( test, hwndMain, hwndMain, hwndMain, 0, hwndMain, hwndMain ); + SetWindowLongA( test, GWL_STYLE, 0 ); + check_parents( test, hwndMain, hwndMain, 0, 0, hwndMain, test ); + SetWindowLongA( test, GWL_STYLE, WS_POPUP ); + check_parents( test, hwndMain, hwndMain, 0, 0, hwndMain, test ); + SetWindowLongA( test, GWL_STYLE, WS_POPUP|WS_CHILD ); + check_parents( test, hwndMain, hwndMain, 0, 0, hwndMain, test ); + SetWindowLongA( test, GWL_STYLE, WS_CHILD ); + DestroyWindow( test ); + + /* normal child window with WS_MAXIMIZE */ + test = create_tool_window( WS_CHILD | WS_MAXIMIZE, hwndMain ); + DestroyWindow( test ); + + /* normal child window with WS_THICKFRAME */ + test = create_tool_window( WS_CHILD | WS_THICKFRAME, hwndMain ); + DestroyWindow( test ); + + /* popup window with WS_THICKFRAME */ + test = create_tool_window( WS_POPUP | WS_THICKFRAME, hwndMain ); + DestroyWindow( test ); + + /* child of desktop */ + test = create_tool_window( WS_CHILD, desktop ); + trace( "created child of desktop %p\n", test ); + check_parents( test, desktop, 0, desktop, 0, test, desktop ); + SetWindowLongA( test, GWL_STYLE, WS_POPUP ); + check_parents( test, desktop, 0, 0, 0, test, test ); + SetWindowLongA( test, GWL_STYLE, 0 ); + check_parents( test, desktop, 0, 0, 0, test, test ); + DestroyWindow( test ); + + /* child of desktop with WS_MAXIMIZE */ + test = create_tool_window( WS_CHILD | WS_MAXIMIZE, desktop ); + DestroyWindow( test ); + + /* child of desktop with WS_MINIMIZE */ + test = create_tool_window( WS_CHILD | WS_MINIMIZE, desktop ); + DestroyWindow( test ); + + /* child of child */ + test = create_tool_window( WS_CHILD, child ); + trace( "created child of child %p\n", test ); + check_parents( test, child, child, child, 0, hwndMain, hwndMain ); + SetWindowLongA( test, GWL_STYLE, 0 ); + check_parents( test, child, child, 0, 0, hwndMain, test ); + SetWindowLongA( test, GWL_STYLE, WS_POPUP ); + check_parents( test, child, child, 0, 0, hwndMain, test ); + DestroyWindow( test ); + + /* child of child with WS_MAXIMIZE */ + test = create_tool_window( WS_CHILD | WS_MAXIMIZE, child ); + DestroyWindow( test ); + + /* child of child with WS_MINIMIZE */ + test = create_tool_window( WS_CHILD | WS_MINIMIZE, child ); + DestroyWindow( test ); + + /* not owned top-level window */ + test = create_tool_window( 0, 0 ); + trace( "created top-level %p\n", test ); + check_parents( test, desktop, 0, 0, 0, test, test ); + SetWindowLongA( test, GWL_STYLE, WS_POPUP ); + check_parents( test, desktop, 0, 0, 0, test, test ); + SetWindowLongA( test, GWL_STYLE, WS_CHILD ); + check_parents( test, desktop, 0, desktop, 0, test, desktop ); + DestroyWindow( test ); + + /* not owned top-level window with WS_MAXIMIZE */ + test = create_tool_window( WS_MAXIMIZE, 0 ); + DestroyWindow( test ); + + /* owned top-level window */ + test = create_tool_window( 0, hwndMain ); + trace( "created owned top-level %p\n", test ); + check_parents( test, desktop, hwndMain, 0, hwndMain, test, test ); + SetWindowLongA( test, GWL_STYLE, WS_POPUP ); + check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain ); + SetWindowLongA( test, GWL_STYLE, WS_CHILD ); + check_parents( test, desktop, hwndMain, desktop, hwndMain, test, desktop ); + DestroyWindow( test ); + + /* owned top-level window with WS_MAXIMIZE */ + test = create_tool_window( WS_MAXIMIZE, hwndMain ); + DestroyWindow( test ); + + /* not owned popup */ + test = create_tool_window( WS_POPUP, 0 ); + trace( "created popup %p\n", test ); + check_parents( test, desktop, 0, 0, 0, test, test ); + SetWindowLongA( test, GWL_STYLE, WS_CHILD ); + check_parents( test, desktop, 0, desktop, 0, test, desktop ); + SetWindowLongA( test, GWL_STYLE, 0 ); + check_parents( test, desktop, 0, 0, 0, test, test ); + DestroyWindow( test ); + + /* not owned popup with WS_MAXIMIZE */ + test = create_tool_window( WS_POPUP | WS_MAXIMIZE, 0 ); + DestroyWindow( test ); + + /* owned popup */ + test = create_tool_window( WS_POPUP, hwndMain ); + trace( "created owned popup %p\n", test ); + check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain ); + SetWindowLongA( test, GWL_STYLE, WS_CHILD ); + check_parents( test, desktop, hwndMain, desktop, hwndMain, test, desktop ); + SetWindowLongA( test, GWL_STYLE, 0 ); + check_parents( test, desktop, hwndMain, 0, hwndMain, test, test ); + DestroyWindow( test ); + + /* owned popup with WS_MAXIMIZE */ + test = create_tool_window( WS_POPUP | WS_MAXIMIZE, hwndMain ); + DestroyWindow( test ); + + /* top-level window owned by child (same as owned by top-level) */ + test = create_tool_window( 0, child ); + trace( "created top-level owned by child %p\n", test ); + check_parents( test, desktop, hwndMain, 0, hwndMain, test, test ); + DestroyWindow( test ); + + /* top-level window owned by child (same as owned by top-level) with WS_MAXIMIZE */ + test = create_tool_window( WS_MAXIMIZE, child ); + DestroyWindow( test ); + + /* popup owned by desktop (same as not owned) */ + test = create_tool_window( WS_POPUP, desktop ); + trace( "created popup owned by desktop %p\n", test ); + check_parents( test, desktop, 0, 0, 0, test, test ); + DestroyWindow( test ); + + /* popup owned by desktop (same as not owned) with WS_MAXIMIZE */ + test = create_tool_window( WS_POPUP | WS_MAXIMIZE, desktop ); + DestroyWindow( test ); + + /* popup owned by child (same as owned by top-level) */ + test = create_tool_window( WS_POPUP, child ); + trace( "created popup owned by child %p\n", test ); + check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain ); + DestroyWindow( test ); + + /* popup owned by child (same as owned by top-level) with WS_MAXIMIZE */ + test = create_tool_window( WS_POPUP | WS_MAXIMIZE, child ); + DestroyWindow( test ); + + /* not owned popup with WS_CHILD (same as WS_POPUP only) */ + test = create_tool_window( WS_POPUP | WS_CHILD, 0 ); + trace( "created WS_CHILD popup %p\n", test ); + check_parents( test, desktop, 0, 0, 0, test, test ); + DestroyWindow( test ); + + /* not owned popup with WS_CHILD | WS_MAXIMIZE (same as WS_POPUP only) */ + test = create_tool_window( WS_POPUP | WS_CHILD | WS_MAXIMIZE, 0 ); + DestroyWindow( test ); + + /* owned popup with WS_CHILD (same as WS_POPUP only) */ + test = create_tool_window( WS_POPUP | WS_CHILD, hwndMain ); + trace( "created owned WS_CHILD popup %p\n", test ); + check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain ); + DestroyWindow( test ); + + /* owned popup with WS_CHILD (same as WS_POPUP only) with WS_MAXIMIZE */ + test = create_tool_window( WS_POPUP | WS_CHILD | WS_MAXIMIZE, hwndMain ); + DestroyWindow( test ); + + /******************** parent changes *************************/ + trace( "testing parent changes\n" ); + + /* desktop window */ + check_parents( desktop, 0, 0, 0, 0, 0, 0 ); +#if 0 /* this test succeeds on NT but crashes on win9x systems */ + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)hwndMain2 ); + ok( !ret, "Set GWL_HWNDPARENT succeeded on desktop\n" ); + check_parents( desktop, 0, 0, 0, 0, 0, 0 ); + ok( !SetParent( desktop, hwndMain ), "SetParent succeeded on desktop\n" ); + check_parents( desktop, 0, 0, 0, 0, 0, 0 ); +#endif + /* normal child window */ + test = create_tool_window( WS_CHILD, hwndMain ); + trace( "created child %p\n", test ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)hwndMain2 ); + ok( ret == hwndMain, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain ); + check_parents( test, hwndMain2, hwndMain2, hwndMain2, 0, hwndMain2, hwndMain2 ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)child ); + ok( ret == hwndMain2, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain2 ); + check_parents( test, child, child, child, 0, hwndMain, hwndMain ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)desktop ); + ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child ); + check_parents( test, desktop, 0, desktop, 0, test, desktop ); + + /* window is now child of desktop so GWL_HWNDPARENT changes owner from now on */ + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)child ); + ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret ); + check_parents( test, desktop, child, desktop, child, test, desktop ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, 0 ); + ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child ); + check_parents( test, desktop, 0, desktop, 0, test, desktop ); + DestroyWindow( test ); + + /* not owned top-level window */ + test = create_tool_window( 0, 0 ); + trace( "created top-level %p\n", test ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)hwndMain2 ); + ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret ); + check_parents( test, desktop, hwndMain2, 0, hwndMain2, test, test ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)child ); + ok( ret == hwndMain2, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain2 ); + check_parents( test, desktop, child, 0, child, test, test ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, 0 ); + ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child ); + check_parents( test, desktop, 0, 0, 0, test, test ); + DestroyWindow( test ); + + /* not owned popup */ + test = create_tool_window( WS_POPUP, 0 ); + trace( "created popup %p\n", test ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)hwndMain2 ); + ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret ); + check_parents( test, desktop, hwndMain2, hwndMain2, hwndMain2, test, hwndMain2 ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)child ); + ok( ret == hwndMain2, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain2 ); + check_parents( test, desktop, child, child, child, test, hwndMain ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, 0 ); + ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child ); + check_parents( test, desktop, 0, 0, 0, test, test ); + DestroyWindow( test ); + + /* normal child window */ + test = create_tool_window( WS_CHILD, hwndMain ); + trace( "created child %p\n", test ); + + ret = SetParent( test, desktop ); + ok( ret == hwndMain, "SetParent return value %p expected %p\n", ret, hwndMain ); + check_parents( test, desktop, 0, desktop, 0, test, desktop ); + + ret = SetParent( test, child ); + ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop ); + check_parents( test, child, child, child, 0, hwndMain, hwndMain ); + + ret = SetParent( test, hwndMain2 ); + ok( ret == child, "SetParent return value %p expected %p\n", ret, child ); + check_parents( test, hwndMain2, hwndMain2, hwndMain2, 0, hwndMain2, hwndMain2 ); + DestroyWindow( test ); + + /* not owned top-level window */ + test = create_tool_window( 0, 0 ); + trace( "created top-level %p\n", test ); + + ret = SetParent( test, child ); + ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop ); + check_parents( test, child, child, 0, 0, hwndMain, test ); + DestroyWindow( test ); + + /* owned popup */ + test = create_tool_window( WS_POPUP, hwndMain2 ); + trace( "created owned popup %p\n", test ); + + ret = SetParent( test, child ); + ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop ); + check_parents( test, child, child, hwndMain2, hwndMain2, hwndMain, hwndMain2 ); + + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (ULONG_PTR)hwndMain ); + ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child ); + check_parents( test, hwndMain, hwndMain, hwndMain2, hwndMain2, hwndMain, hwndMain2 ); + DestroyWindow( test ); + + /**************** test owner destruction *******************/ + + /* owned child popup */ + owner = create_tool_window( 0, 0 ); + test = create_tool_window( WS_POPUP, owner ); + trace( "created owner %p and popup %p\n", owner, test ); + ret = SetParent( test, child ); + ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop ); + check_parents( test, child, child, owner, owner, hwndMain, owner ); + /* window is now child of 'child' but owned by 'owner' */ + DestroyWindow( owner ); + ok( IsWindow(test), "Window %p destroyed by owner destruction\n", test ); + /* Win98 doesn't pass this test. It doesn't allow a destroyed owner, + * while Win95, Win2k, WinXP do. + */ + /*check_parents( test, child, child, owner, owner, hwndMain, owner );*/ + ok( !IsWindow(owner), "Owner %p not destroyed\n", owner ); + DestroyWindow(test); + + /* owned top-level popup */ + owner = create_tool_window( 0, 0 ); + test = create_tool_window( WS_POPUP, owner ); + trace( "created owner %p and popup %p\n", owner, test ); + check_parents( test, desktop, owner, owner, owner, test, owner ); + DestroyWindow( owner ); + ok( !IsWindow(test), "Window %p not destroyed by owner destruction\n", test ); + + /* top-level popup owned by child */ + owner = create_tool_window( WS_CHILD, hwndMain2 ); + test = create_tool_window( WS_POPUP, 0 ); + trace( "created owner %p and popup %p\n", owner, test ); + ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (ULONG_PTR)owner ); + ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret ); + check_parents( test, desktop, owner, owner, owner, test, hwndMain2 ); + DestroyWindow( owner ); + ok( IsWindow(test), "Window %p destroyed by owner destruction\n", test ); + ok( !IsWindow(owner), "Owner %p not destroyed\n", owner ); + /* Win98 doesn't pass this test. It doesn't allow a destroyed owner, + * while Win95, Win2k, WinXP do. + */ + /*check_parents( test, desktop, owner, owner, owner, test, owner );*/ + DestroyWindow(test); + + /* final cleanup */ + DestroyWindow(child); +} + + +static LRESULT WINAPI main_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_GETMINMAXINFO: + { + MINMAXINFO* minmax = (MINMAXINFO *)lparam; + + trace("hwnd %p, WM_GETMINMAXINFO, %08x, %08lx\n", hwnd, wparam, lparam); + trace("ptReserved (%ld,%ld), ptMaxSize (%ld,%ld), ptMaxPosition (%ld,%ld)\n" + " ptMinTrackSize (%ld,%ld), ptMaxTrackSize (%ld,%ld)\n", + minmax->ptReserved.x, minmax->ptReserved.y, + minmax->ptMaxSize.x, minmax->ptMaxSize.y, + minmax->ptMaxPosition.x, minmax->ptMaxPosition.y, + minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y, + minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y); + SetWindowLongA(hwnd, GWL_USERDATA, 0x20031021); + break; + } + case WM_WINDOWPOSCHANGING: + { + BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0; + WINDOWPOS *winpos = (WINDOWPOS *)lparam; + trace("main: WM_WINDOWPOSCHANGING\n"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + if (!(winpos->flags & SWP_NOMOVE)) + { + ok(winpos->x >= -32768 && winpos->x <= 32767, "bad winpos->x %d\n", winpos->x); + ok(winpos->y >= -32768 && winpos->y <= 32767, "bad winpos->y %d\n", winpos->y); + } + /* Win9x does not fixup cx/xy for WM_WINDOWPOSCHANGING */ + if (!(winpos->flags & SWP_NOSIZE) && !is_win9x) + { + ok(winpos->cx >= 0 && winpos->cx <= 32767, "bad winpos->cx %d\n", winpos->cx); + ok(winpos->cy >= 0 && winpos->cy <= 32767, "bad winpos->cy %d\n", winpos->cy); + } + break; + } + case WM_WINDOWPOSCHANGED: + { + RECT rc1, rc2; + WINDOWPOS *winpos = (WINDOWPOS *)lparam; + trace("main: WM_WINDOWPOSCHANGED\n"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + ok(winpos->x >= -32768 && winpos->x <= 32767, "bad winpos->x %d\n", winpos->x); + ok(winpos->y >= -32768 && winpos->y <= 32767, "bad winpos->y %d\n", winpos->y); + + ok(winpos->cx >= 0 && winpos->cx <= 32767, "bad winpos->cx %d\n", winpos->cx); + ok(winpos->cy >= 0 && winpos->cy <= 32767, "bad winpos->cy %d\n", winpos->cy); + + GetWindowRect(hwnd, &rc1); + trace("window: (%ld,%ld)-(%ld,%ld)\n", rc1.left, rc1.top, rc1.right, rc1.bottom); + SetRect(&rc2, winpos->x, winpos->y, winpos->x + winpos->cx, winpos->y + winpos->cy); + /* note: winpos coordinates are relative to parent */ + MapWindowPoints(GetParent(hwnd), 0, (LPPOINT)&rc2, 2); + trace("pos: (%ld,%ld)-(%ld,%ld)\n", rc2.left, rc2.top, rc2.right, rc2.bottom); +#if 0 /* Uncomment this once the test succeeds in all cases */ + ok(EqualRect(&rc1, &rc2), "rects do not match\n"); +#endif + + GetClientRect(hwnd, &rc2); + DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc1); + MapWindowPoints(0, hwnd, (LPPOINT)&rc1, 2); + ok(EqualRect(&rc1, &rc2), "rects do not match (%ld,%ld-%ld,%ld) / (%ld,%ld-%ld,%ld)\n", + rc1.left, rc1.top, rc1.right, rc1.bottom, rc2.left, rc2.top, rc2.right, rc2.bottom ); + break; + } + case WM_NCCREATE: + { + BOOL got_getminmaxinfo = GetWindowLongA(hwnd, GWL_USERDATA) == 0x20031021; + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + + trace("WM_NCCREATE: hwnd %p, parent %p, style %08lx\n", hwnd, cs->hwndParent, cs->style); + if (got_getminmaxinfo) + trace("%p got WM_GETMINMAXINFO\n", hwnd); + + if ((cs->style & WS_THICKFRAME) || !(cs->style & (WS_POPUP | WS_CHILD))) + ok(got_getminmaxinfo, "main: WM_GETMINMAXINFO should have been received before WM_NCCREATE\n"); + else + ok(!got_getminmaxinfo, "main: WM_GETMINMAXINFO should NOT have been received before WM_NCCREATE\n"); + break; + } + case WM_COMMAND: + if (test_lbuttondown_flag) + ShowWindow((HWND)wparam, SW_SHOW); + break; + } + + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static LRESULT WINAPI tool_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_GETMINMAXINFO: + { + MINMAXINFO* minmax = (MINMAXINFO *)lparam; + + trace("hwnd %p, WM_GETMINMAXINFO, %08x, %08lx\n", hwnd, wparam, lparam); + trace("ptReserved (%ld,%ld), ptMaxSize (%ld,%ld), ptMaxPosition (%ld,%ld)\n" + " ptMinTrackSize (%ld,%ld), ptMaxTrackSize (%ld,%ld)\n", + minmax->ptReserved.x, minmax->ptReserved.y, + minmax->ptMaxSize.x, minmax->ptMaxSize.y, + minmax->ptMaxPosition.x, minmax->ptMaxPosition.y, + minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y, + minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y); + SetWindowLongA(hwnd, GWL_USERDATA, 0x20031021); + break; + } + case WM_NCCREATE: + { + BOOL got_getminmaxinfo = GetWindowLongA(hwnd, GWL_USERDATA) == 0x20031021; + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + + trace("WM_NCCREATE: hwnd %p, parent %p, style %08lx\n", hwnd, cs->hwndParent, cs->style); + if (got_getminmaxinfo) + trace("%p got WM_GETMINMAXINFO\n", hwnd); + + if ((cs->style & WS_THICKFRAME) || !(cs->style & (WS_POPUP | WS_CHILD))) + ok(got_getminmaxinfo, "tool: WM_GETMINMAXINFO should have been received before WM_NCCREATE\n"); + else + ok(!got_getminmaxinfo, "tool: WM_GETMINMAXINFO should NOT have been received before WM_NCCREATE\n"); + break; + } + } + + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static BOOL RegisterWindowClasses(void) +{ + WNDCLASSA cls; + + cls.style = CS_DBLCLKS; + cls.lpfnWndProc = main_window_procA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "MainWindowClass"; + + if(!RegisterClassA(&cls)) return FALSE; + + cls.style = 0; + cls.lpfnWndProc = tool_window_procA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "ToolWindowClass"; + + if(!RegisterClassA(&cls)) return FALSE; + + return TRUE; +} + +static void verify_window_info(HWND hwnd, const WINDOWINFO *info, BOOL test_borders) +{ + RECT rcWindow, rcClient; + UINT border; + DWORD status; + + ok(IsWindow(hwnd), "bad window handle\n"); + + GetWindowRect(hwnd, &rcWindow); + ok(EqualRect(&rcWindow, &info->rcWindow), "wrong rcWindow\n"); + + GetClientRect(hwnd, &rcClient); + /* translate to screen coordinates */ + MapWindowPoints(hwnd, 0, (LPPOINT)&rcClient, 2); + ok(EqualRect(&rcClient, &info->rcClient), "wrong rcClient\n"); + + ok(info->dwStyle == (DWORD)GetWindowLongA(hwnd, GWL_STYLE), + "wrong dwStyle: %08lx != %08lx\n", info->dwStyle, GetWindowLongA(hwnd, GWL_STYLE)); + ok(info->dwExStyle == (DWORD)GetWindowLongA(hwnd, GWL_EXSTYLE), + "wrong dwExStyle: %08lx != %08lx\n", info->dwExStyle, GetWindowLongA(hwnd, GWL_EXSTYLE)); + status = (GetActiveWindow() == hwnd) ? WS_ACTIVECAPTION : 0; + ok(info->dwWindowStatus == status, "wrong dwWindowStatus: %04lx != %04lx\n", + info->dwWindowStatus, status); + + if (test_borders && !IsRectEmpty(&rcWindow)) + { + trace("rcWindow: %ld,%ld - %ld,%ld\n", rcWindow.left, rcWindow.top, rcWindow.right, rcWindow.bottom); + trace("rcClient: %ld,%ld - %ld,%ld\n", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + + ok(info->cxWindowBorders == (unsigned)(rcClient.left - rcWindow.left), + "wrong cxWindowBorders %d != %ld\n", info->cxWindowBorders, rcClient.left - rcWindow.left); + border = min(rcWindow.bottom - rcClient.bottom, rcClient.top - rcWindow.top); + ok(info->cyWindowBorders == border, + "wrong cyWindowBorders %d != %d\n", info->cyWindowBorders, border); + } + + ok(info->atomWindowType == GetClassLongA(hwnd, GCW_ATOM), "wrong atomWindowType\n"); + ok(info->wCreatorVersion == 0x0400, "wrong wCreatorVersion %04x\n", info->wCreatorVersion); +} + +static void FixedAdjustWindowRectEx(RECT* rc, LONG style, BOOL menu, LONG exstyle) +{ + AdjustWindowRectEx(rc, style, menu, exstyle); + /* AdjustWindowRectEx does not include scroll bars */ + if (style & WS_VSCROLL) + { + if(exstyle & WS_EX_LEFTSCROLLBAR) + rc->left -= GetSystemMetrics(SM_CXVSCROLL); + else + rc->right += GetSystemMetrics(SM_CXVSCROLL); + } + if (style & WS_HSCROLL) + rc->bottom += GetSystemMetrics(SM_CYHSCROLL); +} + +static void test_nonclient_area(HWND hwnd) +{ + DWORD style, exstyle; + RECT rc_window, rc_client, rc; + BOOL menu; + BOOL is_win9x = GetWindowLongW(hwnd, GWL_WNDPROC) == 0; + + style = GetWindowLongA(hwnd, GWL_STYLE); + exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE); + menu = !(style & WS_CHILD) && GetMenu(hwnd) != 0; + + GetWindowRect(hwnd, &rc_window); + trace("window: (%ld,%ld)-(%ld,%ld)\n", rc_window.left, rc_window.top, rc_window.right, rc_window.bottom); + GetClientRect(hwnd, &rc_client); + trace("client: (%ld,%ld)-(%ld,%ld)\n", rc_client.left, rc_client.top, rc_client.right, rc_client.bottom); + + /* avoid some cases when things go wrong */ + if (IsRectEmpty(&rc_window) || IsRectEmpty(&rc_client) || + rc_window.right > 32768 || rc_window.bottom > 32768) return; + + CopyRect(&rc, &rc_client); + MapWindowPoints(hwnd, 0, (LPPOINT)&rc, 2); + FixedAdjustWindowRectEx(&rc, style, menu, exstyle); + + trace("calc window: (%ld,%ld)-(%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom); + ok(EqualRect(&rc, &rc_window), "window rect does not match: style:exstyle=0x%08lx:0x%08lx, menu=%d\n", style, exstyle, menu); + + + CopyRect(&rc, &rc_window); + DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc); + MapWindowPoints(0, hwnd, (LPPOINT)&rc, 2); + trace("calc client: (%ld,%ld)-(%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom); + ok(EqualRect(&rc, &rc_client), "client rect does not match: style:exstyle=0x%08lx:0x%08lx, menu=%d\n", style, exstyle, menu); + + /* Win9x doesn't like WM_NCCALCSIZE with synthetic data and crashes */; + if (is_win9x) + return; + + /* and now test AdjustWindowRectEx and WM_NCCALCSIZE on synthetic data */ + SetRect(&rc_client, 0, 0, 250, 150); + CopyRect(&rc_window, &rc_client); + MapWindowPoints(hwnd, 0, (LPPOINT)&rc_window, 2); + FixedAdjustWindowRectEx(&rc_window, style, menu, exstyle); + trace("calc window: (%ld,%ld)-(%ld,%ld)\n", + rc_window.left, rc_window.top, rc_window.right, rc_window.bottom); + + CopyRect(&rc, &rc_window); + DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc); + MapWindowPoints(0, hwnd, (LPPOINT)&rc, 2); + trace("calc client: (%ld,%ld)-(%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom); + ok(EqualRect(&rc, &rc_client), "synthetic rect does not match: style:exstyle=0x%08lx:0x%08lx, menu=%d\n", style, exstyle, menu); +} + +static LRESULT CALLBACK cbt_hook_proc(int nCode, WPARAM wParam, LPARAM lParam) +{ + static const char *CBT_code_name[10] = { + "HCBT_MOVESIZE", + "HCBT_MINMAX", + "HCBT_QS", + "HCBT_CREATEWND", + "HCBT_DESTROYWND", + "HCBT_ACTIVATE", + "HCBT_CLICKSKIPPED", + "HCBT_KEYSKIPPED", + "HCBT_SYSCOMMAND", + "HCBT_SETFOCUS" }; + const char *code_name = (nCode >= 0 && nCode <= HCBT_SETFOCUS) ? CBT_code_name[nCode] : "Unknown"; + + trace("CBT: %d (%s), %08x, %08lx\n", nCode, code_name, wParam, lParam); + + /* on HCBT_DESTROYWND window state is undefined */ + if (nCode != HCBT_DESTROYWND && IsWindow((HWND)wParam)) + { + if (pGetWindowInfo) + { + WINDOWINFO info; + + /* Win98 actually does check the info.cbSize and doesn't allow + * it to be anything except sizeof(WINDOWINFO), while Win95, Win2k, + * WinXP do not check it at all. + */ + info.cbSize = sizeof(WINDOWINFO); + ok(pGetWindowInfo((HWND)wParam, &info), "GetWindowInfo should not fail\n"); + /* win2k SP4 returns broken border info if GetWindowInfo + * is being called from HCBT_DESTROYWND or HCBT_MINMAX hook proc. + */ + verify_window_info((HWND)wParam, &info, nCode != HCBT_MINMAX); + } + } + + switch (nCode) + { + case HCBT_CREATEWND: + { +#if 0 /* Uncomment this once the test succeeds in all cases */ + static const RECT rc_null; + RECT rc; +#endif + LONG style; + CBT_CREATEWNDA *createwnd = (CBT_CREATEWNDA *)lParam; + trace("HCBT_CREATEWND: hwnd %p, parent %p, style %08lx\n", + (HWND)wParam, createwnd->lpcs->hwndParent, createwnd->lpcs->style); + ok(createwnd->hwndInsertAfter == HWND_TOP, "hwndInsertAfter should be always HWND_TOP\n"); + + /* WS_VISIBLE should be turned off yet */ + style = createwnd->lpcs->style & ~WS_VISIBLE; + ok(style == GetWindowLongA((HWND)wParam, GWL_STYLE), + "style of hwnd and style in the CREATESTRUCT do not match: %08lx != %08lx\n", + GetWindowLongA((HWND)wParam, GWL_STYLE), style); + +#if 0 /* Uncomment this once the test succeeds in all cases */ + if ((style & (WS_CHILD|WS_POPUP)) == WS_CHILD) + { + ok(GetParent((HWND)wParam) == hwndMessage, + "wrong result from GetParent %p: message window %p\n", + GetParent((HWND)wParam), hwndMessage); + } + else + ok(!GetParent((HWND)wParam), "GetParent should return 0 at this point\n"); + + ok(!GetWindow((HWND)wParam, GW_OWNER), "GW_OWNER should be set to 0 at this point\n"); +#endif +#if 0 /* while NT assigns GW_HWNDFIRST/LAST some values at this point, + * Win9x still has them set to 0. + */ + ok(GetWindow((HWND)wParam, GW_HWNDFIRST) != 0, "GW_HWNDFIRST should not be set to 0 at this point\n"); + ok(GetWindow((HWND)wParam, GW_HWNDLAST) != 0, "GW_HWNDLAST should not be set to 0 at this point\n"); +#endif + ok(!GetWindow((HWND)wParam, GW_HWNDPREV), "GW_HWNDPREV should be set to 0 at this point\n"); + ok(!GetWindow((HWND)wParam, GW_HWNDNEXT), "GW_HWNDNEXT should be set to 0 at this point\n"); + +#if 0 /* Uncomment this once the test succeeds in all cases */ + if (pGetAncestor) + { + ok(pGetAncestor((HWND)wParam, GA_PARENT) == hwndMessage, "GA_PARENT should be set to hwndMessage at this point\n"); + ok(pGetAncestor((HWND)wParam, GA_ROOT) == (HWND)wParam, + "GA_ROOT is set to %p, expected %p\n", pGetAncestor((HWND)wParam, GA_ROOT), (HWND)wParam); + + if ((style & (WS_CHILD|WS_POPUP)) == WS_CHILD) + ok(pGetAncestor((HWND)wParam, GA_ROOTOWNER) == hwndMessage, + "GA_ROOTOWNER should be set to hwndMessage at this point\n"); + else + ok(pGetAncestor((HWND)wParam, GA_ROOTOWNER) == (HWND)wParam, + "GA_ROOTOWNER is set to %p, expected %p\n", pGetAncestor((HWND)wParam, GA_ROOTOWNER), (HWND)wParam); + } + + ok(GetWindowRect((HWND)wParam, &rc), "GetWindowRect failed\n"); + ok(EqualRect(&rc, &rc_null), "window rect should be set to 0 HCBT_CREATEWND\n"); + ok(GetClientRect((HWND)wParam, &rc), "GetClientRect failed\n"); + ok(EqualRect(&rc, &rc_null), "client rect should be set to 0 on HCBT_CREATEWND\n"); +#endif + break; + } + } + + return CallNextHookEx(hhook, nCode, wParam, lParam); +} + +static void test_shell_window(void) +{ + BOOL ret; + DWORD error; + HMODULE hinst, hUser32; + BOOL (WINAPI*SetShellWindow)(HWND); + BOOL (WINAPI*SetShellWindowEx)(HWND, HWND); + HWND hwnd1, hwnd2, hwnd3, hwnd4, hwnd5; + HWND shellWindow, nextWnd; + + if (!GetWindowLongW(GetDesktopWindow(), GWL_STYLE)) + { + trace("Skipping shell window test on Win9x\n"); + return; + } + + shellWindow = GetShellWindow(); + hinst = GetModuleHandle(0); + hUser32 = GetModuleHandleA("user32"); + + SetShellWindow = (void *)GetProcAddress(hUser32, "SetShellWindow"); + SetShellWindowEx = (void *)GetProcAddress(hUser32, "SetShellWindowEx"); + + trace("previous shell window: %p\n", shellWindow); + + if (shellWindow) { + DWORD pid; + HANDLE hProcess; + + ret = DestroyWindow(shellWindow); + error = GetLastError(); + + ok(!ret, "DestroyWindow(shellWindow)\n"); + /* passes on Win XP, but not on Win98 */ + ok(error==ERROR_ACCESS_DENIED, "ERROR_ACCESS_DENIED after DestroyWindow(shellWindow)\n"); + + /* close old shell instance */ + GetWindowThreadProcessId(shellWindow, &pid); + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + ret = TerminateProcess(hProcess, 0); + ok(ret, "termination of previous shell process failed: GetLastError()=%ld\n", GetLastError()); + WaitForSingleObject(hProcess, INFINITE); /* wait for termination */ + CloseHandle(hProcess); + } + + hwnd1 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST1"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 100, 100, 300, 200, 0, 0, hinst, 0); + trace("created window 1: %p\n", hwnd1); + + ret = SetShellWindow(hwnd1); + ok(ret, "first call to SetShellWindow(hwnd1)\n"); + shellWindow = GetShellWindow(); + ok(shellWindow==hwnd1, "wrong shell window: %p\n", shellWindow); + + ret = SetShellWindow(hwnd1); + ok(!ret, "second call to SetShellWindow(hwnd1)\n"); + + ret = SetShellWindow(0); + error = GetLastError(); + /* passes on Win XP, but not on Win98 + ok(!ret, "reset shell window by SetShellWindow(0)\n"); + ok(error==ERROR_INVALID_WINDOW_HANDLE, "ERROR_INVALID_WINDOW_HANDLE after SetShellWindow(0)\n"); */ + + ret = SetShellWindow(hwnd1); + /* passes on Win XP, but not on Win98 + ok(!ret, "third call to SetShellWindow(hwnd1)\n"); */ + + todo_wine + { + SetWindowLong(hwnd1, GWL_EXSTYLE, GetWindowLong(hwnd1,GWL_EXSTYLE)|WS_EX_TOPMOST); + ret = GetWindowLong(hwnd1,GWL_EXSTYLE)&WS_EX_TOPMOST? TRUE: FALSE; + ok(!ret, "SetWindowExStyle(hwnd1, WS_EX_TOPMOST)\n"); + } + + ret = DestroyWindow(hwnd1); + ok(ret, "DestroyWindow(hwnd1)\n"); + + hwnd2 = CreateWindowEx(WS_EX_TOPMOST, TEXT("#32770"), TEXT("TEST2"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 150, 250, 300, 200, 0, 0, hinst, 0); + trace("created window 2: %p\n", hwnd2); + ret = SetShellWindow(hwnd2); + ok(!ret, "SetShellWindow(hwnd2) with WS_EX_TOPMOST\n"); + + hwnd3 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST3"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 200, 400, 300, 200, 0, 0, hinst, 0); + trace("created window 3: %p\n", hwnd3); + + hwnd4 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST4"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 250, 500, 300, 200, 0, 0, hinst, 0); + trace("created window 4: %p\n", hwnd4); + + nextWnd = GetWindow(hwnd4, GW_HWNDNEXT); + ok(nextWnd==hwnd3, "wrong next window for hwnd4: %p - expected hwnd3\n", nextWnd); + + ret = SetShellWindow(hwnd4); + ok(ret, "SetShellWindow(hwnd4)\n"); + shellWindow = GetShellWindow(); + ok(shellWindow==hwnd4, "wrong shell window: %p - expected hwnd4\n", shellWindow); + + nextWnd = GetWindow(hwnd4, GW_HWNDNEXT); + ok(nextWnd==0, "wrong next window for hwnd4: %p - expected 0\n", nextWnd); + + ret = SetWindowPos(hwnd4, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + ok(ret, "SetWindowPos(hwnd4, HWND_TOPMOST)\n"); + + ret = SetWindowPos(hwnd4, hwnd3, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + ok(ret, "SetWindowPos(hwnd4, hwnd3\n"); + + ret = SetShellWindow(hwnd3); + ok(!ret, "SetShellWindow(hwnd3)\n"); + shellWindow = GetShellWindow(); + ok(shellWindow==hwnd4, "wrong shell window: %p - expected hwnd4\n", shellWindow); + + hwnd5 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST5"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 300, 600, 300, 200, 0, 0, hinst, 0); + trace("created window 5: %p\n", hwnd5); + ret = SetWindowPos(hwnd4, hwnd5, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE); + ok(ret, "SetWindowPos(hwnd4, hwnd5)\n"); + + todo_wine + { + nextWnd = GetWindow(hwnd4, GW_HWNDNEXT); + ok(nextWnd==0, "wrong next window for hwnd4 after SetWindowPos(): %p - expected 0\n", nextWnd); + } + + /* destroy test windows */ + DestroyWindow(hwnd2); + DestroyWindow(hwnd3); + DestroyWindow(hwnd4); + DestroyWindow(hwnd5); +} + +/************** MDI test ****************/ + +static const char mdi_lParam_test_message[] = "just a test string"; + +static void test_MDI_create(HWND parent, HWND mdi_client, INT first_id) +{ + MDICREATESTRUCTA mdi_cs; + HWND mdi_child; + static const WCHAR classW[] = {'M','D','I','_','c','h','i','l','d','_','C','l','a','s','s','_','1',0}; + static const WCHAR titleW[] = {'M','D','I',' ','c','h','i','l','d',0}; + BOOL isWin9x = FALSE; + + mdi_cs.szClass = "MDI_child_Class_1"; + mdi_cs.szTitle = "MDI child"; + mdi_cs.hOwner = GetModuleHandle(0); + mdi_cs.x = CW_USEDEFAULT; + mdi_cs.y = CW_USEDEFAULT; + mdi_cs.cx = CW_USEDEFAULT; + mdi_cs.cy = CW_USEDEFAULT; + mdi_cs.style = 0; + mdi_cs.lParam = (LPARAM)mdi_lParam_test_message; + mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + + mdi_cs.style = 0x7fffffff; /* without WS_POPUP */ + mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + + mdi_cs.style = 0xffffffff; /* with WS_POPUP */ + mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs); + if (GetWindowLongA(mdi_client, GWL_STYLE) & MDIS_ALLCHILDSTYLES) + { + ok(!mdi_child, "MDI child with WS_POPUP and with MDIS_ALLCHILDSTYLES should fail\n"); + } + else + { + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + } + + /* test MDICREATESTRUCT A<->W mapping */ + /* MDICREATESTRUCTA and MDICREATESTRUCTW have the same layout */ + mdi_cs.style = 0; + mdi_cs.szClass = (LPCSTR)classW; + mdi_cs.szTitle = (LPCSTR)titleW; + SetLastError(0xdeadbeef); + mdi_child = (HWND)SendMessageW(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs); + if (!mdi_child) + { + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + isWin9x = TRUE; + else + ok(mdi_child != 0, "MDI child creation failed\n"); + } + else + { + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + } + + mdi_child = CreateMDIWindowA("MDI_child_Class_1", "MDI child", + 0, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, GetModuleHandle(0), + (LPARAM)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + + mdi_child = CreateMDIWindowA("MDI_child_Class_1", "MDI child", + 0x7fffffff, /* without WS_POPUP */ + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, GetModuleHandle(0), + (LPARAM)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + + mdi_child = CreateMDIWindowA("MDI_child_Class_1", "MDI child", + 0xffffffff, /* with WS_POPUP */ + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, GetModuleHandle(0), + (LPARAM)mdi_lParam_test_message); + if (GetWindowLongA(mdi_client, GWL_STYLE) & MDIS_ALLCHILDSTYLES) + { + ok(!mdi_child, "MDI child with WS_POPUP and with MDIS_ALLCHILDSTYLES should fail\n"); + } + else + { + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + } + + /* test MDICREATESTRUCT A<->W mapping */ + SetLastError(0xdeadbeef); + mdi_child = CreateMDIWindowW(classW, titleW, + 0, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, GetModuleHandle(0), + (LPARAM)mdi_lParam_test_message); + if (!mdi_child) + { + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + isWin9x = TRUE; + else + ok(mdi_child != 0, "MDI child creation failed\n"); + } + else + { + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + } + + mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_1", "MDI child", + 0, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + + mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_1", "MDI child", + 0x7fffffff, /* without WS_POPUP */ + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + + mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_1", "MDI child", + 0xffffffff, /* with WS_POPUP */ + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + if (GetWindowLongA(mdi_client, GWL_STYLE) & MDIS_ALLCHILDSTYLES) + { + ok(!mdi_child, "MDI child with WS_POPUP and with MDIS_ALLCHILDSTYLES should fail\n"); + } + else + { + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + } + + /* test MDICREATESTRUCT A<->W mapping */ + SetLastError(0xdeadbeef); + mdi_child = CreateWindowExW(WS_EX_MDICHILD, classW, titleW, + 0, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + if (!mdi_child) + { + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + isWin9x = TRUE; + else + ok(mdi_child != 0, "MDI child creation failed\n"); + } + else + { + ok(GetWindowLongA(mdi_child, GWL_ID) == first_id, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0); + ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n"); + } + + /* This test fails on Win9x */ + if (!isWin9x) + { + mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_2", "MDI child", + WS_CHILD, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + parent, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(!mdi_child, "WS_EX_MDICHILD with a not MDIClient parent should fail\n"); + } + + mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child", + WS_CHILD, /* without WS_POPUP */ + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == 0, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + DestroyWindow(mdi_child); + + mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child", + WS_CHILD | WS_POPUP, /* with WS_POPUP */ + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == 0, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + DestroyWindow(mdi_child); + + /* maximized child */ + mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child", + WS_CHILD | WS_MAXIMIZE, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == 0, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + DestroyWindow(mdi_child); + + trace("Creating maximized child with a caption\n"); + mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child", + WS_CHILD | WS_MAXIMIZE | WS_CAPTION, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == 0, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + DestroyWindow(mdi_child); + + trace("Creating maximized child with a caption and a thick frame\n"); + mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child", + WS_CHILD | WS_MAXIMIZE | WS_CAPTION | WS_THICKFRAME, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + mdi_client, 0, GetModuleHandle(0), + (LPVOID)mdi_lParam_test_message); + ok(mdi_child != 0, "MDI child creation failed\n"); + ok(GetWindowLongA(mdi_child, GWL_ID) == 0, "wrong child id %ld\n", GetWindowLongA(mdi_child, GWL_ID)); + DestroyWindow(mdi_child); +} + +/********************************************************************** + * MDI_ChildGetMinMaxInfo (copied from windows/mdi.c) + * + * Note: The rule here is that client rect of the maximized MDI child + * is equal to the client rect of the MDI client window. + */ +static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax ) +{ + RECT rect; + + GetClientRect( client, &rect ); + AdjustWindowRectEx( &rect, GetWindowLongA( hwnd, GWL_STYLE ), + 0, GetWindowLongA( hwnd, GWL_EXSTYLE )); + + rect.right -= rect.left; + rect.bottom -= rect.top; + lpMinMax->ptMaxSize.x = rect.right; + lpMinMax->ptMaxSize.y = rect.bottom; + + lpMinMax->ptMaxPosition.x = rect.left; + lpMinMax->ptMaxPosition.y = rect.top; + + trace("max rect (%ld,%ld - %ld, %ld)\n", + rect.left, rect.top, rect.right, rect.bottom); +} + +static LRESULT WINAPI mdi_child_wnd_proc_1(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_NCCREATE: + case WM_CREATE: + { + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + MDICREATESTRUCTA *mdi_cs = (MDICREATESTRUCTA *)cs->lpCreateParams; + + ok(cs->dwExStyle & WS_EX_MDICHILD, "WS_EX_MDICHILD should be set\n"); + ok(mdi_cs->lParam == (LPARAM)mdi_lParam_test_message, "wrong mdi_cs->lParam\n"); + + ok(!lstrcmpA(cs->lpszClass, "MDI_child_Class_1"), "wrong class name\n"); + ok(!lstrcmpA(cs->lpszClass, mdi_cs->szClass), "class name does not match\n"); + ok(!lstrcmpA(cs->lpszName, "MDI child"), "wrong title\n"); + ok(!lstrcmpA(cs->lpszName, mdi_cs->szTitle), "title does not match\n"); + ok(cs->hInstance == mdi_cs->hOwner, "%p != %p\n", cs->hInstance, mdi_cs->hOwner); + + /* MDICREATESTRUCT should have original values */ + ok(mdi_cs->style == 0 || mdi_cs->style == 0x7fffffff || mdi_cs->style == 0xffffffff, + "mdi_cs->style does not match (%08lx)\n", mdi_cs->style); + ok(mdi_cs->x == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->x); + ok(mdi_cs->y == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->y); + ok(mdi_cs->cx == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->cx); + ok(mdi_cs->cy == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->cy); + + /* CREATESTRUCT should have fixed values */ + ok(cs->x != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->x); + ok(cs->y != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->y); + + /* cx/cy == CW_USEDEFAULT are translated to NOT zero values */ + ok(cs->cx != CW_USEDEFAULT && cs->cx != 0, "%d == CW_USEDEFAULT\n", cs->cx); + ok(cs->cy != CW_USEDEFAULT && cs->cy != 0, "%d == CW_USEDEFAULT\n", cs->cy); + + ok(!(cs->style & WS_POPUP), "WS_POPUP is not allowed\n"); + + if (GetWindowLongA(cs->hwndParent, GWL_STYLE) & MDIS_ALLCHILDSTYLES) + { + LONG style = mdi_cs->style | WS_CHILD | WS_CLIPSIBLINGS; + ok(cs->style == style, + "cs->style does not match (%08lx)\n", cs->style); + } + else + { + LONG style = mdi_cs->style; + style &= ~WS_POPUP; + style |= WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION | + WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + ok(cs->style == style, + "cs->style does not match (%08lx)\n", cs->style); + } + break; + } + + case WM_GETMINMAXINFO: + { + HWND client = GetParent(hwnd); + RECT rc; + MINMAXINFO *minmax = (MINMAXINFO *)lparam; + MINMAXINFO my_minmax; + LONG style, exstyle; + + style = GetWindowLongA(hwnd, GWL_STYLE); + exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE); + + GetWindowRect(client, &rc); + trace("MDI client %p window size = (%ld x %ld)\n", client, rc.right-rc.left, rc.bottom-rc.top); + GetClientRect(client, &rc); + trace("MDI client %p client size = (%ld x %ld)\n", client, rc.right, rc.bottom); + trace("screen size: %d x %d\n", GetSystemMetrics(SM_CXSCREEN), + GetSystemMetrics(SM_CYSCREEN)); + + GetClientRect(client, &rc); + if ((style & WS_CAPTION) == WS_CAPTION) + style &= ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */ + AdjustWindowRectEx(&rc, style, 0, exstyle); + trace("MDI child: calculated max window size = (%ld x %ld)\n", rc.right-rc.left, rc.bottom-rc.top); + + trace("ptReserved = (%ld,%ld)\n" + "ptMaxSize = (%ld,%ld)\n" + "ptMaxPosition = (%ld,%ld)\n" + "ptMinTrackSize = (%ld,%ld)\n" + "ptMaxTrackSize = (%ld,%ld)\n", + minmax->ptReserved.x, minmax->ptReserved.y, + minmax->ptMaxSize.x, minmax->ptMaxSize.y, + minmax->ptMaxPosition.x, minmax->ptMaxPosition.y, + minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y, + minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y); + + ok(minmax->ptMaxSize.x == rc.right - rc.left, "default width of maximized child %ld != %ld\n", + minmax->ptMaxSize.x, rc.right - rc.left); + ok(minmax->ptMaxSize.y == rc.bottom - rc.top, "default height of maximized child %ld != %ld\n", + minmax->ptMaxSize.y, rc.bottom - rc.top); + + DefMDIChildProcA(hwnd, msg, wparam, lparam); + + trace("DefMDIChildProc returned:\n" + "ptReserved = (%ld,%ld)\n" + "ptMaxSize = (%ld,%ld)\n" + "ptMaxPosition = (%ld,%ld)\n" + "ptMinTrackSize = (%ld,%ld)\n" + "ptMaxTrackSize = (%ld,%ld)\n", + minmax->ptReserved.x, minmax->ptReserved.y, + minmax->ptMaxSize.x, minmax->ptMaxSize.y, + minmax->ptMaxPosition.x, minmax->ptMaxPosition.y, + minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y, + minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y); + + MDI_ChildGetMinMaxInfo(client, hwnd, &my_minmax); + ok(minmax->ptMaxSize.x == my_minmax.ptMaxSize.x, "default width of maximized child %ld != %ld\n", + minmax->ptMaxSize.x, my_minmax.ptMaxSize.x); + ok(minmax->ptMaxSize.y == my_minmax.ptMaxSize.y, "default height of maximized child %ld != %ld\n", + minmax->ptMaxSize.y, my_minmax.ptMaxSize.y); + + return 1; + } + + case WM_MDIACTIVATE: + { + HWND active, client = GetParent(hwnd); + /*trace("%p WM_MDIACTIVATE %08x %08lx\n", hwnd, wparam, lparam);*/ + active = (HWND)SendMessageA(client, WM_MDIGETACTIVE, 0, 0); + if (hwnd == (HWND)lparam) /* if we are being activated */ + ok (active == (HWND)lparam, "new active %p != active %p\n", (HWND)lparam, active); + else + ok (active == (HWND)wparam, "old active %p != active %p\n", (HWND)wparam, active); + break; + } + } + return DefMDIChildProcA(hwnd, msg, wparam, lparam); +} + +static LRESULT WINAPI mdi_child_wnd_proc_2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_NCCREATE: + case WM_CREATE: + { + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + + trace("%s\n", (msg == WM_NCCREATE) ? "WM_NCCREATE" : "WM_CREATE"); + trace("x %d, y %d, cx %d, cy %d\n", cs->x, cs->y, cs->cx, cs->cy); + + ok(!(cs->dwExStyle & WS_EX_MDICHILD), "WS_EX_MDICHILD should not be set\n"); + ok(cs->lpCreateParams == mdi_lParam_test_message, "wrong cs->lpCreateParams\n"); + + ok(!lstrcmpA(cs->lpszClass, "MDI_child_Class_2"), "wrong class name\n"); + ok(!lstrcmpA(cs->lpszName, "MDI child"), "wrong title\n"); + + /* CREATESTRUCT should have fixed values */ + /* For some reason Win9x doesn't translate cs->x from CW_USEDEFAULT, + while NT does. */ + /*ok(cs->x != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->x);*/ + ok(cs->y != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->y); + + /* cx/cy == CW_USEDEFAULT are translated to 0 */ + /* For some reason Win98 doesn't translate cs->cx from CW_USEDEFAULT, + while Win95, Win2k, WinXP do. */ + /*ok(cs->cx == 0, "%d != 0\n", cs->cx);*/ + ok(cs->cy == 0, "%d != 0\n", cs->cy); + break; + } + + case WM_GETMINMAXINFO: + { + HWND parent = GetParent(hwnd); + RECT rc; + MINMAXINFO *minmax = (MINMAXINFO *)lparam; + LONG style, exstyle; + + trace("WM_GETMINMAXINFO\n"); + + style = GetWindowLongA(hwnd, GWL_STYLE); + exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE); + + GetClientRect(parent, &rc); + trace("parent %p client size = (%ld x %ld)\n", parent, rc.right, rc.bottom); + + GetClientRect(parent, &rc); + if ((style & WS_CAPTION) == WS_CAPTION) + style &= ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */ + AdjustWindowRectEx(&rc, style, 0, exstyle); + trace("calculated max child window size = (%ld x %ld)\n", rc.right-rc.left, rc.bottom-rc.top); + + trace("ptReserved = (%ld,%ld)\n" + "ptMaxSize = (%ld,%ld)\n" + "ptMaxPosition = (%ld,%ld)\n" + "ptMinTrackSize = (%ld,%ld)\n" + "ptMaxTrackSize = (%ld,%ld)\n", + minmax->ptReserved.x, minmax->ptReserved.y, + minmax->ptMaxSize.x, minmax->ptMaxSize.y, + minmax->ptMaxPosition.x, minmax->ptMaxPosition.y, + minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y, + minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y); + + ok(minmax->ptMaxSize.x == rc.right - rc.left, "default width of maximized child %ld != %ld\n", + minmax->ptMaxSize.x, rc.right - rc.left); + ok(minmax->ptMaxSize.y == rc.bottom - rc.top, "default height of maximized child %ld != %ld\n", + minmax->ptMaxSize.y, rc.bottom - rc.top); + break; + } + + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lparam; + RECT rc1, rc2; + + GetWindowRect(hwnd, &rc1); + trace("window: (%ld,%ld)-(%ld,%ld)\n", rc1.left, rc1.top, rc1.right, rc1.bottom); + SetRect(&rc2, winpos->x, winpos->y, winpos->x + winpos->cx, winpos->y + winpos->cy); + /* note: winpos coordinates are relative to parent */ + MapWindowPoints(GetParent(hwnd), 0, (LPPOINT)&rc2, 2); + trace("pos: (%ld,%ld)-(%ld,%ld)\n", rc2.left, rc2.top, rc2.right, rc2.bottom); + ok(EqualRect(&rc1, &rc2), "rects do not match\n"); + + GetWindowRect(hwnd, &rc1); + GetClientRect(hwnd, &rc2); + DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc1); + MapWindowPoints(0, hwnd, (LPPOINT)&rc1, 2); + ok(EqualRect(&rc1, &rc2), "rects do not match\n"); + } + /* fall through */ + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS *winpos = (WINDOWPOS *)lparam; + WINDOWPOS my_winpos = *winpos; + + trace("%s\n", (msg == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + DefWindowProcA(hwnd, msg, wparam, lparam); + + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + ok(!memcmp(&my_winpos, winpos, sizeof(WINDOWPOS)), + "DefWindowProc should not change WINDOWPOS values\n"); + + return 1; + } + } + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static LRESULT WINAPI mdi_main_wnd_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + static HWND mdi_client; + + switch (msg) + { + case WM_CREATE: + { + CLIENTCREATESTRUCT client_cs; + RECT rc; + + GetClientRect(hwnd, &rc); + + client_cs.hWindowMenu = 0; + client_cs.idFirstChild = 1; + + /* MDIClient without MDIS_ALLCHILDSTYLES */ + mdi_client = CreateWindowExA(0, "mdiclient", + NULL, + WS_CHILD /*| WS_VISIBLE*/, + /* tests depend on a not zero MDIClient size */ + 0, 0, rc.right, rc.bottom, + hwnd, 0, GetModuleHandle(0), + (LPVOID)&client_cs); + assert(mdi_client); + test_MDI_create(hwnd, mdi_client, client_cs.idFirstChild); + DestroyWindow(mdi_client); + + /* MDIClient with MDIS_ALLCHILDSTYLES */ + mdi_client = CreateWindowExA(0, "mdiclient", + NULL, + WS_CHILD | MDIS_ALLCHILDSTYLES /*| WS_VISIBLE*/, + /* tests depend on a not zero MDIClient size */ + 0, 0, rc.right, rc.bottom, + hwnd, 0, GetModuleHandle(0), + (LPVOID)&client_cs); + assert(mdi_client); + test_MDI_create(hwnd, mdi_client, client_cs.idFirstChild); + DestroyWindow(mdi_client); + break; + } + + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *winpos = (WINDOWPOS *)lparam; + RECT rc1, rc2; + + GetWindowRect(hwnd, &rc1); + trace("window: (%ld,%ld)-(%ld,%ld)\n", rc1.left, rc1.top, rc1.right, rc1.bottom); + SetRect(&rc2, winpos->x, winpos->y, winpos->x + winpos->cx, winpos->y + winpos->cy); + /* note: winpos coordinates are relative to parent */ + MapWindowPoints(GetParent(hwnd), 0, (LPPOINT)&rc2, 2); + trace("pos: (%ld,%ld)-(%ld,%ld)\n", rc2.left, rc2.top, rc2.right, rc2.bottom); + ok(EqualRect(&rc1, &rc2), "rects do not match\n"); + + GetWindowRect(hwnd, &rc1); + GetClientRect(hwnd, &rc2); + DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc1); + MapWindowPoints(0, hwnd, (LPPOINT)&rc1, 2); + ok(EqualRect(&rc1, &rc2), "rects do not match\n"); + } + /* fall through */ + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS *winpos = (WINDOWPOS *)lparam; + WINDOWPOS my_winpos = *winpos; + + trace("%s\n", (msg == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED"); + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + DefWindowProcA(hwnd, msg, wparam, lparam); + + trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n", + winpos->hwnd, winpos->hwndInsertAfter, + winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags); + + ok(!memcmp(&my_winpos, winpos, sizeof(WINDOWPOS)), + "DefWindowProc should not change WINDOWPOS values\n"); + + return 1; + } + + case WM_CLOSE: + PostQuitMessage(0); + break; + } + return DefFrameProcA(hwnd, mdi_client, msg, wparam, lparam); +} + +static BOOL mdi_RegisterWindowClasses(void) +{ + WNDCLASSA cls; + + cls.style = 0; + cls.lpfnWndProc = mdi_main_wnd_procA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "MDI_parent_Class"; + if(!RegisterClassA(&cls)) return FALSE; + + cls.lpfnWndProc = mdi_child_wnd_proc_1; + cls.lpszClassName = "MDI_child_Class_1"; + if(!RegisterClassA(&cls)) return FALSE; + + cls.lpfnWndProc = mdi_child_wnd_proc_2; + cls.lpszClassName = "MDI_child_Class_2"; + if(!RegisterClassA(&cls)) return FALSE; + + return TRUE; +} + +static void test_mdi(void) +{ + HWND mdi_hwndMain; + /*MSG msg;*/ + + if (!mdi_RegisterWindowClasses()) assert(0); + + mdi_hwndMain = CreateWindowExA(0, "MDI_parent_Class", "MDI parent window", + WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_MAXIMIZEBOX /*| WS_VISIBLE*/, + 100, 100, CW_USEDEFAULT, CW_USEDEFAULT, + GetDesktopWindow(), 0, + GetModuleHandle(0), NULL); + assert(mdi_hwndMain); +/* + while(GetMessage(&msg, 0, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +*/ +} + +static void test_icons(void) +{ + WNDCLASSEXA cls; + HWND hwnd; + HICON icon = LoadIconA(0, (LPSTR)IDI_APPLICATION); + HICON icon2 = LoadIconA(0, (LPSTR)IDI_QUESTION); + HICON small_icon = LoadImageA(0, (LPSTR)IDI_APPLICATION, IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED ); + HICON res; + + cls.cbSize = sizeof(cls); + cls.style = 0; + cls.lpfnWndProc = DefWindowProcA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = 0; + cls.hIcon = LoadIconA(0, (LPSTR)IDI_HAND); + cls.hIconSm = small_icon; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "IconWindowClass"; + + RegisterClassExA(&cls); + + hwnd = CreateWindowExA(0, "IconWindowClass", "icon test", 0, + 100, 100, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL); + assert( hwnd ); + + res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 ); + ok( res == 0, "wrong big icon %p/0\n", res ); + res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_BIG, (LPARAM)icon ); + ok( res == 0, "wrong previous big icon %p/0\n", res ); + res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 ); + ok( res == icon, "wrong big icon after set %p/%p\n", res, icon ); + res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_BIG, (LPARAM)icon2 ); + ok( res == icon, "wrong previous big icon %p/%p\n", res, icon ); + res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 ); + ok( res == icon2, "wrong big icon after set %p/%p\n", res, icon2 ); + + res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL, 0 ); + ok( res == 0, "wrong small icon %p/0\n", res ); + /* this test is XP specific */ + /*res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL2, 0 ); + ok( res != 0, "wrong small icon %p\n", res );*/ + res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_SMALL, (LPARAM)icon ); + ok( res == 0, "wrong previous small icon %p/0\n", res ); + res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL, 0 ); + ok( res == icon, "wrong small icon after set %p/%p\n", res, icon ); + /* this test is XP specific */ + /*res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL2, 0 ); + ok( res == icon, "wrong small icon after set %p/%p\n", res, icon );*/ + res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_SMALL, (LPARAM)small_icon ); + ok( res == icon, "wrong previous small icon %p/%p\n", res, icon ); + res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL, 0 ); + ok( res == small_icon, "wrong small icon after set %p/%p\n", res, small_icon ); + /* this test is XP specific */ + /*res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL2, 0 ); + ok( res == small_icon, "wrong small icon after set %p/%p\n", res, small_icon );*/ + + /* make sure the big icon hasn't changed */ + res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 ); + ok( res == icon2, "wrong big icon after set %p/%p\n", res, icon2 ); +} + +static LRESULT WINAPI nccalcsize_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (msg == WM_NCCALCSIZE) + { + RECT *rect = (RECT *)lparam; + /* first time around increase the rectangle, next time decrease it */ + if (rect->left == 100) InflateRect( rect, 10, 10 ); + else InflateRect( rect, -10, -10 ); + return 0; + } + return DefWindowProc( hwnd, msg, wparam, lparam ); +} + +static void test_SetWindowPos(HWND hwnd) +{ + RECT orig_win_rc, rect; + LONG_PTR old_proc; + BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0; + + SetRect(&rect, 111, 222, 333, 444); + ok(!GetWindowRect(0, &rect), "GetWindowRect succeeded\n"); + ok(rect.left == 111 && rect.top == 222 && rect.right == 333 && rect.bottom == 444, + "wrong window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + + SetRect(&rect, 111, 222, 333, 444); + ok(!GetClientRect(0, &rect), "GetClientRect succeeded\n"); + ok(rect.left == 111 && rect.top == 222 && rect.right == 333 && rect.bottom == 444, + "wrong window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + + GetWindowRect(hwnd, &orig_win_rc); + + old_proc = SetWindowLongPtr( hwnd, GWLP_WNDPROC, (ULONG_PTR)nccalcsize_proc ); + SetWindowPos(hwnd, 0, 100, 100, 0, 0, SWP_NOZORDER|SWP_FRAMECHANGED); + GetWindowRect( hwnd, &rect ); + ok( rect.left == 100 && rect.top == 100 && rect.right == 100 && rect.bottom == 100, + "invalid window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + GetClientRect( hwnd, &rect ); + MapWindowPoints( hwnd, 0, (POINT *)&rect, 2 ); + ok( rect.left == 90 && rect.top == 90 && rect.right == 110 && rect.bottom == 110, + "invalid client rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + + SetWindowPos(hwnd, 0, 200, 200, 0, 0, SWP_NOZORDER|SWP_FRAMECHANGED); + GetWindowRect( hwnd, &rect ); + ok( rect.left == 200 && rect.top == 200 && rect.right == 200 && rect.bottom == 200, + "invalid window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + GetClientRect( hwnd, &rect ); + MapWindowPoints( hwnd, 0, (POINT *)&rect, 2 ); + ok( rect.left == 210 && rect.top == 210 && rect.right == 190 && rect.bottom == 190, + "invalid client rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom ); + + SetWindowPos(hwnd, 0, orig_win_rc.left, orig_win_rc.top, + orig_win_rc.right, orig_win_rc.bottom, 0); + SetWindowLongPtr( hwnd, GWLP_WNDPROC, old_proc ); + + /* Win9x truncates coordinates to 16-bit irrespectively */ + if (!is_win9x) + { + SetWindowPos(hwnd, 0, -32769, -40000, -32769, -90000, SWP_NOMOVE); + SetWindowPos(hwnd, 0, 32768, 40000, 32768, 40000, SWP_NOMOVE); + + SetWindowPos(hwnd, 0, -32769, -40000, -32769, -90000, SWP_NOSIZE); + SetWindowPos(hwnd, 0, 32768, 40000, 32768, 40000, SWP_NOSIZE); + } + + SetWindowPos(hwnd, 0, orig_win_rc.left, orig_win_rc.top, + orig_win_rc.right, orig_win_rc.bottom, 0); +} + +static void test_SetMenu(HWND parent) +{ + HWND child; + HMENU hMenu, ret; + BOOL is_win9x = GetWindowLongPtrW(parent, GWLP_WNDPROC) == 0; + BOOL retok; + DWORD style; + + hMenu = CreateMenu(); + assert(hMenu); + + ok(SetMenu(parent, hMenu), "SetMenu on a top level window should not fail\n"); +#if 0 + /* fails on (at least) Wine, NT4, XP SP2 */ + test_nonclient_area(parent); +#endif + ret = GetMenu(parent); + ok(ret == hMenu, "unexpected menu id %p\n", ret); + /* test whether we can destroy a menu assigned to a window */ + retok = DestroyMenu(hMenu); + ok( retok, "DestroyMenu error %ld\n", GetLastError()); + ok(!IsMenu(hMenu), "menu handle should be not valid after DestroyMenu\n"); + ret = GetMenu(parent); + /* This test fails on Win9x */ + if (!is_win9x) + ok(ret == hMenu, "unexpected menu id %p\n", ret); + ok(SetMenu(parent, 0), "SetMenu(0) on a top level window should not fail\n"); + test_nonclient_area(parent); + + hMenu = CreateMenu(); + assert(hMenu); + + /* parent */ + ret = GetMenu(parent); + ok(ret == 0, "unexpected menu id %p\n", ret); + + ok(!SetMenu(parent, (HMENU)20), "SetMenu with invalid menu handle should fail\n"); + test_nonclient_area(parent); + ret = GetMenu(parent); + ok(ret == 0, "unexpected menu id %p\n", ret); + + ok(SetMenu(parent, hMenu), "SetMenu on a top level window should not fail\n"); +#if 0 + /* fails on (at least) Wine, NT4, XP SP2 */ + test_nonclient_area(parent); +#endif + ret = GetMenu(parent); + ok(ret == hMenu, "unexpected menu id %p\n", ret); + + ok(SetMenu(parent, 0), "SetMenu(0) on a top level window should not fail\n"); + test_nonclient_area(parent); + ret = GetMenu(parent); + ok(ret == 0, "unexpected menu id %p\n", ret); + + /* child */ + child = CreateWindowExA(0, "static", NULL, WS_CHILD, 0, 0, 0, 0, parent, (HMENU)10, 0, NULL); + assert(child); + + ret = GetMenu(child); + ok(ret == (HMENU)10, "unexpected menu id %p\n", ret); + + ok(!SetMenu(child, (HMENU)20), "SetMenu with invalid menu handle should fail\n"); + test_nonclient_area(child); + ret = GetMenu(child); + ok(ret == (HMENU)10, "unexpected menu id %p\n", ret); + + ok(!SetMenu(child, hMenu), "SetMenu on a child window should fail\n"); + test_nonclient_area(child); + ret = GetMenu(child); + ok(ret == (HMENU)10, "unexpected menu id %p\n", ret); + + ok(!SetMenu(child, 0), "SetMenu(0) on a child window should fail\n"); + test_nonclient_area(child); + ret = GetMenu(child); + ok(ret == (HMENU)10, "unexpected menu id %p\n", ret); + + style = GetWindowLong(child, GWL_STYLE); + SetWindowLong(child, GWL_STYLE, style | WS_POPUP); + ok(SetMenu(child, hMenu), "SetMenu on a popup child window should not fail\n"); + ok(SetMenu(child, 0), "SetMenu on a popup child window should not fail\n"); + SetWindowLong(child, GWL_STYLE, style); + + SetWindowLong(child, GWL_STYLE, style | WS_OVERLAPPED); + ok(!SetMenu(child, hMenu), "SetMenu on a overlapped child window should fail\n"); + SetWindowLong(child, GWL_STYLE, style); + + DestroyWindow(child); + DestroyMenu(hMenu); +} + +static void test_window_tree(HWND parent, const DWORD *style, const int *order, int total) +{ + HWND child[5], hwnd; + int i; + + assert(total <= 5); + + hwnd = GetWindow(parent, GW_CHILD); + ok(!hwnd, "have to start without children to perform the test\n"); + + for (i = 0; i < total; i++) + { + child[i] = CreateWindowExA(0, "static", "", style[i], 0,0,10,10, + parent, 0, 0, NULL); + trace("child[%d] = %p\n", i, child[i]); + ok(child[i] != 0, "CreateWindowEx failed to create child window\n"); + } + + hwnd = GetWindow(parent, GW_CHILD); + ok(hwnd != 0, "GetWindow(GW_CHILD) failed\n"); + ok(hwnd == GetWindow(child[total - 1], GW_HWNDFIRST), "GW_HWNDFIRST is wrong\n"); + ok(child[order[total - 1]] == GetWindow(child[0], GW_HWNDLAST), "GW_HWNDLAST is wrong\n"); + + for (i = 0; i < total; i++) + { + trace("hwnd[%d] = %p\n", i, hwnd); + ok(child[order[i]] == hwnd, "Z order of child #%d is wrong\n", i); + + hwnd = GetWindow(hwnd, GW_HWNDNEXT); + } + + for (i = 0; i < total; i++) + ok(DestroyWindow(child[i]), "DestroyWindow failed\n"); +} + +static void test_children_zorder(HWND parent) +{ + const DWORD simple_style[5] = { WS_CHILD, WS_CHILD, WS_CHILD, WS_CHILD, + WS_CHILD }; + const int simple_order[5] = { 0, 1, 2, 3, 4 }; + + const DWORD complex_style[5] = { WS_CHILD, WS_CHILD | WS_MAXIMIZE, + WS_CHILD | WS_VISIBLE, WS_CHILD, + WS_CHILD | WS_MAXIMIZE | WS_VISIBLE }; + const int complex_order_1[1] = { 0 }; + const int complex_order_2[2] = { 1, 0 }; + const int complex_order_3[3] = { 1, 0, 2 }; + const int complex_order_4[4] = { 1, 0, 2, 3 }; + const int complex_order_5[5] = { 4, 1, 0, 2, 3 }; + + /* simple WS_CHILD */ + test_window_tree(parent, simple_style, simple_order, 5); + + /* complex children styles */ + test_window_tree(parent, complex_style, complex_order_1, 1); + test_window_tree(parent, complex_style, complex_order_2, 2); + test_window_tree(parent, complex_style, complex_order_3, 3); + test_window_tree(parent, complex_style, complex_order_4, 4); + test_window_tree(parent, complex_style, complex_order_5, 5); +} + +static void test_SetFocus(HWND hwnd) +{ + HWND child; + + /* check if we can set focus to non-visible windows */ + + ShowWindow(hwnd, SW_SHOW); + SetFocus(0); + SetFocus(hwnd); + ok( GetFocus() == hwnd, "Failed to set focus to visible window %p\n", hwnd ); + ok( GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE, "Window %p not visible\n", hwnd ); + ShowWindow(hwnd, SW_HIDE); + SetFocus(0); + SetFocus(hwnd); + ok( GetFocus() == hwnd, "Failed to set focus to invisible window %p\n", hwnd ); + ok( !(GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE), "Window %p still visible\n", hwnd ); + child = CreateWindowExA(0, "static", NULL, WS_CHILD, 0, 0, 0, 0, hwnd, 0, 0, NULL); + assert(child); + SetFocus(child); + ok( GetFocus() == child, "Failed to set focus to invisible child %p\n", child ); + ok( !(GetWindowLong(child,GWL_STYLE) & WS_VISIBLE), "Child %p is visible\n", child ); + ShowWindow(child, SW_SHOW); + ok( GetWindowLong(child,GWL_STYLE) & WS_VISIBLE, "Child %p is not visible\n", child ); + ok( GetFocus() == child, "Focus no longer on child %p\n", child ); + ShowWindow(child, SW_HIDE); + ok( !(GetWindowLong(child,GWL_STYLE) & WS_VISIBLE), "Child %p is visible\n", child ); + ok( GetFocus() == hwnd, "Focus should be on parent %p, not %p\n", hwnd, GetFocus() ); + ShowWindow(child, SW_SHOW); + SetFocus(child); + ok( GetFocus() == child, "Focus should be on child %p\n", child ); + SetWindowPos(child,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_HIDEWINDOW); + ok( GetFocus() == child, "Focus should still be on child %p\n", child ); + + ShowWindow(child, SW_HIDE); + SetFocus(hwnd); + ok( GetFocus() == hwnd, "Focus should be on parent %p, not %p\n", hwnd, GetFocus() ); + SetWindowPos(child,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW); + ok( GetFocus() == hwnd, "Focus should still be on parent %p, not %p\n", hwnd, GetFocus() ); + ShowWindow(child, SW_HIDE); + ok( GetFocus() == hwnd, "Focus should still be on parent %p, not %p\n", hwnd, GetFocus() ); + + ShowWindow(hwnd, SW_SHOW); + ShowWindow(child, SW_SHOW); + SetFocus(child); + ok( GetFocus() == child, "Focus should be on child %p\n", child ); + EnableWindow(hwnd, FALSE); + ok( GetFocus() == child, "Focus should still be on child %p\n", child ); + EnableWindow(hwnd, TRUE); + + DestroyWindow( child ); +} + +static void test_SetActiveWindow(HWND hwnd) +{ + HWND hwnd2; + + ShowWindow(hwnd, SW_SHOW); + SetActiveWindow(0); + SetActiveWindow(hwnd); + ok( GetActiveWindow() == hwnd, "Failed to set focus to visible window %p\n", hwnd ); + SetWindowPos(hwnd,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_HIDEWINDOW); + ok( GetActiveWindow() == hwnd, "Window %p no longer active\n", hwnd ); + SetWindowPos(hwnd,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_SHOWWINDOW); + ShowWindow(hwnd, SW_HIDE); + ok( GetActiveWindow() != hwnd, "Window %p is still active\n", hwnd ); + + /* trace("**testing an invisible window now\n"); */ + SetActiveWindow(hwnd); + ok( GetActiveWindow() == hwnd, "Window %p not active\n", hwnd ); + ok( !(GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE), "Window %p is visible\n", hwnd ); + + ShowWindow(hwnd, SW_SHOW); + + hwnd2 = CreateWindowExA(0, "static", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, hwnd, 0, 0, NULL); + ok( GetActiveWindow() == hwnd2, "Window %p is not active\n", hwnd2 ); + DestroyWindow(hwnd2); + ok( GetActiveWindow() != hwnd2, "Window %p is still active\n", hwnd2 ); + + hwnd2 = CreateWindowExA(0, "static", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, hwnd, 0, 0, NULL); + ok( GetActiveWindow() == hwnd2, "Window %p is not active\n", hwnd2 ); + SetWindowPos(hwnd2,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_HIDEWINDOW); + ok( GetActiveWindow() == hwnd2, "Window %p no longer active (%p)\n", hwnd2, GetActiveWindow() ); + DestroyWindow(hwnd2); + ok( GetActiveWindow() != hwnd2, "Window %p is still active\n", hwnd2 ); +} + +static void check_wnd_state(HWND active, HWND foreground, HWND focus, HWND capture) +{ + ok(active == GetActiveWindow(), "GetActiveWindow() = %p\n", GetActiveWindow()); + if (foreground) + ok(foreground == GetForegroundWindow(), "GetForegroundWindow() = %p\n", GetForegroundWindow()); + ok(focus == GetFocus(), "GetFocus() = %p\n", GetFocus()); + ok(capture == GetCapture(), "GetCapture() = %p\n", GetCapture()); +} + +static WNDPROC old_button_proc; + +static LRESULT WINAPI button_hook_proc(HWND button, UINT msg, WPARAM wparam, LPARAM lparam) +{ + LRESULT ret; + USHORT key_state; + + key_state = GetKeyState(VK_LBUTTON); + ok(!(key_state & 0x8000), "VK_LBUTTON should not be pressed, state %04x\n", key_state); + + ret = CallWindowProcA(old_button_proc, button, msg, wparam, lparam); + + if (msg == WM_LBUTTONDOWN) + { + HWND hwnd, capture; + + check_wnd_state(button, button, button, button); + + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL); + assert(hwnd); + trace("hwnd %p\n", hwnd); + + check_wnd_state(button, button, button, button); + + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + + check_wnd_state(button, button, button, button); + + DestroyWindow(hwnd); + + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL); + assert(hwnd); + trace("hwnd %p\n", hwnd); + + check_wnd_state(button, button, button, button); + + /* button wnd proc should release capture on WM_KILLFOCUS if it does + * match internal button state. + */ + SendMessage(button, WM_KILLFOCUS, 0, 0); + check_wnd_state(button, button, button, 0); + + ShowWindow(hwnd, SW_SHOW); + check_wnd_state(hwnd, hwnd, hwnd, 0); + + capture = SetCapture(hwnd); + ok(capture == 0, "SetCapture() = %p\n", capture); + + check_wnd_state(hwnd, hwnd, hwnd, hwnd); + + DestroyWindow(hwnd); + + check_wnd_state(button, 0, button, 0); + } + + return ret; +} + +static void test_capture_1(void) +{ + HWND button, capture; + + capture = GetCapture(); + ok(capture == 0, "GetCapture() = %p\n", capture); + + button = CreateWindowExA(0, "button", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL); + assert(button); + trace("button %p\n", button); + + old_button_proc = (WNDPROC)SetWindowLongPtrA(button, GWLP_WNDPROC, (LONG_PTR)button_hook_proc); + + SendMessageA(button, WM_LBUTTONDOWN, 0, 0); + + capture = SetCapture(button); + ok(capture == 0, "SetCapture() = %p\n", capture); + check_wnd_state(button, 0, button, button); + + DestroyWindow(button); + check_wnd_state(0, 0, 0, 0); +} + +static void test_capture_2(void) +{ + HWND button, hwnd, capture; + + check_wnd_state(0, 0, 0, 0); + + button = CreateWindowExA(0, "button", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL); + assert(button); + trace("button %p\n", button); + + check_wnd_state(button, button, button, 0); + + capture = SetCapture(button); + ok(capture == 0, "SetCapture() = %p\n", capture); + + check_wnd_state(button, button, button, button); + + /* button wnd proc should ignore WM_KILLFOCUS if it doesn't match + * internal button state. + */ + SendMessage(button, WM_KILLFOCUS, 0, 0); + check_wnd_state(button, button, button, button); + + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL); + assert(hwnd); + trace("hwnd %p\n", hwnd); + + check_wnd_state(button, button, button, button); + + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + + check_wnd_state(button, button, button, button); + + DestroyWindow(hwnd); + + hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL); + assert(hwnd); + trace("hwnd %p\n", hwnd); + + check_wnd_state(button, button, button, button); + + ShowWindow(hwnd, SW_SHOW); + + check_wnd_state(hwnd, hwnd, hwnd, button); + + capture = SetCapture(hwnd); + ok(capture == button, "SetCapture() = %p\n", capture); + + check_wnd_state(hwnd, hwnd, hwnd, hwnd); + + DestroyWindow(hwnd); + check_wnd_state(button, button, button, 0); + + DestroyWindow(button); + check_wnd_state(0, 0, 0, 0); +} + +static void test_capture_3(HWND hwnd1, HWND hwnd2) +{ + ShowWindow(hwnd1, SW_HIDE); + ShowWindow(hwnd2, SW_HIDE); + + ok(!IsWindowVisible(hwnd1), "%p should be invisible\n", hwnd1); + ok(!IsWindowVisible(hwnd2), "%p should be invisible\n", hwnd2); + + SetCapture(hwnd1); + check_wnd_state(0, 0, 0, hwnd1); + + SetCapture(hwnd2); + check_wnd_state(0, 0, 0, hwnd2); + + ShowWindow(hwnd1, SW_SHOW); + check_wnd_state(hwnd1, hwnd1, hwnd1, hwnd2); + + ReleaseCapture(); +} + +static void test_keyboard_input(HWND hwnd) +{ + MSG msg; + BOOL ret; + + ShowWindow(hwnd, SW_SHOW); + UpdateWindow(hwnd); + + ok(GetActiveWindow() == hwnd, "wrong active window %p\n", GetActiveWindow()); + + SetFocus(hwnd); + ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus()); + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + + PostMessageA(hwnd, WM_KEYDOWN, 0, 0); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); + + ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus()); + + PostThreadMessageA(GetCurrentThreadId(), WM_KEYDOWN, 0, 0); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(!msg.hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); + + ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus()); + + keybd_event(VK_SPACE, 0, 0, 0); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); + + SetFocus(0); + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg); + + PostMessageA(hwnd, WM_KEYDOWN, 0, 0); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); + + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + PostThreadMessageA(GetCurrentThreadId(), WM_KEYDOWN, 0, 0); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(!msg.hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); + + ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus()); + + keybd_event(VK_SPACE, 0, 0, 0); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == hwnd && msg.message == WM_SYSKEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); +} + +static void test_mouse_input(HWND hwnd) +{ + RECT rc; + POINT pt; + int x, y; + HWND popup; + MSG msg; + BOOL ret; + + ShowWindow(hwnd, SW_SHOW); + UpdateWindow(hwnd); + + GetWindowRect(hwnd, &rc); + trace("main window %p: (%ld,%ld)-(%ld,%ld)\n", hwnd, rc.left, rc.top, rc.right, rc.bottom); + + popup = CreateWindowExA(0, "MainWindowClass", NULL, WS_POPUP, + rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, + hwnd, 0, 0, NULL); + assert(popup != 0); + ShowWindow(popup, SW_SHOW); + UpdateWindow(popup); + + GetWindowRect(popup, &rc); + trace("popup window %p: (%ld,%ld)-(%ld,%ld)\n", popup, rc.left, rc.top, rc.right, rc.bottom); + + x = rc.left + (rc.right - rc.left) / 2; + y = rc.top + (rc.bottom - rc.top) / 2; + trace("setting cursor to (%d,%d)\n", x, y); + + SetCursorPos(x, y); + GetCursorPos(&pt); + ok(x == pt.x && y == pt.y, "wrong cursor pos (%ld,%ld), expected (%d,%d)\n", pt.x, pt.y, x, y); + + /* force the system to update its internal queue mouse position, + * otherwise it won't generate relative mouse movements below. + */ + mouse_event(MOUSEEVENTF_MOVE, -1, -1, 0, 0); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + + msg.message = 0; + mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_MOUSEMOVE, "hwnd %p message %04x\n", msg.hwnd, msg.message); + /* FIXME: SetCursorPos in Wine generates additional WM_MOUSEMOVE message */ + if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + ok(msg.hwnd == popup && msg.message == WM_MOUSEMOVE, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); + + mouse_event(MOUSEEVENTF_MOVE, -1, -1, 0, 0); + ShowWindow(popup, SW_HIDE); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == hwnd && msg.message == WM_MOUSEMOVE, "hwnd %p message %04x\n", msg.hwnd, msg.message); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + + mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0); + ShowWindow(hwnd, SW_HIDE); + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok( !ret, "message %04x available\n", msg.message); + + /* test mouse clicks */ + + ShowWindow(hwnd, SW_SHOW); + ShowWindow(popup, SW_SHOW); + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + + ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + + ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message); + + ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONDBLCLK, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONDBLCLK, "hwnd %p message %04x\n", msg.hwnd, msg.message); + + ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message); + + ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE); + ok(!ret, "message %04x available\n", msg.message); + + ShowWindow(popup, SW_HIDE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); + mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); + + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == hwnd && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == hwnd && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message); + + test_lbuttondown_flag = TRUE; + SendMessageA(hwnd, WM_COMMAND, (WPARAM)popup, 0); + test_lbuttondown_flag = FALSE; + + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message); + ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n"); + + DestroyWindow(popup); +} + +static void test_validatergn(HWND hwnd) +{ + HWND child; + RECT rc, rc2; + HRGN rgn; + int ret; + child = CreateWindowExA(0, "static", NULL, WS_CHILD| WS_VISIBLE, 10, 10, 10, 10, hwnd, 0, 0, NULL); + ShowWindow(hwnd, SW_SHOW); + UpdateWindow( hwnd); + /* test that ValidateRect validates children*/ + InvalidateRect( child, NULL, 1); + GetWindowRect( child, &rc); + MapWindowPoints( NULL, hwnd, (POINT*) &rc, 2); + ret = GetUpdateRect( child, &rc2, 0); + ok( rc2.right > rc2.left && rc2.bottom > rc2.top, + "Update rectangle is empty!\n"); + ValidateRect( hwnd, &rc); + ret = GetUpdateRect( child, &rc2, 0); + ok( rc2.left == 0 && rc2.top == 0 && rc2.right == 0 && rc2.bottom == 0, + "Update rectangle %ld,%ld-%ld,%ld is not empty!\n", rc2.left, rc2.top, + rc2.right, rc2.bottom); + + /* now test ValidateRgn */ + InvalidateRect( child, NULL, 1); + GetWindowRect( child, &rc); + MapWindowPoints( NULL, hwnd, (POINT*) &rc, 2); + rgn = CreateRectRgnIndirect( &rc); + ValidateRgn( hwnd, rgn); + ret = GetUpdateRect( child, &rc2, 0); + ok( rc2.left == 0 && rc2.top == 0 && rc2.right == 0 && rc2.bottom == 0, + "Update rectangle %ld,%ld-%ld,%ld is not empty!\n", rc2.left, rc2.top, + rc2.right, rc2.bottom); + + DeleteObject( rgn); + DestroyWindow( child ); +} + +static void nccalchelper(HWND hwnd, INT x, INT y, RECT *prc) +{ + MoveWindow( hwnd, 0, 0, x, y, 0); + GetWindowRect( hwnd, prc); + trace("window rect is %ld,%ld - %ld,%ld\n", + prc->left,prc->top,prc->right,prc->bottom); + DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)prc); + trace("nccalc rect is %ld,%ld - %ld,%ld\n", + prc->left,prc->top,prc->right,prc->bottom); +} + +static void test_nccalcscroll(HWND parent) +{ + RECT rc1; + INT sbheight = GetSystemMetrics( SM_CYHSCROLL); + INT sbwidth = GetSystemMetrics( SM_CXVSCROLL); + HWND hwnd = CreateWindowExA(0, "static", NULL, + WS_CHILD| WS_VISIBLE | WS_VSCROLL | WS_HSCROLL , + 10, 10, 200, 200, parent, 0, 0, NULL); + ShowWindow( parent, SW_SHOW); + UpdateWindow( parent); + + /* test window too low for a horizontal scroll bar */ + nccalchelper( hwnd, 100, sbheight, &rc1); + ok( rc1.bottom - rc1.top == sbheight, "Height should be %d size is %ld,%ld - %ld,%ld\n", + sbheight, rc1.left, rc1.top, rc1.right, rc1.bottom); + + /* test window just high enough for a horizontal scroll bar */ + nccalchelper( hwnd, 100, sbheight + 1, &rc1); + ok( rc1.bottom - rc1.top == 1, "Height should be %d size is %ld,%ld - %ld,%ld\n", + 1, rc1.left, rc1.top, rc1.right, rc1.bottom); + + /* test window too narrow for a vertical scroll bar */ + nccalchelper( hwnd, sbwidth - 1, 100, &rc1); + ok( rc1.right - rc1.left == sbwidth - 1 , "Width should be %d size is %ld,%ld - %ld,%ld\n", + sbwidth - 1, rc1.left, rc1.top, rc1.right, rc1.bottom); + + /* test window just wide enough for a vertical scroll bar */ + nccalchelper( hwnd, sbwidth, 100, &rc1); + ok( rc1.right - rc1.left == 0, "Width should be %d size is %ld,%ld - %ld,%ld\n", + 0, rc1.left, rc1.top, rc1.right, rc1.bottom); + + /* same test, but with client edge: not enough width */ + SetWindowLong( hwnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE | GetWindowLong( hwnd, GWL_EXSTYLE)); + nccalchelper( hwnd, sbwidth, 100, &rc1); + ok( rc1.right - rc1.left == sbwidth - 2 * GetSystemMetrics(SM_CXEDGE), + "Width should be %d size is %ld,%ld - %ld,%ld\n", + sbwidth - 2 * GetSystemMetrics(SM_CXEDGE), rc1.left, rc1.top, rc1.right, rc1.bottom); + + DestroyWindow( hwnd); +} + +static void test_SetParent(void) +{ + BOOL ret; + HWND desktop = GetDesktopWindow(); + HMENU hMenu; + BOOL is_win9x = GetWindowLongPtrW(desktop, GWLP_WNDPROC) == 0; + HWND parent, child1, child2, child3, child4, sibling; + + parent = CreateWindowExA(0, "static", NULL, WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, 0, 0, NULL); + assert(parent != 0); + child1 = CreateWindowExA(0, "static", NULL, WS_CHILD, + 0, 0, 50, 50, parent, 0, 0, NULL); + assert(child1 != 0); + child2 = CreateWindowExA(0, "static", NULL, WS_POPUP, + 0, 0, 50, 50, child1, 0, 0, NULL); + assert(child2 != 0); + child3 = CreateWindowExA(0, "static", NULL, WS_CHILD, + 0, 0, 50, 50, child2, 0, 0, NULL); + assert(child3 != 0); + child4 = CreateWindowExA(0, "static", NULL, WS_POPUP, + 0, 0, 50, 50, child3, 0, 0, NULL); + assert(child4 != 0); + + trace("parent %p, child1 %p, child2 %p, child3 %p, child4 %p\n", + parent, child1, child2, child3, child4); + + check_parents(parent, desktop, 0, 0, 0, parent, parent); + check_parents(child1, parent, parent, parent, 0, parent, parent); + check_parents(child2, desktop, parent, parent, parent, child2, parent); + check_parents(child3, child2, child2, child2, 0, child2, parent); + check_parents(child4, desktop, child2, child2, child2, child4, parent); + +todo_wine { + ok(!IsChild(desktop, parent), "wrong parent/child %p/%p\n", desktop, parent); + ok(!IsChild(desktop, child1), "wrong parent/child %p/%p\n", desktop, child1); + ok(!IsChild(desktop, child2), "wrong parent/child %p/%p\n", desktop, child2); + ok(!IsChild(desktop, child3), "wrong parent/child %p/%p\n", desktop, child3); + ok(!IsChild(desktop, child4), "wrong parent/child %p/%p\n", desktop, child4); +} + + ok(IsChild(parent, child1), "wrong parent/child %p/%p\n", parent, child1); +todo_wine { + ok(!IsChild(desktop, child2), "wrong parent/child %p/%p\n", desktop, child2); +} + ok(!IsChild(parent, child2), "wrong parent/child %p/%p\n", parent, child2); + ok(!IsChild(child1, child2), "wrong parent/child %p/%p\n", child1, child2); + ok(!IsChild(parent, child3), "wrong parent/child %p/%p\n", parent, child3); + ok(IsChild(child2, child3), "wrong parent/child %p/%p\n", child2, child3); + ok(!IsChild(parent, child4), "wrong parent/child %p/%p\n", parent, child4); + ok(!IsChild(child3, child4), "wrong parent/child %p/%p\n", child3, child4); +todo_wine { + ok(!IsChild(desktop, child4), "wrong parent/child %p/%p\n", desktop, child4); +} + + if (!is_win9x) /* Win9x doesn't survive this test */ + { + ok(!SetParent(parent, child1), "SetParent should fail\n"); + ok(!SetParent(child2, child3), "SetParent should fail\n"); + ok(SetParent(child1, parent) != 0, "SetParent should not fail\n"); + ok(SetParent(parent, child2) != 0, "SetParent should not fail\n"); + ok(SetParent(parent, child3) != 0, "SetParent should not fail\n"); + ok(!SetParent(child2, parent), "SetParent should fail\n"); + ok(SetParent(parent, child4) != 0, "SetParent should not fail\n"); + + check_parents(parent, child4, child4, 0, 0, child4, parent); + check_parents(child1, parent, parent, parent, 0, child4, parent); + check_parents(child2, desktop, parent, parent, parent, child2, parent); + check_parents(child3, child2, child2, child2, 0, child2, parent); + check_parents(child4, desktop, child2, child2, child2, child4, parent); + } + + hMenu = CreateMenu(); + sibling = CreateWindowExA(0, "static", NULL, WS_OVERLAPPEDWINDOW, + 100, 100, 200, 200, 0, hMenu, 0, NULL); + assert(sibling != 0); + + ok(SetParent(sibling, parent) != 0, "SetParent should not fail\n"); + ok(GetMenu(sibling) == hMenu, "SetParent should not remove menu\n"); + + ret = DestroyWindow(parent); + ok( ret, "DestroyWindow() error %ld\n", GetLastError()); + + ok(!IsWindow(parent), "parent still exists\n"); + ok(!IsWindow(sibling), "sibling still exists\n"); + ok(!IsWindow(child1), "child1 still exists\n"); + ok(!IsWindow(child2), "child2 still exists\n"); + ok(!IsWindow(child3), "child3 still exists\n"); + ok(!IsWindow(child4), "child4 still exists\n"); +} + +static LRESULT WINAPI StyleCheckProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + LPCREATESTRUCT lpcs; + LPSTYLESTRUCT lpss; + + switch (msg) + { + case WM_NCCREATE: + case WM_CREATE: + lpcs = (LPCREATESTRUCT)lparam; + lpss = (LPSTYLESTRUCT)lpcs->lpCreateParams; + if (lpss) + { + if ((lpcs->dwExStyle & WS_EX_DLGMODALFRAME) || + ((!(lpcs->dwExStyle & WS_EX_STATICEDGE)) && + (lpcs->style & (WS_DLGFRAME | WS_THICKFRAME)))) + ok(lpcs->dwExStyle & WS_EX_WINDOWEDGE, "Window should have WS_EX_WINDOWEDGE style\n"); + else + ok(!(lpcs->dwExStyle & WS_EX_WINDOWEDGE), "Window shouldn't have WS_EX_WINDOWEDGE style\n"); + + ok((lpss->styleOld & ~WS_EX_WINDOWEDGE) == (lpcs->dwExStyle & ~WS_EX_WINDOWEDGE), + "Ex style (0x%08lx) should match what the caller passed to CreateWindowEx (0x%08lx)\n", + (lpss->styleOld & ~WS_EX_WINDOWEDGE), (lpcs->dwExStyle & ~WS_EX_WINDOWEDGE)); + + ok(lpss->styleNew == lpcs->style, + "Style (0x%08lx) should match what the caller passed to CreateWindowEx (0x%08lx)\n", + lpss->styleNew, lpcs->style); + } + break; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +static ATOM atomStyleCheckClass; + +static void register_style_check_class(void) +{ + WNDCLASS wc = + { + 0, + StyleCheckProc, + 0, + 0, + GetModuleHandle(NULL), + NULL, + LoadCursor(NULL, IDC_ARROW), + (HBRUSH)(COLOR_BTNFACE+1), + NULL, + TEXT("WineStyleCheck"), + }; + + atomStyleCheckClass = RegisterClass(&wc); +} + +static void check_window_style(DWORD dwStyleIn, DWORD dwExStyleIn, DWORD dwStyleOut, DWORD dwExStyleOut) +{ + DWORD dwActualStyle; + DWORD dwActualExStyle; + STYLESTRUCT ss; + HWND hwnd; + HWND hwndParent = NULL; + MSG msg; + + ss.styleNew = dwStyleIn; + ss.styleOld = dwExStyleIn; + + if (dwStyleIn & WS_CHILD) + { + hwndParent = CreateWindowEx(0, MAKEINTATOM(atomStyleCheckClass), NULL, + WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + } + + hwnd = CreateWindowEx(dwExStyleIn, MAKEINTATOM(atomStyleCheckClass), NULL, + dwStyleIn, 0, 0, 0, 0, hwndParent, NULL, NULL, &ss); + assert(hwnd); + + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + dwActualStyle = GetWindowLong(hwnd, GWL_STYLE); + dwActualExStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + ok((dwActualStyle == dwStyleOut) && (dwActualExStyle == dwExStyleOut), + "Style (0x%08lx) should really be 0x%08lx and/or Ex style (0x%08lx) should really be 0x%08lx\n", + dwActualStyle, dwStyleOut, dwActualExStyle, dwExStyleOut); + + DestroyWindow(hwnd); + if (hwndParent) DestroyWindow(hwndParent); +} + +/* tests what window styles the window manager automatically adds */ +static void test_window_styles(void) +{ + register_style_check_class(); + + check_window_style(0, 0, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_WINDOWEDGE); + check_window_style(WS_OVERLAPPEDWINDOW, 0, WS_CLIPSIBLINGS|WS_OVERLAPPEDWINDOW, WS_EX_WINDOWEDGE); + check_window_style(WS_CHILD, 0, WS_CHILD, 0); + check_window_style(WS_CHILD, WS_EX_WINDOWEDGE, WS_CHILD, 0); + check_window_style(0, WS_EX_TOOLWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW); + check_window_style(WS_POPUP, 0, WS_POPUP|WS_CLIPSIBLINGS, 0); + check_window_style(WS_POPUP, WS_EX_WINDOWEDGE, WS_POPUP|WS_CLIPSIBLINGS, 0); + check_window_style(WS_CHILD, WS_EX_DLGMODALFRAME, WS_CHILD, WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME); + check_window_style(WS_CHILD, WS_EX_DLGMODALFRAME|WS_EX_STATICEDGE, WS_CHILD, WS_EX_STATICEDGE|WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME); + check_window_style(WS_CAPTION, WS_EX_STATICEDGE, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_STATICEDGE|WS_EX_WINDOWEDGE); + check_window_style(0, WS_EX_APPWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_APPWINDOW|WS_EX_WINDOWEDGE); +} + +static void test_scrollvalidate( HWND parent) +{ + HDC hdc; + HRGN hrgn=CreateRectRgn(0,0,0,0); + HRGN exprgn, tmprgn, clipping; + RECT rc, rcu, cliprc; + /* create two overlapping child windows. The visual region + * of hwnd1 is clipped by the overlapping part of + * hwnd2 because of the WS_CLIPSIBLING style */ + HWND hwnd1, hwnd2; + + hwnd2 = CreateWindowExA(0, "static", NULL, + WS_CHILD| WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER , + 75, 30, 100, 100, parent, 0, 0, NULL); + hwnd1 = CreateWindowExA(0, "static", NULL, + WS_CHILD| WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER , + 25, 50, 100, 100, parent, 0, 0, NULL); + ShowWindow( parent, SW_SHOW); + UpdateWindow( parent); + GetClientRect( hwnd1, &rc); + cliprc=rc; + clipping = CreateRectRgn( 10, 10, 90, 90); + hdc = GetDC( hwnd1); + /* for a visual touch */ + TextOut( hdc, 0,10, "0123456789", 10); + ScrollDC( hdc, -10, -5, &rc, &cliprc, hrgn, &rcu); + if (winetest_debug > 0) dump_region(hrgn); + /* create a region with what is expected */ + exprgn = CreateRectRgn( 39,0,49,74); + tmprgn = CreateRectRgn( 88,79,98,93); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + tmprgn = CreateRectRgn( 0,93,98,98); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + ok( EqualRgn( exprgn, hrgn), "wrong update region\n"); + trace("update rect is %ld,%ld - %ld,%ld\n", + rcu.left,rcu.top,rcu.right,rcu.bottom); + /* now with clipping region */ + SelectClipRgn( hdc, clipping); + ScrollDC( hdc, -10, -5, &rc, &cliprc, hrgn, &rcu); + if (winetest_debug > 0) dump_region(hrgn); + /* create a region with what is expected */ + exprgn = CreateRectRgn( 39,10,49,74); + tmprgn = CreateRectRgn( 80,79,90,85); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + tmprgn = CreateRectRgn( 10,85,90,90); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + ok( EqualRgn( exprgn, hrgn), "wrong update region\n"); + trace("update rect is %ld,%ld - %ld,%ld\n", + rcu.left,rcu.top,rcu.right,rcu.bottom); + ReleaseDC( hwnd1, hdc); + + /* test scrolling a window with an update region */ + DestroyWindow( hwnd2); + ValidateRect( hwnd1, NULL); + SetRect( &rc, 40,40, 50,50); + InvalidateRect( hwnd1, &rc, 1); + GetClientRect( hwnd1, &rc); + cliprc=rc; + ScrollWindowEx( hwnd1, -10, 0, &rc, &cliprc, hrgn, &rcu, + SW_SCROLLCHILDREN | SW_INVALIDATE); + if (winetest_debug > 0) dump_region(hrgn); + exprgn = CreateRectRgn( 88,0,98,98); + tmprgn = CreateRectRgn( 30, 40, 50, 50); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + ok( EqualRgn( exprgn, hrgn), "wrong update region\n"); + + /* now test ScrollWindowEx with a combination of + * WS_CLIPCHILDREN style and SW_SCROLLCHILDREN flag */ + /* make hwnd2 the child of hwnd1 */ + hwnd2 = CreateWindowExA(0, "static", NULL, + WS_CHILD| WS_VISIBLE | WS_BORDER , + 50, 50, 100, 100, hwnd1, 0, 0, NULL); + SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) & ~WS_CLIPSIBLINGS); + GetClientRect( hwnd1, &rc); + cliprc=rc; + + /* WS_CLIPCHILDREN and SW_SCROLLCHILDREN */ + SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) | WS_CLIPCHILDREN ); + ValidateRect( hwnd1, NULL); + ValidateRect( hwnd2, NULL); + ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu, + SW_SCROLLCHILDREN | SW_INVALIDATE); + if (winetest_debug > 0) dump_region(hrgn); + exprgn = CreateRectRgn( 88,0,98,88); + tmprgn = CreateRectRgn( 0,88,98,98); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + ok( EqualRgn( exprgn, hrgn), "wrong update region\n"); + + /* SW_SCROLLCHILDREN */ + SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) & ~WS_CLIPCHILDREN ); + ValidateRect( hwnd1, NULL); + ValidateRect( hwnd2, NULL); + ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu, SW_SCROLLCHILDREN | SW_INVALIDATE); + if (winetest_debug > 0) dump_region(hrgn); + /* expected region is the same as in previous test */ + ok( EqualRgn( exprgn, hrgn), "wrong update region\n"); + + /* no SW_SCROLLCHILDREN */ + SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) & ~WS_CLIPCHILDREN ); + ValidateRect( hwnd1, NULL); + ValidateRect( hwnd2, NULL); + ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu, SW_INVALIDATE); + if (winetest_debug > 0) dump_region(hrgn); + /* expected region is the same as in previous test */ + ok( EqualRgn( exprgn, hrgn), "wrong update region\n"); + + /* WS_CLIPCHILDREN and no SW_SCROLLCHILDREN */ + SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) | WS_CLIPCHILDREN ); + ValidateRect( hwnd1, NULL); + ValidateRect( hwnd2, NULL); + ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu, SW_INVALIDATE); + if (winetest_debug > 0) dump_region(hrgn); + exprgn = CreateRectRgn( 88,0,98,20); + tmprgn = CreateRectRgn( 20,20,98,30); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + tmprgn = CreateRectRgn( 20,30,30,88); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + tmprgn = CreateRectRgn( 0,88,30,98); + CombineRgn( exprgn, exprgn, tmprgn, RGN_OR); + ok( EqualRgn( exprgn, hrgn), "wrong update region\n"); + + /* clean up */ + DeleteObject( hrgn); + DeleteObject( exprgn); + DeleteObject( tmprgn); + DestroyWindow( hwnd1); + DestroyWindow( hwnd2); +} + +/* couple of tests of return values of scrollbar functions + * called on a scrollbarless window */ +static void test_scroll(void) +{ + BOOL ret; + INT min, max; + SCROLLINFO si; + HWND hwnd = CreateWindowExA(0, "Static", "Wine test window", + WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP, + 100, 100, 200, 200, 0, 0, 0, NULL); + /* horizontal */ + ret = GetScrollRange( hwnd, SB_HORZ, &min, &max); + ok( ret, "GetScrollRange returns FALSE\n"); + ok( min == 0, "minimum scroll pos is %d (should be zero)\n", min); + ok( max == 0, "maximum scroll pos is %d (should be zero)\n", min); + si.cbSize = sizeof( si); + si.fMask = SIF_PAGE; + si.nPage = 0xdeadbeef; + ret = GetScrollInfo( hwnd, SB_HORZ, &si); + ok( !ret, "GetScrollInfo returns %d (should be zero)\n", ret); + ok( si.nPage == 0xdeadbeef, "unexpected value for nPage is %d\n", si.nPage); + /* vertical */ + ret = GetScrollRange( hwnd, SB_VERT, &min, &max); + ok( ret, "GetScrollRange returns FALSE\n"); + ok( min == 0, "minimum scroll pos is %d (should be zero)\n", min); + ok( max == 0, "maximum scroll pos is %d (should be zero)\n", min); + si.cbSize = sizeof( si); + si.fMask = SIF_PAGE; + si.nPage = 0xdeadbeef; + ret = GetScrollInfo( hwnd, SB_VERT, &si); + ok( !ret, "GetScrollInfo returns %d (should be zero)\n", ret); + ok( si.nPage == 0xdeadbeef, "unexpected value for nPage is %d\n", si.nPage); + /* clean up */ + DestroyWindow( hwnd); +} + +static void test_params(void) +{ + INT rc; + + /* Just a param check */ + SetLastError(0xdeadbeef); + rc = GetWindowText(hwndMain2, NULL, 1024); + ok( rc==0, "GetWindowText: rc=%d err=%ld\n",rc,GetLastError()); +} + +static void test_AWRwindow(LPCSTR class, LONG style, LONG exStyle, BOOL menu) +{ + HWND hwnd = 0; + + hwnd = CreateWindowEx(exStyle, class, class, style, + 110, 100, + 225, 200, + 0, + menu ? hmenu : 0, + 0, 0); + if (!hwnd) { + trace("Failed to create window class=%s, style=0x%08lx, exStyle=0x%08lx\n", class, style, exStyle); + return; + } + ShowWindow(hwnd, SW_SHOW); + + test_nonclient_area(hwnd); + + SetMenu(hwnd, 0); + DestroyWindow(hwnd); +} + +static BOOL AWR_init(void) +{ + WNDCLASS class; + + class.style = CS_HREDRAW | CS_VREDRAW; + class.lpfnWndProc = DefWindowProcA; + class.cbClsExtra = 0; + class.cbWndExtra = 0; + class.hInstance = 0; + class.hIcon = LoadIcon (0, IDI_APPLICATION); + class.hCursor = LoadCursor (0, IDC_ARROW); + class.hbrBackground = 0; + class.lpszMenuName = 0; + class.lpszClassName = szAWRClass; + + if (!RegisterClass (&class)) { + ok(FALSE, "RegisterClass failed\n"); + return FALSE; + } + + hmenu = CreateMenu(); + if (!hmenu) + return FALSE; + ok(hmenu != 0, "Failed to create menu\n"); + ok(AppendMenu(hmenu, MF_STRING, 1, "Test!"), "Failed to create menu item\n"); + + return TRUE; +} + + +static void test_AWR_window_size(BOOL menu) +{ + LONG styles[] = { + WS_POPUP, + WS_MAXIMIZE, WS_BORDER, WS_DLGFRAME, + WS_SYSMENU, + WS_THICKFRAME, + WS_MINIMIZEBOX, WS_MAXIMIZEBOX, + WS_HSCROLL, WS_VSCROLL + }; + LONG exStyles[] = { + WS_EX_CLIENTEDGE, + WS_EX_TOOLWINDOW, WS_EX_WINDOWEDGE, + WS_EX_APPWINDOW, +#if 0 + /* These styles have problems on (at least) WinXP (SP2) and Wine */ + WS_EX_DLGMODALFRAME, + WS_EX_STATICEDGE, +#endif + }; + + int i; + + /* A exhaustive check of all the styles takes too long + * so just do a (hopefully representative) sample + */ + for (i = 0; i < COUNTOF(styles); ++i) + test_AWRwindow(szAWRClass, styles[i], 0, menu); + for (i = 0; i < COUNTOF(exStyles); ++i) { + test_AWRwindow(szAWRClass, WS_POPUP, exStyles[i], menu); + test_AWRwindow(szAWRClass, WS_THICKFRAME, exStyles[i], menu); + } +} +#undef COUNTOF + +#define SHOWSYSMETRIC(SM) trace(#SM "=%d\n", GetSystemMetrics(SM)) + +static void test_AdjustWindowRect(void) +{ + if (!AWR_init()) + return; + + SHOWSYSMETRIC(SM_CYCAPTION); + SHOWSYSMETRIC(SM_CYSMCAPTION); + SHOWSYSMETRIC(SM_CYMENU); + SHOWSYSMETRIC(SM_CXEDGE); + SHOWSYSMETRIC(SM_CYEDGE); + SHOWSYSMETRIC(SM_CXVSCROLL); + SHOWSYSMETRIC(SM_CYHSCROLL); + SHOWSYSMETRIC(SM_CXFRAME); + SHOWSYSMETRIC(SM_CYFRAME); + SHOWSYSMETRIC(SM_CXDLGFRAME); + SHOWSYSMETRIC(SM_CYDLGFRAME); + SHOWSYSMETRIC(SM_CXBORDER); + SHOWSYSMETRIC(SM_CYBORDER); + + test_AWR_window_size(FALSE); + test_AWR_window_size(TRUE); + + DestroyMenu(hmenu); +} +#undef SHOWSYSMETRIC + + +/* Global variables to trigger exit from loop */ +static int redrawComplete, WMPAINT_count; + +static LRESULT WINAPI redraw_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_PAINT: + trace("doing WM_PAINT %d\n", WMPAINT_count); + WMPAINT_count++; + if (WMPAINT_count > 10 && redrawComplete == 0) { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + EndPaint(hwnd, &ps); + return 1; + } + return 0; + break; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +/* Ensure we exit from RedrawNow regardless of invalidated area */ +static void test_redrawnow(void) +{ + WNDCLASSA cls; + HWND hwndMain; + + cls.style = CS_DBLCLKS; + cls.lpfnWndProc = redraw_window_procA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "RedrawWindowClass"; + + if(!RegisterClassA(&cls)) { + trace("Register failed %ld\n", GetLastError()); + return; + } + + hwndMain = CreateWindowA("RedrawWindowClass", "Main Window", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, 100, 100, NULL, NULL, 0, NULL); + + ok( WMPAINT_count == 0, "Multiple unexpected WM_PAINT calls %d\n", WMPAINT_count); + ShowWindow(hwndMain, SW_SHOW); + ok( WMPAINT_count == 0, "Multiple unexpected WM_PAINT calls %d\n", WMPAINT_count); + RedrawWindow(hwndMain, NULL,NULL,RDW_UPDATENOW | RDW_ALLCHILDREN); + ok( WMPAINT_count == 1, "Multiple unexpected WM_PAINT calls %d\n", WMPAINT_count); + redrawComplete = TRUE; + ok( WMPAINT_count < 10, "RedrawWindow (RDW_UPDATENOW) never completed (%d)\n", WMPAINT_count); + + /* clean up */ + DestroyWindow( hwndMain); +} + +struct parentdc_stat { + RECT client; + RECT clip; + RECT paint; +}; + +struct parentdc_test { + struct parentdc_stat main, main_todo; + struct parentdc_stat child1, child1_todo; + struct parentdc_stat child2, child2_todo; +}; + +static LRESULT WINAPI parentdc_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + RECT rc; + PAINTSTRUCT ps; + + struct parentdc_stat *t = (struct parentdc_stat *)GetWindowLongPtrA(hwnd, GWL_USERDATA); + + switch (msg) + { + case WM_PAINT: + trace("doing WM_PAINT on %p\n", hwnd); + GetClientRect(hwnd, &rc); + CopyRect(&t->client, &rc); + trace("client rect (%ld, %ld)-(%ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom); + GetWindowRect(hwnd, &rc); + trace("window rect (%ld, %ld)-(%ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom); + BeginPaint(hwnd, &ps); + CopyRect(&t->paint, &ps.rcPaint); + GetClipBox(ps.hdc, &rc); + CopyRect(&t->clip, &rc); + trace("clip rect (%ld, %ld)-(%ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom); + trace("paint rect (%ld, %ld)-(%ld, %ld)\n", ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + EndPaint(hwnd, &ps); + return 0; + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +static void zero_parentdc_stat(struct parentdc_stat *t) +{ + SetRectEmpty(&t->client); + SetRectEmpty(&t->clip); + SetRectEmpty(&t->paint); +} + +static void zero_parentdc_test(struct parentdc_test *t) +{ + zero_parentdc_stat(&t->main); + zero_parentdc_stat(&t->child1); + zero_parentdc_stat(&t->child2); +} + +#define parentdc_field_ok(t, w, r, f, got) \ + ok (t.w.r.f==got.w.r.f, "window " #w ", rect " #r ", field " #f \ + ": expected %ld, got %ld\n", \ + t.w.r.f, got.w.r.f) + +#define parentdc_todo_field_ok(t, w, r, f, got) \ + if (t.w##_todo.r.f) todo_wine { parentdc_field_ok(t, w, r, f, got); } \ + else parentdc_field_ok(t, w, r, f, got) + +#define parentdc_rect_ok(t, w, r, got) \ + parentdc_todo_field_ok(t, w, r, left, got); \ + parentdc_todo_field_ok(t, w, r, top, got); \ + parentdc_todo_field_ok(t, w, r, right, got); \ + parentdc_todo_field_ok(t, w, r, bottom, got); + +#define parentdc_win_ok(t, w, got) \ + parentdc_rect_ok(t, w, client, got); \ + parentdc_rect_ok(t, w, clip, got); \ + parentdc_rect_ok(t, w, paint, got); + +#define parentdc_ok(t, got) \ + parentdc_win_ok(t, main, got); \ + parentdc_win_ok(t, child1, got); \ + parentdc_win_ok(t, child2, got); + +static void test_csparentdc(void) +{ + WNDCLASSA clsMain, cls; + HWND hwndMain, hwnd1, hwnd2; + MSG msg; + RECT rc; + + struct parentdc_test test_answer; + +#define nothing_todo {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}} + const struct parentdc_test test1 = + { + {{0, 0, 150, 150}, {0, 0, 150, 150}, {0, 0, 150, 150}}, nothing_todo, + {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, + {{0, 0, 40, 40}, {-40, -40, 110, 110}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, + }; + + const struct parentdc_test test2 = + { + {{0, 0, 150, 150}, {0, 0, 50, 50}, {0, 0, 50, 50}}, nothing_todo, + {{0, 0, 40, 40}, {-20, -20, 30, 30}, {0, 0, 30, 30}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}, + {{0, 0, 40, 40}, {-40, -40, 10, 10}, {0, 0, 10, 10}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}, + }; + + const struct parentdc_test test3 = + { + {{0, 0, 150, 150}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo, + {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, + {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, + }; + + const struct parentdc_test test4 = + { + {{0, 0, 150, 150}, {40, 40, 50, 50}, {40, 40, 50, 50}}, nothing_todo, + {{0, 0, 40, 40}, {20, 20, 30, 30}, {20, 20, 30, 30}}, nothing_todo, + {{0, 0, 40, 40}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo, + }; + + const struct parentdc_test test5 = + { + {{0, 0, 150, 150}, {20, 20, 60, 60}, {20, 20, 60, 60}}, nothing_todo, + {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, + {{0, 0, 40, 40}, {-20, -20, 20, 20}, {0, 0, 20, 20}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}}, + }; + + const struct parentdc_test test6 = + { + {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, + {{0, 0, 40, 40}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo, + {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, + }; + + const struct parentdc_test test7 = + { + {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, + {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}}, + {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo, + }; +#undef nothing_todo + + clsMain.style = CS_DBLCLKS; + clsMain.lpfnWndProc = parentdc_window_procA; + clsMain.cbClsExtra = 0; + clsMain.cbWndExtra = 0; + clsMain.hInstance = GetModuleHandleA(0); + clsMain.hIcon = 0; + clsMain.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + clsMain.hbrBackground = GetStockObject(WHITE_BRUSH); + clsMain.lpszMenuName = NULL; + clsMain.lpszClassName = "ParentDcMainWindowClass"; + + if(!RegisterClassA(&clsMain)) { + trace("Register failed %ld\n", GetLastError()); + return; + } + + cls.style = CS_DBLCLKS | CS_PARENTDC; + cls.lpfnWndProc = parentdc_window_procA; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(0); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "ParentDcWindowClass"; + + if(!RegisterClassA(&cls)) { + trace("Register failed %ld\n", GetLastError()); + return; + } + + SetRect(&rc, 0, 0, 150, 150); + AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0); + hwndMain = CreateWindowA("ParentDcMainWindowClass", "Main Window", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, 0, NULL); + SetWindowLongPtrA(hwndMain, GWL_USERDATA, (DWORD_PTR)&test_answer.main); + hwnd1 = CreateWindowA("ParentDcWindowClass", "Child Window 1", WS_CHILD, + 20, 20, 40, 40, hwndMain, NULL, 0, NULL); + SetWindowLongPtrA(hwnd1, GWL_USERDATA, (DWORD_PTR)&test_answer.child1); + hwnd2 = CreateWindowA("ParentDcWindowClass", "Child Window 2", WS_CHILD, + 40, 40, 40, 40, hwndMain, NULL, 0, NULL); + SetWindowLongPtrA(hwnd2, GWL_USERDATA, (DWORD_PTR)&test_answer.child2); + ShowWindow(hwndMain, SW_SHOW); + ShowWindow(hwnd1, SW_SHOW); + ShowWindow(hwnd2, SW_SHOW); + + zero_parentdc_test(&test_answer); + InvalidateRect(hwndMain, NULL, TRUE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + parentdc_ok(test1, test_answer); + + zero_parentdc_test(&test_answer); + SetRect(&rc, 0, 0, 50, 50); + InvalidateRect(hwndMain, &rc, TRUE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + parentdc_ok(test2, test_answer); + + zero_parentdc_test(&test_answer); + SetRect(&rc, 0, 0, 10, 10); + InvalidateRect(hwndMain, &rc, TRUE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + parentdc_ok(test3, test_answer); + + zero_parentdc_test(&test_answer); + SetRect(&rc, 40, 40, 50, 50); + InvalidateRect(hwndMain, &rc, TRUE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + parentdc_ok(test4, test_answer); + + zero_parentdc_test(&test_answer); + SetRect(&rc, 20, 20, 60, 60); + InvalidateRect(hwndMain, &rc, TRUE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + parentdc_ok(test5, test_answer); + + zero_parentdc_test(&test_answer); + SetRect(&rc, 0, 0, 10, 10); + InvalidateRect(hwnd1, &rc, TRUE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + parentdc_ok(test6, test_answer); + + zero_parentdc_test(&test_answer); + SetRect(&rc, -5, -5, 65, 65); + InvalidateRect(hwnd1, &rc, TRUE); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + parentdc_ok(test7, test_answer); + + DestroyWindow(hwndMain); + DestroyWindow(hwnd1); + DestroyWindow(hwnd2); +} + +static void test_IsWindowUnicode(void) +{ + static const char ansi_class_nameA[] = "ansi class name"; + static const WCHAR ansi_class_nameW[] = {'a','n','s','i',' ','c','l','a','s','s',' ','n','a','m','e',0}; + static const char unicode_class_nameA[] = "unicode class name"; + static const WCHAR unicode_class_nameW[] = {'u','n','i','c','o','d','e',' ','c','l','a','s','s',' ','n','a','m','e',0}; + WNDCLASSA classA; + WNDCLASSW classW; + HWND hwnd; + + memset(&classW, 0, sizeof(classW)); + classW.hInstance = GetModuleHandleA(0); + classW.lpfnWndProc = DefWindowProcW; + classW.lpszClassName = unicode_class_nameW; + if (!RegisterClassW(&classW)) return; + + memset(&classA, 0, sizeof(classA)); + classA.hInstance = GetModuleHandleA(0); + classA.lpfnWndProc = DefWindowProcA; + classA.lpszClassName = ansi_class_nameA; + assert(RegisterClassA(&classA)); + + /* unicode class: window proc */ + hwnd = CreateWindowExW(0, unicode_class_nameW, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA); + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW); + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + + DestroyWindow(hwnd); + + hwnd = CreateWindowExA(0, unicode_class_nameA, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA); + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW); + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + + DestroyWindow(hwnd); + + /* ansi class: window proc */ + hwnd = CreateWindowExW(0, ansi_class_nameW, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW); + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA); + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + + DestroyWindow(hwnd); + + hwnd = CreateWindowExA(0, ansi_class_nameA, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW); + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA); + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + + DestroyWindow(hwnd); + + /* unicode class: class proc */ + hwnd = CreateWindowExW(0, unicode_class_nameW, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + SetClassLongPtrA(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcA); + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + /* do not restore class window proc back to unicode */ + + DestroyWindow(hwnd); + + hwnd = CreateWindowExA(0, unicode_class_nameA, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + SetClassLongPtrW(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcW); + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + + DestroyWindow(hwnd); + + /* ansi class: class proc */ + hwnd = CreateWindowExW(0, ansi_class_nameW, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + SetClassLongPtrW(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcW); + ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n"); + /* do not restore class window proc back to ansi */ + + DestroyWindow(hwnd); + + hwnd = CreateWindowExA(0, ansi_class_nameA, NULL, WS_POPUP, + 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL); + assert(hwnd); + + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + SetClassLongPtrA(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcA); + ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n"); + + DestroyWindow(hwnd); +} + +START_TEST(win) +{ + pGetAncestor = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetAncestor" ); + pGetWindowInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetWindowInfo" ); + + hwndMain = CreateWindowExA(0, "static", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, NULL); + if (hwndMain) + { + ok(!GetParent(hwndMain), "GetParent should return 0 for message only windows\n"); + if (pGetAncestor) + { + hwndMessage = pGetAncestor(hwndMain, GA_PARENT); + ok(hwndMessage != 0, "GetAncestor(GA_PARENT) should not return 0 for message only windows\n"); + trace("hwndMessage %p\n", hwndMessage); + } + DestroyWindow(hwndMain); + } + else + trace("CreateWindowExA with parent HWND_MESSAGE failed\n"); + + if (!RegisterWindowClasses()) assert(0); + + hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId()); + assert(hhook); + + hwndMain = CreateWindowExA(/*WS_EX_TOOLWINDOW*/ 0, "MainWindowClass", "Main window", + WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_MAXIMIZEBOX | WS_POPUP, + 100, 100, 200, 200, + 0, 0, 0, NULL); + test_nonclient_area(hwndMain); + + hwndMain2 = CreateWindowExA(/*WS_EX_TOOLWINDOW*/ 0, "MainWindowClass", "Main window 2", + WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_MAXIMIZEBOX | WS_POPUP, + 100, 100, 200, 200, + 0, 0, 0, NULL); + assert( hwndMain ); + assert( hwndMain2 ); + + test_params(); + + test_capture_1(); + test_capture_2(); + test_capture_3(hwndMain, hwndMain2); + + test_parent_owner(); + test_SetParent(); + test_shell_window(); + + test_mdi(); + test_icons(); + test_SetWindowPos(hwndMain); + test_SetMenu(hwndMain); + test_SetFocus(hwndMain); + test_SetActiveWindow(hwndMain); + + test_children_zorder(hwndMain); + test_keyboard_input(hwndMain); + test_mouse_input(hwndMain); + test_validatergn(hwndMain); + test_nccalcscroll( hwndMain); + test_scrollvalidate( hwndMain); + test_scroll(); + test_IsWindowUnicode(); + + UnhookWindowsHookEx(hhook); + + test_AdjustWindowRect(); + test_window_styles(); + test_redrawnow(); + test_csparentdc(); +} diff --git a/reactos/regtests/winetests/user32/winstation.c b/reactos/regtests/winetests/user32/winstation.c new file mode 100755 index 00000000000..83965d8bfc1 --- /dev/null +++ b/reactos/regtests/winetests/user32/winstation.c @@ -0,0 +1,218 @@ +/* + * Unit tests for window stations and desktops + * + * Copyright 2002 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "wine/test.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" + +#define WINSTA_ALL_ACCESS 0x037f// fixme +#define DESKTOP_ALL_ACCESS 0x01ff + +static void print_object( HANDLE obj ) +{ + char buffer[100]; + DWORD size; + + strcpy( buffer, "foobar" ); + if (!GetUserObjectInformationA( obj, UOI_NAME, buffer, sizeof(buffer), &size )) + trace( "could not get info for %p\n", obj ); + else + trace( "obj %p name '%s'\n", obj, buffer ); + strcpy( buffer, "foobar" ); + if (!GetUserObjectInformationA( obj, UOI_TYPE, buffer, sizeof(buffer), &size )) + trace( "could not get type for %p\n", obj ); + else + trace( "obj %p type '%s'\n", obj, buffer ); +} + +static HDESK initial_desktop; + +static DWORD CALLBACK thread( LPVOID arg ) +{ + HDESK d1, d2; + HWND hwnd = CreateWindowExA(0,"BUTTON","test",WS_POPUP,0,0,100,100,GetDesktopWindow(),0,0,0); + ok( hwnd != 0, "CreateWindow failed\n" ); + d1 = GetThreadDesktop(GetCurrentThreadId()); + trace( "thread %p desktop: %p\n", arg, d1 ); + ok( d1 == initial_desktop, "thread %p doesn't use initial desktop\n", arg ); + + SetLastError( 0xdeadbeef ); + ok( !CloseHandle( d1 ), "CloseHandle succeeded\n" ); + ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() ); + SetLastError( 0xdeadbeef ); + ok( !CloseDesktop( d1 ), "CloseDesktop succeeded\n" ); + ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() ); + print_object( d1 ); + d2 = CreateDesktop( "foobar2", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); + trace( "created desktop %p\n", d2 ); + ok( d2 != 0, "CreateDesktop failed\n" ); + + SetLastError( 0xdeadbeef ); + ok( !SetThreadDesktop( d2 ), "set thread desktop succeeded with existing window\n" ); + ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() ); + + DestroyWindow( hwnd ); + ok( SetThreadDesktop( d2 ), "set thread desktop failed\n" ); + d1 = GetThreadDesktop(GetCurrentThreadId()); + ok( d1 == d2, "GetThreadDesktop did not return set desktop %p/%p\n", d1, d2 ); + print_object( d2 ); + if (arg < (LPVOID)5) + { + HANDLE hthread = CreateThread( NULL, 0, thread, (char *)arg + 1, 0, NULL ); + Sleep(1000); + WaitForSingleObject( hthread, INFINITE ); + CloseHandle( hthread ); + } + return 0; +} + +static void test_handles(void) +{ + HWINSTA w1, w2, w3; + HDESK d1, d2, d3; + HANDLE hthread; + DWORD id, flags; + + /* win stations */ + + w1 = GetProcessWindowStation(); + ok( GetProcessWindowStation() == w1, "GetProcessWindowStation returned different handles\n" ); + ok( !CloseWindowStation(w1), "closing process win station succeeded\n" ); + SetLastError( 0xdeadbeef ); + ok( !CloseHandle(w1), "closing process win station handle succeeded\n" ); + ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() ); + print_object( w1 ); + + flags = 0; + ok( GetHandleInformation( w1, &flags ), "GetHandleInformation failed\n" ); + ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), "handle %p PROTECT_FROM_CLOSE set\n", w1 ); + + ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0, + TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); + ok( CloseWindowStation(w2), "closing dup win station failed\n" ); + + ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0, + TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); + ok( CloseHandle(w2), "closing dup win station handle failed\n" ); + + w2 = CreateWindowStation("WinSta0", 0, WINSTA_ALL_ACCESS, NULL ); + ok( w2 != 0, "CreateWindowStation failed\n" ); + ok( w2 != w1, "CreateWindowStation returned default handle\n" ); + SetLastError( 0xdeadbeef ); + ok( !CloseDesktop( (HDESK)w2 ), "CloseDesktop succeeded on win station\n" ); + ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() ); + ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" ); + + w2 = CreateWindowStation("WinSta0", 0, WINSTA_ALL_ACCESS, NULL ); + ok( CloseHandle( w2 ), "CloseHandle failed\n" ); + + w2 = OpenWindowStation("winsta0", TRUE, WINSTA_ALL_ACCESS ); + ok( w2 != 0, "OpenWindowStation failed\n" ); + ok( w2 != w1, "OpenWindowStation returned default handle\n" ); + ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" ); + + w2 = OpenWindowStation("dummy name", TRUE, WINSTA_ALL_ACCESS ); + ok( !w2, "open dummy win station succeeded\n" ); + + CreateMutexA( NULL, 0, "foobar" ); + w2 = CreateWindowStation("foobar", 0, WINSTA_ALL_ACCESS, NULL ); + ok( w2 != 0, "create foobar station failed\n" ); + + w3 = OpenWindowStation("foobar", TRUE, WINSTA_ALL_ACCESS ); + ok( w3 != 0, "open foobar station failed\n" ); + ok( w3 != w2, "open foobar station returned same handle\n" ); + ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" ); + ok( CloseWindowStation( w3 ), "CloseWindowStation failed\n" ); + + w3 = OpenWindowStation("foobar", TRUE, WINSTA_ALL_ACCESS ); + ok( !w3, "open foobar station succeeded\n" ); + + /* desktops */ + d1 = GetThreadDesktop(GetCurrentThreadId()); + initial_desktop = d1; + ok( GetThreadDesktop(GetCurrentThreadId()) == d1, + "GetThreadDesktop returned different handles\n" ); + + flags = 0; + ok( GetHandleInformation( d1, &flags ), "GetHandleInformation failed\n" ); + ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), "handle %p PROTECT_FROM_CLOSE set\n", d1 ); + + SetLastError( 0xdeadbeef ); + ok( !CloseDesktop(d1), "closing thread desktop succeeded\n" ); + ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + ok( !CloseHandle(d1), "closing thread desktop handle failed\n" ); + ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() ); + + ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0, + TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); + ok( CloseDesktop(d2), "closing dup desktop failed\n" ); + + ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0, + TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" ); + ok( CloseHandle(d2), "closing dup desktop handle failed\n" ); + + d2 = OpenDesktop( "dummy name", 0, TRUE, DESKTOP_ALL_ACCESS ); + ok( !d2, "open dummy desktop succeeded\n" ); + + d2 = CreateDesktop( "foobar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL ); + ok( d2 != 0, "create foobar desktop failed\n" ); + SetLastError( 0xdeadbeef ); + ok( !CloseWindowStation( (HWINSTA)d2 ), "CloseWindowStation succeeded on desktop\n" ); + ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() ); + + d3 = OpenDesktop( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS ); + ok( d3 != 0, "open foobar desktop failed\n" ); + ok( d3 != d2, "open foobar desktop returned same handle\n" ); + ok( CloseDesktop( d2 ), "CloseDesktop failed\n" ); + ok( CloseDesktop( d3 ), "CloseDesktop failed\n" ); + + d3 = OpenDesktop( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS ); + ok( !d3, "open foobar desktop succeeded\n" ); + + ok( !CloseHandle(d1), "closing thread desktop handle succeeded\n" ); + d2 = GetThreadDesktop(GetCurrentThreadId()); + ok( d1 == d2, "got different handles after close\n" ); + + trace( "thread 1 desktop: %p\n", d1 ); + print_object( d1 ); + hthread = CreateThread( NULL, 0, thread, (LPVOID)2, 0, &id ); + Sleep(1000); + trace( "get other thread desktop: %p\n", GetThreadDesktop(id) ); + WaitForSingleObject( hthread, INFINITE ); + CloseHandle( hthread ); +} + +START_TEST(winstation) +{ + /* Check whether this platform supports WindowStation calls */ + + SetLastError( 0xdeadbeef ); + GetProcessWindowStation(); + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + trace("WindowStation calls not supported on this platform\n"); + return; + } + + test_handles(); +} diff --git a/reactos/regtests/winetests/user32/wsprintf.c b/reactos/regtests/winetests/user32/wsprintf.c new file mode 100755 index 00000000000..bbebdd75f9d --- /dev/null +++ b/reactos/regtests/winetests/user32/wsprintf.c @@ -0,0 +1,57 @@ + /* Unit test suite for the wsprintf functions + * + * Copyright 2002 Bill Medland + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" + +static void wsprintfATest(void) +{ + char buf[25]; + int rc; + + rc=wsprintfA(buf, "%010ld", -1); + ok(rc == 10, "wsPrintfA length failure: rc=%d error=%ld\n",rc,GetLastError()); + ok((lstrcmpA(buf, "-000000001") == 0), + "wsprintfA zero padded negative value failure: buf=[%s]\n",buf); +} + +static void wsprintfWTest(void) +{ + static const WCHAR fmt[] = {'%','0','1','0','l','d','\0'}; + static const WCHAR target[] = {'-','0','0','0','0','0','0','0','0','1', '\0'}; + WCHAR buf[25]; + int rc; + + rc=wsprintfW(buf, fmt, -1); + if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) + return; + ok(rc == 10, "wsPrintfW length failure: rc=%d error=%ld\n",rc,GetLastError()); + ok((lstrcmpW(buf, target) == 0), + "wsprintfW zero padded negative value failure\n"); +} + +START_TEST(wsprintf) +{ + wsprintfATest(); + wsprintfWTest(); +}