Module: wine Branch: master Commit: 9092a4ccca9d9da93a5fb0d611c05c3aae4882c8 URL: https://gitlab.winehq.org/wine/wine/-/commit/9092a4ccca9d9da93a5fb0d611c05c3...
Author: Zhiyi Zhang zzhang@codeweavers.com Date: Tue Sep 19 17:30:23 2023 +0800
ddraw: Restore WS_EX_TOPMOST in exclusive fullscreen mode if it got removed.
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 94b38258acd..5887854556b 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 d8bd335f615..f40188f75ef 100644 --- a/dlls/ddraw/ddraw_private.h +++ b/dlls/ddraw/ddraw_private.h @@ -64,6 +64,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 e7483c30863..02d6adbb433 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -2807,7 +2807,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 b51ebff0006..f4cc5df0558 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -2893,7 +2893,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 0b1eb1e197b..4811053a231 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -3131,7 +3131,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 68b96ff6e7c..ab43c1bbb2d 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -2850,7 +2850,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 ad90c0d6b36..7c51c9cfef9 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