[PATCH 3/3] winex11.drv: Remove topmost state for DXGI fullscreen windows when focus is lost.

Gabriel Ivăncescu gabrielopcode at gmail.com
Thu Jan 28 07:51:09 CST 2021


This is what Windows' DWM does to DXGI fullscreen windows when e.g. Alt-Tab
is released.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at 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 6e586cd..6f14dbf 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");
@@ -439,8 +442,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;
     }
@@ -922,6 +928,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:
@@ -1954,6 +1962,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);
     }
@@ -2290,8 +2299,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;
     }
@@ -3199,6 +3212,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




More information about the wine-devel mailing list