Fullscreen without _NET_WM_FULLSCREEN_MONITORS
authorArmin Novak <armin.novak@thincast.com>
Wed, 20 Dec 2017 11:02:23 +0000 (12:02 +0100)
committerArmin Novak <armin.novak@thincast.com>
Wed, 20 Dec 2017 15:54:44 +0000 (16:54 +0100)
Some window managers do not support _NET_WM_FULLSCREEN_MONITORS.
In that case multimonitor fullscreen does not properly work, so
add a path resizing the window over all screens instead.
Based on @erbth pull request, adding proper X11 atom checks.

client/X11/xf_client.c
client/X11/xf_window.c
client/X11/xfreerdp.h

index 8196933..d74f303 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
+#include <X11/Xatom.h>
 
 #ifdef WITH_XRENDER
 #include <X11/extensions/Xrender.h>
@@ -1738,6 +1739,19 @@ static int xfreerdp_client_stop(rdpContext* context)
        return 0;
 }
 
+static Atom get_supported_atom(xfContext* xfc, const char* atomName)
+{
+       unsigned long i;
+       const Atom atom = XInternAtom(xfc->display, atomName, False);
+
+       for (i = 0;  i < xfc->supportedAtomCount;  i++)
+       {
+               if (xfc->supportedAtoms[i] == atom)
+                       return atom;
+       }
+
+       return None;
+}
 static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
 {
        xfContext* xfc = (xfContext*) instance->context;
@@ -1792,20 +1806,48 @@ static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
                goto fail_create_mutex;
        }
 
+       xfc->xfds = ConnectionNumber(xfc->display);
+       xfc->screen_number = DefaultScreen(xfc->display);
+       xfc->screen = ScreenOfDisplay(xfc->display, xfc->screen_number);
+       xfc->depth = DefaultDepthOfScreen(xfc->screen);
+       xfc->big_endian = (ImageByteOrder(xfc->display) == MSBFirst);
+       xfc->invert = TRUE;
+       xfc->complex_regions = TRUE;
+       xfc->_NET_SUPPORTED = XInternAtom(xfc->display, "_NET_SUPPORTED", True);
+       xfc->_NET_SUPPORTING_WM_CHECK = XInternAtom(xfc->display, "_NET_SUPPORTING_WM_CHECK", True);
+
+       if ((xfc->_NET_SUPPORTED != None) && (xfc->_NET_SUPPORTING_WM_CHECK != None))
+       {
+               Atom actual_type;
+               int actual_format;
+               unsigned long nitems, after;
+               unsigned char* data = NULL;
+               int status = XGetWindowProperty(xfc->display, RootWindowOfScreen(xfc->screen),
+                                               xfc->_NET_SUPPORTED, 0, 1024, False, XA_ATOM,
+                                               &actual_type, &actual_format, &nitems, &after, &data);
+
+               if ((status == Success) && (actual_type == XA_ATOM) && (actual_format == 32))
+               {
+                       xfc->supportedAtomCount = nitems;
+                       xfc->supportedAtoms = calloc(nitems, sizeof(Atom));
+                       memcpy(xfc->supportedAtoms, data, nitems * sizeof(Atom));
+               }
+
+               if (data)
+                       XFree(data);
+       }
        xfc->_NET_WM_ICON = XInternAtom(xfc->display, "_NET_WM_ICON", False);
        xfc->_MOTIF_WM_HINTS = XInternAtom(xfc->display, "_MOTIF_WM_HINTS", False);
        xfc->_NET_CURRENT_DESKTOP = XInternAtom(xfc->display, "_NET_CURRENT_DESKTOP",
                                                False);
        xfc->_NET_WORKAREA = XInternAtom(xfc->display, "_NET_WORKAREA", False);
