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
40#include "xf_monitor.h"
42#include <freerdp/log.h>
43#define TAG CLIENT_TAG("x11disp")
44#define RESIZE_MIN_DELAY_NS 200000000UL
49 DispClientContext* disp;
54 UINT32 lastSentHeight;
61 UINT16 lastSentDesktopOrientation;
63 UINT32 lastSentDesktopScaleFactor;
64 UINT32 lastSentDeviceScaleFactor;
66 FreeRDP_TimerID timerID;
69static BOOL xf_disp_check_context(
void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
70 rdpSettings** ppSettings);
71static BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer);
72static UINT xf_disp_sendLayout(DispClientContext* disp,
const rdpMonitor* monitors,
75static BOOL xf_disp_settings_changed(xfDispContext* xfDisp)
77 rdpSettings* settings = NULL;
80 WINPR_ASSERT(xfDisp->xfc);
82 settings = xfDisp->xfc->common.context.settings;
83 WINPR_ASSERT(settings);
85 if (xfDisp->lastSentWidth != xfDisp->targetWidth)
88 if (xfDisp->lastSentHeight != xfDisp->targetHeight)
91 if (xfDisp->lastSentDesktopOrientation !=
95 if (xfDisp->lastSentDesktopScaleFactor !=
99 if (xfDisp->lastSentDeviceScaleFactor !=
103 if (xfDisp->fullscreen != xfDisp->xfc->fullscreen)
109static BOOL xf_update_last_sent(xfDispContext* xfDisp)
111 rdpSettings* settings = NULL;
113 WINPR_ASSERT(xfDisp);
114 WINPR_ASSERT(xfDisp->xfc);
116 settings = xfDisp->xfc->common.context.settings;
117 WINPR_ASSERT(settings);
119 xfDisp->lastSentWidth = xfDisp->targetWidth;
120 xfDisp->lastSentHeight = xfDisp->targetHeight;
121 xfDisp->lastSentDesktopOrientation =
123 xfDisp->lastSentDesktopScaleFactor =
125 xfDisp->lastSentDeviceScaleFactor =
127 xfDisp->fullscreen = xfDisp->xfc->fullscreen;
131static uint64_t xf_disp_OnTimer(rdpContext* context, WINPR_ATTR_UNUSED
void* userdata,
132 WINPR_ATTR_UNUSED FreeRDP_TimerID timerID,
133 WINPR_ATTR_UNUSED uint64_t timestamp,
134 WINPR_ATTR_UNUSED uint64_t interval)
137 xfContext* xfc = NULL;
138 xfDispContext* xfDisp = NULL;
139 rdpSettings* settings = NULL;
141 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
144 if (!xfDisp->activated)
147 xf_disp_sendResize(xfDisp, TRUE);
152static BOOL update_timer(xfDispContext* xfDisp, uint64_t intervalNS)
154 WINPR_ASSERT(xfDisp);
156 if (xfDisp->timerID == 0)
158 rdpContext* context = &xfDisp->xfc->common.context;
160 xfDisp->timerID = freerdp_timer_add(context, intervalNS, xf_disp_OnTimer, NULL,
true);
165BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer)
169 if (!xfDisp || !xfDisp->xfc)
173 if ((xfDisp->timerID != 0) && !fromTimer)
176 xfContext* xfc = xfDisp->xfc;
177 rdpSettings* settings = xfc->common.context.settings;
182 if (!xfDisp->activated || !xfDisp->disp)
183 return update_timer(xfDisp, RESIZE_MIN_DELAY_NS);
185 const uint64_t diff = winpr_GetTickCount64NS() - xfDisp->lastSentDate;
186 if (diff < RESIZE_MIN_DELAY_NS)
188 const uint64_t interval = RESIZE_MIN_DELAY_NS - diff;
189 return update_timer(xfDisp, interval);
192 if (!xf_disp_settings_changed(xfDisp))
195 xfDisp->lastSentDate = winpr_GetTickCount64NS();
202 if (xf_disp_sendLayout(xfDisp->disp, monitors, mcount) != CHANNEL_RC_OK)
207 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
208 layout.Top = layout.Left = 0;
209 layout.Width = xfDisp->targetWidth;
210 layout.Height = xfDisp->targetHeight;
212 layout.DesktopScaleFactor =
216 const double dw = xfDisp->targetWidth / 75.0 * 25.4;
217 const double dh = xfDisp->targetHeight / 75.0 * 25.4;
218 layout.PhysicalWidth = (UINT32)lround(dw);
219 layout.PhysicalHeight = (UINT32)lround(dh);
221 if (IFCALLRESULT(CHANNEL_RC_OK, xfDisp->disp->SendMonitorLayout, xfDisp->disp, 1,
222 &layout) != CHANNEL_RC_OK)
226 return xf_update_last_sent(xfDisp);
229static BOOL xf_disp_queueResize(xfDispContext* xfDisp, UINT32 width, UINT32 height)
231 if ((xfDisp->targetWidth == (INT64)width) && (xfDisp->targetHeight == (INT64)height))
233 xfDisp->targetWidth = width;
234 xfDisp->targetHeight = height;
235 return xf_disp_sendResize(xfDisp, FALSE);
238static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
240 XSizeHints* size_hints = NULL;
242 if (!(size_hints = XAllocSizeHints()))
245 size_hints->flags = PMinSize | PMaxSize | PWinGravity;
246 size_hints->win_gravity = NorthWestGravity;
247 size_hints->min_width = size_hints->min_height = 320;
248 size_hints->max_width = size_hints->max_height = 8192;
250 if (xfDisp->xfc->window)
251 XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints);
257BOOL xf_disp_check_context(
void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
258 rdpSettings** ppSettings)
260 xfContext* xfc = NULL;
265 xfc = (xfContext*)context;
270 if (!xfc->common.context.settings)
274 *ppXfDisp = xfc->xfDisp;
275 *ppSettings = xfc->common.context.settings;
279static void xf_disp_OnActivated(
void* context,
const ActivatedEventArgs* e)
281 xfContext* xfc = NULL;
282 xfDispContext* xfDisp = NULL;
283 rdpSettings* settings = NULL;
285 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
288 if (xfDisp->activated && !xfc->fullscreen)
290 xf_disp_set_window_resizable(xfDisp);
292 if (e->firstActivation)
295 xf_disp_sendResize(xfDisp, FALSE);
299static void xf_disp_OnGraphicsReset(
void* context,
const GraphicsResetEventArgs* e)
301 xfContext* xfc = NULL;
302 xfDispContext* xfDisp = NULL;
303 rdpSettings* settings = NULL;
307 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
312 xf_disp_set_window_resizable(xfDisp);
313 xf_disp_sendResize(xfDisp, FALSE);
317static void xf_disp_OnWindowStateChange(
void* context,
const WindowStateChangeEventArgs* e)
319 xfContext* xfc = NULL;
320 xfDispContext* xfDisp = NULL;
321 rdpSettings* settings = NULL;
325 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
328 if (!xfDisp->activated || !xfc->fullscreen)
331 xf_disp_sendResize(xfDisp, FALSE);
334xfDispContext* xf_disp_new(xfContext* xfc)
336 xfDispContext* ret = NULL;
337 const rdpSettings* settings = NULL;
338 wPubSub* pubSub = NULL;
342 pubSub = xfc->common.context.pubSub;
343 WINPR_ASSERT(pubSub);
345 settings = xfc->common.context.settings;
346 WINPR_ASSERT(settings);
348 ret = calloc(1,
sizeof(xfDispContext));
356 if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase))
358 ret->haveXRandr = TRUE;
362 ret->lastSentWidth = ret->targetWidth =
364 ret->lastSentHeight = ret->targetHeight =
366 PubSub_SubscribeActivated(pubSub, xf_disp_OnActivated);
367 PubSub_SubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
368 PubSub_SubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
372void xf_disp_free(xfDispContext* disp)
379 wPubSub* pubSub = disp->xfc->common.context.pubSub;
380 PubSub_UnsubscribeActivated(pubSub, xf_disp_OnActivated);
381 PubSub_UnsubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
382 PubSub_UnsubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
388UINT xf_disp_sendLayout(DispClientContext* disp,
const rdpMonitor* monitors, UINT32 nmonitors)
390 UINT ret = CHANNEL_RC_OK;
391 xfDispContext* xfDisp = NULL;
392 rdpSettings* settings = NULL;
396 WINPR_ASSERT(monitors);
397 WINPR_ASSERT(nmonitors > 0);
399 xfDisp = (xfDispContext*)disp->custom;
400 WINPR_ASSERT(xfDisp);
401 WINPR_ASSERT(xfDisp->xfc);
403 settings = xfDisp->xfc->common.context.settings;
404 WINPR_ASSERT(settings);
409 return CHANNEL_RC_NO_MEMORY;
411 for (UINT32 i = 0; i < nmonitors; i++)
416 layout->Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
417 layout->Left = monitor->x;
418 layout->Top = monitor->y;
419 layout->Width = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
420 layout->Height = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
421 layout->Orientation = ORIENTATION_LANDSCAPE;
422 layout->PhysicalWidth = monitor->attributes.physicalWidth;
423 layout->PhysicalHeight = monitor->attributes.physicalHeight;
425 switch (monitor->attributes.orientation)
428 layout->Orientation = ORIENTATION_PORTRAIT;
432 layout->Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
436 layout->Orientation = ORIENTATION_PORTRAIT_FLIPPED;
448 layout->Orientation = ORIENTATION_LANDSCAPE;
452 layout->DesktopScaleFactor =
454 layout->DeviceScaleFactor =
458 ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, nmonitors, layouts);
463BOOL xf_disp_handle_xevent(xfContext* xfc,
const XEvent* event)
465 xfDispContext* xfDisp = NULL;
466 rdpSettings* settings = NULL;
468 UINT32 maxHeight = 0;
473 xfDisp = xfc->xfDisp;
478 settings = xfc->common.context.settings;
483 if (!xfDisp->haveXRandr || !xfDisp->disp)
488 if (event->type != xfDisp->eventBase + RRScreenChangeNotify)
493 WLog_DBG(TAG,
"RRScreenChangeNotify event");
495 xf_detect_monitors(xfc, &maxWidth, &maxHeight);
498 return xf_disp_sendLayout(xfDisp->disp, monitors, mcount) == CHANNEL_RC_OK;
501BOOL xf_disp_handle_configureNotify(xfContext* xfc,
int width,
int height)
503 xfDispContext* xfDisp = NULL;
508 xfDisp = xfc->xfDisp;
513 WLog_DBG(TAG,
"ConfigureNotify (%dx%d)", width, height);
514 return xf_disp_queueResize(xfDisp, WINPR_ASSERTING_INT_CAST(uint32_t, width),
515 WINPR_ASSERTING_INT_CAST(uint32_t, height));
518static UINT xf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
519 UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
522 xfDispContext* xfDisp = NULL;
523 rdpSettings* settings = NULL;
527 xfDisp = (xfDispContext*)disp->custom;
528 WINPR_ASSERT(xfDisp);
529 WINPR_ASSERT(xfDisp->xfc);
531 settings = xfDisp->xfc->common.context.settings;
532 WINPR_ASSERT(settings);
535 "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32
" MaxMonitorAreaFactorA: %" PRIu32
536 " MaxMonitorAreaFactorB: %" PRIu32
"",
537 maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
538 xfDisp->activated = TRUE;
541 return CHANNEL_RC_OK;
543 WLog_DBG(TAG,
"DisplayControlCapsPdu: setting the window as resizable");
544 return xf_disp_set_window_resizable(xfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
547BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp)
549 rdpSettings* settings = NULL;
551 if (!xfDisp || !xfDisp->xfc || !disp)
554 settings = xfDisp->xfc->common.context.settings;
560 disp->custom = (
void*)xfDisp;
564 disp->DisplayControlCaps = xf_DisplayControlCaps;
570 XRRSelectInput(xfDisp->xfc->display, DefaultRootWindow(xfDisp->xfc->display),
571 RRScreenChangeNotifyMask);
577 WLog_DBG(TAG,
"Channel %s opened", DISP_CHANNEL_NAME);
581BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp)
583 if (!xfDisp || !disp)
586 WLog_DBG(TAG,
"Channel %s closed", DISP_CHANNEL_NAME);
WINPR_ATTR_NODISCARD FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.