FreeRDP
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 
42 struct 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 
64 static UINT xf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors,
65  UINT32 nmonitors);
66 
67 static 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 
101 static 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 
123 static 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 (xfc->fullscreen && (mcount > 0))
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 
181 static 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 
191 static 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 
210 static 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 
232 static 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 
252 static 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 
270 static 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 || xfc->fullscreen)
282  return;
283 
284  xf_disp_sendResize(xfDisp);
285 }
286 
287 static 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 
304 xfDispContext* 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 
343 void 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 
360 UINT 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 = monitor->width;
392  layout->Height = 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 
435 BOOL 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 
470 BOOL 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, width, height);
483 }
484 
485 static UINT xf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
486  UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
487 {
488  /* we're called only if dynamic resolution update is activated */
489  xfDispContext* xfDisp = NULL;
490  rdpSettings* settings = NULL;
491 
492  WINPR_ASSERT(disp);
493 
494  xfDisp = (xfDispContext*)disp->custom;
495  WINPR_ASSERT(xfDisp);
496  WINPR_ASSERT(xfDisp->xfc);
497 
498  settings = xfDisp->xfc->common.context.settings;
499  WINPR_ASSERT(settings);
500 
501  WLog_DBG(TAG,
502  "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
503  " MaxMonitorAreaFactorB: %" PRIu32 "",
504  maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
505  xfDisp->activated = TRUE;
506 
507  if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
508  return CHANNEL_RC_OK;
509 
510  WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
511  return xf_disp_set_window_resizable(xfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
512 }
513 
514 BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp)
515 {
516  rdpSettings* settings = NULL;
517 
518  if (!xfDisp || !xfDisp->xfc || !disp)
519  return FALSE;
520 
521  settings = xfDisp->xfc->common.context.settings;
522 
523  if (!settings)
524  return FALSE;
525 
526  xfDisp->disp = disp;
527  disp->custom = (void*)xfDisp;
528 
529  if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
530  {
531  disp->DisplayControlCaps = xf_DisplayControlCaps;
532 #ifdef USABLE_XRANDR
533 
534  if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
535  {
536  /* ask X11 to notify us of screen changes */
537  XRRSelectInput(xfDisp->xfc->display, DefaultRootWindow(xfDisp->xfc->display),
538  RRScreenChangeNotifyMask);
539  }
540 
541 #endif
542  }
543 
544  return TRUE;
545 }
546 
547 BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp)
548 {
549  if (!xfDisp || !disp)
550  return FALSE;
551 
552  xfDisp->disp = NULL;
553  return TRUE;
554 }
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 const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.
FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.