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