FreeRDP
Loading...
Searching...
No Matches
xf_disp.c
1
20#include <math.h>
21#include <winpr/assert.h>
22#include <winpr/sysinfo.h>
23
24#include <freerdp/timer.h>
25
26#include <X11/Xutil.h>
27
28#ifdef WITH_XRANDR
29#include <X11/extensions/Xrandr.h>
30#include <X11/extensions/randr.h>
31
32#if (RANDR_MAJOR * 100 + RANDR_MINOR) >= 105
33#define USABLE_XRANDR
34#endif
35
36#endif
37
38#include "xfreerdp.h"
39#include "xf_disp.h"
40#include "xf_monitor.h"
41
42#include <freerdp/log.h>
43#define TAG CLIENT_TAG("x11disp")
44#define RESIZE_MIN_DELAY_NS 200000000UL /* minimum delay in ms between two resizes */
45
46struct s_xfDispContext
47{
48 xfContext* xfc;
49 DispClientContext* disp;
50 BOOL haveXRandr;
51 int eventBase;
52 int errorBase;
53 UINT32 lastSentWidth;
54 UINT32 lastSentHeight;
55 BYTE reserved[4];
56 UINT64 lastSentDate;
57 UINT32 targetWidth;
58 UINT32 targetHeight;
59 BOOL activated;
60 BOOL fullscreen;
61 UINT16 lastSentDesktopOrientation;
62 BYTE reserved2[2];
63 UINT32 lastSentDesktopScaleFactor;
64 UINT32 lastSentDeviceScaleFactor;
65 BYTE reserved3[4];
66 FreeRDP_TimerID timerID;
67};
68
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,
73 UINT32 nmonitors);
74
75static BOOL xf_disp_settings_changed(xfDispContext* xfDisp)
76{
77 rdpSettings* settings = NULL;
78
79 WINPR_ASSERT(xfDisp);
80 WINPR_ASSERT(xfDisp->xfc);
81
82 settings = xfDisp->xfc->common.context.settings;
83 WINPR_ASSERT(settings);
84
85 if (xfDisp->lastSentWidth != xfDisp->targetWidth)
86 return TRUE;
87
88 if (xfDisp->lastSentHeight != xfDisp->targetHeight)
89 return TRUE;
90
91 if (xfDisp->lastSentDesktopOrientation !=
92 freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation))
93 return TRUE;
94
95 if (xfDisp->lastSentDesktopScaleFactor !=
96 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor))
97 return TRUE;
98
99 if (xfDisp->lastSentDeviceScaleFactor !=
100 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor))
101 return TRUE;
102
103 if (xfDisp->fullscreen != xfDisp->xfc->fullscreen)
104 return TRUE;
105
106 return FALSE;
107}
108
109static BOOL xf_update_last_sent(xfDispContext* xfDisp)
110{
111 rdpSettings* settings = NULL;
112
113 WINPR_ASSERT(xfDisp);
114 WINPR_ASSERT(xfDisp->xfc);
115
116 settings = xfDisp->xfc->common.context.settings;
117 WINPR_ASSERT(settings);
118
119 xfDisp->lastSentWidth = xfDisp->targetWidth;
120 xfDisp->lastSentHeight = xfDisp->targetHeight;
121 xfDisp->lastSentDesktopOrientation =
122 freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
123 xfDisp->lastSentDesktopScaleFactor =
124 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
125 xfDisp->lastSentDeviceScaleFactor =
126 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
127 xfDisp->fullscreen = xfDisp->xfc->fullscreen;
128 return TRUE;
129}
130
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)
135
136{
137 xfContext* xfc = NULL;
138 xfDispContext* xfDisp = NULL;
139 rdpSettings* settings = NULL;
140
141 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
142 return interval;
143
144 if (!xfDisp->activated)
145 return interval;
146
147 xf_disp_sendResize(xfDisp, TRUE);
148 xfDisp->timerID = 0;
149 return 0;
150}
151
152static BOOL update_timer(xfDispContext* xfDisp, uint64_t intervalNS)
153{
154 WINPR_ASSERT(xfDisp);
155
156 if (xfDisp->timerID == 0)
157 {
158 rdpContext* context = &xfDisp->xfc->common.context;
159
160 xfDisp->timerID = freerdp_timer_add(context, intervalNS, xf_disp_OnTimer, NULL, true);
161 }
162 return TRUE;
163}
164
165BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer)
166{
167 DISPLAY_CONTROL_MONITOR_LAYOUT layout = { 0 };
168
169 if (!xfDisp || !xfDisp->xfc)
170 return FALSE;
171
172 /* If there is already a timer running skip the update and wait for the timer to expire. */
173 if ((xfDisp->timerID != 0) && !fromTimer)
174 return TRUE;
175
176 xfContext* xfc = xfDisp->xfc;
177 rdpSettings* settings = xfc->common.context.settings;
178
179 if (!settings)
180 return FALSE;
181
182 if (!xfDisp->activated || !xfDisp->disp)
183 return update_timer(xfDisp, RESIZE_MIN_DELAY_NS);
184
185 const uint64_t diff = winpr_GetTickCount64NS() - xfDisp->lastSentDate;
186 if (diff < RESIZE_MIN_DELAY_NS)
187 {
188 const uint64_t interval = RESIZE_MIN_DELAY_NS - diff;
189 return update_timer(xfDisp, interval);
190 }
191
192 if (!xf_disp_settings_changed(xfDisp))
193 return TRUE;
194
195 xfDisp->lastSentDate = winpr_GetTickCount64NS();
196
197 const UINT32 mcount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
198 if (mcount > 1)
199 {
200 const rdpMonitor* monitors =
201 freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray);
202 if (xf_disp_sendLayout(xfDisp->disp, monitors, mcount) != CHANNEL_RC_OK)
203 return FALSE;
204 }
205 else
206 {
207 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
208 layout.Top = layout.Left = 0;
209 layout.Width = xfDisp->targetWidth;
210 layout.Height = xfDisp->targetHeight;
211 layout.Orientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
212 layout.DesktopScaleFactor =
213 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
214 layout.DeviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
215
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);
220
221 if (IFCALLRESULT(CHANNEL_RC_OK, xfDisp->disp->SendMonitorLayout, xfDisp->disp, 1,
222 &layout) != CHANNEL_RC_OK)
223 return FALSE;
224 }
225
226 return xf_update_last_sent(xfDisp);
227}
228
229static BOOL xf_disp_queueResize(xfDispContext* xfDisp, UINT32 width, UINT32 height)
230{
231 if ((xfDisp->targetWidth == (INT64)width) && (xfDisp->targetHeight == (INT64)height))
232 return TRUE;
233 xfDisp->targetWidth = width;
234 xfDisp->targetHeight = height;
235 return xf_disp_sendResize(xfDisp, FALSE);
236}
237
238static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
239{
240 XSizeHints* size_hints = NULL;
241
242 if (!(size_hints = XAllocSizeHints()))
243 return FALSE;
244
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;
249
250 if (xfDisp->xfc->window)
251 XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints);
252
253 XFree(size_hints);
254 return TRUE;
255}
256
257BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
258 rdpSettings** ppSettings)
259{
260 xfContext* xfc = NULL;
261
262 if (!context)
263 return FALSE;
264
265 xfc = (xfContext*)context;
266
267 if (!(xfc->xfDisp))
268 return FALSE;
269
270 if (!xfc->common.context.settings)
271 return FALSE;
272
273 *ppXfc = xfc;
274 *ppXfDisp = xfc->xfDisp;
275 *ppSettings = xfc->common.context.settings;
276 return TRUE;
277}
278
279static void xf_disp_OnActivated(void* context, const ActivatedEventArgs* e)
280{
281 xfContext* xfc = NULL;
282 xfDispContext* xfDisp = NULL;
283 rdpSettings* settings = NULL;
284
285 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
286 return;
287
288 if (xfDisp->activated && !xfc->fullscreen)
289 {
290 xf_disp_set_window_resizable(xfDisp);
291
292 if (e->firstActivation)
293 return;
294
295 xf_disp_sendResize(xfDisp, FALSE);
296 }
297}
298
299static void xf_disp_OnGraphicsReset(void* context, const GraphicsResetEventArgs* e)
300{
301 xfContext* xfc = NULL;
302 xfDispContext* xfDisp = NULL;
303 rdpSettings* settings = NULL;
304
305 WINPR_UNUSED(e);
306
307 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
308 return;
309
310 if (xfDisp->activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
311 {
312 xf_disp_set_window_resizable(xfDisp);
313 xf_disp_sendResize(xfDisp, FALSE);
314 }
315}
316
317static void xf_disp_OnWindowStateChange(void* context, const WindowStateChangeEventArgs* e)
318{
319 xfContext* xfc = NULL;
320 xfDispContext* xfDisp = NULL;
321 rdpSettings* settings = NULL;
322
323 WINPR_UNUSED(e);
324
325 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
326 return;
327
328 if (!xfDisp->activated || !xfc->fullscreen)
329 return;
330
331 xf_disp_sendResize(xfDisp, FALSE);
332}
333
334xfDispContext* xf_disp_new(xfContext* xfc)
335{
336 xfDispContext* ret = NULL;
337 const rdpSettings* settings = NULL;
338 wPubSub* pubSub = NULL;
339
340 WINPR_ASSERT(xfc);
341
342 pubSub = xfc->common.context.pubSub;
343 WINPR_ASSERT(pubSub);
344
345 settings = xfc->common.context.settings;
346 WINPR_ASSERT(settings);
347
348 ret = calloc(1, sizeof(xfDispContext));
349
350 if (!ret)
351 return NULL;
352
353 ret->xfc = xfc;
354#ifdef USABLE_XRANDR
355
356 if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase))
357 {
358 ret->haveXRandr = TRUE;
359 }
360
361#endif
362 ret->lastSentWidth = ret->targetWidth =
363 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
364 ret->lastSentHeight = ret->targetHeight =
365 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
366 PubSub_SubscribeActivated(pubSub, xf_disp_OnActivated);
367 PubSub_SubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
368 PubSub_SubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
369 return ret;
370}
371
372void xf_disp_free(xfDispContext* disp)
373{
374 if (!disp)
375 return;
376
377 if (disp->xfc)
378 {
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);
383 }
384
385 free(disp);
386}
387
388UINT xf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors, UINT32 nmonitors)
389{
390 UINT ret = CHANNEL_RC_OK;
391 xfDispContext* xfDisp = NULL;
392 rdpSettings* settings = NULL;
393 DISPLAY_CONTROL_MONITOR_LAYOUT* layouts = NULL;
394
395 WINPR_ASSERT(disp);
396 WINPR_ASSERT(monitors);
397 WINPR_ASSERT(nmonitors > 0);
398
399 xfDisp = (xfDispContext*)disp->custom;
400 WINPR_ASSERT(xfDisp);
401 WINPR_ASSERT(xfDisp->xfc);
402
403 settings = xfDisp->xfc->common.context.settings;
404 WINPR_ASSERT(settings);
405
406 layouts = calloc(nmonitors, sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
407
408 if (!layouts)
409 return CHANNEL_RC_NO_MEMORY;
410
411 for (UINT32 i = 0; i < nmonitors; i++)
412 {
413 const rdpMonitor* monitor = &monitors[i];
414 DISPLAY_CONTROL_MONITOR_LAYOUT* layout = &layouts[i];
415
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;
424
425 switch (monitor->attributes.orientation)
426 {
427 case 90:
428 layout->Orientation = ORIENTATION_PORTRAIT;
429 break;
430
431 case 180:
432 layout->Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
433 break;
434
435 case 270:
436 layout->Orientation = ORIENTATION_PORTRAIT_FLIPPED;
437 break;
438
439 case 0:
440 default:
441 /* MS-RDPEDISP - 2.2.2.2.1:
442 * Orientation (4 bytes): A 32-bit unsigned integer that specifies the
443 * orientation of the monitor in degrees. Valid values are 0, 90, 180
444 * or 270
445 *
446 * So we default to ORIENTATION_LANDSCAPE
447 */
448 layout->Orientation = ORIENTATION_LANDSCAPE;
449 break;
450 }
451
452 layout->DesktopScaleFactor =
453 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
454 layout->DeviceScaleFactor =
455 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
456 }
457
458 ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, nmonitors, layouts);
459 free(layouts);
460 return ret;
461}
462
463BOOL xf_disp_handle_xevent(xfContext* xfc, const XEvent* event)
464{
465 xfDispContext* xfDisp = NULL;
466 rdpSettings* settings = NULL;
467 UINT32 maxWidth = 0;
468 UINT32 maxHeight = 0;
469
470 if (!xfc || !event)
471 return FALSE;
472
473 xfDisp = xfc->xfDisp;
474
475 if (!xfDisp)
476 return FALSE;
477
478 settings = xfc->common.context.settings;
479
480 if (!settings)
481 return FALSE;
482
483 if (!xfDisp->haveXRandr || !xfDisp->disp)
484 return TRUE;
485
486#ifdef USABLE_XRANDR
487
488 if (event->type != xfDisp->eventBase + RRScreenChangeNotify)
489 return TRUE;
490
491#endif
492
493 WLog_DBG(TAG, "RRScreenChangeNotify event");
494
495 xf_detect_monitors(xfc, &maxWidth, &maxHeight);
496 const rdpMonitor* monitors = freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray);
497 const UINT32 mcount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
498 return xf_disp_sendLayout(xfDisp->disp, monitors, mcount) == CHANNEL_RC_OK;
499}
500
501BOOL xf_disp_handle_configureNotify(xfContext* xfc, int width, int height)
502{
503 xfDispContext* xfDisp = NULL;
504
505 if (!xfc)
506 return FALSE;
507
508 xfDisp = xfc->xfDisp;
509
510 if (!xfDisp)
511 return FALSE;
512
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));
516}
517
518static UINT xf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
519 UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
520{
521 /* we're called only if dynamic resolution update is activated */
522 xfDispContext* xfDisp = NULL;
523 rdpSettings* settings = NULL;
524
525 WINPR_ASSERT(disp);
526
527 xfDisp = (xfDispContext*)disp->custom;
528 WINPR_ASSERT(xfDisp);
529 WINPR_ASSERT(xfDisp->xfc);
530
531 settings = xfDisp->xfc->common.context.settings;
532 WINPR_ASSERT(settings);
533
534 WLog_DBG(TAG,
535 "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
536 " MaxMonitorAreaFactorB: %" PRIu32 "",
537 maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
538 xfDisp->activated = TRUE;
539
540 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
541 return CHANNEL_RC_OK;
542
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;
545}
546
547BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp)
548{
549 rdpSettings* settings = NULL;
550
551 if (!xfDisp || !xfDisp->xfc || !disp)
552 return FALSE;
553
554 settings = xfDisp->xfc->common.context.settings;
555
556 if (!settings)
557 return FALSE;
558
559 xfDisp->disp = disp;
560 disp->custom = (void*)xfDisp;
561
562 if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
563 {
564 disp->DisplayControlCaps = xf_DisplayControlCaps;
565#ifdef USABLE_XRANDR
566
567 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
568 {
569 /* ask X11 to notify us of screen changes */
570 XRRSelectInput(xfDisp->xfc->display, DefaultRootWindow(xfDisp->xfc->display),
571 RRScreenChangeNotifyMask);
572 }
573
574#endif
575 }
576
577 WLog_DBG(TAG, "Channel %s opened", DISP_CHANNEL_NAME);
578 return TRUE;
579}
580
581BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp)
582{
583 if (!xfDisp || !disp)
584 return FALSE;
585
586 WLog_DBG(TAG, "Channel %s closed", DISP_CHANNEL_NAME);
587 xfDisp->disp = NULL;
588 return TRUE;
589}
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.