Test that there is a ~1.5s timer after entering fullscreen mode and restores WS_EX_TOPMOST if it's missing when the timer times out.
-- v2: ddraw: Restore WS_EX_TOPMOST in exclusive fullscreen mode if it got removed. ddraw/tests: Test WS_EX_TOPMOST restoration.
From: Zhiyi Zhang zzhang@codeweavers.com
Test that there is a ~1.5s timer after entering fullscreen mode and restores WS_EX_TOPMOST if it's missing when the timer times out. --- dlls/ddraw/tests/ddraw1.c | 30 ++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw2.c | 30 ++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw4.c | 30 ++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw7.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+)
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index f5fc7b04053..7a76e9004ee 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -2780,6 +2780,36 @@ static void test_window_style(void) expected_style = exstyle | WS_EX_TOPMOST; todo_wine ok(tmp == expected_style, "Expected window extended style %#lx, got %#lx.\n", expected_style, tmp);
+ /* Test that there is a ~1.5s timer that checks and restores WS_EX_TOPMOST if it's missing */ + ret = ShowWindow(window, SW_RESTORE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(SUCCEEDED(hr), "SetCooperativeLevel failed, hr %#lx.\n", hr); + flush_events(); + + /* Remove WS_VISIBLE and WS_EX_TOPMOST */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(tmp & WS_VISIBLE, "Expected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ret = ShowWindow(window, SW_HIDE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + ret = SetWindowPos(window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + ok(ret, "SetWindowPos failed, error %#lx.\n", GetLastError()); + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(!(tmp & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + + Sleep(2000); + flush_events(); + + /* WS_VISIBLE is not restored but WS_EX_TOPMOST is */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ref = IDirectDraw_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref);
diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c index 1e33e7c7779..5d0017e92d6 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -2866,6 +2866,36 @@ static void test_window_style(void) expected_style = exstyle | WS_EX_TOPMOST; todo_wine ok(tmp == expected_style, "Expected window extended style %#lx, got %#lx.\n", expected_style, tmp);
+ /* Test that there is a ~1.5s timer that checks and restores WS_EX_TOPMOST if it's missing */ + ret = ShowWindow(window, SW_RESTORE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(SUCCEEDED(hr), "SetCooperativeLevel failed, hr %#lx.\n", hr); + flush_events(); + + /* Remove WS_VISIBLE and WS_EX_TOPMOST */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(tmp & WS_VISIBLE, "Expected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ret = ShowWindow(window, SW_HIDE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + ret = SetWindowPos(window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + ok(ret, "SetWindowPos failed, error %#lx.\n", GetLastError()); + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(!(tmp & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + + Sleep(2000); + flush_events(); + + /* WS_VISIBLE is not restored but WS_EX_TOPMOST is */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ref = IDirectDraw2_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref);
diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index 379ba245571..6ee39ec45c9 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -3104,6 +3104,36 @@ static void test_window_style(void) expected_style = exstyle | WS_EX_TOPMOST; todo_wine ok(tmp == expected_style, "Expected window extended style %#lx, got %#lx.\n", expected_style, tmp);
+ /* Test that there is a ~1.5s timer that checks and restores WS_EX_TOPMOST if it's missing */ + ret = ShowWindow(window, SW_RESTORE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(SUCCEEDED(hr), "SetCooperativeLevel failed, hr %#lx.\n", hr); + flush_events(); + + /* Remove WS_VISIBLE and WS_EX_TOPMOST */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(tmp & WS_VISIBLE, "Expected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ret = ShowWindow(window, SW_HIDE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + ret = SetWindowPos(window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + ok(ret, "SetWindowPos failed, error %#lx.\n", GetLastError()); + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(!(tmp & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + + Sleep(2000); + flush_events(); + + /* WS_VISIBLE is not restored but WS_EX_TOPMOST is */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ref = IDirectDraw4_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref);
diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index 779c8c982cf..990d002a381 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -2823,6 +2823,36 @@ static void test_window_style(void) expected_style = exstyle | WS_EX_TOPMOST; todo_wine ok(tmp == expected_style, "Expected window extended style %#lx, got %#lx.\n", expected_style, tmp);
+ /* Test that there is a ~1.5s timer that checks and restores WS_EX_TOPMOST if it's missing */ + ret = ShowWindow(window, SW_RESTORE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(SUCCEEDED(hr), "SetCooperativeLevel failed, hr %#lx.\n", hr); + flush_events(); + + /* Remove WS_VISIBLE and WS_EX_TOPMOST */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(tmp & WS_VISIBLE, "Expected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ret = ShowWindow(window, SW_HIDE); + ok(ret, "ShowWindow failed, error %#lx.\n", GetLastError()); + ret = SetWindowPos(window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); + ok(ret, "SetWindowPos failed, error %#lx.\n", GetLastError()); + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + ok(!(tmp & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + + Sleep(2000); + flush_events(); + + /* WS_VISIBLE is not restored but WS_EX_TOPMOST is */ + tmp = GetWindowLongA(window, GWL_STYLE); + ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + tmp = GetWindowLongA(window, GWL_EXSTYLE); + todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ref = IDirectDraw7_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref);
From: Zhiyi Zhang zzhang@codeweavers.com
Tests show that there is a ~1.5s timer that checks for the presence of WS_EX_TOPMOST and restore it if it got removed when the timer times out. Manual tests that skip WM_TIMER handling show there is a 1.5s timer of ID 0x4242 keeps firing. The timer stops when its WM_TIMER message gets handled.
Fix Deus Ex: Game of the Year Edition missing WS_EX_TOPMOST after changing resolutions in fullscreen mode. The application removes WS_EX_TOPMOST from its game window after entering fullscreen. --- dlls/ddraw/ddraw.c | 2 +- dlls/ddraw/ddraw_private.h | 3 +++ dlls/ddraw/tests/ddraw1.c | 2 +- dlls/ddraw/tests/ddraw2.c | 2 +- dlls/ddraw/tests/ddraw4.c | 2 +- dlls/ddraw/tests/ddraw7.c | 2 +- dlls/wined3d/swapchain.c | 28 ++++++++++++++++++++++++++++ include/wine/wined3d.h | 1 + 8 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c index da803a3a5f7..490fd2cf907 100644 --- a/dlls/ddraw/ddraw.c +++ b/dlls/ddraw/ddraw.c @@ -587,7 +587,7 @@ static HRESULT ddraw_attach_d3d_device(struct ddraw *ddraw, HWND window, swapchain_desc.swap_effect = WINED3D_SWAP_EFFECT_DISCARD; swapchain_desc.device_window = window; swapchain_desc.windowed = !(cooplevel & DDSCL_FULLSCREEN); - swapchain_desc.flags = WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH | WINED3D_SWAPCHAIN_IMPLICIT; + swapchain_desc.flags = DDRAW_WINED3D_SWAPCHAIN_FLAGS;
if ((cooplevel & DDSCL_NOWINDOWCHANGES) || window != GetForegroundWindow()) swapchain_desc.flags |= WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES; diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h index 01a9579651c..727487f1efb 100644 --- a/dlls/ddraw/ddraw_private.h +++ b/dlls/ddraw/ddraw_private.h @@ -65,6 +65,9 @@ struct FvfToDecl | WINED3D_FOCUS_MESSAGES | WINED3D_PIXEL_CENTER_INTEGER | WINED3D_LEGACY_UNBOUND_RESOURCE_COLOR \ | WINED3D_NO_PRIMITIVE_RESTART | WINED3D_LEGACY_CUBEMAP_FILTERING | WINED3D_NO_DRAW_INDIRECT)
+#define DDRAW_WINED3D_SWAPCHAIN_FLAGS (WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH \ + | WINED3D_SWAPCHAIN_IMPLICIT | WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER) + #define DDRAW_MAX_ACTIVE_LIGHTS 32 #define DDRAW_MAX_TEXTURES 8
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 7a76e9004ee..238a92ee6e8 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -2808,7 +2808,7 @@ static void test_window_style(void) tmp = GetWindowLongA(window, GWL_STYLE); ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); tmp = GetWindowLongA(window, GWL_EXSTYLE); - todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n");
ref = IDirectDraw_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref); diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c index 5d0017e92d6..59f52dacfb4 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -2894,7 +2894,7 @@ static void test_window_style(void) tmp = GetWindowLongA(window, GWL_STYLE); ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); tmp = GetWindowLongA(window, GWL_EXSTYLE); - todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n");
ref = IDirectDraw2_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref); diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index 6ee39ec45c9..32ce2563f90 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -3132,7 +3132,7 @@ static void test_window_style(void) tmp = GetWindowLongA(window, GWL_STYLE); ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); tmp = GetWindowLongA(window, GWL_EXSTYLE); - todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n");
ref = IDirectDraw4_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref); diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index 990d002a381..91a3aafbe5a 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -2851,7 +2851,7 @@ static void test_window_style(void) tmp = GetWindowLongA(window, GWL_STYLE); ok(!(tmp & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); tmp = GetWindowLongA(window, GWL_EXSTYLE); - todo_wine ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n"); + ok(tmp & WS_EX_TOPMOST, "Expected WS_EX_TOPMOST.\n");
ref = IDirectDraw7_Release(ddraw); ok(!ref, "Unexpected refcount %lu.\n", ref); diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index 99d717641e1..c30c43734c0 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -2151,8 +2151,20 @@ struct wined3d_window_state int x, y, width, height; uint32_t flags; bool set_style; + bool register_topmost_timer; + bool set_topmost_timer; };
+#define WINED3D_WINDOW_TOPMOST_TIMER_ID 0x4242 + +static void CALLBACK topmost_timer_proc(HWND hwnd, UINT msg, UINT_PTR id, DWORD time) +{ + if (!(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + KillTimer(hwnd, WINED3D_WINDOW_TOPMOST_TIMER_ID); +} + static DWORD WINAPI set_window_state_thread(void *ctx) { struct wined3d_window_state *s = ctx; @@ -2176,6 +2188,7 @@ static DWORD WINAPI set_window_state_thread(void *ctx)
static void set_window_state(struct wined3d_window_state *s) { + static const UINT timeout = 1500; DWORD window_tid = GetWindowThreadProcessId(s->window, NULL); DWORD tid = GetCurrentThreadId(); HANDLE thread; @@ -2187,6 +2200,17 @@ static void set_window_state(struct wined3d_window_state *s) if (window_tid == tid) { set_window_state_thread(s); + + /* Deus Ex: Game of the Year Edition removes WS_EX_TOPMOST after changing resolutions in + * exclusive fullscreen mode. Tests show that WS_EX_TOPMOST will be restored when a ~1.5s + * timer times out */ + if (s->register_topmost_timer) + { + if (s->set_topmost_timer) + SetTimer(s->window, WINED3D_WINDOW_TOPMOST_TIMER_ID, timeout, topmost_timer_proc); + else + KillTimer(s->window, WINED3D_WINDOW_TOPMOST_TIMER_ID); + } } else if ((thread = CreateThread(NULL, 0, set_window_state_thread, s, 0, NULL))) { @@ -2239,6 +2263,8 @@ HRESULT wined3d_swapchain_state_setup_fullscreen(struct wined3d_swapchain_state s->style = fullscreen_style(state->style); s->exstyle = fullscreen_exstyle(state->exstyle); s->set_style = true; + s->register_topmost_timer = !!(state->desc.flags & WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER); + s->set_topmost_timer = true;
TRACE("Old style was %08lx, %08lx, setting to %08lx, %08lx.\n", state->style, state->exstyle, s->style, s->exstyle); @@ -2293,6 +2319,8 @@ void wined3d_swapchain_state_restore_from_fullscreen(struct wined3d_swapchain_st * when switching between windowed and fullscreen modes (HL2), some * depend on the original style (Eve Online). */ s->set_style = style == fullscreen_style(state->style) && exstyle == fullscreen_exstyle(state->exstyle); + s->register_topmost_timer = !!(state->desc.flags & WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER); + s->set_topmost_timer = false;
if (window_rect) { diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index 7bc9559526e..7ea5cf03cfc 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -902,6 +902,7 @@ enum wined3d_memory_segment_group #define WINED3D_SWAPCHAIN_REGISTER_STATE 0x00020000u #define WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES 0x00040000u #define WINED3D_SWAPCHAIN_RESTORE_WINDOW_STATE 0x00080000u +#define WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER 0x00100000u
#define WINED3DDP_MAXTEXCOORD 8
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=138522
Your paranoid android.
=== w10pro64 (64 bit report) ===
ddraw: ddraw7.c:5807: Test failed: Expected WM_ACTIVATEAPP, but did not receive it. ddraw7.c:5819: Test failed: Expected WM_ACTIVATEAPP, but did not receive it. ddraw7.c:5831: Test failed: Expected WM_ACTIVATEAPP, but did not receive it. ddraw7.c:5853: Test failed: Expected WM_ACTIVATEAPP, but did not receive it.