Implement support for the EWMH fullscreening hint

Mike Hearn mh at codeweavers.com
Thu May 27 19:21:25 CDT 2004


This let's IE fullscreen correctly in standards compliant WMs, though
it also reveals a bug in our rebar control which can cause a (temporary?) 
deadlock when leaving fullscreen mode.


Mike Hearn <mh at codeweavers.com>
Implement support for the EWMH fullscreening hint.

Generated from:
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-1
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-2
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-3
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-4
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-5
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-6
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-7
* mike at navi.cx--2004/wine--fullscreen--0.9--patch-8

--- mod/dlls/x11drv/window.c
+++ mod/dlls/x11drv/window.c
@@ -74,6 +74,8 @@
     "DndSelection",
     "_MOTIF_WM_HINTS",
     "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
+    "_NET_WM_STATE",
+    "_NET_WM_STATE_FULLSCREEN",
     "_NET_WM_PID",
     "_NET_WM_PING",
     "_NET_WM_NAME",
@@ -114,6 +116,7 @@
 inline static BOOL is_window_managed( WND *win )
 {
     if (!managed_mode) return FALSE;
+    
     /* tray window is always managed */
     if (win->dwExStyle & WS_EX_TRAYWINDOW) return TRUE;
     /* child windows are not managed */
@@ -126,13 +129,9 @@
     if (win->dwStyle & WS_THICKFRAME) return TRUE;
     /* application windows are managed */
     if (win->dwExStyle & WS_EX_APPWINDOW) return TRUE;
-    /* full-screen popup windows are managed */
-    if ((win->dwStyle & WS_POPUP) && 
-        (win->rectWindow.right-win->rectWindow.left) == screen_width && 
-        (win->rectWindow.bottom-win->rectWindow.top) == screen_height) 
-    {
-        return TRUE;
-    }
+    /* windows with a system menu are managed */
+    if (win->dwStyle & WS_SYSMENU) return TRUE;
+    
     /* default: not managed */
     return FALSE;
 }
@@ -177,7 +176,6 @@
     return (CWOverrideRedirect | CWSaveUnder | CWEventMask | CWColormap | CWCursor);
 }
 
-
 /***********************************************************************
  *              X11DRV_sync_window_style
  *
@@ -354,7 +352,10 @@
         size_hints->y = data->whole_rect.top;
         size_hints->flags = PWinGravity | PPosition;
 
-        if ( !(win->dwStyle & WS_THICKFRAME) )
+        /* we only want to be able to resize windows with WS_THICKFRAME.
+	      * in addition, metacity refuses to fullscreen non-resizable windows
+	      */
+        if ( !(win->dwStyle & WS_THICKFRAME) && !data->fullscreened)
         {
             size_hints->max_width = data->whole_rect.right - data->whole_rect.left;
             size_hints->max_height = data->whole_rect.bottom - data->whole_rect.top;
@@ -448,7 +449,7 @@
     if (win->dwStyle & WS_MAXIMIZEBOX) mwm_hints.functions |= MWM_FUNC_MAXIMIZE;
     if (win->dwStyle & WS_SYSMENU)    mwm_hints.functions |= MWM_FUNC_CLOSE;
     mwm_hints.decorations = 0;
-    if ((win->dwStyle & WS_CAPTION) == WS_CAPTION) mwm_hints.decorations |= MWM_DECOR_TITLE;
+    if (((win->dwStyle & WS_CAPTION) == WS_CAPTION) && !data->fullscreened) mwm_hints.decorations |= MWM_DECOR_TITLE;
     if (win->dwExStyle & WS_EX_DLGMODALFRAME) mwm_hints.decorations |= MWM_DECOR_BORDER;
     else if (win->dwStyle & WS_THICKFRAME) mwm_hints.decorations |= MWM_DECOR_BORDER | MWM_DECOR_RESIZEH;
     else if ((win->dwStyle & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME) mwm_hints.decorations |= MWM_DECOR_BORDER;
@@ -465,6 +466,15 @@
     XChangeProperty( display, data->whole_window, x11drv_atom(XdndAware),
                      XA_ATOM, 32, PropModeReplace, (unsigned char*)&dndVersion, 1 );
 
+    /* for windows that are already mapped, toggle_fullscreen sends a message to the WM */        
+    if (data->fullscreened && !(win->dwStyle & WS_VISIBLE)) {
+        Atom newstate = x11drv_atom(_NET_WM_STATE_FULLSCREEN);
+
+        TRACE("setting state to _NET_WM_STATE_FULLSCREEN\n");
+        XChangeProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE),
+                         XA_ATOM, 32, PropModeReplace, (unsigned char*)&newstate, 1 );
+    }
+    
     wm_hints = XAllocWMHints();
     wine_tsx11_unlock();
 
@@ -762,6 +772,8 @@
     RECT rect;
     BOOL is_top_level = is_window_top_level( win );
 
