From ce89a9096e4450b79e835f24d0d0d73d7e3206cf Mon Sep 17 00:00:00 2001 From: David Fort Date: Tue, 19 Dec 2017 15:16:14 +0100 Subject: [PATCH] disp: improve window resizing To workaround a bug with 2012r2 servers, don't send any resize during renegociation and don't resize at a too high rate (every 200ms is good enough). --- client/X11/xf_client.c | 48 ++++++++++---- client/X11/xf_disp.c | 173 ++++++++++++++++++++++++++++++++++--------------- client/X11/xf_disp.h | 2 +- client/X11/xf_event.c | 2 +- 4 files changed, 157 insertions(+), 68 deletions(-) diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index 41f3523..8196933 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -86,6 +86,7 @@ #include #include #include +#include #include #include "xf_gdi.h" @@ -281,9 +282,6 @@ static BOOL xf_desktop_resize(rdpContext* context) xfc->window->height); } - if (xfc->xfDisp) - xf_disp_resized(xfc->xfDisp); - return TRUE; } @@ -1114,9 +1112,9 @@ static BOOL xf_pre_connect(freerdp* instance) settings->OrderSupport[NEG_ELLIPSE_SC_INDEX] = FALSE; settings->OrderSupport[NEG_ELLIPSE_CB_INDEX] = FALSE; PubSub_SubscribeChannelConnected(instance->context->pubSub, - (pChannelConnectedEventHandler) xf_OnChannelConnectedEventHandler); + (pChannelConnectedEventHandler) xf_OnChannelConnectedEventHandler); PubSub_SubscribeChannelDisconnected(instance->context->pubSub, - (pChannelDisconnectedEventHandler) xf_OnChannelDisconnectedEventHandler); + (pChannelDisconnectedEventHandler) xf_OnChannelDisconnectedEventHandler); if (!freerdp_client_load_addins(channels, instance->settings)) return FALSE; @@ -1465,8 +1463,12 @@ static void* xf_client_thread(void* param) rdpContext* context; HANDLE inputEvent = NULL; HANDLE inputThread = NULL; + HANDLE timer = NULL; + LARGE_INTEGER due; rdpSettings* settings; + TimerEventArgs timerEvent; + EventArgsInit(&timerEvent, "xfreerdp"); exit_code = 0; instance = (freerdp*) param; context = instance->context; @@ -1508,10 +1510,24 @@ static void* xf_client_thread(void* param) settings = context->settings; + timer = CreateWaitableTimerA(NULL, FALSE, NULL); + if (!timer) + { + WLog_ERR(TAG, "failed to create timer"); + goto disconnect; + } + + due.QuadPart = 0; + if (!SetWaitableTimer(timer, &due, 100, NULL, NULL, FALSE)) + { + goto disconnect; + } + handles[0] = timer; + if (!settings->AsyncInput) { inputEvent = xfc->x11event; - handles[0] = inputEvent; + handles[1] = inputEvent; } else { @@ -1527,22 +1543,21 @@ static void* xf_client_thread(void* param) while (!freerdp_shall_disconnect(instance)) { /* - * win8 and server 2k12 seem to have some timing issue/race condition - * when a initial sync request is send to sync the keyboard indicators - * sending the sync event twice fixed this problem - */ + * win8 and server 2k12 seem to have some timing issue/race condition + * when a initial sync request is send to sync the keyboard indicators + * sending the sync event twice fixed this problem + */ if (freerdp_focus_required(instance)) { xf_keyboard_focus_in(xfc); xf_keyboard_focus_in(xfc); } - nCount = (settings->AsyncInput) ? 0 : 1; + nCount = (settings->AsyncInput) ? 1 : 2; if (!settings->AsyncTransport) { DWORD tmp = freerdp_get_event_handles(context, &handles[nCount], 64 - nCount); - if (tmp == 0) { WLog_ERR(TAG, "freerdp_get_event_handles failed"); @@ -1553,7 +1568,6 @@ static void* xf_client_thread(void* param) } waitStatus = WaitForMultipleObjects(nCount, handles, FALSE, 100); - if (waitStatus == WAIT_FAILED) break; @@ -1579,6 +1593,12 @@ static void* xf_client_thread(void* param) break; } } + + if (status != WAIT_TIMEOUT && WaitForSingleObject(timer, 0)) + { + timerEvent.now = GetTickCount64(); + PubSub_OnTimer(context->pubSub, context, &timerEvent); + } } if (settings->AsyncInput) @@ -1591,6 +1611,8 @@ static void* xf_client_thread(void* param) exit_code = freerdp_error_info(instance); disconnect: + if (timer) + CloseHandle(timer); freerdp_disconnect(instance); ExitThread(exit_code); return NULL; diff --git a/client/X11/xf_disp.c b/client/X11/xf_disp.c index 8347a18..7c78717 100644 --- a/client/X11/xf_disp.c +++ b/client/X11/xf_disp.c @@ -17,11 +17,17 @@ * limitations under the License. */ +#include #include #ifdef WITH_XRANDR #include #include + +#if (RANDR_MAJOR * 100 + RANDR_MINOR) > 105 +# define USABLE_XRANDR +#endif + #endif #include "xf_disp.h" @@ -29,17 +35,109 @@ #define TAG CLIENT_TAG("x11disp") +#define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */ struct _xfDispContext { xfContext *xfc; BOOL haveXRandr; int eventBase, errorBase; - int lastWidth, lastHeight; + int lastSentWidth, lastSentHeight; + UINT64 lastSentDate; + int targetWidth, targetHeight; BOOL activated; BOOL waitingResize; }; + +static BOOL xf_disp_sendResize(xfDispContext *xfDisp, int width, int height) +{ + DISPLAY_CONTROL_MONITOR_LAYOUT layout; + xfContext *xfc = xfDisp->xfc; + + xfDisp->lastSentDate = GetTickCount64(); + xfDisp->lastSentWidth = width; + xfDisp->lastSentHeight = height; + xfDisp->waitingResize = TRUE; + + layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY; + layout.Top = layout.Left = 0; + layout.Width = width; + layout.Height = height; + layout.Orientation = ORIENTATION_LANDSCAPE; + layout.DesktopScaleFactor = 100; + layout.DeviceScaleFactor = 100; + layout.PhysicalWidth = width; + layout.PhysicalHeight = height; + + return xfc->disp->SendMonitorLayout(xfc->disp, 1, &layout) == CHANNEL_RC_OK; +} + + +static BOOL xf_disp_set_window_resizable(xfDispContext *xfDisp) +{ + XSizeHints *size_hints; + + if (!(size_hints = XAllocSizeHints())) + return FALSE; + + size_hints->flags = PMinSize | PMaxSize | PWinGravity; + size_hints->win_gravity = NorthWestGravity; + size_hints->min_width = size_hints->min_height = 320; + size_hints->max_width = size_hints->max_height = 8192; + XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints); + XFree(size_hints); + return TRUE; +} + + +static void xf_disp_OnActivated(rdpContext* context, ActivatedEventArgs* e) +{ + xfContext *xfc = (xfContext *)context; + xfDispContext *xfDisp = xfc->xfDisp; + rdpSettings *settings = context->settings; + + xfDisp->waitingResize = FALSE; + + if (xfDisp->activated && !settings->Fullscreen) + { + xf_disp_set_window_resizable(xfDisp); + + if (e->firstActivation) + return; + + /* if a resize has been done recently don't do anything and let the timer + * perform the resize */ + if (GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY) + return; + + if ((xfDisp->lastSentWidth != xfDisp->targetWidth) || (xfDisp->lastSentHeight != xfDisp->targetHeight)) + { + WLog_DBG(TAG, "performing delayed resize to %dx%d", xfDisp->targetWidth, xfDisp->targetHeight); + xf_disp_sendResize(xfDisp, xfDisp->targetWidth, xfDisp->targetHeight); + } + } +} + +static void xf_disp_OnTimer(rdpContext* context, TimerEventArgs* e) +{ + xfContext *xfc = (xfContext *)context; + xfDispContext *xfDisp = xfc->xfDisp; + rdpSettings *settings = context->settings; + + if (!xfDisp->activated || settings->Fullscreen) + return; + + if (e->now - xfDisp->lastSentDate < RESIZE_MIN_DELAY) + return; + + if ((xfDisp->lastSentWidth != xfDisp->targetWidth) || (xfDisp->lastSentHeight != xfDisp->targetHeight)) + { + WLog_DBG(TAG, "timer performing delayed resize to %dx%d", xfDisp->targetWidth, xfDisp->targetHeight); + xf_disp_sendResize(xfDisp, xfDisp->targetWidth, xfDisp->targetHeight); + } +} + xfDispContext *xf_disp_new(xfContext* xfc) { xfDispContext *ret = calloc(1, sizeof(xfDispContext)); @@ -47,20 +145,24 @@ xfDispContext *xf_disp_new(xfContext* xfc) return NULL; ret->xfc = xfc; -#ifdef WITH_XRANDR +#ifdef USABLE_XRANDR if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase)) { ret->haveXRandr = TRUE; } #endif - ret->lastWidth = xfc->context.settings->DesktopWidth; - ret->lastHeight = xfc->context.settings->DesktopHeight; + ret->lastSentWidth = ret->targetWidth = xfc->context.settings->DesktopWidth; + ret->lastSentHeight = ret->targetHeight = xfc->context.settings->DesktopHeight; + PubSub_SubscribeActivated(xfc->context.pubSub, (pActivatedEventHandler)xf_disp_OnActivated); + PubSub_SubscribeTimer(xfc->context.pubSub, (pTimerEventHandler)xf_disp_OnTimer); return ret; } void xf_disp_free(xfDispContext *disp) { + PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, (pActivatedEventHandler)xf_disp_OnActivated); + PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, (pTimerEventHandler)xf_disp_OnTimer); free(disp); } @@ -86,7 +188,6 @@ static UINT xf_disp_sendLayout(DispClientContext *disp, rdpMonitor *monitors, in layouts[i].PhysicalHeight = monitors[i].height; layouts[i].DesktopScaleFactor = 100; layouts[i].DeviceScaleFactor = 100; - } ret = disp->SendMonitorLayout(disp, nmonitors, layouts); @@ -105,7 +206,7 @@ BOOL xf_disp_handle_xevent(xfContext *xfc, XEvent *event) if (!xfDisp->haveXRandr) return TRUE; -#ifdef WITH_XRANDR +#ifdef USABLE_XRANDR if (event->type != xfDisp->eventBase + RRScreenChangeNotify) return TRUE; #endif @@ -114,61 +215,27 @@ BOOL xf_disp_handle_xevent(xfContext *xfc, XEvent *event) return xf_disp_sendLayout(xfc->disp, settings->MonitorDefArray, settings->MonitorCount) == CHANNEL_RC_OK; } -BOOL xf_disp_handle_resize(xfContext *xfc, int width, int height) + +BOOL xf_disp_handle_configureNotify(xfContext *xfc, int width, int height) { - DISPLAY_CONTROL_MONITOR_LAYOUT layout; xfDispContext *xfDisp = xfc->xfDisp; - if (xfDisp->lastWidth == width && xfDisp->lastHeight == height) + if (xfDisp->lastSentWidth == width && xfDisp->lastSentHeight == height) return TRUE; - if (xfDisp->waitingResize || !xfDisp->activated) + if (xfDisp->waitingResize || !xfDisp->activated || + (GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY)) + { + WLog_DBG(TAG, "delaying resize to %dx%d", width, height); + xfDisp->targetWidth = width; + xfDisp->targetHeight = height; return TRUE; + } - xfDisp->lastWidth = width; - xfDisp->lastHeight = height; - xfDisp->waitingResize = TRUE; - - layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY; - layout.Top = layout.Left = 0; - layout.Width = width; - layout.Height = height; - layout.Orientation = ORIENTATION_LANDSCAPE; - layout.DesktopScaleFactor = 100; - layout.DeviceScaleFactor = 100; - layout.PhysicalWidth = width; - layout.PhysicalHeight = height; - - return xfc->disp->SendMonitorLayout(xfc->disp, 1, &layout) == CHANNEL_RC_OK; + WLog_DBG(TAG, "resizing on ConfigureNotify to %dx%d", width, height); + return xf_disp_sendResize(xfDisp, width, height); } -BOOL xf_disp_set_window_resizable(xfDispContext *xfDisp) -{ - XSizeHints *size_hints; - - if (!(size_hints = XAllocSizeHints())) - return FALSE; - - size_hints->flags = PMinSize | PMaxSize | PWinGravity; - size_hints->win_gravity = NorthWestGravity; - size_hints->min_width = size_hints->min_height = 320; - size_hints->max_width = size_hints->max_height = 8192; - XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints); - XFree(size_hints); - return TRUE; -} - -void xf_disp_resized(xfDispContext *xfDisp) -{ - rdpSettings *settings = xfDisp->xfc->context.settings; - - xfDisp->waitingResize = FALSE; - - if (xfDisp->activated && !settings->Fullscreen) - { - xf_disp_set_window_resizable(xfDisp); - } -} UINT xf_DisplayControlCaps(DispClientContext *disp, UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB) { @@ -197,7 +264,7 @@ BOOL xf_disp_init(xfContext* xfc, DispClientContext *disp) if (settings->DynamicResolutionUpdate) { disp->DisplayControlCaps = xf_DisplayControlCaps; -#ifdef WITH_XRANDR +#ifdef USABLE_XRANDR if (settings->Fullscreen) { /* ask X11 to notify us of screen changes */ diff --git a/client/X11/xf_disp.h b/client/X11/xf_disp.h index 515c984..987607b 100644 --- a/client/X11/xf_disp.h +++ b/client/X11/xf_disp.h @@ -32,7 +32,7 @@ FREERDP_API BOOL xf_disp_init(xfContext* xfc, DispClientContext *disp); xfDispContext *xf_disp_new(xfContext* xfc); void xf_disp_free(xfDispContext *disp); BOOL xf_disp_handle_xevent(xfContext *xfc, XEvent *event); -BOOL xf_disp_handle_resize(xfContext *xfc, int width, int height); +BOOL xf_disp_handle_configureNotify(xfContext *xfc, int width, int height); void xf_disp_resized(xfDispContext *disp); #endif /* FREERDP_CLIENT_X11_DISP_H */ diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index 5950b97..944f462 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -673,7 +673,7 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, XEvent* event, BOOL app) alignedHeight = (xfc->window->height / 2) * 2; /* ask the server to resize using the display channel */ - xf_disp_handle_resize(xfc, alignedWidth, alignedHeight); + xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight); } return TRUE; -- 2.7.4