1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * X11 Display Control channel
4 *
5 * Copyright 2017 David Fort <contact@hardening-consulting.com>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20 #include <winpr/sysinfo.h>
21 #include <X11/Xutil.h>
22
23 #ifdef WITH_XRANDR
24 #include <X11/extensions/Xrandr.h>
25 #include <X11/extensions/randr.h>
26
27 #if (RANDR_MAJOR * 100 + RANDR_MINOR) >= 105
28 #define USABLE_XRANDR
29 #endif
30
31 #endif
32
33 #include "xf_disp.h"
34 #include "xf_monitor.h"
35
36 #define TAG CLIENT_TAG("x11disp")
37 #define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */
38
39 struct _xfDispContext
40 {
41 xfContext* xfc;
42 DispClientContext* disp;
43 BOOL haveXRandr;
44 int eventBase, errorBase;
45 int lastSentWidth, lastSentHeight;
46 UINT64 lastSentDate;
47 int targetWidth, targetHeight;
48 BOOL activated;
49 BOOL fullscreen;
50 UINT16 lastSentDesktopOrientation;
51 UINT32 lastSentDesktopScaleFactor;
52 UINT32 lastSentDeviceScaleFactor;
53 };
54
55 static UINT xf_disp_sendLayout(DispClientContext* disp, rdpMonitor* monitors, int nmonitors);
56
xf_disp_settings_changed(xfDispContext * xfDisp)57 static BOOL xf_disp_settings_changed(xfDispContext* xfDisp)
58 {
59 rdpSettings* settings = xfDisp->xfc->context.settings;
60
61 if (xfDisp->lastSentWidth != xfDisp->targetWidth)
62 return TRUE;
63
64 if (xfDisp->lastSentHeight != xfDisp->targetHeight)
65 return TRUE;
66
67 if (xfDisp->lastSentDesktopOrientation != settings->DesktopOrientation)
68 return TRUE;
69
70 if (xfDisp->lastSentDesktopScaleFactor != settings->DesktopScaleFactor)
71 return TRUE;
72
73 if (xfDisp->lastSentDeviceScaleFactor != settings->DeviceScaleFactor)
74 return TRUE;
75
76 if (xfDisp->fullscreen != xfDisp->xfc->fullscreen)
77 return TRUE;
78
79 return FALSE;
80 }
81
xf_update_last_sent(xfDispContext * xfDisp)82 static BOOL xf_update_last_sent(xfDispContext* xfDisp)
83 {
84 rdpSettings* settings = xfDisp->xfc->context.settings;
85 xfDisp->lastSentWidth = xfDisp->targetWidth;
86 xfDisp->lastSentHeight = xfDisp->targetHeight;
87 xfDisp->lastSentDesktopOrientation = settings->DesktopOrientation;
88 xfDisp->lastSentDesktopScaleFactor = settings->DesktopScaleFactor;
89 xfDisp->lastSentDeviceScaleFactor = settings->DeviceScaleFactor;
90 xfDisp->fullscreen = xfDisp->xfc->fullscreen;
91 return TRUE;
92 }
93
xf_disp_sendResize(xfDispContext * xfDisp)94 static BOOL xf_disp_sendResize(xfDispContext* xfDisp)
95 {
96 DISPLAY_CONTROL_MONITOR_LAYOUT layout;
97 xfContext* xfc;
98 rdpSettings* settings;
99
100 if (!xfDisp || !xfDisp->xfc)
101 return FALSE;
102
103 xfc = xfDisp->xfc;
104 settings = xfc->context.settings;
105
106 if (!settings)
107 return FALSE;
108
109 if (!xfDisp->activated || !xfDisp->disp)
110 return TRUE;
111
112 if (GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY)
113 return TRUE;
114
115 if (!xf_disp_settings_changed(xfDisp))
116 return TRUE;
117
118 xfDisp->lastSentDate = GetTickCount64();
119 if (xfc->fullscreen && (settings->MonitorCount > 0))
120 {
121 if (xf_disp_sendLayout(xfDisp->disp, settings->MonitorDefArray, settings->MonitorCount) !=
122 CHANNEL_RC_OK)
123 return FALSE;
124 }
125 else
126 {
127 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
128 layout.Top = layout.Left = 0;
129 layout.Width = xfDisp->targetWidth;
130 layout.Height = xfDisp->targetHeight;
131 layout.Orientation = settings->DesktopOrientation;
132 layout.DesktopScaleFactor = settings->DesktopScaleFactor;
133 layout.DeviceScaleFactor = settings->DeviceScaleFactor;
134 layout.PhysicalWidth = xfDisp->targetWidth / 75 * 25.4f;
135 layout.PhysicalHeight = xfDisp->targetHeight / 75 * 25.4f;
136
137 if (IFCALLRESULT(CHANNEL_RC_OK, xfDisp->disp->SendMonitorLayout, xfDisp->disp, 1,
138 &layout) != CHANNEL_RC_OK)
139 return FALSE;
140 }
141
142 return xf_update_last_sent(xfDisp);
143 }
144
xf_disp_queueResize(xfDispContext * xfDisp,UINT32 width,UINT32 height)145 static BOOL xf_disp_queueResize(xfDispContext* xfDisp, UINT32 width, UINT32 height)
146 {
147 if ((xfDisp->targetWidth == width) && (xfDisp->targetHeight == height))
148 return TRUE;
149 xfDisp->targetWidth = width;
150 xfDisp->targetHeight = height;
151 xfDisp->lastSentDate = GetTickCount64();
152 return xf_disp_sendResize(xfDisp);
153 }
154
xf_disp_set_window_resizable(xfDispContext * xfDisp)155 static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
156 {
157 XSizeHints* size_hints;
158
159 if (!(size_hints = XAllocSizeHints()))
160 return FALSE;
161
162 size_hints->flags = PMinSize | PMaxSize | PWinGravity;
163 size_hints->win_gravity = NorthWestGravity;
164 size_hints->min_width = size_hints->min_height = 320;
165 size_hints->max_width = size_hints->max_height = 8192;
166
167 if (xfDisp->xfc->window)
168 XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints);
169
170 XFree(size_hints);
171 return TRUE;
172 }
173
xf_disp_check_context(void * context,xfContext ** ppXfc,xfDispContext ** ppXfDisp,rdpSettings ** ppSettings)174 static BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
175 rdpSettings** ppSettings)
176 {
177 xfContext* xfc;
178
179 if (!context)
180 return FALSE;
181
182 xfc = (xfContext*)context;
183
184 if (!(xfc->xfDisp))
185 return FALSE;
186
187 if (!xfc->context.settings)
188 return FALSE;
189
190 *ppXfc = xfc;
191 *ppXfDisp = xfc->xfDisp;
192 *ppSettings = xfc->context.settings;
193 return TRUE;
194 }
195
xf_disp_OnActivated(void * context,ActivatedEventArgs * e)196 static void xf_disp_OnActivated(void* context, ActivatedEventArgs* e)
197 {
198 xfContext* xfc;
199 xfDispContext* xfDisp;
200 rdpSettings* settings;
201
202 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
203 return;
204
205 if (xfDisp->activated && !xfc->fullscreen)
206 {
207 xf_disp_set_window_resizable(xfDisp);
208
209 if (e->firstActivation)
210 return;
211
212 xf_disp_sendResize(xfDisp);
213 }
214 }
215
xf_disp_OnGraphicsReset(void * context,GraphicsResetEventArgs * e)216 static void xf_disp_OnGraphicsReset(void* context, GraphicsResetEventArgs* e)
217 {
218 xfContext* xfc;
219 xfDispContext* xfDisp;
220 rdpSettings* settings;
221
222 WINPR_UNUSED(e);
223
224 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
225 return;
226
227 if (xfDisp->activated && !settings->Fullscreen)
228 {
229 xf_disp_set_window_resizable(xfDisp);
230 xf_disp_sendResize(xfDisp);
231 }
232 }
233
xf_disp_OnTimer(void * context,TimerEventArgs * e)234 static void xf_disp_OnTimer(void* context, TimerEventArgs* e)
235 {
236 xfContext* xfc;
237 xfDispContext* xfDisp;
238 rdpSettings* settings;
239
240 WINPR_UNUSED(e);
241
242 if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
243 return;
244
245 if (!xfDisp->activated || settings->Fullscreen)
246 return;
247
248 xf_disp_sendResize(xfDisp);
249 }
250
xf_disp_new(xfContext * xfc)251 xfDispContext* xf_disp_new(xfContext* xfc)
252 {
253 xfDispContext* ret;
254
255 if (!xfc || !xfc->context.settings || !xfc->context.pubSub)
256 return NULL;
257
258 ret = calloc(1, sizeof(xfDispContext));
259
260 if (!ret)
261 return NULL;
262
263 ret->xfc = xfc;
264 #ifdef USABLE_XRANDR
265
266 if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase))
267 {
268 ret->haveXRandr = TRUE;
269 }
270
271 #endif
272 ret->lastSentWidth = ret->targetWidth = xfc->context.settings->DesktopWidth;
273 ret->lastSentHeight = ret->targetHeight = xfc->context.settings->DesktopHeight;
274 PubSub_SubscribeActivated(xfc->context.pubSub, xf_disp_OnActivated);
275 PubSub_SubscribeGraphicsReset(xfc->context.pubSub, xf_disp_OnGraphicsReset);
276 PubSub_SubscribeTimer(xfc->context.pubSub, xf_disp_OnTimer);
277 return ret;
278 }
279
xf_disp_free(xfDispContext * disp)280 void xf_disp_free(xfDispContext* disp)
281 {
282 if (!disp)
283 return;
284
285 if (disp->xfc)
286 {
287 PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated);
288 PubSub_UnsubscribeGraphicsReset(disp->xfc->context.pubSub, xf_disp_OnGraphicsReset);
289 PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer);
290 }
291
292 free(disp);
293 }
294
xf_disp_sendLayout(DispClientContext * disp,rdpMonitor * monitors,int nmonitors)295 UINT xf_disp_sendLayout(DispClientContext* disp, rdpMonitor* monitors, int nmonitors)
296 {
297 UINT ret = CHANNEL_RC_OK;
298 DISPLAY_CONTROL_MONITOR_LAYOUT* layouts;
299 int i;
300 xfDispContext* xfDisp = (xfDispContext*)disp->custom;
301 rdpSettings* settings = xfDisp->xfc->context.settings;
302 layouts = calloc(nmonitors, sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
303
304 if (!layouts)
305 return CHANNEL_RC_NO_MEMORY;
306
307 for (i = 0; i < nmonitors; i++)
308 {
309 layouts[i].Flags = (monitors[i].is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
310 layouts[i].Left = monitors[i].x;
311 layouts[i].Top = monitors[i].y;
312 layouts[i].Width = monitors[i].width;
313 layouts[i].Height = monitors[i].height;
314 layouts[i].Orientation = ORIENTATION_LANDSCAPE;
315 layouts[i].PhysicalWidth = monitors[i].attributes.physicalWidth;
316 layouts[i].PhysicalHeight = monitors[i].attributes.physicalHeight;
317
318 switch (monitors[i].attributes.orientation)
319 {
320 case 90:
321 layouts[i].Orientation = ORIENTATION_PORTRAIT;
322 break;
323
324 case 180:
325 layouts[i].Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
326 break;
327
328 case 270:
329 layouts[i].Orientation = ORIENTATION_PORTRAIT_FLIPPED;
330 break;
331
332 case 0:
333 default:
334 /* MS-RDPEDISP - 2.2.2.2.1:
335 * Orientation (4 bytes): A 32-bit unsigned integer that specifies the
336 * orientation of the monitor in degrees. Valid values are 0, 90, 180
337 * or 270
338 *
339 * So we default to ORIENTATION_LANDSCAPE
340 */
341 layouts[i].Orientation = ORIENTATION_LANDSCAPE;
342 break;
343 }
344
345 layouts[i].DesktopScaleFactor = settings->DesktopScaleFactor;
346 layouts[i].DeviceScaleFactor = settings->DeviceScaleFactor;
347 }
348
349 ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, nmonitors, layouts);
350 free(layouts);
351 return ret;
352 }
353
xf_disp_handle_xevent(xfContext * xfc,const XEvent * event)354 BOOL xf_disp_handle_xevent(xfContext* xfc, const XEvent* event)
355 {
356 xfDispContext* xfDisp;
357 rdpSettings* settings;
358 UINT32 maxWidth, maxHeight;
359
360 if (!xfc || !event)
361 return FALSE;
362
363 xfDisp = xfc->xfDisp;
364
365 if (!xfDisp)
366 return FALSE;
367
368 settings = xfc->context.settings;
369
370 if (!settings)
371 return FALSE;
372
373 if (!xfDisp->haveXRandr || !xfDisp->disp)
374 return TRUE;
375
376 #ifdef USABLE_XRANDR
377
378 if (event->type != xfDisp->eventBase + RRScreenChangeNotify)
379 return TRUE;
380
381 #endif
382 xf_detect_monitors(xfc, &maxWidth, &maxHeight);
383 return xf_disp_sendLayout(xfDisp->disp, settings->MonitorDefArray, settings->MonitorCount) ==
384 CHANNEL_RC_OK;
385 }
386
xf_disp_handle_configureNotify(xfContext * xfc,int width,int height)387 BOOL xf_disp_handle_configureNotify(xfContext* xfc, int width, int height)
388 {
389 xfDispContext* xfDisp;
390
391 if (!xfc)
392 return FALSE;
393
394 xfDisp = xfc->xfDisp;
395
396 if (!xfDisp)
397 return FALSE;
398
399 return xf_disp_queueResize(xfDisp, width, height);
400 }
401
xf_DisplayControlCaps(DispClientContext * disp,UINT32 maxNumMonitors,UINT32 maxMonitorAreaFactorA,UINT32 maxMonitorAreaFactorB)402 static UINT xf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
403 UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
404 {
405 /* we're called only if dynamic resolution update is activated */
406 xfDispContext* xfDisp = (xfDispContext*)disp->custom;
407 rdpSettings* settings = xfDisp->xfc->context.settings;
408 WLog_DBG(TAG,
409 "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
410 " MaxMonitorAreaFactorB: %" PRIu32 "",
411 maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
412 xfDisp->activated = TRUE;
413
414 if (settings->Fullscreen)
415 return CHANNEL_RC_OK;
416
417 WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
418 return xf_disp_set_window_resizable(xfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
419 }
420
xf_disp_init(xfDispContext * xfDisp,DispClientContext * disp)421 BOOL xf_disp_init(xfDispContext* xfDisp, DispClientContext* disp)
422 {
423 rdpSettings* settings;
424
425 if (!xfDisp || !xfDisp->xfc || !disp)
426 return FALSE;
427
428 settings = xfDisp->xfc->context.settings;
429
430 if (!settings)
431 return FALSE;
432
433 xfDisp->disp = disp;
434 disp->custom = (void*)xfDisp;
435
436 if (settings->DynamicResolutionUpdate)
437 {
438 disp->DisplayControlCaps = xf_DisplayControlCaps;
439 #ifdef USABLE_XRANDR
440
441 if (settings->Fullscreen)
442 {
443 /* ask X11 to notify us of screen changes */
444 XRRSelectInput(xfDisp->xfc->display, DefaultRootWindow(xfDisp->xfc->display),
445 RRScreenChangeNotifyMask);
446 }
447
448 #endif
449 }
450
451 return TRUE;
452 }
453
xf_disp_uninit(xfDispContext * xfDisp,DispClientContext * disp)454 BOOL xf_disp_uninit(xfDispContext* xfDisp, DispClientContext* disp)
455 {
456 if (!xfDisp || !disp)
457 return FALSE;
458
459 xfDisp->disp = NULL;
460 return TRUE;
461 }
462