+    data->fullscreened = FALSE;
+    
     rect = win->rectWindow;
     X11DRV_window_to_X_rect( win, &rect );
 
--- mod/dlls/x11drv/winpos.c
+++ mod/dlls/x11drv/winpos.c
@@ -3,6 +3,7 @@
  *
  * Copyright 1993, 1994, 1995, 2001 Alexandre Julliard
  * Copyright 1995, 1996, 1999 Alex Korobka
+ * Copyright 2004 Mike Hearn for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -74,6 +75,46 @@
 
 
 /***********************************************************************
+ *		toggle_fullscreen
+ *
+ * Use the NETWM fullscreening protocol to toggle fullscreened state. This only
+ * works for mapped windows.
+ */
+static void toggle_fullscreen( HWND hwnd )
+{
+    WND *win = WIN_GetPtr(hwnd);
+    struct x11drv_win_data *data = win->pDriverData;
+    XEvent xev;
+
+    TRACE("hwnd=%p, current=%s\n", hwnd, data->fullscreened ? "true" : "false");
+    data->fullscreened = !data->fullscreened;
+    
+    wine_tsx11_lock();
+
+    X11DRV_set_wm_hints(thread_display(), win);
+
+    if (win->dwStyle & WS_VISIBLE) {
+        TRACE("toggling fullscreen state\n");
+        xev.xclient.type = ClientMessage;
+        xev.xclient.window = data->whole_window;
+        xev.xclient.message_type = x11drv_atom(_NET_WM_STATE);
+        xev.xclient.serial = 0;
+        xev.xclient.display = thread_display();
+        xev.xclient.send_event = True;
+        xev.xclient.format = 32;
+        /* using _NET_WM_STATE_TOGGLE here does not work correctly with some WMs */
+        xev.xclient.data.l[0] = (data->fullscreened ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE);
+        xev.xclient.data.l[1] = x11drv_atom(_NET_WM_STATE_FULLSCREEN);
+        xev.xclient.data.l[2] = 0;
+        XSendEvent(thread_display(), root_window, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+        TRACE("toggled\n");
+    }
+
+    wine_tsx11_unlock();
+    WIN_ReleasePtr(win);
+}
+
+/***********************************************************************
  *		clip_children
  *
  * Clip all children of a given window out of the visible region
@@ -861,7 +902,7 @@
     if (wndPtr == WND_OTHER_PROCESS) return;
 
     changed = wndPtr->dwStyle ^ oldStyle;
-
+    
     if (changed & WS_VISIBLE)
     {
         if (!IsRectEmpty( &wndPtr->rectWindow ))
@@ -919,12 +960,14 @@
     RECT newWindowRect, newClientRect;
     RECT oldWindowRect, oldClientRect;
     UINT wvrFlags = 0;
-    BOOL bChangePos;
+    BOOL bChangePos, fromXEvent;
+    struct x11drv_win_data *data;
 
     TRACE( "hwnd %p, after %p, swp %d,%d %dx%d flags %08x\n",
            winpos->hwnd, winpos->hwndInsertAfter, winpos->x, winpos->y,
            winpos->cx, winpos->cy, winpos->flags);
 
+    fromXEvent = winpos->flags & SWP_WINE_NOHOSTMOVE;
     bChangePos = !(winpos->flags & SWP_WINE_NOHOSTMOVE);
     winpos->flags &= ~SWP_WINE_NOHOSTMOVE;
 
@@ -949,11 +992,28 @@
 
     if (!SWP_DoWinPosChanging( winpos, &newWindowRect, &newClientRect )) return FALSE;
 
+    if (!(wndPtr = WIN_FindWndPtr( winpos->hwnd ))) return FALSE;
+    data = wndPtr->pDriverData;
+    
+    if (data->fullscreened) {
+        
+        /* Windows has no concept of fullscreen, so to unfullscreen an app you just resize it or move it.
+         * Therefore we need to watch for this behaviour and release fullscreen mode so we can once again
+         * control our position.
+         */
+        
+        if (!fromXEvent /* ignore spurious WM changes once we are fullscreened */
+            && !(winpos->flags & (SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE)) /* ignore SWP calls that aren't changing our geometry */
+            && !((newWindowRect.left < 0) && (newWindowRect.top < 0))) /* probably moving us beyond the screen boundaries to hide the borders, ignore */ {
+            
+            TRACE("releasing fullscreen for %p\n", winpos->hwnd);
+            toggle_fullscreen( winpos->hwnd );
+        }
+    }
+    
     /* Fix redundant flags */
     if (!fixup_flags( winpos )) return FALSE;
-
-    if (!(wndPtr = WIN_FindWndPtr( winpos->hwnd ))) return FALSE;
-
+    
     TRACE("\tcurrent (%ld,%ld)-(%ld,%ld), style %08x\n",
           wndPtr->rectWindow.left, wndPtr->rectWindow.top,
           wndPtr->rectWindow.right, wndPtr->rectWindow.bottom, (unsigned)wndPtr->dwStyle );