-       xfc->_NET_WM_STATE = XInternAtom(xfc->display, "_NET_WM_STATE", False);
-       xfc->_NET_WM_STATE_FULLSCREEN = XInternAtom(xfc->display,
-                                       "_NET_WM_STATE_FULLSCREEN", False);
+       xfc->_NET_WM_STATE = get_supported_atom(xfc, "_NET_WM_STATE");
+       xfc->_NET_WM_STATE_FULLSCREEN = get_supported_atom(xfc, "_NET_WM_STATE_FULLSCREEN");
        xfc->_NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(xfc->display,
                                            "_NET_WM_STATE_MAXIMIZED_HORZ", False);
        xfc->_NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(xfc->display,
                                            "_NET_WM_STATE_MAXIMIZED_VERT", False);
-       xfc->_NET_WM_FULLSCREEN_MONITORS = XInternAtom(xfc->display,
-                                          "_NET_WM_FULLSCREEN_MONITORS", False);
+       xfc->_NET_WM_FULLSCREEN_MONITORS = get_supported_atom(xfc, "_NET_WM_FULLSCREEN_MONITORS");
        xfc->_NET_WM_NAME = XInternAtom(xfc->display, "_NET_WM_NAME", False);
        xfc->_NET_WM_PID = XInternAtom(xfc->display, "_NET_WM_PID", False);
        xfc->_NET_WM_WINDOW_TYPE = XInternAtom(xfc->display, "_NET_WM_WINDOW_TYPE",
@@ -1832,13 +1874,6 @@ static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
        xfc->WM_PROTOCOLS = XInternAtom(xfc->display, "WM_PROTOCOLS", False);
        xfc->WM_DELETE_WINDOW = XInternAtom(xfc->display, "WM_DELETE_WINDOW", False);
        xfc->WM_STATE = XInternAtom(xfc->display, "WM_STATE", False);
-       xfc->xfds = ConnectionNumber(xfc->display);
-       xfc->screen_number = DefaultScreen(xfc->display);
-       xfc->screen = ScreenOfDisplay(xfc->display, xfc->screen_number);
-       xfc->depth = DefaultDepthOfScreen(xfc->screen);
-       xfc->big_endian = (ImageByteOrder(xfc->display) == MSBFirst);
-       xfc->invert = TRUE;
-       xfc->complex_regions = TRUE;
        xfc->x11event = CreateFileDescriptorEvent(NULL, FALSE, FALSE, xfc->xfds,
                        WINPR_FD_READ);
 
@@ -1915,6 +1950,8 @@ static void xfreerdp_client_free(freerdp* instance, rdpContext* context)
                free(xfc->vscreen.monitors);
                xfc->vscreen.monitors = NULL;
        }
+
+       free(xfc->supportedAtoms);
 }
 
 int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
index c715ad2..a84f0c0 100644 (file)
@@ -174,7 +174,7 @@ void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
        }
 
        /* Determine the x,y starting location for the fullscreen window */
-       if (fullscreen && xfc->context.settings->MonitorCount)
+       if (fullscreen)
        {
                /* Initialize startX and startY with reasonable values */
                startX = xfc->context.settings->MonitorDefArray[0].x;
@@ -190,9 +190,12 @@ void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
                /* Lastly apply any monitor shift(translation from remote to local coordinate system)
                 *  to startX and startY values
                 */
-               startX = startX + xfc->context.settings->MonitorLocalShiftX;
-               startY = startY + xfc->context.settings->MonitorLocalShiftY;
+               startX += xfc->context.settings->MonitorLocalShiftX;
+               startY += xfc->context.settings->MonitorLocalShiftY;
+       }
 
+       if (xfc->_NET_WM_FULLSCREEN_MONITORS != None)
+       {
                /* Set monitor bounds */
                if (settings->MonitorCount > 1)
                {
@@ -203,25 +206,119 @@ void xf_SetWindowFullscreen(xfContext* xfc, xfWindow* window, BOOL fullscreen)
                                           xfc->fullscreenMonitors.right,
                                           1);
                }
-       }
 
-       xf_ResizeDesktopWindow(xfc, window, width, height);
+               xf_ResizeDesktopWindow(xfc, window, width, height);
 
