From 1628045f6702bb922b73e53b8de72dde4c5cbedc Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Wed, 20 Dec 2017 12:02:23 +0100 Subject: [PATCH] Fullscreen without _NET_WM_FULLSCREEN_MONITORS 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 | 61 ++++++++++++++---- client/X11/xf_window.c | 141 ++++++++++++++++++++++++++++++++++------- client/X11/xfreerdp.h | 11 +++- 3 files changed, 177 insertions(+), 36 deletions(-) diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index 819693303..d74f3036f 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -30,6 +30,7 @@ #include #include +#include #ifdef WITH_XRENDER #include @@ -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) diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index c715ad245..a84f0c0a1 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -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); + + 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); + + if (!fullscreen) + { + /* leave full screen: move the window after removing NET_WM_STATE_FULLSCREEN */ + XMoveWindow(xfc->display, window->handle, startX, startY); + } } - - xf_ResizeDesktopWindow(xfc, window, width, height); - - if (fullscreen) + else { - /* enter full screen: move the window before adding NET_WM_STATE_FULLSCREEN */ - XMoveWindow(xfc->display, window->handle, startX, startY); - } + if (fullscreen) + { + xf_SetWindowDecorations(xfc, window->handle, FALSE); - /* 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 (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 (!fullscreen) - { - /* leave full screen: move the window after removing NET_WM_STATE_FULLSCREEN */ - XMoveWindow(xfc->display, window->handle, startX, startY); + /* 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; + rdpSettings* settings = 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; diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h index d049f0bee..8ecfdb7db 100644 --- a/client/X11/xfreerdp.h +++ b/client/X11/xfreerdp.h @@ -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; + xfDispContext* xfDisp; + DispClientContext* disp; 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);