Tidy Cauldron (2708320), a Unity game, uses EnumWindows() to find the first window in the current thread and maximizes the window when changing to windowed mode. However, before this patch, the IME UI window and the DXGI fallback device window are on top of the game window at creation and thus they could get maximized instead. This causes the game window to lose focus and freeze.
-- v2: dxgi: Move the fallback device window to the bottom at creation. d3d11/tests: Test that the fallback device window shouldn't be above normal windows at creation. imm32: Move the IME UI window to the bottom at creation. imm32/tests: Test that the IME UI window shouldn't be above normal windows at creation.
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/imm32/tests/imm32.c | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)
diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index db73463ef49..7cec65f9b27 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -8037,8 +8037,58 @@ static void test_nihongo_no(void) ime_call_count = 0; }
+static BOOL CALLBACK enum_first_current_thread_window_proc( HWND hwnd, LPARAM lparam ) +{ + if (GetWindowThreadProcessId( hwnd, NULL ) == GetCurrentThreadId()) + { + *(HWND *)lparam = hwnd; + return FALSE; + } + return TRUE; +} + +static void test_ime_ui_window_child( void ) +{ + HWND hwnd, result_hwnd = 0; + + /* Unity expects the first window in the current thread to be its game window, not Wine IME */ + hwnd = CreateWindowW( L"static", L"", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); + ok_ne( 0, hwnd, HWND, "%p" ); + EnumWindows( enum_first_current_thread_window_proc, (LPARAM)&result_hwnd ); + todo_wine + ok_eq( result_hwnd, hwnd, HWND, "%p" ); + DestroyWindow( hwnd ); +} + +static void test_ime_ui_window( const char *argv0 ) +{ + PROCESS_INFORMATION info; + STARTUPINFOA startup; + char cmd[MAX_PATH]; + + /* Run in a new process to avoid interference from windows in the current process */ + sprintf( cmd, "%s imm32 test_ime_ui_window_child", argv0 ); + memset( &startup, 0, sizeof(startup) ); + startup.cb = sizeof(startup); + CreateProcessA( NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ); + + wait_child_process( info.hProcess ); + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); +} + START_TEST(imm32) { + char **argv; + int argc; + + argc = winetest_get_mainargs( &argv ); + if (argc == 3 && !strcmp( argv[2], "test_ime_ui_window_child" )) + { + test_ime_ui_window_child(); + return; + } + default_hkl = GetKeyboardLayout( 0 );
test_class.hInstance = GetModuleHandleW( NULL ); @@ -8050,6 +8100,7 @@ START_TEST(imm32) return; }
+ test_ime_ui_window( argv[0] ); test_com_initialization();
test_ImmEnumInputContext();
From: Zhiyi Zhang zzhang@codeweavers.com
Tidy Cauldron (2708320), a Unity game, uses EnumWindows() to find the first window in the current thread and maximizes the window when changing to windowed mode. However, before this patch, the IME UI window and the DXGI fallback device window are on top of the game window at creation and thus they could get maximized instead. This causes the game window to lose focus and freeze. --- dlls/imm32/imm.c | 1 + dlls/imm32/tests/imm32.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index dbf8db63e11..5639c2ae11c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -948,6 +948,7 @@ static HWND get_ime_ui_window(void) { imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, ImmGetDefaultIMEWnd( 0 ), 0, ime->module, 0 ); + SetWindowPos( imc->ui_hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ); SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)NtUserGetWindowInputContext( GetFocus() ) ); } return imc->ui_hwnd; diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 7cec65f9b27..5b4e194c367 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -8055,7 +8055,6 @@ static void test_ime_ui_window_child( void ) hwnd = CreateWindowW( L"static", L"", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL ); ok_ne( 0, hwnd, HWND, "%p" ); EnumWindows( enum_first_current_thread_window_proc, (LPARAM)&result_hwnd ); - todo_wine ok_eq( result_hwnd, hwnd, HWND, "%p" ); DestroyWindow( hwnd ); }
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/d3d11/tests/d3d11.c | 84 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-)
diff --git a/dlls/d3d11/tests/d3d11.c b/dlls/d3d11/tests/d3d11.c index 3e9ccc42e33..b4c585b2b41 100644 --- a/dlls/d3d11/tests/d3d11.c +++ b/dlls/d3d11/tests/d3d11.c @@ -2046,6 +2046,66 @@ static void draw_color_quad_(unsigned int line, struct d3d11_test_context *conte draw_quad_vs_(line, context, vs_code, vs_code_size); }
+static BOOL CALLBACK enum_first_current_thread_window_proc(HWND hwnd, LPARAM lparam) +{ + if (GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId()) + { + *(HWND *)lparam = hwnd; + return FALSE; + } + return TRUE; +} + +static void test_create_device_child(void) +{ + DXGI_SWAP_CHAIN_DESC swapchain_desc; + HWND hwnd, result_hwnd; + ID3D11Device *device; + HRESULT hr; + + /* Unity expects the first window in the current thread to be its game window, not DXGI device window */ + hwnd = CreateWindowW(L"static", L"", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL); + + /* Create a device without a device window in windowed mode */ + swapchain_desc.BufferDesc.Width = 800; + swapchain_desc.BufferDesc.Height = 600; + swapchain_desc.BufferDesc.RefreshRate.Numerator = 60; + swapchain_desc.BufferDesc.RefreshRate.Denominator = 60; + swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + swapchain_desc.SampleDesc.Count = 1; + swapchain_desc.SampleDesc.Quality = 0; + swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapchain_desc.BufferCount = 1; + swapchain_desc.OutputWindow = NULL; + swapchain_desc.Windowed = TRUE; + swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.Flags = 0; + hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, NULL, 0, D3D11_SDK_VERSION, + &swapchain_desc, NULL, &device, NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + EnumWindows(enum_first_current_thread_window_proc, (LPARAM)&result_hwnd); + todo_wine + ok(result_hwnd == hwnd, "Got unexpected window %p.\n", result_hwnd); + + ID3D11Device_Release(device); + + /* Create a device without a device window in fullscreen mode */ + swapchain_desc.Windowed = FALSE; + hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, NULL, 0, D3D11_SDK_VERSION, + &swapchain_desc, NULL, &device, NULL, NULL); + ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr); + + EnumWindows(enum_first_current_thread_window_proc, (LPARAM)&result_hwnd); + todo_wine + ok(result_hwnd == hwnd, "Got unexpected window %p.\n", result_hwnd); + + ID3D11Device_Release(device); + DestroyWindow(hwnd); +} + static void test_create_device(void) { static const D3D_FEATURE_LEVEL default_feature_levels[] = @@ -2060,8 +2120,11 @@ static void test_create_device(void) D3D_FEATURE_LEVEL feature_level, supported_feature_level; DXGI_SWAP_CHAIN_DESC swapchain_desc, obtained_desc; ID3D11DeviceContext *immediate_context; + char **argv, cmd[MAX_PATH]; IDXGISwapChain *swapchain; + PROCESS_INFORMATION info; ID3D11Device *device; + STARTUPINFOA startup; ULONG refcount; HWND window; HRESULT hr; @@ -2268,6 +2331,19 @@ static void test_create_device(void) ok(!immediate_context, "Got unexpected immediate context pointer %p.\n", immediate_context);
DestroyWindow(window); + + /* Test that creating a swapchain without a device window shouldn't create a fallback device + * window that's on top of normal windows at creation. Run the tests in a new process to avoid + * interference from windows in the current process */ + winetest_get_mainargs(&argv); + sprintf(cmd, "%s d3d11 test_create_device_child", argv[0]); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + ok(CreateProcessA(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess failed.\n"); + + wait_child_process(info.hProcess); + CloseHandle(info.hProcess); + CloseHandle(info.hThread); }
static void test_device_interfaces(const D3D_FEATURE_LEVEL feature_level) @@ -36580,6 +36656,13 @@ START_TEST(d3d11) HMODULE wined3d; char **argv;
+ argc = winetest_get_mainargs(&argv); + if (argc == 3 && !strcmp(argv[2], "test_create_device_child")) + { + test_create_device_child(); + return; + } + if ((wined3d = GetModuleHandleA("wined3d.dll"))) { enum wined3d_renderer (CDECL *p_wined3d_get_renderer)(void); @@ -36596,7 +36679,6 @@ START_TEST(d3d11) if (sizeof(void *) == 4 && !strcmp(winetest_platform, "wine")) use_mt = FALSE;
- argc = winetest_get_mainargs(&argv); for (i = 2; i < argc; ++i) { if (!strcmp(argv[i], "--validate"))
From: Zhiyi Zhang zzhang@codeweavers.com
Tidy Cauldron (2708320), a Unity game, uses EnumWindows() to find the first window in the current thread and maximizes the window when changing to windowed mode. However, before this patch, the IME UI window and the DXGI fallback device window are on top of the game window at creation and thus they could get maximized instead. This causes the game window to lose focus and freeze. --- dlls/d3d11/tests/d3d11.c | 2 -- dlls/dxgi/factory.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/dlls/d3d11/tests/d3d11.c b/dlls/d3d11/tests/d3d11.c index b4c585b2b41..67367425b7d 100644 --- a/dlls/d3d11/tests/d3d11.c +++ b/dlls/d3d11/tests/d3d11.c @@ -2087,7 +2087,6 @@ static void test_create_device_child(void) ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
EnumWindows(enum_first_current_thread_window_proc, (LPARAM)&result_hwnd); - todo_wine ok(result_hwnd == hwnd, "Got unexpected window %p.\n", result_hwnd);
ID3D11Device_Release(device); @@ -2099,7 +2098,6 @@ static void test_create_device_child(void) ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
EnumWindows(enum_first_current_thread_window_proc, (LPARAM)&result_hwnd); - todo_wine ok(result_hwnd == hwnd, "Got unexpected window %p.\n", result_hwnd);
ID3D11Device_Release(device); diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c index 32d54f033eb..ebdb42a3806 100644 --- a/dlls/dxgi/factory.c +++ b/dlls/dxgi/factory.c @@ -617,6 +617,7 @@ HWND dxgi_factory_get_device_window(struct dxgi_factory *factory) ERR("Failed to create a window.\n"); return NULL; } + SetWindowPos(factory->device_window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); TRACE("Created device window %p for factory %p.\n", factory->device_window, factory); }
v2: Fix code style. Thanks, Rémi.
This merge request was approved by Rémi Bernon.
This merge request was approved by Elizabeth Figura.