FreeRDP
SDL3/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 <SDL3/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 
36 static constexpr UINT64 RESIZE_MIN_DELAY = 200; /* minimum delay in ms between two resizes */
37 static constexpr unsigned MAX_RETRIES = 5;
38 
39 BOOL 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 
68 BOOL 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 
84 BOOL 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 = _targetWidth;
117  layout.Height = _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 = _targetWidth;
123  layout.PhysicalHeight = _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 
132 BOOL sdlDispContext::set_window_resizable()
133 {
134  _sdl->update_resizeable(TRUE);
135  return TRUE;
136 }
137 
138 static 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 
157 void 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 
179 void 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 
198 Uint32 sdlDispContext::OnTimer(void* param, SDL_TimerID timerID, Uint32 interval)
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 
233 UINT 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[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 = monitor->width;
255  layout->Height = 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 =
290  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 
301 BOOL sdlDispContext::addTimer()
302 {
303  if (SDL_WasInit(SDL_INIT_EVENTS) == 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 BOOL sdlDispContext::handle_display_event(const SDL_DisplayEvent* ev)
316 {
317  WINPR_ASSERT(ev);
318 
319  switch (ev->type)
320  {
321  case SDL_EVENT_DISPLAY_ADDED:
322  SDL_Log("A new display with id %u was connected", ev->displayID);
323  return TRUE;
324  case SDL_EVENT_DISPLAY_REMOVED:
325  SDL_Log("The display with id %u was disconnected", ev->displayID);
326  return TRUE;
327  case SDL_EVENT_DISPLAY_ORIENTATION:
328  SDL_Log("The orientation of display with id %u was changed", ev->displayID);
329  return TRUE;
330  default:
331  return TRUE;
332  }
333 }
334 
335 BOOL sdlDispContext::handle_window_event(const SDL_WindowEvent* ev)
336 {
337  WINPR_ASSERT(ev);
338 
339  auto bordered = freerdp_settings_get_bool(_sdl->context()->settings, FreeRDP_Decorations);
340 
341  auto it = _sdl->windows.find(ev->windowID);
342  if (it != _sdl->windows.end())
343  it->second.setBordered(bordered);
344 
345  switch (ev->type)
346  {
347  case SDL_EVENT_WINDOW_HIDDEN:
348  case SDL_EVENT_WINDOW_MINIMIZED:
349  gdi_send_suppress_output(_sdl->context()->gdi, TRUE);
350 
351  return TRUE;
352 
353  case SDL_EVENT_WINDOW_EXPOSED:
354  case SDL_EVENT_WINDOW_SHOWN:
355  case SDL_EVENT_WINDOW_MAXIMIZED:
356  case SDL_EVENT_WINDOW_RESTORED:
357  gdi_send_suppress_output(_sdl->context()->gdi, FALSE);
358  return TRUE;
359 
360  case SDL_EVENT_WINDOW_RESIZED:
361  case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
362  _targetWidth = ev->data1;
363  _targetHeight = ev->data2;
364  return addTimer();
365 
366  case SDL_EVENT_WINDOW_MOUSE_LEAVE:
367  WINPR_ASSERT(_sdl);
368  _sdl->input.keyboard_grab(ev->windowID, false);
369  return TRUE;
370  case SDL_EVENT_WINDOW_MOUSE_ENTER:
371  WINPR_ASSERT(_sdl);
372  _sdl->input.keyboard_grab(ev->windowID, true);
373  return _sdl->input.keyboard_focus_in();
374  case SDL_EVENT_WINDOW_FOCUS_GAINED:
375  return _sdl->input.keyboard_focus_in();
376 
377  default:
378  return TRUE;
379  }
380 }
381 
382 UINT sdlDispContext::DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
383  UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
384 {
385  /* we're called only if dynamic resolution update is activated */
386  WINPR_ASSERT(disp);
387 
388  auto sdlDisp = reinterpret_cast<sdlDispContext*>(disp->custom);
389  return sdlDisp->DisplayControlCaps(maxNumMonitors, maxMonitorAreaFactorA,
390  maxMonitorAreaFactorB);
391 }
392 
393 UINT sdlDispContext::DisplayControlCaps(UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA,
394  UINT32 maxMonitorAreaFactorB)
395 {
396  auto settings = _sdl->context()->settings;
397  WINPR_ASSERT(settings);
398 
399  WLog_DBG(TAG,
400  "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
401  " MaxMonitorAreaFactorB: %" PRIu32 "",
402  maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
403  _activated = TRUE;
404 
405  if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
406  return CHANNEL_RC_OK;
407 
408  WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
409  return set_window_resizable() ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
410 }
411 
412 BOOL sdlDispContext::init(DispClientContext* disp)
413 {
414  if (!disp)
415  return FALSE;
416 
417  auto settings = _sdl->context()->settings;
418 
419  if (!settings)
420  return FALSE;
421 
422  _disp = disp;
423  disp->custom = this;
424 
425  if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
426  {
427  disp->DisplayControlCaps = sdlDispContext::DisplayControlCaps;
428  }
429 
430  _sdl->update_resizeable(TRUE);
431  return TRUE;
432 }
433 
434 BOOL sdlDispContext::uninit(DispClientContext* disp)
435 {
436  if (!disp)
437  return FALSE;
438 
439  _disp = nullptr;
440  _sdl->update_resizeable(FALSE);
441  return TRUE;
442 }
443 
444 sdlDispContext::sdlDispContext(SdlContext* sdl) : _sdl(sdl)
445 {
446  SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO);
447 
448  WINPR_ASSERT(_sdl);
449  WINPR_ASSERT(_sdl->context()->settings);
450  WINPR_ASSERT(_sdl->context()->pubSub);
451 
452  auto settings = _sdl->context()->settings;
453  auto pubSub = _sdl->context()->pubSub;
454 
455  _lastSentWidth = _targetWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
456  _lastSentHeight = _targetHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
457  PubSub_SubscribeActivated(pubSub, sdlDispContext::OnActivated);
458  PubSub_SubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
459  addTimer();
460 }
461 
462 sdlDispContext::~sdlDispContext()
463 {
464  wPubSub* pubSub = _sdl->context()->pubSub;
465  WINPR_ASSERT(pubSub);
466 
467  PubSub_UnsubscribeActivated(pubSub, sdlDispContext::OnActivated);
468  PubSub_UnsubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
469  SDL_RemoveTimer(_timer);
470  SDL_Quit();
471 }
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.