From: Stefan Dösinger Subject: [PATCH 4/7] d3d9/tests: Port test_wndproc to d3d9ex (v2). Message-Id: <1412595782-19231-4-git-send-email-stefan@codeweavers.com> Date: Mon, 6 Oct 2014 13:42:59 +0200 v2: Don't test wparam of WM_ACTIVATE during creation. --- dlls/d3d9/tests/d3d9ex.c | 358 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 353 insertions(+), 5 deletions(-) diff --git a/dlls/d3d9/tests/d3d9ex.c b/dlls/d3d9/tests/d3d9ex.c index 0309013..c8e7c69 100644 --- a/dlls/d3d9/tests/d3d9ex.c +++ b/dlls/d3d9/tests/d3d9ex.c @@ -420,7 +420,6 @@ static void test_get_adapter_displaymode_ex(void) D3DDISPLAYMODE mode; D3DDISPLAYMODEEX mode_ex; D3DDISPLAYROTATION rotation; - HANDLE hdll; DEVMODEA startmode; LONG retval; @@ -447,10 +446,6 @@ static void test_get_adapter_displaymode_ex(void) hr); ok(d3d9 != NULL && d3d9 != (void *) 0xdeadbeef, "QueryInterface returned interface %p, expected != NULL && != 0xdeadbeef\n", d3d9); - /* change displayorientation*/ - hdll = GetModuleHandleA("user32.dll"); - pEnumDisplaySettingsExA = (void*)GetProcAddress(hdll, "EnumDisplaySettingsExA"); - pChangeDisplaySettingsExA = (void*)GetProcAddress(hdll, "ChangeDisplaySettingsExA"); if (!pEnumDisplaySettingsExA || !pChangeDisplaySettingsExA) goto out; @@ -1331,8 +1326,356 @@ done: DestroyWindow(window); } +/* try to make sure pending X events have been processed before continuing */ +static void flush_events(void) +{ + MSG msg; + int diff = 200; + int min_timeout = 100; + DWORD time = GetTickCount() + diff; + + while (diff > 0) + { + if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break; + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + diff = time - GetTickCount(); + } +} + +static HWND filter_messages; + +enum message_window +{ + DEVICE_WINDOW, + FOCUS_WINDOW, +}; + +struct message +{ + UINT message; + enum message_window window; + BOOL check_wparam; + WPARAM expect_wparam; +}; + +static const struct message *expect_messages; +static HWND device_window, focus_window; + +struct wndproc_thread_param +{ + HWND dummy_window; + HANDLE window_created; + HANDLE test_finished; + BOOL running_in_foreground; +}; + +static LRESULT CALLBACK test_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + if (filter_messages && filter_messages == hwnd) + { + if (message != WM_DISPLAYCHANGE && message != WM_IME_NOTIFY) + todo_wine ok( 0, "Received unexpected message %#x for window %p.\n", message, hwnd); + } + + if (expect_messages) + { + HWND w; + + switch (expect_messages->window) + { + case DEVICE_WINDOW: + w = device_window; + break; + + case FOCUS_WINDOW: + w = focus_window; + break; + + default: + w = NULL; + break; + }; + + + if (hwnd == w && expect_messages->message == message) + { + if (expect_messages->check_wparam) + ok(wparam == expect_messages->expect_wparam, + "Got unexpected wparam %lx for message %x, expected %lx.\n", + wparam, message, expect_messages->expect_wparam); + + ++expect_messages; + } + } + + return DefWindowProcA(hwnd, message, wparam, lparam); +} + +static DWORD WINAPI wndproc_thread(void *param) +{ + struct wndproc_thread_param *p = param; + DWORD res; + BOOL ret; + + p->dummy_window = CreateWindowA("d3d9_test_wndproc_wc", "d3d9_test", + WS_MAXIMIZE | WS_VISIBLE | WS_CAPTION, 0, 0, 200, 200, 0, 0, 0, 0); + p->running_in_foreground = SetForegroundWindow(p->dummy_window); + + ret = SetEvent(p->window_created); + ok(ret, "SetEvent failed, last error %#x.\n", GetLastError()); + + for (;;) + { + MSG msg; + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + res = WaitForSingleObject(p->test_finished, 100); + if (res == WAIT_OBJECT_0) break; + if (res != WAIT_TIMEOUT) + { + ok(0, "Wait failed (%#x), last error %#x.\n", res, GetLastError()); + break; + } + } + + DestroyWindow(p->dummy_window); + + return 0; +} + +static void test_wndproc(void) +{ + struct wndproc_thread_param thread_params; + IDirect3DDevice9Ex *device; + WNDCLASSA wc = {0}; + IDirect3D9Ex *d3d9; + HANDLE thread; + LONG_PTR proc; + ULONG ref; + DWORD res, tid; + HWND tmp; + D3DPRESENT_PARAMETERS present_parameters = {0}; + DEVMODEA devmode, orig_mode; + BOOL ret; + D3DDISPLAYMODEEX mode; + HRESULT hr; + + static const struct message create_messages[] = + { + {WM_WINDOWPOSCHANGING, FOCUS_WINDOW, FALSE, 0}, + /* Do not test wparam here. If device creation succeeds, + * wparam is WA_ACTIVE. If device creation fails (testbot) + * wparam is set to WA_INACTIVE on some Windows versions. */ + {WM_ACTIVATE, FOCUS_WINDOW, FALSE, 0}, + {WM_SETFOCUS, FOCUS_WINDOW, FALSE, 0}, + {0, 0, FALSE, 0}, + }; + static const struct message focus_loss_messages[] = + { + {WM_ACTIVATE, FOCUS_WINDOW, TRUE, WA_INACTIVE}, + {WM_DISPLAYCHANGE, DEVICE_WINDOW, FALSE, 0}, + /* WM_DISPLAYCHANGE is sent to the focus window too, but the order is + * not deterministic. */ + {WM_WINDOWPOSCHANGING, DEVICE_WINDOW, FALSE, 0}, + /* Windows sends WM_ACTIVATE to the device window, indicating that + * SW_SHOWMINIMIZED is used instead of SW_MINIMIZE. Yet afterwards + * the foreground and focus window are NULL. On Wine SW_SHOWMINIMIZED + * leaves the device window active, breaking re-activation in the + * lost device test. + * {WM_ACTIVATE, DEVICE_WINDOW, TRUE, 0x200000 | WA_ACTIVE}, */ + {WM_WINDOWPOSCHANGED, DEVICE_WINDOW, FALSE, 0}, + {WM_SIZE, DEVICE_WINDOW, TRUE, SIZE_MINIMIZED}, + {WM_ACTIVATEAPP, FOCUS_WINDOW, TRUE, FALSE}, + /* WM_ACTIVATEAPP is sent to the device window too, but the order is + * not deterministic. It may be sent after the focus window handling + * or before. */ + {0, 0, FALSE, 0}, + }; + + memset(&orig_mode, 0, sizeof(orig_mode)); + orig_mode.dmSize = sizeof(orig_mode); + ret = pEnumDisplaySettingsExA(NULL, ENUM_CURRENT_SETTINGS, &orig_mode, 0); + ok(ret, "Failed to get display mode.\n"); + + if (FAILED(pDirect3DCreate9Ex(D3D_SDK_VERSION, &d3d9))) + return; + + wc.lpfnWndProc = test_proc; + wc.lpszClassName = "d3d9_test_wndproc_wc"; + ok(RegisterClassA(&wc), "Failed to register window class.\n"); + + thread_params.window_created = CreateEventA(NULL, FALSE, FALSE, NULL); + ok(!!thread_params.window_created, "CreateEvent failed, last error %#x.\n", GetLastError()); + thread_params.test_finished = CreateEventA(NULL, FALSE, FALSE, NULL); + ok(!!thread_params.test_finished, "CreateEvent failed, last error %#x.\n", GetLastError()); + + focus_window = CreateWindowA("d3d9_test_wndproc_wc", "d3d9_test", + WS_MAXIMIZE | WS_VISIBLE | WS_CAPTION, 0, 0, orig_mode.dmPelsWidth, orig_mode.dmPelsHeight, 0, 0, 0, 0); + device_window = CreateWindowA("d3d9_test_wndproc_wc", "d3d9_test", + WS_MAXIMIZE | WS_VISIBLE | WS_CAPTION, 0, 0, orig_mode.dmPelsWidth, orig_mode.dmPelsHeight, 0, 0, 0, 0); + thread = CreateThread(NULL, 0, wndproc_thread, &thread_params, 0, &tid); + ok(!!thread, "Failed to create thread, last error %#x.\n", GetLastError()); + + res = WaitForSingleObject(thread_params.window_created, INFINITE); + ok(res == WAIT_OBJECT_0, "Wait failed (%#x), last error %#x.\n", res, GetLastError()); + + proc = GetWindowLongPtrA(device_window, GWLP_WNDPROC); + ok(proc == (LONG_PTR)test_proc, "Expected wndproc %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + proc = GetWindowLongPtrA(focus_window, GWLP_WNDPROC); + ok(proc == (LONG_PTR)test_proc, "Expected wndproc %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + + trace("device_window %p, focus_window %p, dummy_window %p.\n", + device_window, focus_window, thread_params.dummy_window); + + tmp = GetFocus(); + ok(tmp == device_window, "Expected focus %p, got %p.\n", device_window, tmp); + if (thread_params.running_in_foreground) + { + tmp = GetForegroundWindow(); + ok(tmp == thread_params.dummy_window, "Expected foreground window %p, got %p.\n", + thread_params.dummy_window, tmp); + } + else + skip("Not running in foreground, skip foreground window test\n"); + + flush_events(); + + expect_messages = create_messages; + + present_parameters.Windowed = FALSE; + present_parameters.hDeviceWindow = device_window; + present_parameters.SwapEffect = D3DSWAPEFFECT_DISCARD; + present_parameters.BackBufferWidth = 640; + present_parameters.BackBufferHeight = 480; + present_parameters.BackBufferFormat = D3DFMT_A8R8G8B8; + mode.Size = sizeof(mode); + mode.Width = 640; + mode.Height = 480; + mode.RefreshRate = 0; + mode.Format = D3DFMT_A8R8G8B8; + mode.ScanLineOrdering = 0; + if (FAILED(IDirect3D9Ex_CreateDeviceEx(d3d9, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, focus_window, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_parameters, &mode, &device))) + { + skip("Failed to create a D3D device, skipping tests.\n"); + goto done; + } + + ok(!expect_messages->message, "Expected message %#x for window %#x, but didn't receive it.\n", + expect_messages->message, expect_messages->window); + expect_messages = NULL; + + if (0) /* Disabled until we can make this work in a reliable way on Wine. */ + { + tmp = GetFocus(); + ok(tmp == focus_window, "Expected focus %p, got %p.\n", focus_window, tmp); + tmp = GetForegroundWindow(); + ok(tmp == focus_window, "Expected foreground window %p, got %p.\n", focus_window, tmp); + } + SetForegroundWindow(focus_window); + flush_events(); + + proc = GetWindowLongPtrA(device_window, GWLP_WNDPROC); + ok(proc == (LONG_PTR)test_proc, "Expected wndproc %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + + proc = GetWindowLongPtrA(focus_window, GWLP_WNDPROC); + ok(proc != (LONG_PTR)test_proc, "Expected wndproc != %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + + filter_messages = NULL; + expect_messages = focus_loss_messages; + SetForegroundWindow(GetDesktopWindow()); + ok(!expect_messages->message, "Expected message %#x for window %#x, but didn't receive it.\n", + expect_messages->message, expect_messages->window); + expect_messages = NULL; + tmp = GetFocus(); + ok(tmp != device_window, "The device window is active.\n"); + ok(tmp != focus_window, "The focus window is active.\n"); + + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = pEnumDisplaySettingsExA(NULL, ENUM_CURRENT_SETTINGS, &devmode, 0); + ok(ret, "Failed to get display mode.\n"); + ok(devmode.dmPelsWidth == orig_mode.dmPelsWidth, "Got unexpect width %u.\n", devmode.dmPelsWidth); + ok(devmode.dmPelsHeight == orig_mode.dmPelsHeight, "Got unexpect height %u.\n", devmode.dmPelsHeight); + + proc = GetWindowLongPtrA(device_window, GWLP_WNDPROC); + ok(proc == (LONG_PTR)test_proc, "Expected wndproc %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + + proc = GetWindowLongPtrA(focus_window, GWLP_WNDPROC); + ok(proc != (LONG_PTR)test_proc, "Expected wndproc != %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + + /* For some reason I have to minimize and restore the focus window, + * otherwise native d3d9 fails device::reset. This may be related + * to the WM_ACTIVATE message during focus loss. */ + ShowWindow(focus_window, SW_MINIMIZE); + ShowWindow(focus_window, SW_RESTORE); + SetForegroundWindow(focus_window); + flush_events(); + + filter_messages = focus_window; + hr = reset_device(device, device_window, FALSE); + ok(SUCCEEDED(hr), "Failed to reset device, hr %#x.\n", hr); + + ref = IDirect3DDevice9_Release(device); + ok(ref == 0, "The device was not properly freed: refcount %u.\n", ref); + + proc = GetWindowLongPtrA(focus_window, GWLP_WNDPROC); + ok(proc == (LONG_PTR)test_proc, "Expected wndproc %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + + device = create_device(focus_window, focus_window, FALSE); + if (!device) + { + skip("Failed to create a D3D device, skipping tests.\n"); + goto done; + } + + ref = IDirect3DDevice9_Release(device); + ok(ref == 0, "The device was not properly freed: refcount %u.\n", ref); + + device = create_device(device_window, focus_window, FALSE); + if (!device) + { + skip("Failed to create a D3D device, skipping tests.\n"); + goto done; + } + + proc = SetWindowLongPtrA(focus_window, GWLP_WNDPROC, (LONG_PTR)DefWindowProcA); + ok(proc != (LONG_PTR)test_proc, "Expected wndproc != %#lx, got %#lx.\n", + (LONG_PTR)test_proc, proc); + + ref = IDirect3DDevice9_Release(device); + ok(ref == 0, "The device was not properly freed: refcount %u.\n", ref); + + proc = GetWindowLongPtrA(focus_window, GWLP_WNDPROC); + ok(proc == (LONG_PTR)DefWindowProcA, "Expected wndproc %#lx, got %#lx.\n", + (LONG_PTR)DefWindowProcA, proc); + +done: + filter_messages = NULL; + IDirect3D9_Release(d3d9); + + SetEvent(thread_params.test_finished); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread_params.test_finished); + CloseHandle(thread_params.window_created); + CloseHandle(thread); + + DestroyWindow(device_window); + DestroyWindow(focus_window); + UnregisterClassA("d3d9_test_wndproc_wc", GetModuleHandleA(NULL)); +} + START_TEST(d3d9ex) { + HANDLE user32_handle; + d3d9_handle = LoadLibraryA("d3d9.dll"); if (!d3d9_handle) { @@ -1346,6 +1689,10 @@ START_TEST(d3d9ex) return; } + user32_handle = GetModuleHandleA("user32.dll"); + pEnumDisplaySettingsExA = (void*)GetProcAddress(user32_handle, "EnumDisplaySettingsExA"); + pChangeDisplaySettingsExA = (void*)GetProcAddress(user32_handle, "ChangeDisplaySettingsExA"); + test_qi_base_to_ex(); test_qi_ex_to_base(); test_swapchain_get_displaymode_ex(); @@ -1357,4 +1704,5 @@ START_TEST(d3d9ex) test_vidmem_accounting(); test_user_memory_getdc(); test_lost_device(); + test_wndproc(); } -- 2.0.4