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.
This commit is contained in:
Zhiyi Zhang 2023-09-19 17:30:23 +08:00 committed by Alexandre Julliard
parent a87c1d251e
commit 9092a4ccca
8 changed files with 37 additions and 5 deletions

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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)
{

View file

@ -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