FreeRDP
Loading...
Searching...
No Matches
SDL2/sdl_disp.cpp
1
20#include <vector>
21#include <winpr/sysinfo.h>
22#include <winpr/assert.h>
23
24#include <freerdp/gdi/gdi.h>
25
26#include <SDL.h>
27
28#include "sdl_disp.hpp"
29#include "sdl_kbd.hpp"
30#include "sdl_utils.hpp"
31#include "sdl_freerdp.hpp"
32
33#include <freerdp/log.h>
34#define TAG CLIENT_TAG("sdl.disp")
35
36static constexpr UINT64 RESIZE_MIN_DELAY = 500; /* minimum delay in ms between two resizes */
37static constexpr unsigned MAX_RETRIES = 5;
38
39BOOL sdlDispContext::settings_changed()
40{
41 auto settings = _sdl->context()->settings;
42 WINPR_ASSERT(settings);
43
44 if (_lastSentWidth != _targetWidth)
45 return TRUE;
46
47 if (_lastSentHeight != _targetHeight)
48 return TRUE;
49
50 if (_lastSentDesktopOrientation !=
51 freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation))
52 return TRUE;
53
54 if (_lastSentDesktopScaleFactor !=
55 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor))
56 return TRUE;
57
58 if (_lastSentDeviceScaleFactor !=
59 freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor))
60 return TRUE;
61 /* TODO
62 if (_fullscreen != _sdl->fullscreen)
63 return TRUE;
64 */
65 return FALSE;
66}
67
68BOOL sdlDispContext::update_last_sent()
69{
70 WINPR_ASSERT(_sdl);
71
72 auto settings = _sdl->context()->settings;
73 WINPR_ASSERT(settings);
74
75 _lastSentWidth = _targetWidth;
76 _lastSentHeight = _targetHeight;
77 _lastSentDesktopOrientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
78 _lastSentDesktopScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
79 _lastSentDeviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
80 // TODO _fullscreen = _sdl->fullscreen;
81 return TRUE;
82}
83
84BOOL sdlDispContext::sendResize()
85{
87 auto settings = _sdl->context()->settings;
88
89 if (!settings)
90 return FALSE;
91
92 if (!_activated || !_disp)
93 return TRUE;
94
95 if (GetTickCount64() - _lastSentDate < RESIZE_MIN_DELAY)
96 return TRUE;
97
98 _lastSentDate = GetTickCount64();
99
100 if (!settings_changed())
101 return TRUE;
102
103 const UINT32 mcount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
104 if (_sdl->fullscreen && (mcount > 0))
105 {
106 auto monitors = static_cast<const rdpMonitor*>(
107 freerdp_settings_get_pointer(settings, FreeRDP_MonitorDefArray));
108 if (sendLayout(monitors, mcount) != CHANNEL_RC_OK)
109 return FALSE;
110 }
111 else
112 {
113 _waitingResize = TRUE;
114 layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
115 layout.Top = layout.Left = 0;
116 layout.Width = WINPR_ASSERTING_INT_CAST(uint32_t, _targetWidth);
117 layout.Height = WINPR_ASSERTING_INT_CAST(uint32_t, _targetHeight);
118 layout.Orientation = freerdp_settings_get_uint16(settings, FreeRDP_DesktopOrientation);
119 layout.DesktopScaleFactor =
120 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
121 layout.DeviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
122 /* millimetres, unknown here: 0 (not given) rather than the size in pixels */
123 layout.PhysicalWidth = 0;
124 layout.PhysicalHeight = 0;
125
126 if (IFCALLRESULT(CHANNEL_RC_OK, _disp->SendMonitorLayout, _disp, 1, &layout) !=
127 CHANNEL_RC_OK)
128 return FALSE;
129 }
130 return update_last_sent();
131}
132
133BOOL sdlDispContext::set_window_resizable()
134{
135 _sdl->update_resizeable(TRUE);
136 return TRUE;
137}
138
139static BOOL sdl_disp_check_context(void* context, SdlContext** ppsdl, sdlDispContext** ppsdlDisp,
140 rdpSettings** ppSettings)
141{
142 if (!context)
143 return FALSE;
144
145 auto sdl = get_context(context);
146 if (!sdl)
147 return FALSE;
148
149 if (!sdl->context()->settings)
150 return FALSE;
151
152 *ppsdl = sdl;
153 *ppsdlDisp = &sdl->disp;
154 *ppSettings = sdl->context()->settings;
155 return TRUE;
156}
157
158void sdlDispContext::OnActivated(void* context, const ActivatedEventArgs* e)
159{
160 SdlContext* sdl = nullptr;
161 sdlDispContext* sdlDisp = nullptr;
162 rdpSettings* settings = nullptr;
163
164 if (!sdl_disp_check_context(context, &sdl, &sdlDisp, &settings))
165 return;
166
167 sdlDisp->_waitingResize = FALSE;
168
169 if (sdlDisp->_activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
170 {
171 sdlDisp->set_window_resizable();
172
173 if (e->firstActivation)
174 return;
175
176 sdlDisp->addTimer();
177 }
178}
179
180void sdlDispContext::OnGraphicsReset(void* context, const GraphicsResetEventArgs* e)
181{
182 SdlContext* sdl = nullptr;
183 sdlDispContext* sdlDisp = nullptr;
184 rdpSettings* settings = nullptr;
185
186 WINPR_UNUSED(e);
187 if (!sdl_disp_check_context(context, &sdl, &sdlDisp, &settings))
188 return;
189
190 sdlDisp->_waitingResize = FALSE;
191
192 if (sdlDisp->_activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
193 {
194 sdlDisp->set_window_resizable();
195 sdlDisp->addTimer();
196 }
197}
198
199Uint32 sdlDispContext::OnTimer(Uint32 interval, void* param)
200{
201 auto ctx = static_cast<sdlDispContext*>(param);
202 if (!ctx)
203 return 0;
204
205 SdlContext* sdl = ctx->_sdl;
206 if (!sdl)
207 return 0;
208
209 sdlDispContext* sdlDisp = nullptr;
210 rdpSettings* settings = nullptr;
211
212 if (!sdl_disp_check_context(sdl->context(), &sdl, &sdlDisp, &settings))
213 return 0;
214
215 WLog_Print(sdl->log, WLOG_TRACE, "checking for display changes...");
216 if (!sdlDisp->_activated || freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
217 return 0;
218 else
219 {
220 auto rc = sdlDisp->sendResize();
221 if (!rc)
222 WLog_Print(sdl->log, WLOG_TRACE, "sent new display layout, result %d", rc);
223 }
224 if (sdlDisp->_timer_retries++ >= MAX_RETRIES)
225 {
226 WLog_Print(sdl->log, WLOG_TRACE, "deactivate timer, retries exceeded");
227 return 0;
228 }
229
230 WLog_Print(sdl->log, WLOG_TRACE, "fire timer one more time");
231 return interval;
232}
233
234UINT sdlDispContext::sendLayout(const rdpMonitor* monitors, size_t nmonitors)
235{
236 UINT ret = CHANNEL_RC_OK;
237
238 WINPR_ASSERT(monitors);
239 WINPR_ASSERT(nmonitors > 0);
240
241 auto settings = _sdl->context()->settings;
242 WINPR_ASSERT(settings);
243
244 std::vector<DISPLAY_CONTROL_MONITOR_LAYOUT> layouts;
245 layouts.resize(nmonitors);
246
247 for (size_t i = 0; i < nmonitors; i++)
248 {
249 auto monitor = &monitors[i];
250 auto& layout = layouts.at(i);
251
252 layout.Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
253 layout.Left = monitor->x;
254 layout.Top = monitor->y;
255 layout.Width = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
256 layout.Height = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
257 layout.Orientation = ORIENTATION_LANDSCAPE;
258 layout.PhysicalWidth = monitor->attributes.physicalWidth;
259 layout.PhysicalHeight = monitor->attributes.physicalHeight;
260
261 switch (monitor->attributes.orientation)
262 {
263 case 90:
264 layout.Orientation = ORIENTATION_PORTRAIT;
265 break;
266
267 case 180:
268 layout.Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
269 break;
270
271 case 270:
272 layout.Orientation = ORIENTATION_PORTRAIT_FLIPPED;
273 break;
274
275 case 0:
276 default:
277 /* MS-RDPEDISP - 2.2.2.2.1:
278 * Orientation (4 bytes): A 32-bit unsigned integer that specifies the
279 * orientation of the monitor in degrees. Valid values are 0, 90, 180
280 * or 270
281 *
282 * So we default to ORIENTATION_LANDSCAPE
283 */
284 layout.Orientation = ORIENTATION_LANDSCAPE;
285 break;
286 }
287
288 layout.DesktopScaleFactor =
289 freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor);
290 layout.DeviceScaleFactor = freerdp_settings_get_uint32(settings, FreeRDP_DeviceScaleFactor);
291 }
292
293 WINPR_ASSERT(_disp);
294 const size_t len = layouts.size();
295 WINPR_ASSERT(len <= UINT32_MAX);
296 ret = IFCALLRESULT(CHANNEL_RC_OK, _disp->SendMonitorLayout, _disp, static_cast<UINT32>(len),
297 layouts.data());
298 return ret;
299}
300
301BOOL sdlDispContext::addTimer()
302{
303 if (SDL_WasInit(SDL_INIT_TIMER) == 0)
304 return FALSE;
305
306 SDL_RemoveTimer(_timer);
307 WLog_Print(_sdl->log, WLOG_TRACE, "adding new display check timer");
308
309 _timer_retries = 0;
310 sendResize();
311 _timer = SDL_AddTimer(1000, sdlDispContext::OnTimer, this);
312 return TRUE;
313}
314
315#if SDL_VERSION_ATLEAST(2, 0, 10)
316BOOL sdlDispContext::handle_display_event(const SDL_DisplayEvent* ev)
317{
318 WINPR_ASSERT(ev);
319
320 switch (ev->event)
321 {
322#if SDL_VERSION_ATLEAST(2, 0, 14)
323 case SDL_DISPLAYEVENT_CONNECTED:
324 SDL_Log("A new display with id %u was connected", ev->display);
325 return TRUE;
326 case SDL_DISPLAYEVENT_DISCONNECTED:
327 SDL_Log("The display with id %u was disconnected", ev->display);
328 return TRUE;
329#endif
330 case SDL_DISPLAYEVENT_ORIENTATION:
331 SDL_Log("The orientation of display with id %u was changed", ev->display);
332 return TRUE;
333 default:
334 return TRUE;
335 }
336}
337#endif
338
339BOOL sdlDispContext::handle_window_event(const SDL_WindowEvent* ev)
340{
341 WINPR_ASSERT(ev);
342 WLog_Print(_sdl->log, WLOG_TRACE, "got windowEvent %s [0x%08" PRIx32 "]",
343 sdl_window_event_str(ev->event).c_str(), ev->event);
344 auto bordered = freerdp_settings_get_bool(_sdl->context()->settings, FreeRDP_Decorations)
345 ? SDL_TRUE
346 : SDL_FALSE;
347
348 auto it = _sdl->windows.find(ev->windowID);
349 if (it != _sdl->windows.end())
350 it->second.setBordered(bordered);
351
352 switch (ev->event)
353 {
354 case SDL_WINDOWEVENT_HIDDEN:
355 case SDL_WINDOWEVENT_MINIMIZED:
356 return gdi_send_suppress_output(_sdl->context()->gdi, TRUE);
357
358 case SDL_WINDOWEVENT_EXPOSED:
359 case SDL_WINDOWEVENT_SHOWN:
360 case SDL_WINDOWEVENT_MAXIMIZED:
361 case SDL_WINDOWEVENT_RESTORED:
362 return gdi_send_suppress_output(_sdl->context()->gdi, FALSE);
363
364 case SDL_WINDOWEVENT_RESIZED:
365 case SDL_WINDOWEVENT_SIZE_CHANGED:
366 _targetWidth = ev->data1;
367 _targetHeight = ev->data2;
368 return addTimer();
369
370 case SDL_WINDOWEVENT_LEAVE:
371 WINPR_ASSERT(_sdl);
372 _sdl->input.keyboard_grab(ev->windowID, false);
373 return TRUE;
374 case SDL_WINDOWEVENT_ENTER:
375 WINPR_ASSERT(_sdl);
376 _sdl->input.keyboard_grab(ev->windowID, true);
377 return _sdl->input.keyboard_focus_in();
378 case SDL_WINDOWEVENT_FOCUS_GAINED:
379 case SDL_WINDOWEVENT_TAKE_FOCUS:
380 return _sdl->input.keyboard_focus_in();
381
382 default:
383 return TRUE;
384 }
385}
386
387UINT sdlDispContext::DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
388 UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
389{
390 /* we're called only if dynamic resolution update is activated */
391 WINPR_ASSERT(disp);
392
393 auto sdlDisp = reinterpret_cast<sdlDispContext*>(disp->custom);
394 return sdlDisp->DisplayControlCaps(maxNumMonitors, maxMonitorAreaFactorA,
395 maxMonitorAreaFactorB);
396}
397
398UINT sdlDispContext::DisplayControlCaps(UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA,
399 UINT32 maxMonitorAreaFactorB)
400{
401 auto settings = _sdl->context()->settings;
402 WINPR_ASSERT(settings);
403
404 WLog_DBG(TAG,
405 "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
406 " MaxMonitorAreaFactorB: %" PRIu32 "",
407 maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
408 _activated = TRUE;
409
410 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
411 return CHANNEL_RC_OK;
412
413 WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
414 return set_window_resizable() ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
415}
416
417BOOL sdlDispContext::init(DispClientContext* disp)
418{
419 if (!disp)
420 return FALSE;
421
422 auto settings = _sdl->context()->settings;
423
424 if (!settings)
425 return FALSE;
426
427 _disp = disp;
428 disp->custom = this;
429
430 if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
431 {
432 disp->DisplayControlCaps = sdlDispContext::DisplayControlCaps;
433 }
434
435 _sdl->update_resizeable(TRUE);
436 return TRUE;
437}
438
439BOOL sdlDispContext::uninit(DispClientContext* disp)
440{
441 if (!disp)
442 return FALSE;
443
444 _disp = nullptr;
445 _sdl->update_resizeable(FALSE);
446 return TRUE;
447}
448
449sdlDispContext::sdlDispContext(SdlContext* sdl) : _sdl(sdl)
450{
451 SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
452
453 WINPR_ASSERT(_sdl);
454 WINPR_ASSERT(_sdl->context()->settings);
455 WINPR_ASSERT(_sdl->context()->pubSub);
456
457 auto settings = _sdl->context()->settings;
458 auto pubSub = _sdl->context()->pubSub;
459
460 _lastSentWidth = _targetWidth =
461 WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth));
462 _lastSentHeight = _targetHeight =
463 WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
464 if (PubSub_SubscribeActivated(pubSub, sdlDispContext::OnActivated) < 0)
465 throw std::exception();
466 if (PubSub_SubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset) < 0)
467 throw std::exception();
468 addTimer();
469}
470
471sdlDispContext::~sdlDispContext()
472{
473 wPubSub* pubSub = _sdl->context()->pubSub;
474 WINPR_ASSERT(pubSub);
475
476 PubSub_UnsubscribeActivated(pubSub, sdlDispContext::OnActivated);
477 PubSub_UnsubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
478 SDL_RemoveTimer(_timer);
479 SDL_Quit();
480}
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.