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 | 26 ++++++++++++++++++++++++++ dlls/wined3d/wined3d_private.h | 4 ++++ include/wine/wined3d.h | 1 + 9 files changed, 39 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 3ba738b1324..d8c56242332 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 cacd1804869..0584cc9d754 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 ed7c8de3043..c8008504523 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 896d304fd24..8a46ebe4de0 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..343d6a6314f 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -2151,8 +2151,18 @@ struct wined3d_window_state int x, y, width, height; uint32_t flags; bool set_style; + bool register_topmost_timer; + bool set_topmost_timer; };
+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; @@ -2187,6 +2197,18 @@ 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, + WINED3D_WINDOW_TOPMOST_TIMER_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 +2261,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 = (s->window_pos_after == HWND_TOPMOST && !(s->flags & SWP_NOZORDER));
TRACE("Old style was %08lx, %08lx, setting to %08lx, %08lx.\n", state->style, state->exstyle, s->style, s->exstyle); @@ -2293,6 +2317,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/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 09f078344c8..df9b0e183c6 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -5078,6 +5078,10 @@ static inline bool wined3d_map_persistent(void) /* The WNDCLASS-Name for the fake window which we use to retrieve the GL capabilities */ #define WINED3D_OPENGL_WINDOW_CLASS_NAME "WineD3D_OpenGL"
+/* For the timer that checks and restores WS_EX_TOPMOST */ +#define WINED3D_WINDOW_TOPMOST_TIMER_ID 0x4242 +#define WINED3D_WINDOW_TOPMOST_TIMER_TIMEOUT 1500 + extern CRITICAL_SECTION wined3d_command_cs;
#endif 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