These calls are Wine-specific and expected to fail on Windows, as the escape codes and parameter combinations are likely invalid there. There doesn't seem to be any mechanism to do this otherwise.
When a window presentation rect has been set, the window is meant to be presented to that rect on screen exactly, mapped to physical coords.
Then this also has the following side effects:
* In exclusive fullscreen mode, GDI drawn child windows are clipped out of the rendered screen, regardless of the normal clipping rules.
* The window position and size changes should be ignored wrt its visible area, and in particular the window should not be unmapped even if its Win32 rect is moved to an invisible location.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443
---
This only introduces the mechanism and calls it from Wine D3D side. The side effects will be implemented later in win32u and the user drivers.
This is necessary both for Command & Conquer 2, which has dialog child windows covering the entire game window while using WS_CLIPCHILDREN, and for Fallout 3 which does a bogus call sequence after window restoration which moves the window outside of screen area and causes its window and client rects to be empty:
SetRect(&rect, 0, 0, width, height); AdjustWindowRectEx(&rect, style, FALSE, 0); SetWindowPos(hwnd, 0, rect.left, rect.bottom, rect.right - rect.left, rect.top - rect.bottom, 0);
We need either this kind of mechanism (which could be later changed to some D3DKMTPresent call, but it would require a lot more work and I'd like to fix those regressions before the release), or to hook the window proc like modern Windows seems to be doing as tested in !9549 (and more tests suggest that invalid window moves are also now inhibited and corrected on modern Windows).
From: Rémi Bernon rbernon@codeweavers.com
These calls are Wine-specific and expected to fail on Windows, as the escape codes and parameter combinations are likely invalid there. There doesn't seem to be any mechanism to do this otherwise.
When a window presentation rect has been set, the window is meant to be presented to that rect on screen exactly, mapped to physical coords.
Then this also has the following side effects:
* In exclusive fullscreen mode, GDI drawn child windows are clipped out of the rendered screen, regardless of the normal clipping rules.
* The window position and size changes should be ignored wrt its visible area, and in particular the window should not be unmapped even if its Win32 rect is moved to an invisible location.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443 --- dlls/win32u/d3dkmt.c | 17 +++++++++++++++++ dlls/win32u/ntuser_private.h | 1 + include/ddk/d3dkmthk.h | 1 + 3 files changed, 19 insertions(+)
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c index 8da6886154a..baf99f69291 100644 --- a/dlls/win32u/d3dkmt.c +++ b/dlls/win32u/d3dkmt.c @@ -547,6 +547,23 @@ NTSTATUS WINAPI NtGdiDdDDIEscape( const D3DKMT_ESCAPE *desc ) return d3dkmt_object_update( &resource->obj, desc->pPrivateDriverData, desc->PrivateDriverDataSize ); }
+ case D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE: + { + HWND hwnd = UlongToHandle( desc->hContext ); + RECT *rect = desc->pPrivateDriverData; + UINT dpi = get_dpi_for_window( hwnd ); + WND *win; + + if (desc->PrivateDriverDataSize != sizeof(*rect)) return STATUS_INVALID_PARAMETER; + + TRACE( "hwnd %p, rect %s\n", hwnd, wine_dbgstr_rect( rect ) ); + if (!(win = get_win_ptr( hwnd ))) return STATUS_INVALID_PARAMETER; + win->present_rect = map_dpi_rect( *rect, get_thread_dpi(), dpi ); + release_win_ptr( win ); + + return STATUS_SUCCESS; + } + default: FIXME( "(%p): stub\n", desc ); return STATUS_NO_MEMORY; diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 14dcff3ad38..697e4c74203 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -51,6 +51,7 @@ typedef struct tagWND HINSTANCE hInstance; /* Window hInstance (from CreateWindow) */ struct window_rects rects; /* window rects in window DPI, relative to the parent client area */ RECT normal_rect; /* Normal window rect saved when maximized/minimized */ + RECT present_rect; /* present rect for exclusive fullscreen mode */ POINT min_pos; /* Position for minimized window */ POINT max_pos; /* Position for maximized window */ WCHAR *text; /* Window text */ diff --git a/include/ddk/d3dkmthk.h b/include/ddk/d3dkmthk.h index 9f7e6d55a3d..a4da022e9aa 100644 --- a/include/ddk/d3dkmthk.h +++ b/include/ddk/d3dkmthk.h @@ -770,6 +770,7 @@ typedef enum _D3DKMT_ESCAPETYPE D3DKMT_ESCAPE_DIAGNOSTICS, /* Wine-specific escape codes */ D3DKMT_ESCAPE_UPDATE_RESOURCE_WINE = 0x80000000, + D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE = 0x80000001, } D3DKMT_ESCAPETYPE;
typedef struct _D3DKMT_ESCAPE
From: Rémi Bernon rbernon@codeweavers.com
And unset it when exiting fullscreen.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443 --- dlls/wined3d/device.c | 16 ++++++++++++++++ dlls/wined3d/swapchain.c | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 443bf10ab76..fb515e8c3be 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -4979,6 +4979,19 @@ static void update_swapchain_flags(struct wined3d_texture *texture) texture->flags &= ~WINED3D_TEXTURE_GET_DC; }
+static BOOL set_window_present_rect(HWND hwnd, UINT x, UINT y, UINT width, UINT height) +{ + RECT rect = {x, y, x + width, y + height}; + D3DKMT_ESCAPE escape = {0}; + + escape.Type = D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE; + escape.hContext = HandleToULong(hwnd); + escape.pPrivateDriverData = ▭ + escape.PrivateDriverDataSize = sizeof(rect); + + return !D3DKMTEscape(&escape); +} + HRESULT CDECL wined3d_device_reset(struct wined3d_device *device, const struct wined3d_swapchain_desc *swapchain_desc, const struct wined3d_display_mode *mode, wined3d_device_reset_cb callback, BOOL reset_state) @@ -5135,6 +5148,9 @@ HRESULT CDECL wined3d_device_reset(struct wined3d_device *device,
swapchain_state->style = 0; swapchain_state->exstyle = 0; + set_window_present_rect(swapchain_state->device_window, output_desc.desktop_rect.left, + output_desc.desktop_rect.top, swapchain_desc->backbuffer_width, + swapchain_desc->backbuffer_height); wined3d_swapchain_state_setup_fullscreen(swapchain_state, swapchain_state->device_window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, swapchain_desc->backbuffer_width, swapchain_desc->backbuffer_height); diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index a1000ec2393..d1f835fa809 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -27,6 +27,19 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d); WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
+static BOOL set_window_present_rect(HWND hwnd, UINT x, UINT y, UINT width, UINT height) +{ + RECT rect = {x, y, x + width, y + height}; + D3DKMT_ESCAPE escape = {0}; + + escape.Type = D3DKMT_ESCAPE_SET_PRESENT_RECT_WINE; + escape.hContext = HandleToULong(hwnd); + escape.pPrivateDriverData = ▭ + escape.PrivateDriverDataSize = sizeof(rect); + + return !D3DKMTEscape(&escape); +} + void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) { HRESULT hr; @@ -78,12 +91,14 @@ void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) { wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, swapchain->state.device_window, &swapchain->state.original_window_rect); + set_window_present_rect(swapchain->state.device_window, 0, 0, 0, 0); wined3d_device_release_focus_window(swapchain->device); } } else { wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, swapchain->state.device_window, NULL); + set_window_present_rect(swapchain->state.device_window, 0, 0, 0, 0); } } } @@ -1559,6 +1574,8 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc goto err; }
+ set_window_present_rect(window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, + desc->backbuffer_width, desc->backbuffer_height); wined3d_swapchain_state_setup_fullscreen(&swapchain->state, window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, desc->backbuffer_width, desc->backbuffer_height); @@ -2432,6 +2449,8 @@ HRESULT CDECL wined3d_swapchain_state_set_fullscreen(struct wined3d_swapchain_st return hr; }
+ set_window_present_rect(state->device_window, output_desc.desktop_rect.left, + output_desc.desktop_rect.top, width, height); if (state->desc.windowed) { /* Switch from windowed to fullscreen */ @@ -2460,6 +2479,7 @@ HRESULT CDECL wined3d_swapchain_state_set_fullscreen(struct wined3d_swapchain_st if (state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT) window_rect = &state->original_window_rect; wined3d_swapchain_state_restore_from_fullscreen(state, state->device_window, window_rect); + set_window_present_rect(state->device_window, 0, 0, 0, 0); }
state->desc.output = swapchain_desc->output;
From: Rémi Bernon rbernon@codeweavers.com
The client rect might be empty or invalid. Fallout 3 in particular does a bogus call sequence after restoring minimized window which causes the window and client rects to be empty:
SetRect(&rect, 0, 0, width, height); AdjustWindowRectEx(&rect, style, FALSE, 0); SetWindowPos(hwnd, 0, rect.left, rect.bottom, rect.right - rect.left, rect.top - rect.bottom, 0);
We either need to hook the window messages and inhibit such window size changes, or we need to avoid relying on client and window rect when in fullscreen mode.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58844 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=58443 --- dlls/wined3d/context_gl.c | 18 ++++++++++++++++++ dlls/wined3d/swapchain.c | 29 +++++++++++++++++++++++++++-- dlls/wined3d/texture.c | 15 +++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/dlls/wined3d/context_gl.c b/dlls/wined3d/context_gl.c index ac1b9370fc1..223652a51cc 100644 --- a/dlls/wined3d/context_gl.c +++ b/dlls/wined3d/context_gl.c @@ -2393,6 +2393,24 @@ static void wined3d_context_gl_get_rt_size(const struct wined3d_context_gl *cont { RECT window_size;
+ if (!rt->swapchain->state.desc.windowed) + { + struct wined3d_display_mode actual_mode; + struct wined3d_output *output; + HRESULT hr; + + if (!(output = wined3d_swapchain_get_output(rt->swapchain))) + ERR("Failed to get swapchain output.\n"); + else if (FAILED(hr = wined3d_output_get_display_mode(output, &actual_mode, NULL))) + ERR("Failed to get display mode, hr %#lx.\n", hr); + else + { + size->cx = actual_mode.width; + size->cy = actual_mode.height; + return; + } + } + GetClientRect(context_gl->window, &window_size); size->cx = window_size.right - window_size.left; size->cy = window_size.bottom - window_size.top; diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index d1f835fa809..f6b71021bd4 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -40,6 +40,31 @@ static BOOL set_window_present_rect(HWND hwnd, UINT x, UINT y, UINT width, UINT return !D3DKMTEscape(&escape); }
+static void get_swapchain_present_rect(struct wined3d_swapchain *swapchain, RECT *rect) +{ + if (!swapchain->state.desc.windowed) + { + struct wined3d_display_mode actual_mode; + struct wined3d_output_desc output_desc; + struct wined3d_output *output; + HRESULT hr; + + if (!(output = wined3d_swapchain_get_output(swapchain))) + ERR("Failed to get swapchain output.\n"); + else if (FAILED(hr = wined3d_output_get_desc(output, &output_desc))) + ERR("Failed to get output description, hr %#lx.\n", hr); + else if (FAILED(hr = wined3d_output_get_display_mode(output, &actual_mode, NULL))) + ERR("Failed to get display mode, hr %#lx.\n", hr); + else + SetRect(rect, output_desc.desktop_rect.left, output_desc.desktop_rect.top, + output_desc.desktop_rect.left + actual_mode.width, + output_desc.desktop_rect.top + actual_mode.height); + return; + } + + GetClientRect(swapchain->win_handle, rect); +} + void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain) { HRESULT hr; @@ -235,7 +260,7 @@ HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain,
if (!dst_rect) { - GetClientRect(swapchain->win_handle, &d); + get_swapchain_present_rect(swapchain, &d); dst_rect = &d; }
@@ -583,7 +608,7 @@ static bool swapchain_present_is_partial_copy(struct wined3d_swapchain *swapchai if (swap_effect != WINED3D_SWAP_EFFECT_COPY && swap_effect != WINED3D_SWAP_EFFECT_COPY_VSYNC) return false;
- GetClientRect(swapchain->win_handle, &client_rect); + get_swapchain_present_rect(swapchain, &client_rect);
t = client_rect.right - client_rect.left; if ((dst_rect->left && dst_rect->right) || abs(dst_rect->right - dst_rect->left) != t) diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c index dd61a2f0d3c..0d2ccea4541 100644 --- a/dlls/wined3d/texture.c +++ b/dlls/wined3d/texture.c @@ -82,6 +82,21 @@ void wined3d_texture_translate_drawable_coords(const struct wined3d_texture *tex }
GetClientRect(window, &windowsize); + + if (!texture->swapchain->state.desc.windowed) + { + struct wined3d_display_mode actual_mode; + struct wined3d_output *output; + HRESULT hr; + + if (!(output = wined3d_swapchain_get_output(texture->swapchain))) + ERR("Failed to get swapchain output.\n"); + else if (FAILED(hr = wined3d_output_get_display_mode(output, &actual_mode, NULL))) + ERR("Failed to get display mode, hr %#lx.\n", hr); + else + SetRect(&windowsize, 0, 0, actual_mode.width, actual_mode.height); + } + drawable_height = windowsize.bottom - windowsize.top;
rect->top = drawable_height - rect->top;