From: "Gabriel Ivăncescu" Subject: [PATCH v2 3/3] winex11.drv: Remove topmost state for DXGI fullscreen windows when focus is lost. Message-Id: <9ce3de38cc2dbb6350ca42d588f89c066076796e.1611929223.git.gabrielopcode@gmail.com> Date: Fri, 29 Jan 2021 16:08:24 +0200 In-Reply-To: <7080f41da892a95a35455976640aebfdca4378c4.1611929223.git.gabrielopcode@gmail.com> References: <7080f41da892a95a35455976640aebfdca4378c4.1611929223.git.gabrielopcode@gmail.com> This is what Windows' DWM does to DXGI fullscreen windows when e.g. Alt-Tab is released. Signed-off-by: Gabriel Ivăncescu --- 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", -- 2.29.2