FreeRDP
Loading...
Searching...
No Matches
wlf_disp.c
1
21#include <winpr/sysinfo.h>
22#include <winpr/cast.h>
23
24#include <freerdp/timer.h>
25
26#include "wlf_disp.h"
27
28#define TAG CLIENT_TAG("wayland.disp")
29
30#define RESIZE_MIN_DELAY_NS 500000000UL /* minimum delay in ns between two resizes */
31
32struct s_wlfDispContext
33{
34 wlfContext* wlc;
35 DispClientContext* disp;
36 BOOL haveXRandr;
37 int eventBase, errorBase;
38 int lastSentWidth, lastSentHeight;
39 UINT64 lastSentDate;
40 int targetWidth, targetHeight;
41 BOOL activated;
42 BOOL waitingResize;
43 BOOL fullscreen;
44 UINT16 lastSentDesktopOrientation;
45 UINT32 lastSentDesktopScaleFactor;
46 UINT32 lastSentDeviceScaleFactor;
47 FreeRDP_TimerID timerID;
48};
49
50static BOOL wlf_disp_sendResize(wlfDispContext* wlfDisp, BOOL fromTimer);
51static BOOL wlf_disp_check_context(void* context, wlfContext** ppwlc, wlfDispContext** ppwlfDisp,
52 rdpSettings** ppSettings);
53static UINT wlf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors,
54 size_t nmonitors);
55
56static BOOL wlf_disp_settings_changed(wlfDispContext* wlfDisp)
57{
58 rdpSettings* settings = nullptr;
59
60 WINPR_ASSERT(wlfDisp);
61 WINPR_ASSERT(wlfDisp->wlc);
62
63 settings = wlfDisp->wlc->common.context.settings;
64 WINPR_ASSERT(settings);
65
66 if (wlfDisp->lastSentWidth != wlfDisp->targetWidth)
67 return TRUE;
68
69 if (wlfDisp->lastSentHeight != wlfDisp->targetHeight)
70 return TRUE;
71
72 if (wlfDisp->lastSentDesktopOrientation !=
73 freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation))
74 return TRUE;
75
76 if (wlfDisp->lastSentDesktopScaleFactor !=
77 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor))
78 return TRUE;
79
80 if (wlfDisp->lastSentDeviceScaleFactor !=
81 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor))
82 return TRUE;
83
84 if (wlfDisp->fullscreen != wlfDisp->wlc->fullscreen)
85 return TRUE;
86
87 return FALSE;
88}
89
90static BOOL wlf_update_last_sent(wlfDispContext* wlfDisp)
91{
92 rdpSettings* settings = nullptr;
93
94 WINPR_ASSERT(wlfDisp);
95 WINPR_ASSERT(wlfDisp->wlc);
96
97 settings = wlfDisp->wlc->common.context.settings;
98 WINPR_ASSERT(settings);
99
100 wlfDisp->lastSentDate = winpr_GetTickCount64NS();
101 wlfDisp->lastSentWidth = wlfDisp->targetWidth;
102 wlfDisp->lastSentHeight = wlfDisp->targetHeight;
103 wlfDisp->lastSentDesktopOrientation =
104 freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
105 wlfDisp->lastSentDesktopScaleFactor =
106 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
107 wlfDisp->lastSentDeviceScaleFactor =
108 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
109 wlfDisp->fullscreen = wlfDisp->wlc->fullscreen;
110 return TRUE;
111}
112
113static uint64_t wlf_disp_OnTimer(rdpContext* context, WINPR_ATTR_UNUSED void* userdata,
114 WINPR_ATTR_UNUSED FreeRDP_TimerID timerID,
115 WINPR_ATTR_UNUSED uint64_t timestamp, uint64_t interval)
116{
117 wlfContext* wlc = nullptr;
118 wlfDispContext* wlfDisp = nullptr;
119 rdpSettings* settings = nullptr;
120
121 if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
122 return interval;
123
124 if (!wlfDisp->activated || freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
125 return interval;
126
127 wlf_disp_sendResize(wlfDisp, TRUE);
128 wlfDisp->timerID = 0;
129 return 0;
130}
131
132static BOOL update_timer(wlfDispContext* wlfDisp, uint64_t intervalNS)
133{
134 WINPR_ASSERT(wlfDisp);
135
136 rdpContext* context = &wlfDisp->wlc->common.context;
137 if (wlfDisp->timerID != 0)
138 freerdp_timer_remove(context, wlfDisp->timerID);
139 wlfDisp->timerID = freerdp_timer_add(context, intervalNS, wlf_disp_OnTimer, nullptr, true);
140 return wlfDisp->timerID != 0;
141}
142
143BOOL wlf_disp_sendResize(wlfDispContext* wlfDisp, BOOL fromTimer)
144{
146 wlfContext* wlc = nullptr;
147 rdpSettings* settings = nullptr;
148
149 if (!wlfDisp || !wlfDisp->wlc)
150 return FALSE;
151
152 wlc = wlfDisp->wlc;
153 settings = wlc->common.context.settings;
154
155 if (!settings)
156 return FALSE;
157
158 if (!wlfDisp->activated || !wlfDisp->disp)
159 return update_timer(wlfDisp, RESIZE_MIN_DELAY_NS);
160
161 const uint64_t now = winpr_GetTickCount64NS();
162 const uint64_t diff = now - wlfDisp->lastSentDate;
163 if (diff < RESIZE_MIN_DELAY_NS)
164 return update_timer(wlfDisp, RESIZE_MIN_DELAY_NS);
165
166 if (!fromTimer && (wlfDisp->timerID != 0))
167 return TRUE;
168
169 if (!wlf_disp_settings_changed(wlfDisp))
170 return TRUE;
171
172 if (wlc->fullscreen && (freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount) > 0))
173 {
174 const rdpMonitor* monitors =
175 freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray);
176 const size_t nmonitors = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
177 if (wlf_disp_sendLayout(wlfDisp->disp, monitors, nmonitors) != CHANNEL_RC_OK)
178 return FALSE;
179 }
180 else
181 {
182 wlfDisp->waitingResize = TRUE;
183 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
184 layout.Top = layout.Left = 0;
185 layout.Width = WINPR_ASSERTING_INT_CAST(uint32_t, wlfDisp->targetWidth);
186 layout.Height = WINPR_ASSERTING_INT_CAST(uint32_t, wlfDisp->targetHeight);
187 layout.Orientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
188 layout.DesktopScaleFactor =
189 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
190 layout.DeviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
191 layout.PhysicalWidth = WINPR_ASSERTING_INT_CAST(uint32_t, wlfDisp->targetWidth);
192 layout.PhysicalHeight = WINPR_ASSERTING_INT_CAST(uint32_t, wlfDisp->targetHeight);
193
194 if (IFCALLRESULT(CHANNEL_RC_OK, wlfDisp->disp->SendMonitorLayout, wlfDisp->disp, 1,
195 &layout) != CHANNEL_RC_OK)
196 return FALSE;
197 }
198 return wlf_update_last_sent(wlfDisp);
199}
200
201static BOOL wlf_disp_set_window_resizable(WINPR_ATTR_UNUSED wlfDispContext* wlfDisp)
202{
203 WLog_ERR("TODO", "TODO: implement");
204 return TRUE;
205}
206
207BOOL wlf_disp_check_context(void* context, wlfContext** ppwlc, wlfDispContext** ppwlfDisp,
208 rdpSettings** ppSettings)
209{
210 wlfContext* wlc = nullptr;
211
212 if (!context)
213 return FALSE;
214
215 wlc = (wlfContext*)context;
216
217 if (!(wlc->disp))
218 return FALSE;
219
220 if (!wlc->common.context.settings)
221 return FALSE;
222
223 *ppwlc = wlc;
224 *ppwlfDisp = wlc->disp;
225 *ppSettings = wlc->common.context.settings;
226 return TRUE;
227}
228
229static void wlf_disp_OnActivated(void* context, const ActivatedEventArgs* e)
230{
231 wlfContext* wlc = nullptr;
232 wlfDispContext* wlfDisp = nullptr;
233 rdpSettings* settings = nullptr;
234
235 if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
236 return;
237
238 wlfDisp->waitingResize = FALSE;
239
240 if (wlfDisp->activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
241 {
242 wlf_disp_set_window_resizable(wlfDisp);
243
244 if (e->firstActivation)
245 return;
246
247 wlf_disp_sendResize(wlfDisp, FALSE);
248 }
249}
250
251static void wlf_disp_OnGraphicsReset(void* context, const GraphicsResetEventArgs* e)
252{
253 wlfContext* wlc = nullptr;
254 wlfDispContext* wlfDisp = nullptr;
255 rdpSettings* settings = nullptr;
256
257 WINPR_UNUSED(e);
258 if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
259 return;
260
261 wlfDisp->waitingResize = FALSE;
262
263 if (wlfDisp->activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
264 {
265 wlf_disp_set_window_resizable(wlfDisp);
266 wlf_disp_sendResize(wlfDisp, FALSE);
267 }
268}
269
270wlfDispContext* wlf_disp_new(wlfContext* wlc)
271{
272 if (!wlc || !wlc->common.context.settings || !wlc->common.context.pubSub)
273 return nullptr;
274
275 rdpSettings* settings = wlc->common.context.settings;
276 wPubSub* pubSub = wlc->common.context.pubSub;
277
278 if (PubSub_SubscribeActivated(pubSub, wlf_disp_OnActivated) < 0)
279 return nullptr;
280 if (PubSub_SubscribeGraphicsReset(pubSub, wlf_disp_OnGraphicsReset) < 0)
281 return nullptr;
282
283 wlfDispContext* ret = calloc(1, sizeof(wlfDispContext));
284
285 if (!ret)
286 return nullptr;
287
288 ret->wlc = wlc;
289 ret->lastSentWidth = ret->targetWidth =
290 WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
291 ret->lastSentHeight = ret->targetHeight =
292 WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
293 return ret;
294}
295
296void wlf_disp_free(wlfDispContext* disp)
297{
298 if (!disp)
299 return;
300
301 if (disp->wlc)
302 {
303 wPubSub* pubSub = disp->wlc->common.context.pubSub;
304 PubSub_UnsubscribeActivated(pubSub, wlf_disp_OnActivated);
305 PubSub_UnsubscribeGraphicsReset(pubSub, wlf_disp_OnGraphicsReset);
306 }
307
308 free(disp);
309}
310
311UINT wlf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors, size_t nmonitors)
312{
313 UINT ret = CHANNEL_RC_OK;
314 DISPLAY_CONTROL_MONITOR_LAYOUT* layouts = nullptr;
315 wlfDispContext* wlfDisp = nullptr;
316 rdpSettings* settings = nullptr;
317
318 WINPR_ASSERT(disp);
319 WINPR_ASSERT(monitors);
320 WINPR_ASSERT(nmonitors > 0);
321 WINPR_ASSERT(nmonitors <= UINT32_MAX);
322
323 wlfDisp = (wlfDispContext*)disp->custom;
324 WINPR_ASSERT(wlfDisp);
325 WINPR_ASSERT(wlfDisp->wlc);
326
327 settings = wlfDisp->wlc->common.context.settings;
328 WINPR_ASSERT(settings);
329
330 layouts = calloc(nmonitors, sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
331
332 if (!layouts)
333 return CHANNEL_RC_NO_MEMORY;
334
335 for (size_t i = 0; i < nmonitors; i++)
336 {
337 const rdpMonitor* monitor = &monitors[i];
338 DISPLAY_CONTROL_MONITOR_LAYOUT* layout = &layouts[i];
339
340 layout->Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
341 layout->Left = monitor->x;
342 layout->Top = monitor->y;
343 layout->Width = WINPR_ASSERTING_INT_CAST(UINT32, monitor->width);
344 layout->Height = WINPR_ASSERTING_INT_CAST(UINT32, monitor->height);
345 layout->Orientation = ORIENTATION_LANDSCAPE;
346 layout->PhysicalWidth = monitor->attributes.physicalWidth;
347 layout->PhysicalHeight = monitor->attributes.physicalHeight;
348
349 switch (monitor->attributes.orientation)
350 {
351 case 90:
352 layout->Orientation = ORIENTATION_PORTRAIT;
353 break;
354
355 case 180:
356 layout->Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
357 break;
358
359 case 270:
360 layout->Orientation = ORIENTATION_PORTRAIT_FLIPPED;
361 break;
362
363 case 0:
364 default:
365 /* MS-RDPEDISP - 2.2.2.2.1:
366 * Orientation (4 bytes): A 32-bit unsigned integer that specifies the
367 * orientation of the monitor in degrees. Valid values are 0, 90, 180
368 * or 270
369 *
370 * So we default to ORIENTATION_LANDSCAPE
371 */
372 layout->Orientation = ORIENTATION_LANDSCAPE;
373 break;
374 }
375
376 layout->DesktopScaleFactor =
377 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
378 layout->DeviceScaleFactor =
379 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
380 }
381
382 ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, (UINT32)nmonitors, layouts);
383 free(layouts);
384 return ret;
385}
386
387BOOL wlf_disp_handle_configure(wlfDispContext* disp, int32_t width, int32_t height)
388{
389 if (!disp)
390 return FALSE;
391
392 disp->targetWidth = width;
393 disp->targetHeight = height;
394 return wlf_disp_sendResize(disp, FALSE);
395}
396
397static UINT wlf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
398 UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
399{
400 /* we're called only if dynamic resolution update is activated */
401 wlfDispContext* wlfDisp = nullptr;
402 rdpSettings* settings = nullptr;
403
404 WINPR_ASSERT(disp);
405
406 wlfDisp = (wlfDispContext*)disp->custom;
407 WINPR_ASSERT(wlfDisp);
408 WINPR_ASSERT(wlfDisp->wlc);
409
410 settings = wlfDisp->wlc->common.context.settings;
411 WINPR_ASSERT(settings);
412
413 WLog_DBG(TAG,
414 "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
415 " MaxMonitorAreaFactorB: %" PRIu32 "",
416 maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
417 wlfDisp->activated = TRUE;
418
419 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
420 return CHANNEL_RC_OK;
421
422 WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
423 return wlf_disp_set_window_resizable(wlfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
424}
425
426BOOL wlf_disp_init(wlfDispContext* wlfDisp, DispClientContext* disp)
427{
428 rdpSettings* settings = nullptr;
429
430 if (!wlfDisp || !wlfDisp->wlc || !disp)
431 return FALSE;
432
433 settings = wlfDisp->wlc->common.context.settings;
434
435 if (!settings)
436 return FALSE;
437
438 wlfDisp->disp = disp;
439 disp->custom = (void*)wlfDisp;
440
441 if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
442 {
443 disp->DisplayControlCaps = wlf_DisplayControlCaps;
444 }
445
446 return TRUE;
447}
448
449BOOL wlf_disp_uninit(wlfDispContext* wlfDisp, DispClientContext* disp)
450{
451 if (!wlfDisp || !disp)
452 return FALSE;
453
454 wlfDisp->disp = nullptr;
455 return TRUE;
456}
457
458int wlf_list_monitors(wlfContext* wlc)
459{
460 uint32_t nmonitors = UwacDisplayGetNbOutputs(wlc->display);
461
462 for (uint32_t i = 0; i < nmonitors; i++)
463 {
464 const UwacOutput* monitor =
465 UwacDisplayGetOutput(wlc->display, WINPR_ASSERTING_INT_CAST(int, i));
466 UwacSize resolution;
467 UwacPosition pos;
468
469 if (!monitor)
470 continue;
471 UwacOutputGetPosition(monitor, &pos);
472 UwacOutputGetResolution(monitor, &resolution);
473
474 printf(" %s [%u] %dx%d\t+%d+%d\n", (i == 0) ? "*" : " ", i, resolution.width,
475 resolution.height, pos.x, pos.y);
476 }
477
478 return 0;
479}
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.