21#include <winpr/assert.h>
22#include <winpr/sysinfo.h>
24#include <freerdp/timer.h>
29#include <X11/extensions/Xrandr.h>
30#include <X11/extensions/randr.h>
32#if (RANDR_MAJOR * 100 + RANDR_MINOR) >= 105
39#include "xf_monitor.h"
41#include <freerdp/log.h>
42#define TAG CLIENT_TAG("x11disp")
43#define RESIZE_MIN_DELAY_NS 200000UL
48 DispClientContext* disp;
53 UINT32 lastSentHeight;
60 UINT16 lastSentDesktopOrientation;
62 UINT32 lastSentDesktopScaleFactor;
63 UINT32 lastSentDeviceScaleFactor;
65 FreeRDP_TimerID timerID;
68static BOOL xf_disp_check_context(
void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
69 rdpSettings** ppSettings);
70static BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer);
71static UINT xf_disp_sendLayout(DispClientContext* disp,
const rdpMonitor* monitors,
74static BOOL xf_disp_settings_changed(xfDispContext* xfDisp)
76 rdpSettings* settings = NULL;
79 WINPR_ASSERT(xfDisp->xfc);
81 settings = xfDisp->xfc->common.context.settings;
82 WINPR_ASSERT(settings);
84 if (xfDisp->lastSentWidth != xfDisp->targetWidth)
87 if (xfDisp->lastSentHeight != xfDisp->targetHeight)
90 if (xfDisp->lastSentDesktopOrientation !=
94 if (xfDisp->lastSentDesktopScaleFactor !=
98 if (xfDisp->lastSentDeviceScaleFactor !=
102 if (xfDisp->fullscreen != xfDisp->xfc->fullscreen)
108static BOOL xf_update_last_sent(xfDispContext* xfDisp)
110 rdpSettings* settings = NULL;
112 WINPR_ASSERT(xfDisp);
113 WINPR_ASSERT(xfDisp->xfc);
115 settings = xfDisp->xfc->common.context.settings;
116 WINPR_ASSERT(settings);
118 xfDisp->lastSentWidth = xfDisp->targetWidth;
119 xfDisp->lastSentHeight = xfDisp->targetHeight;
120 xfDisp->lastSentDesktopOrientation =
122 xfDisp->lastSentDesktopScaleFactor =
124 xfDisp->lastSentDeviceScaleFactor =
126 xfDisp->fullscreen = xfDisp->xfc->fullscreen;
130static uint64_t xf_disp_OnTimer(rdpContext* context, WINPR_ATTR_UNUSED
void* userdata,
131 WINPR_ATTR_UNUSED FreeRDP_TimerID timerID,
132 WINPR_ATTR_UNUSED uint64_t timestamp,
133 WINPR_ATTR_UNUSED uint64_t interval)
136 xfContext* xfc = NULL;
137 xfDispContext* xfDisp = NULL;
138 rdpSettings* settings = NULL;
140 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
143 if (!xfDisp->activated)
146 xf_disp_sendResize(xfDisp, TRUE);
151static BOOL update_timer(xfDispContext* xfDisp, uint64_t intervalNS)
153 WINPR_ASSERT(xfDisp);
155 if (xfDisp->timerID == 0)
157 rdpContext* context = &xfDisp->xfc->common.context;
159 xfDisp->timerID = freerdp_timer_add(context, intervalNS, xf_disp_OnTimer, NULL,
true);
164BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer)
168 if (!xfDisp || !xfDisp->xfc)
172 if ((xfDisp->timerID != 0) && !fromTimer)
175 xfContext* xfc = xfDisp->xfc;
176 rdpSettings* settings = xfc->common.context.settings;
181 if (!xfDisp->activated || !xfDisp->disp)
182 return update_timer(xfDisp, RESIZE_MIN_DELAY_NS);
184 const uint64_t diff = winpr_GetTickCount64NS() - xfDisp->lastSentDate;
185 if (diff < RESIZE_MIN_DELAY_NS)
187 const uint64_t interval = RESIZE_MIN_DELAY_NS - diff;
188 return update_timer(xfDisp, interval);
191 if (!xf_disp_settings_changed(xfDisp))
194 xfDisp->lastSentDate = winpr_GetTickCount64NS();
201 if (xf_disp_sendLayout(xfDisp->disp, monitors, mcount) != CHANNEL_RC_OK)
206 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
207 layout.Top = layout.Left = 0;
208 layout.Width = xfDisp->targetWidth;
209 layout.Height = xfDisp->targetHeight;
211 layout.DesktopScaleFactor =
215 const double dw = xfDisp->targetWidth / 75.0 * 25.4;
216 const double dh = xfDisp->targetHeight / 75.0 * 25.4;
217 layout.PhysicalWidth = (UINT32)lround(dw);
218 layout.PhysicalHeight = (UINT32)lround(dh);
220 if (IFCALLRESULT(CHANNEL_RC_OK, xfDisp->disp->SendMonitorLayout, xfDisp->disp, 1,
221 &layout) != CHANNEL_RC_OK)
225 return xf_update_last_sent(xfDisp);
228static BOOL xf_disp_queueResize(xfDispContext* xfDisp, UINT32 width, UINT32 height)
230 if ((xfDisp->targetWidth == (INT64)width) && (xfDisp->targetHeight == (INT64)height))
232 xfDisp->targetWidth = width;
233 xfDisp->targetHeight = height;
234 return xf_disp_sendResize(xfDisp, FALSE);
237static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
239 XSizeHints* size_hints = NULL;
241 if (!(size_hints = XAllocSizeHints()))
244 size_hints->flags = PMinSize | PMaxSize | PWinGravity;
245 size_hints->win_gravity = NorthWestGravity;
246 size_hints->min_width = size_hints->min_height = 320;
247 size_hints->max_width = size_hints->max_height = 8192;
249 if (xfDisp->xfc->window)
250 XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints);
256BOOL xf_disp_check_context(
void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
257 rdpSettings** ppSettings)
259 xfContext* xfc = NULL;
264 xfc = (xfContext*)context;
269 if (!xfc->common.context.settings)
273 *ppXfDisp = xfc->xfDisp;
274 *ppSettings = xfc->common.context.settings;
278static void xf_disp_OnActivated(
void* context,
const ActivatedEventArgs* e)
280 xfContext* xfc = NULL;
281 xfDispContext* xfDisp = NULL;
282 rdpSettings* settings = NULL;
284 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
287 if (xfDisp->activated && !xfc->fullscreen)
289 xf_disp_set_window_resizable(xfDisp);
291 if (e->firstActivation)
294 xf_disp_sendResize(xfDisp, FALSE);
298static void xf_disp_OnGraphicsReset(
void* context,
const GraphicsResetEventArgs* e)
300 xfContext* xfc = NULL;
301 xfDispContext* xfDisp = NULL;
302 rdpSettings* settings = NULL;
306 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
311 xf_disp_set_window_resizable(xfDisp);
312 xf_disp_sendResize(xfDisp, FALSE);
316static void xf_disp_OnWindowStateChange(
void* context,
const WindowStateChangeEventArgs* e)
318 xfContext* xfc = NULL;
319 xfDispContext* xfDisp = NULL;
320 rdpSettings* settings = NULL;
324 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
327 if (!xfDisp->activated || !xfc->fullscreen)
330 xf_disp_sendResize(xfDisp, FALSE);
333xfDispContext* xf_disp_new(xfContext* xfc)
335 xfDispContext* ret = NULL;
336 const rdpSettings* settings = NULL;
337 wPubSub* pubSub = NULL;
341 pubSub = xfc->common.context.pubSub;
342 WINPR_ASSERT(pubSub);
344 settings = xfc->common.context.settings;
345 WINPR_ASSERT(settings);
347 ret = calloc(1,
sizeof(xfDispContext));
355 if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase))
357 ret->haveXRandr = TRUE;
361 ret->lastSentWidth = ret->targetWidth =
363 ret->lastSentHeight = ret->targetHeight =
365 PubSub_SubscribeActivated(pubSub, xf_disp_OnActivated);
366 PubSub_SubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
367 PubSub_SubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
371void xf_disp_free(xfDispContext* disp)
378 wPubSub* pubSub = disp->xfc->common.context.pubSub;
379 PubSub_UnsubscribeActivated(pubSub, xf_disp_OnActivated);
380 PubSub_UnsubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
381 PubSub_UnsubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
387UINT xf_disp_sendLayout(DispClientContext* disp,
const rdpMonitor* monitors, UINT32 nmonitors)
389 UINT ret = CHANNEL_RC_OK;
390 xfDispContext* xfDisp = NULL;
391 rdpSettings* settings = NULL;
395 WINPR_ASSERT(monitors);
396 WINPR_ASSERT(nmonitors > 0);
398 xfDisp = (xfDispContext*)disp->custom;
399 WINPR_ASSERT(xfDisp);
400 WINPR_ASSERT(xfDisp->xfc);
402 settings = xfDisp->xfc->common.context.settings;
403 WINPR_ASSERT(settings);
408 return CHANNEL_RC_NO_MEMORY;
410 for (UINT32 i = 0; i < nmonitors; i++)
415 layout->Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
416 layout->Left = monitor->x;
417 layout->Top = monitor->y;
418 layout->Width = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
419 layout->Height = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
420 layout->Orientation = ORIENTATION_LANDSCAPE;
421 layout->PhysicalWidth = monitor->attributes.physicalWidth;
422 layout->PhysicalHeight = monitor->attributes.physicalHeight;
424 switch (monitor->attributes.orientation)
427 layout->Orientation = ORIENTATION_PORTRAIT;
431 layout->Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
435 layout->Orientation = ORIENTATION_PORTRAIT_FLIPPED;
447 layout->Orientation = ORIENTATION_LANDSCAPE;
451 layout->DesktopScaleFactor =
453 layout->DeviceScaleFactor =
457 ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, nmonitors, layouts);
462BOOL xf_disp_handle_xevent(xfContext* xfc,
const XEvent* event)
464 xfDispContext* xfDisp = NULL;
465 rdpSettings* settings = NULL;
467 UINT32 maxHeight = 0;
472 xfDisp = xfc->xfDisp;
477 settings = xfc->common.context.settings;
482 if (!xfDisp->haveXRandr || !xfDisp->disp)
487 if (event->type != xfDisp->eventBase + RRScreenChangeNotify)
491 xf_detect_monitors(xfc, &maxWidth, &maxHeight);
494 return xf_disp_sendLayout(xfDisp->disp, monitors, mcount) == CHANNEL_RC_OK;
497BOOL xf_disp_handle_configureNotify(xfContext* xfc,
int width,
int height)
499 xfDispContext* xfDisp = NULL;
504 xfDisp = xfc->xfDisp;
509 return xf_disp_queueResize(xfDisp, WINPR_ASSERTING_INT_CAST(uint32_t, width),
510 WINPR_ASSERTING_INT_CAST(uint32_t, height));
513static UINT xf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
514 UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
517 xfDispContext* xfDisp = NULL;
518 rdpSettings* settings = NULL;
522 xfDisp = (xfDispContext*)disp->custom;
523 WINPR_ASSERT(xfDisp);
524 WINPR_ASSERT(xfDisp->xfc);
526 settings = xfDisp->xfc->common.context.settings;
527 WINPR_ASSERT(settings);
530 "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32
" MaxMonitorAreaFactorA: %" PRIu32
531 " MaxMonitorAreaFactorB: %" PRIu32
"",
532 maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
533 xfDisp->activated = TRUE;
536 return CHANNEL_RC_OK;
538 WLog_DBG(TAG,
"DisplayControlCapsPdu: setting the window as resizable");
539 return xf_disp_set_window_resizable(xfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
542BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp)
544 rdpSettings* settings = NULL;
546 if (!xfDisp || !xfDisp->xfc || !disp)
549 settings = xfDisp->xfc->common.context.settings;
555 disp->custom = (
void*)xfDisp;
559 disp->DisplayControlCaps = xf_DisplayControlCaps;
565 XRRSelectInput(xfDisp->xfc->display, DefaultRootWindow(xfDisp->xfc->display),
566 RRScreenChangeNotifyMask);
575BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp)
577 if (!xfDisp || !disp)
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.