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