-       if (fullscreen)
-       {
-               /* enter full screen: move the window before adding NET_WM_STATE_FULLSCREEN */
-               XMoveWindow(xfc->display, window->handle, startX, startY);
-       }
+               if (fullscreen)
+               {
+                       /* enter full screen: move the window before adding NET_WM_STATE_FULLSCREEN */
+                       XMoveWindow(xfc->display, window->handle, startX, startY);
+               }
 
-       /* Set the fullscreen state */
-       xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
-                          fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
-                          xfc->_NET_WM_STATE_FULLSCREEN, 0, 0);
+               /* Set the fullscreen state */
+               xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
+                                  fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
+                                  xfc->_NET_WM_STATE_FULLSCREEN, 0, 0);
 
-       if (!fullscreen)
+               if (!fullscreen)
+               {
+                       /* leave full screen: move the window after removing NET_WM_STATE_FULLSCREEN */
+                       XMoveWindow(xfc->display, window->handle, startX, startY);
+               }
+       }
+       else
        {
-               /* leave full screen: move the window after removing NET_WM_STATE_FULLSCREEN */
-               XMoveWindow(xfc->display, window->handle, startX, startY);
+               if (fullscreen)
+               {
+                       xf_SetWindowDecorations(xfc, window->handle, FALSE);
+
+                       if (xfc->fullscreenMonitors.top)
+                       {
+                               xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
+                                                  _NET_WM_STATE_ADD,
+                                                  xfc->fullscreenMonitors.top, 0, 0);
+                       }
+                       else
+                       {
+                               XSetWindowAttributes xswa;
+                               xswa.override_redirect = True;
+                               XChangeWindowAttributes(xfc->display, window->handle, CWOverrideRedirect, &xswa);
+                               XRaiseWindow(xfc->display, window->handle);
+                               xswa.override_redirect = False;
+                               XChangeWindowAttributes(xfc->display, window->handle, CWOverrideRedirect, &xswa);
+                       }
+
+                       /* if window is in maximized state, save and remove */
+                       if (xfc->_NET_WM_STATE_MAXIMIZED_VERT != None)
+                       {
+                               BYTE state;
+                               unsigned long nitems;
+                               unsigned long bytes;
+                               BYTE* prop;
+
+                               if (xf_GetWindowProperty(xfc, window->handle, xfc->_NET_WM_STATE, 255, &nitems, &bytes, &prop))
+                               {
+                                       state = 0;
+
+                                       while (nitems-- > 0)
+                                       {
+                                               if (((Atom*) prop)[nitems] == xfc->_NET_WM_STATE_MAXIMIZED_VERT)
+                                                       state |= 0x01;
+
+                                               if (((Atom*) prop)[nitems] == xfc->_NET_WM_STATE_MAXIMIZED_HORZ)
+                                                       state |= 0x02;
+                                       }
+
+                                       if (state)
+                                       {
+                                               xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
+                                                                  _NET_WM_STATE_REMOVE, xfc->_NET_WM_STATE_MAXIMIZED_VERT,
+                                                                  0, 0);
+                                               xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
+                                                                  _NET_WM_STATE_REMOVE, xfc->_NET_WM_STATE_MAXIMIZED_HORZ,
+                                                                  0, 0);
+                                               xfc->savedMaximizedState = state;
+                                       }
+
+                                       XFree(prop);
+                               }
+                       }
+
+                       width = xfc->vscreen.area.right - xfc->vscreen.area.left + 1;
+                       height = xfc->vscreen.area.bottom - xfc->vscreen.area.top + 1;
+                       xf_ResizeDesktopWindow(xfc, window, width, height);
+                       XMoveWindow(xfc->display, window->handle, startX, startY);
+               }
+               else
+               {
+                       xf_SetWindowDecorations(xfc, window->handle, window->decorations);
+                       xf_ResizeDesktopWindow(xfc, window, width, height);
+                       XMoveWindow(xfc->display, window->handle, startX, startY);
+
+                       if (xfc->fullscreenMonitors.top)
+                       {
+                               xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
+                                                  _NET_WM_STATE_REMOVE,
+                                                  xfc->fullscreenMonitors.top, 0, 0);
+                       }
+
+                       /* restore maximized state, if the window was maximized before setting fullscreen */
+                       if (xfc->savedMaximizedState & 0x01)
+                       {
+                               xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
+                                                  _NET_WM_STATE_ADD, xfc->_NET_WM_STATE_MAXIMIZED_VERT,
+                                                  0, 0);
+                       }
+
+                       if (xfc->savedMaximizedState & 0x02)
+                       {
+                               xf_SendClientEvent(xfc, window->handle, xfc->_NET_WM_STATE, 4,
+                                                  _NET_WM_STATE_ADD, xfc->_NET_WM_STATE_MAXIMIZED_HORZ,
+                                                  0, 0);
+                       }
+
+                       xfc->savedMaximizedState = 0;
+               }
        }
 }
 
