Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
v2: - Check for WS_EX_TOPMOST before removing it. - Get rid of broken() on recent Windows 10 builds by using ResizeBuffers. - Test Present with the DXGI_PRESENT_TEST flag. - Test on hidden window as well. (it seems reliable enough, perhaps I messed up by clicking on stuff by mistake while it was running)
dlls/dxgi/tests/dxgi.c | 86 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 2d8826f..6c9f293 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -2826,10 +2826,94 @@ static void test_set_fullscreen(IUnknown *device, BOOL is_d3d12) refcount = IDXGISwapChain_Release(swapchain); ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
- swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", WS_VISIBLE, 0, 0, 400, 200, 0, 0, 0, 0); check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state); swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!(GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE) & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + ok(GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE) & WS_EX_TOPMOST, "Did not get WS_EX_TOPMOST.\n"); + + /* Removing topmost style makes the swapchain lose fullscreen state on next Present */ + SetWindowPos(swapchain_desc.OutputWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + ok(!(GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE) & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + flush_events(); + + hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); + todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + + hr = IDXGISwapChain_Present(swapchain, 0, DXGI_PRESENT_TEST); + todo_wine ok(hr == DXGI_STATUS_OCCLUDED, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + + hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); + todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + hr = IDXGISwapChain_Present(swapchain, 0, 0); + todo_wine ok(hr == DXGI_STATUS_OCCLUDED, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + todo_wine ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + + hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); + todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_Present(swapchain, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + /* But not if the window is hidden */ + ShowWindow(swapchain_desc.OutputWindow, SW_HIDE); + ok(!(GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE) & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + ok(!(GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE) & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + ok(GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE) & WS_EX_TOPMOST, "Did not get WS_EX_TOPMOST.\n"); + + SetWindowPos(swapchain_desc.OutputWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_HIDEWINDOW); + ok(!(GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE) & WS_VISIBLE), "Got unexpected WS_VISIBLE.\n"); + ok(!(GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE) & WS_EX_TOPMOST), "Got unexpected WS_EX_TOPMOST.\n"); + flush_events(); + + hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); + todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + + hr = IDXGISwapChain_Present(swapchain, 0, 0); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + refcount = IDXGISwapChain_Release(swapchain); + ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount); + + DestroyWindow(swapchain_desc.OutputWindow); + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state); + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr); check_swapchain_fullscreen_state(swapchain, &initial_state); test_swapchain_fullscreen_state(swapchain, adapter, &initial_state);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/dxgi/swapchain.c | 20 +++++++++++++++++++- dlls/dxgi/tests/dxgi.c | 8 ++++---- 2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 82e5fbf..5611c83 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -328,13 +328,31 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDevice(IDXGISwapChain1 *ifac static HRESULT d3d11_swapchain_present(struct d3d11_swapchain *swapchain, unsigned int sync_interval, unsigned int flags) { + HWND hwnd = d3d11_swapchain_get_hwnd(swapchain); + BOOL fullscreen; + HRESULT hr; + if (sync_interval > 4) { WARN("Invalid sync interval %u.\n", sync_interval); return DXGI_ERROR_INVALID_CALL; }
- if (IsIconic(d3d11_swapchain_get_hwnd(swapchain))) + if (IsWindowVisible(hwnd)) + { + hr = IDXGISwapChain1_GetFullscreenState(&swapchain->IDXGISwapChain1_iface, &fullscreen, NULL); + if (FAILED(hr)) + return hr; + + if (fullscreen && !(GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)) + { + if (!(flags & DXGI_PRESENT_TEST)) + IDXGISwapChain1_SetFullscreenState(&swapchain->IDXGISwapChain1_iface, FALSE, NULL); + return DXGI_STATUS_OCCLUDED; + } + } + + if (IsIconic(hwnd)) return DXGI_STATUS_OCCLUDED;
if (flags & ~DXGI_PRESENT_TEST) diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 6c9f293..24bfad8 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -2854,7 +2854,7 @@ static void test_set_fullscreen(IUnknown *device, BOOL is_d3d12) ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
hr = IDXGISwapChain_Present(swapchain, 0, DXGI_PRESENT_TEST); - todo_wine ok(hr == DXGI_STATUS_OCCLUDED, "Got unexpected hr %#x.\n", hr); + ok(hr == DXGI_STATUS_OCCLUDED, "Got unexpected hr %#x.\n", hr); hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); @@ -2865,13 +2865,13 @@ static void test_set_fullscreen(IUnknown *device, BOOL is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); hr = IDXGISwapChain_Present(swapchain, 0, 0); - todo_wine ok(hr == DXGI_STATUS_OCCLUDED, "Got unexpected hr %#x.\n", hr); + ok(hr == DXGI_STATUS_OCCLUDED, "Got unexpected hr %#x.\n", hr); hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); - todo_wine ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen); + ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); - todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); hr = IDXGISwapChain_Present(swapchain, 0, 0); ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=84742
Your paranoid android.
=== w1064_2qxl (64 bit report) ===
dxgi: dxgi.c:2840: Test failed: Did not get WS_EX_TOPMOST. dxgi.c:2854: Test failed: Got unexpected fullscreen 0. dxgi.c:2857: Test failed: Got unexpected hr 0. dxgi.c:2860: Test failed: Got unexpected fullscreen 0. dxgi.c:2866: Test failed: Got unexpected fullscreen 0. dxgi.c:2868: Test failed: Got unexpected hr 0.
This is what Windows' DWM does to DXGI fullscreen windows when e.g. Alt-Tab is released.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Handling it in FocusOut seems like the closest option, although we have to workaround the Z-order by bringing the focused window to the top, because it should actually happen before the focus change, but that's out of our control.
dlls/dxgi/swapchain.c | 19 ++++++++++++-- dlls/winex11.drv/event.c | 48 ++++++++++++++++++++++++++++++++-- dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + 4 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 5611c83..48e2949 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -38,6 +38,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(dxgi); WINE_DECLARE_DEBUG_CHANNEL(winediag);
+static const WCHAR wine_dxgi_fs_propW[] = { '_','_','w','i','n','e','_','d','x','g','i','_','f','u','l','l','s','c','r','e','e','n',0 }; + static DXGI_SWAP_EFFECT dxgi_swap_effect_from_wined3d(enum wined3d_swap_effect swap_effect) { switch (swap_effect) @@ -242,6 +244,7 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain1 *iface) if (!refcount) { IWineDXGIDevice *device = swapchain->device; + RemovePropW(d3d11_swapchain_get_hwnd(swapchain), wine_dxgi_fs_propW); if (swapchain->target) { WARN("Releasing fullscreen swapchain.\n"); @@ -443,8 +446,11 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_SetFullscreen return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; }
- if (!fullscreen) + if (fullscreen) + SetPropW(d3d11_swapchain_get_hwnd(swapchain), wine_dxgi_fs_propW, (HANDLE)TRUE); + else { + RemovePropW(d3d11_swapchain_get_hwnd(swapchain), wine_dxgi_fs_propW); IDXGIOutput_Release(target); target = NULL; } @@ -926,6 +932,8 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi } wined3d_mutex_unlock();
+ if (fullscreen) + SetPropW(d3d11_swapchain_get_hwnd(swapchain), wine_dxgi_fs_propW, (HANDLE)TRUE); return S_OK;
cleanup: @@ -1958,6 +1966,7 @@ static ULONG STDMETHODCALLTYPE d3d12_swapchain_Release(IDXGISwapChain4 *iface)
if (!refcount) { + RemovePropW(swapchain->window, wine_dxgi_fs_propW); d3d12_swapchain_destroy(swapchain); heap_free(swapchain); } @@ -2294,8 +2303,12 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d12_swapchain_SetFullscreen goto fail;
fullscreen_desc->Windowed = wined3d_desc.windowed; - if (!fullscreen) + + if (fullscreen) + SetPropW(swapchain->window, wine_dxgi_fs_propW, (HANDLE)TRUE); + else { + RemovePropW(swapchain->window, wine_dxgi_fs_propW); IDXGIOutput_Release(target); target = NULL; } @@ -3203,6 +3216,8 @@ static HRESULT d3d12_swapchain_init(struct d3d12_swapchain *swapchain, IWineDXGI
IWineDXGIFactory_AddRef(swapchain->factory = factory);
+ if (!fullscreen_desc->Windowed) + SetPropW(swapchain->window, wine_dxgi_fs_propW, (HANDLE)TRUE); return S_OK; }
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index d21d2a7..e37c0f4 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -817,6 +817,8 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) */ static void focus_out( Display *display , HWND hwnd ) { + static const WCHAR wine_dxgi_fs_propW[] = { '_','_','w','i','n','e','_','d','x','g','i','_','f','u','l','l','s','c','r','e','e','n',0 }; + struct x11drv_win_data *data; HWND hwnd_tmp; Window focus_win; int revert; @@ -835,10 +837,52 @@ static void focus_out( Display *display , HWND hwnd ) if (hwnd != GetForegroundWindow()) return; SendMessageW( hwnd, WM_CANCELMODE, 0, 0 );
+ XGetInputFocus( display, &focus_win, &revert ); + + /* when focus is changed by the WM (e.g. alt-tab) while a + dxgi fullscreen window is active, remove its topmost */ + if ((GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST) && + GetPropW( hwnd, wine_dxgi_fs_propW )) + { + SetWindowPos( hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING ); + + if ((data = get_win_data( hwnd ))) + { + int format, screen = data->vis.screen; + unsigned long count, remaining; + Window active_win = None; + XWindowAttributes attr; + XWindowChanges changes; + unsigned char *prop; + Atom type; + + /* reset the input focus in case the WM changed it */ + release_win_data( data ); + XSetInputFocus( display, focus_win, RevertToParent, CurrentTime ); + + /* move the active window to the top so it's not below the ex-topmost window */ + if (!XGetWindowProperty( display, DefaultRootWindow(display), x11drv_atom(_NET_ACTIVE_WINDOW), + 0, 1, False, XA_WINDOW, &type, &format, &count, &remaining, &prop )) + { + if (type == XA_WINDOW && format == 32 && count == 1) + active_win = *(long*)prop; + XFree(prop); + } + + if (XGetWindowAttributes( display, active_win != None ? active_win : focus_win, &attr ) && + XScreenNumberOfScreen( attr.screen ) == screen) + { + changes.stack_mode = Above; + if (active_win != None) + XReconfigureWMWindow( display, active_win, screen, CWStackMode, &changes ); + else if (focus_win) + XConfigureWindow( display, focus_win, CWStackMode, &changes ); + } + } + } + /* don't reset the foreground window, if the window which is getting the focus is a Wine window */ - - XGetInputFocus( display, &focus_win, &revert ); if (focus_win) { if (XFindContext( display, focus_win, winContext, (char **)&hwnd_tmp ) != 0) diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 4585597..be3a30b 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -437,6 +437,7 @@ enum x11drv_atoms XATOM_DndSelection, XATOM__ICC_PROFILE, XATOM__MOTIF_WM_HINTS, + XATOM__NET_ACTIVE_WINDOW, XATOM__NET_STARTUP_INFO_BEGIN, XATOM__NET_STARTUP_INFO, XATOM__NET_SUPPORTED, diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 9ec4c7a..3fcb117 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -150,6 +150,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "DndSelection", "_ICC_PROFILE", "_MOTIF_WM_HINTS", + "_NET_ACTIVE_WINDOW", "_NET_STARTUP_INFO_BEGIN", "_NET_STARTUP_INFO", "_NET_SUPPORTED",