For Assassin's Creed Syndicate. When switching from fullscreen to a smaller windowed resolution, it calls `SetWindowPos()` centering the window, then calls `ResizeTarget()` and finally `SetFullscreenState()`, and expects the window to preserve the position.
Window style handling is a bit iffy because of the manipulation we do when entering fullscreen, but that's not really new.
-- v4: dxgi/tests: Test window states tracking and restoration in fullscreen mode. dxgi/tests: Use test contexts in test_resize_target(). wined3d: Conditionally update saved window state in fullscreen mode.
From: Matteo Bruni mbruni@codeweavers.com
--- dlls/wined3d/swapchain.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index 1a010958a21..a1000ec2393 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -2101,6 +2101,22 @@ HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_sta } else { + if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + } + width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; + height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; + + GetWindowRect(window, &window_rect); + if (width != window_rect.right - window_rect.left || height != window_rect.bottom - window_rect.top) + { + TRACE("Update saved window state.\n"); + state->original_window_rect = window_rect; + } + if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH) { actual_mode = *mode; @@ -2111,19 +2127,18 @@ HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_sta wined3d_mutex_unlock(); return hr; } - } + if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) + { + ERR("Failed to get output description, hr %#lx.\n", hr); + wined3d_mutex_unlock(); + return hr; + }
- if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc))) - { - ERR("Failed to get output description, hr %#lx.\n", hr); - wined3d_mutex_unlock(); - return hr; + width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; + height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; } - x = output_desc.desktop_rect.left; y = output_desc.desktop_rect.top; - width = output_desc.desktop_rect.right - output_desc.desktop_rect.left; - height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top; }
wined3d_mutex_unlock();
From: Matteo Bruni mbruni@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 03bccc50a37..f223ab96c2d 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -3257,14 +3257,18 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter)); ++adapter_idx) { + winetest_push_context("Adapter %u", adapter_idx); for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output)); ++output_idx) { + winetest_push_context("output %u", adapter_idx); hr = IDXGIOutput_GetDesc(output, &output_desc); - ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#lx.\n", adapter_idx, output_idx, hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
for (test_idx = 0; test_idx < ARRAY_SIZE(tests); ++test_idx) { + winetest_push_context("test %u", test_idx); + swapchain_desc.Flags = tests[test_idx].flags; swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, output_desc.DesktopCoordinates.left + tests[test_idx].origin.x, @@ -3282,8 +3286,7 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) capture_fullscreen_state(&initial_state.fullscreen_state, swapchain_desc.OutputWindow);
hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); - ok(hr == S_OK, "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); check_swapchain_fullscreen_state(swapchain, &initial_state);
expected_state = initial_state; @@ -3294,36 +3297,33 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) &swapchain_desc, &initial_state.fullscreen_state.monitor_rect, 800, 600, NULL); hr = IDXGISwapChain_GetContainingOutput(swapchain, &expected_state.target); ok(hr == S_OK || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Win 7 testbot */, - "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + "Unexpected hr %#lx.\n", hr); if (hr == DXGI_ERROR_UNSUPPORTED) { - win_skip("Adapter %u output %u test %u: GetContainingOutput() not supported.\n", - adapter_idx, output_idx, test_idx); + win_skip("GetContainingOutput() not supported.\n"); IDXGISwapChain_Release(swapchain); DestroyWindow(swapchain_desc.OutputWindow); + winetest_pop_context(); continue; }
hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, - "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + "Unexpected hr %#lx.\n", hr); if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) { - skip("Adapter %u output %u test %u: Could not change fullscreen state.\n", - adapter_idx, output_idx, test_idx); + skip("Could not change fullscreen state.\n"); IDXGIOutput_Release(expected_state.target); IDXGISwapChain_Release(swapchain); DestroyWindow(swapchain_desc.OutputWindow); + winetest_pop_context(); continue; } } check_swapchain_fullscreen_state(swapchain, &expected_state);
hr = IDXGISwapChain_ResizeTarget(swapchain, NULL); - ok(hr == DXGI_ERROR_INVALID_CALL, "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + ok(hr == DXGI_ERROR_INVALID_CALL, "Unexpected hr %#lx.\n", hr); check_swapchain_fullscreen_state(swapchain, &expected_state);
if (tests[test_idx].fullscreen) @@ -3331,8 +3331,7 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) test_fullscreen_resize_target(swapchain, &expected_state);
hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); - ok(hr == S_OK, "Adapter %u output %u test %u: Got unexpected hr %#lx.\n", - adapter_idx, output_idx, test_idx, hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); check_swapchain_fullscreen_state(swapchain, &initial_state); IDXGIOutput_Release(expected_state.target); check_swapchain_fullscreen_state(swapchain, &initial_state); @@ -3346,14 +3345,16 @@ static void test_resize_target(IUnknown *device, BOOL is_d3d12) }
refcount = IDXGISwapChain_Release(swapchain); - ok(!refcount, "Adapter %u output %u test %u: IDXGISwapChain has %lu references left.\n", - adapter_idx, output_idx, test_idx, refcount); + ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount); check_window_fullscreen_state(swapchain_desc.OutputWindow, &expected_state.fullscreen_state); DestroyWindow(swapchain_desc.OutputWindow); + winetest_pop_context(); } IDXGIOutput_Release(output); + winetest_pop_context(); } IDXGIAdapter_Release(adapter); + winetest_pop_context(); } refcount = IDXGIFactory_Release(factory); ok(refcount == !is_d3d12, "Got unexpected refcount %lu.\n", refcount);
From: Matteo Bruni mbruni@codeweavers.com
--- dlls/dxgi/tests/dxgi.c | 211 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index f223ab96c2d..b04f99684a9 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -3056,6 +3056,215 @@ static void test_default_fullscreen_target_output(IUnknown *device, BOOL is_d3d1 ok(refcount == !is_d3d12, "IDXGIFactory has %lu references left.\n", refcount); }
+static void test_resize_fullscreen_window(IUnknown *device, BOOL is_d3d12, BOOL change_style) +{ + struct swapchain_fullscreen_state initial_state, expected_state; + unsigned int desktop_width, desktop_height; + DXGI_SWAP_CHAIN_DESC swapchain_desc; + IDXGIAdapter *adapter = NULL; + DXGI_OUTPUT_DESC output_desc; + IDXGIOutput *output = NULL; + IDXGISwapChain *swapchain; + IDXGIFactory *factory; + BOOL fullscreen, ret; + DXGI_MODE_DESC mode; + ULONG refcount; + HRESULT hr; + RECT r; + + winetest_push_context(change_style ? "Style change" : "No style change"); + + get_factory(device, is_d3d12, &factory); + + 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 = is_d3d12 ? 2 : 1; + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + swapchain_desc.Windowed = TRUE; + swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.Flags = 0; + + memset(&initial_state, 0, sizeof(initial_state)); + capture_fullscreen_state(&initial_state.fullscreen_state, swapchain_desc.OutputWindow); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetContainingOutput(swapchain, &output); + ok(hr == S_OK || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */ + "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) + { + skip("Could not get output.\n"); + goto done; + } + hr = IDXGIOutput_GetParent(output, &IID_IDXGIAdapter, (void **)&adapter); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + check_swapchain_fullscreen_state(swapchain, &initial_state); + flush_events(); + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE + || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */ + "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) + { + skip("Could not change fullscreen state.\n"); + goto done; + } + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + refcount = IDXGISwapChain_Release(swapchain); + ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount); + + DestroyWindow(swapchain_desc.OutputWindow); + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + flush_events(); + check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + + hr = IDXGIOutput_GetDesc(output, &output_desc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (change_style) + SetWindowLongPtrA(swapchain_desc.OutputWindow, GWL_STYLE, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); + + expected_state = initial_state; + SetRect(&expected_state.fullscreen_state.client_rect, 0, 0, 640, 480); + r = expected_state.fullscreen_state.client_rect; + ret = AdjustWindowRectEx(&r, GetWindowLongW(swapchain_desc.OutputWindow, GWL_STYLE), + FALSE, GetWindowLongW(swapchain_desc.OutputWindow, GWL_EXSTYLE)); + ok(ret, "Unexpected ret %#x.\n", ret); + desktop_width = output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left; + desktop_height = output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top; + OffsetRect(&r, (desktop_width - 640) / 2, (desktop_height - 480) / 2); + expected_state.fullscreen_state.window_rect = r; + + /* Center the window on the screen. */ + ret = SetWindowPos(swapchain_desc.OutputWindow, HWND_NOTOPMOST, r.left, + r.top, r.right - r.left, r.bottom - r.top, SWP_FRAMECHANGED); + ok(ret, "Unexpected ret %#x.\n", ret); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + expected_state.fullscreen = TRUE; + expected_state.target = output; + if (change_style) + { + expected_state.fullscreen_state.style = WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + if (winetest_platform_is_wine) + { + todo_wine ok(FALSE, "Currently ignoring window styles.\n"); + /* To avoid a success on the todo_wine inside check_fullscreen_state_() */ + expected_state.fullscreen_state.style = 0; + } + } + else + { + expected_state.fullscreen_state.style = WS_CLIPSIBLINGS; + expected_state.fullscreen_state.exstyle = 0; + } + check_swapchain_fullscreen_state(swapchain, &expected_state); + + /* Update DXGI swapchain. */ + memset(&mode, 0, sizeof(mode)); + mode.Width = 640; + mode.Height = 480; + hr = IDXGISwapChain_ResizeTarget(swapchain, &mode); + ok(!hr, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + SetRect(&expected_state.fullscreen_state.window_rect, 0, 0, desktop_width, desktop_height); + expected_state.fullscreen_state.client_rect = expected_state.fullscreen_state.window_rect; + if (change_style) + { + expected_state.fullscreen_state.style = WS_CLIPSIBLINGS; + expected_state.fullscreen_state.exstyle = 0; + if (winetest_platform_is_wine) + { + todo_wine ok(FALSE, "Currently ignoring window styles.\n"); + expected_state.fullscreen_state.exstyle = WS_EX_WINDOWEDGE; + GetClientRect(swapchain_desc.OutputWindow, &expected_state.fullscreen_state.client_rect); + } + } + check_swapchain_fullscreen_state(swapchain, &expected_state); + hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(swapchain_desc.BufferDesc.Width == 800, "Unexpected width %u.\n", swapchain_desc.BufferDesc.Width); + ok(swapchain_desc.BufferDesc.Height == 600, "Unexpected height %u.\n", swapchain_desc.BufferDesc.Height); + + /* Now actually leave fullscreen mode. */ + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!fullscreen, "Unexpected fullscreen %#x.\n", fullscreen); + expected_state.fullscreen_state.window_rect = r; + if (winetest_platform_is_wine) + { + todo_wine ok(FALSE, "Currently ignoring window styles.\n"); + GetClientRect(swapchain_desc.OutputWindow, &expected_state.fullscreen_state.client_rect); + } + else + { + SetRect(&expected_state.fullscreen_state.client_rect, 0, 0, 640, 480); + } + expected_state.fullscreen = FALSE; + expected_state.target = NULL; + if (change_style) + { + expected_state.fullscreen_state.style = WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + expected_state.fullscreen_state.exstyle = WS_EX_WINDOWEDGE; + } + if (winetest_platform_is_wine) + { + todo_wine ok(FALSE, "Currently ignoring window styles.\n"); + if (change_style) + { + GetClientRect(swapchain_desc.OutputWindow, &expected_state.fullscreen_state.client_rect); + } + else + { + expected_state.fullscreen_state.style = WS_CLIPSIBLINGS | WS_CAPTION; + expected_state.fullscreen_state.exstyle = WS_EX_WINDOWEDGE; + } + } + check_swapchain_fullscreen_state(swapchain, &expected_state); + +done: + if (output) + IDXGIOutput_Release(output); + if (adapter) + IDXGIAdapter_Release(adapter); + refcount = IDXGISwapChain_Release(swapchain); + ok(!refcount, "IDXGISwapChain has %lu references left.\n", refcount); + DestroyWindow(swapchain_desc.OutputWindow); + + refcount = IDXGIFactory_Release(factory); + ok(refcount == !is_d3d12, "Unexpected refcount %lu.\n", refcount); + + winetest_pop_context(); +} + +static void test_resize_fullscreen(IUnknown *device, BOOL is_d3d12) +{ + test_resize_fullscreen_window(device, is_d3d12, FALSE); + test_resize_fullscreen_window(device, is_d3d12, TRUE); +} + static void test_windowed_resize_target(IDXGISwapChain *swapchain, HWND window, struct swapchain_fullscreen_state *state) { @@ -8336,6 +8545,7 @@ START_TEST(dxgi) test_swapchain_window_styles(); run_on_d3d10(test_set_fullscreen); run_on_d3d10(test_resize_target); + run_on_d3d10(test_resize_fullscreen); run_on_d3d10(test_swapchain_resize); run_on_d3d10(test_swapchain_present); run_on_d3d10(test_swapchain_backbuffer_index); @@ -8368,6 +8578,7 @@ START_TEST(dxgi)
run_on_d3d12(test_set_fullscreen); run_on_d3d12(test_resize_target); + run_on_d3d12(test_resize_fullscreen); run_on_d3d12(test_swapchain_resize); run_on_d3d12(test_swapchain_present); run_on_d3d12(test_swapchain_backbuffer_index);
I would be inclined to just leave it alone until we find something that cares. I'd rather keep the code as simple as possible in cases like this.
Done. The tests had to be adjusted a bit; they're not particularly nice now but it's either that, effectively duplicating a bunch of test functions or reducing the tested surface.
On Mon Sep 29 12:22:15 2025 +0000, Matteo Bruni wrote:
changed this line in [version 4 of the diff](/wine/wine/-/merge_requests/9036/diffs?diff_id=212758&start_sha=898e8c71d9e876775c8b7d525ca6e33d2cd1106f#06f6a7decc34b79b8d444693d16a3ee0c5a5f96a_8550_8581)
:face_palm: Thanks!