@@ -239,7 +336,7 @@ BOOL xf_GetWindowProperty(xfContext* xfc, Window window, Atom property,
                return FALSE;
 
        status = XGetWindowProperty(xfc->display, window,
-                                   property, 0, length, FALSE, AnyPropertyType,
+                                   property, 0, length, False, AnyPropertyType,
                                    &actual_type, &actual_format, nitems, bytes, prop);
 
        if (status != Success)
@@ -480,7 +577,7 @@ void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width,
                             int height)
 {
        XSizeHints* size_hints;
-       rdpSettings *settings = xfc->context.settings;
+       rdpSettingssettings = xfc->context.settings;
 
        if (!xfc || !window)
                return;
@@ -494,8 +591,8 @@ void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width,
        size_hints->max_width = size_hints->max_height = 16384;
        XSetWMNormalHints(xfc->display, window->handle, size_hints);
        XResizeWindow(xfc->display, window->handle, width, height);
-
 #ifdef WITH_XRENDER
+
        if (!settings->SmartSizing)
 #endif
        {
@@ -595,7 +692,7 @@ void xf_SetWindowText(xfContext* xfc, xfAppWindow* appWindow, const char* name)
 }
 
 static void xf_FixWindowCoordinates(xfContext* xfc, int* x, int* y, int* width,
-                             int* height)
+                                    int* height)
 {
        int vscreen_width;
        int vscreen_height;
index d049f0b..8ecfdb7 100644 (file)
@@ -173,6 +173,9 @@ struct xf_context
        VIRTUAL_SCREEN vscreen;
        void* xv_context;
 
+       Atom* supportedAtoms;
+       unsigned long supportedAtomCount;
+
        Atom UTF8_STRING;
 
        Atom _NET_WM_ICON;
@@ -180,6 +183,9 @@ struct xf_context
        Atom _NET_CURRENT_DESKTOP;
        Atom _NET_WORKAREA;
 
+       Atom _NET_SUPPORTED;
+       ATOM _NET_SUPPORTING_WM_CHECK;
+
        Atom _NET_WM_STATE;
        Atom _NET_WM_STATE_FULLSCREEN;
        Atom _NET_WM_STATE_MAXIMIZED_HORZ;
@@ -213,8 +219,8 @@ struct xf_context
        RdpeiClientContext* rdpei;
        RdpgfxClientContext* gfx;
        EncomspClientContext* encomsp;
-       xfDispContext *xfDisp;
-       DispClientContext *disp;
+       xfDispContextxfDisp;
+       DispClientContextdisp;
 
        RailClientContext* rail;
        wHashTable* railWindows;
@@ -224,6 +230,7 @@ struct xf_context
 
        /* value to be sent over wire for each logical client mouse button */
        int button_map[NUM_BUTTONS_MAPPED];
+       BYTE savedMaximizedState;
 };
 
 BOOL xf_create_window(xfContext* xfc);