@@ -965,15 +1025,16 @@
     }
 
     /* Common operations */
+    wvrFlags = SWP_DoNCCalcSize( winpos, &newWindowRect, &newClientRect ); /* FIXME: this should only be called if the size is actually being changed, unless SWP_FRAMECHANGED is set */
 
-    wvrFlags = SWP_DoNCCalcSize( winpos, &newWindowRect, &newClientRect );
-
+    TRACE("after NC_CALCSIZE (%ld,%ld,%ld,%ld)\n", newWindowRect.left, newWindowRect.top, newWindowRect.right, newWindowRect.bottom);
+    
     if(!(winpos->flags & SWP_NOZORDER) && winpos->hwnd != winpos->hwndInsertAfter)
     {
         HWND parent = GetAncestor( winpos->hwnd, GA_PARENT );
         if (parent) WIN_LinkWindow( winpos->hwnd, parent, winpos->hwndInsertAfter );
     }
-
+        
     /* Reset active DCEs */
 
     if( (((winpos->flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) &&
@@ -1039,6 +1100,7 @@
             XClearArea( display, get_whole_window(wndPtr), 0, 0, 0, 0, True );
             winpos->flags |= SWP_FRAMECHANGED;
         }
+	
         if (winpos->flags & SWP_SHOWWINDOW)
         {
             set_visible_style( winpos->hwnd, TRUE );
@@ -1177,6 +1239,7 @@
     POINT size;
     LONG old_style;
     WINDOWPLACEMENT wpl;
+    struct x11drv_win_data* data;
 
     TRACE("%p %u\n", hwnd, cmd );
 
@@ -1195,13 +1258,24 @@
 
     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
 
+    data = wndPtr->pDriverData;
+    
     size.x = wndPtr->rectWindow.left;
     size.y = wndPtr->rectWindow.top;
 
+    /* if the window has no caption and is being maximized or restored,
+       this is a good sign we need to (un)fullscreen it */
+    if ((wndPtr->dwStyle & WS_CAPTION) != WS_CAPTION) {
+        if (((cmd == SW_RESTORE) && data->fullscreened) || ((cmd == SW_MAXIMIZE) && !data->fullscreened)) {
+            TRACE("toggling fullscreen due to %s of captionless window %p\n", cmd == SW_MAXIMIZE ? "maximization" : "restoration", hwnd);
+            toggle_fullscreen( hwnd );
+        }
+    }
+    
     switch( cmd )
     {
     case SW_MINIMIZE:
-        if( wndPtr->dwStyle & WS_MAXIMIZE) wndPtr->flags |= WIN_RESTORE_MAX;
+        if (wndPtr->dwStyle & WS_MAXIMIZE) wndPtr->flags |= WIN_RESTORE_MAX;
         else wndPtr->flags &= ~WIN_RESTORE_MAX;
 
         WIN_SetStyle( hwnd, (wndPtr->dwStyle & ~WS_MAXIMIZE) | WS_MINIMIZE );
@@ -1692,8 +1766,10 @@
     /* if nothing changed, don't do anything */
     if (winpos.flags == (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE)) return;
 
+    TRACE("syncing window pos enter\n");
     SetWindowPos( hwnd, winpos.hwndInsertAfter, winpos.x, winpos.y,
                   winpos.cx, winpos.cy, winpos.flags | SWP_WINE_NOHOSTMOVE );
+    TRACE("syncing window pos leave\n");
 }
 
 
--- orig/dlls/x11drv/x11drv.h
+++ mod/dlls/x11drv/x11drv.h
@@ -413,6 +413,8 @@
     XATOM_DndSelection,
     XATOM__MOTIF_WM_HINTS,
     XATOM__KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
+    XATOM__NET_WM_STATE,
+    XATOM__NET_WM_STATE_FULLSCREEN,
     XATOM__NET_WM_PID,
     XATOM__NET_WM_PING,
     XATOM__NET_WM_NAME,
@@ -446,6 +448,10 @@
 
 #define x11drv_atom(name) (X11DRV_Atoms[XATOM_##name - FIRST_XATOM])
 
+#define _NET_WM_STATE_REMOVE 0
+#define _NET_WM_STATE_ADD 1
+#define _NET_WM_STATE_TOGGLE 2
+
 /* X11 clipboard driver */
 
 typedef struct tagWINE_CLIPDATA {
@@ -511,6 +517,7 @@
     XIC     xic;            /* X input context */
     HBITMAP hWMIconBitmap;
     HBITMAP hWMIconMask;
+    BOOL    fullscreened;   /* does the window have a NETWM fullscreening hint set? */
 };
 
 typedef struct x11drv_win_data X11DRV_WND_DATA;




More information about the wine-patches mailing list