FreeRDP
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 
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(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 
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_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)
316 BOOL 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 
339 BOOL sdlDispContext::handle_window_event(const SDL_WindowEvent* ev)
340 {
341  WINPR_ASSERT(ev);
342 #if defined(WITH_DEBUG_SDL_EVENTS)
343  SDL_Log("got windowEvent %s [0x%08" PRIx32 "]", sdl_window_event_str(ev->event).c_str(),
344  ev->event);
345 #endif
346  auto bordered = freerdp_settings_get_bool(_sdl->context()->settings, FreeRDP_Decorations)
347  ? SDL_TRUE
348  : SDL_FALSE;
349 
350  auto it = _sdl->windows.find(ev->windowID);
351  if (it != _sdl->windows.end())
352  it->second.setBordered(bordered);
353 
354  switch (ev->event)
355  {
356  case SDL_WINDOWEVENT_HIDDEN:
357  case SDL_WINDOWEVENT_MINIMIZED:
358  gdi_send_suppress_output(_sdl->context()->gdi, TRUE);
359  return TRUE;
360 
361  case SDL_WINDOWEVENT_EXPOSED:
362  case SDL_WINDOWEVENT_SHOWN:
363  case SDL_WINDOWEVENT_MAXIMIZED:
364  case SDL_WINDOWEVENT_RESTORED:
365  gdi_send_suppress_output(_sdl->context()->gdi, FALSE);
366  return TRUE;
367 
368  case SDL_WINDOWEVENT_RESIZED:
369  case SDL_WINDOWEVENT_SIZE_CHANGED:
370  _targetWidth = ev->data1;
371  _targetHeight = ev->data2;
372  return addTimer();
373 
374  case SDL_WINDOWEVENT_LEAVE:
375  WINPR_ASSERT(_sdl);
376  _sdl->input.keyboard_grab(ev->windowID, SDL_FALSE);
377  return TRUE;
378  case SDL_WINDOWEVENT_ENTER:
379  WINPR_ASSERT(_sdl);
380  _sdl->input.keyboard_grab(ev->windowID, SDL_TRUE);
381  return _sdl->input.keyboard_focus_in();
382  case SDL_WINDOWEVENT_FOCUS_GAINED:
383  case SDL_WINDOWEVENT_TAKE_FOCUS:
384  return _sdl->input.keyboard_focus_in();
385 
386  default:
387  return TRUE;
388  }
389 }
390 
391 UINT sdlDispContext::DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
392  UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
393 {
394  /* we're called only if dynamic resolution update is activated */
395  WINPR_ASSERT(disp);
396 
397  auto sdlDisp = reinterpret_cast<sdlDispContext*>(disp->custom);
398  return sdlDisp->DisplayControlCaps(maxNumMonitors, maxMonitorAreaFactorA,
399  maxMonitorAreaFactorB);
400 }
401 
402 UINT sdlDispContext::DisplayControlCaps(UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA,
403  UINT32 maxMonitorAreaFactorB)
404 {
405  auto settings = _sdl->context()->settings;
406  WINPR_ASSERT(settings);
407 
408  WLog_DBG(TAG,
409  "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
410  " MaxMonitorAreaFactorB: %" PRIu32 "",
411  maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
412  _activated = TRUE;
413 
414  if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
415  return CHANNEL_RC_OK;
416 
417  WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
418  return set_window_resizable() ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
419 }
420 
421 BOOL sdlDispContext::init(DispClientContext* disp)
422 {
423  if (!disp)
424  return FALSE;
425 
426  auto settings = _sdl->context()->settings;
427 
428  if (!settings)
429  return FALSE;
430 
431  _disp = disp;
432  disp->custom = this;
433 
434  if (freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
435  {
436  disp->DisplayControlCaps = sdlDispContext::DisplayControlCaps;
437  }
438 
439  _sdl->update_resizeable(TRUE);
440  return TRUE;
441 }
442 
443 BOOL sdlDispContext::uninit(DispClientContext* disp)
444 {
445  if (!disp)
446  return FALSE;
447 
448  _disp = nullptr;
449  _sdl->update_resizeable(FALSE);
450  return TRUE;
451 }
452 
453 sdlDispContext::sdlDispContext(SdlContext* sdl) : _sdl(sdl)
454 {
455  SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO);
456 
457  WINPR_ASSERT(_sdl);
458  WINPR_ASSERT(_sdl->context()->settings);
459  WINPR_ASSERT(_sdl->context()->pubSub);
460 
461  auto settings = _sdl->context()->settings;
462  auto pubSub = _sdl->context()->pubSub;
463 
464  _lastSentWidth = _targetWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
465  _lastSentHeight = _targetHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
466  PubSub_SubscribeActivated(pubSub, sdlDispContext::OnActivated);
467  PubSub_SubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
468  addTimer();
469 }
470 
471 sdlDispContext::~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 }
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.