21#include <winpr/sysinfo.h>
22#include <winpr/assert.h>
24#include <freerdp/gdi/gdi.h>
28#include "sdl_disp.hpp"
29#include "sdl_input.hpp"
30#include "sdl_context.hpp"
32#include <freerdp/log.h>
33#define TAG CLIENT_TAG("sdl.disp")
35static constexpr UINT64 RESIZE_MIN_DELAY = 500;
36static constexpr unsigned MAX_RETRIES = 5;
41 if (a.Flags != b.Flags)
47 if (a.Width != b.Width)
49 if (a.Height != b.Height)
51 if (a.PhysicalWidth != b.PhysicalWidth)
53 if (a.PhysicalHeight != b.PhysicalHeight)
55 if (a.Orientation != b.Orientation)
57 if (a.DesktopScaleFactor != b.DesktopScaleFactor)
59 if (a.DeviceScaleFactor != b.DeviceScaleFactor)
64bool sdlDispContext::settings_changed(
const std::vector<DISPLAY_CONTROL_MONITOR_LAYOUT>& layout)
66 return (layout != _last_sent_layout);
69bool sdlDispContext::sendResize()
71 auto settings = _sdl->context()->settings;
76 if (!_activated || !_disp)
79 if (GetTickCount64() - _lastSentDate < RESIZE_MIN_DELAY)
82 _lastSentDate = GetTickCount64();
85 auto monitors =
static_cast<const rdpMonitor*
>(
87 return sendLayout(monitors, mcount);
90bool sdlDispContext::setWindowResizeable()
92 return _sdl->setResizeable(
true);
96 rdpSettings** ppSettings)
101 auto sdl = get_context(context);
105 if (!
sdl->context()->settings)
109 *ppsdlDisp = &
sdl->getDisplayChannelContext();
110 *ppSettings =
sdl->context()->settings;
114void sdlDispContext::OnActivated(
void* context,
const ActivatedEventArgs* e)
118 rdpSettings* settings =
nullptr;
120 if (!sdl_disp_check_context(context, &
sdl, &sdlDisp, &settings))
123 sdlDisp->_waitingResize =
false;
125 if (!sdlDisp->_activated)
134 if (!sdlDisp->setWindowResizeable())
140#if defined(__EMSCRIPTEN__)
141 if (!
sdl->updateWindowList())
145 const bool split =
false;
147 if (e->firstActivation && !split)
151 std::ignore = sdlDisp->addTimer();
154void sdlDispContext::OnGraphicsReset(
void* context,
const GraphicsResetEventArgs* e)
158 rdpSettings* settings =
nullptr;
161 if (!sdl_disp_check_context(context, &
sdl, &sdlDisp, &settings))
164 sdlDisp->_waitingResize =
false;
166 if (!sdlDisp->_activated)
170 std::ignore = sdlDisp->addTimer();
171 else if (sdlDisp->setWindowResizeable())
172 std::ignore = sdlDisp->addTimer();
175Uint32 sdlDispContext::OnTimer(
void* param, [[maybe_unused]] SDL_TimerID timerID, Uint32 interval)
186 rdpSettings* settings =
nullptr;
188 if (!sdl_disp_check_context(
sdl->context(), &
sdl, &sdlDisp, &settings))
191 WLog_Print(
sdl->getWLog(), WLOG_TRACE,
"checking for display changes...");
193 if (!sdlDisp->sendResize())
194 WLog_Print(
sdl->getWLog(), WLOG_TRACE,
"failed to send the display layout");
196 if (sdlDisp->_timer_retries++ >= MAX_RETRIES)
198 WLog_Print(
sdl->getWLog(), WLOG_TRACE,
"deactivate timer, retries exceeded");
202 WLog_Print(
sdl->getWLog(), WLOG_TRACE,
"fire timer one more time");
206bool sdlDispContext::sendLayout(
const rdpMonitor* monitors,
size_t nmonitors)
208 WINPR_ASSERT(monitors);
209 WINPR_ASSERT(nmonitors > 0);
211 auto settings = _sdl->context()->settings;
212 WINPR_ASSERT(settings);
214 std::vector<DISPLAY_CONTROL_MONITOR_LAYOUT> layouts;
215 layouts.reserve(nmonitors);
217 for (
size_t i = 0; i < nmonitors; i++)
219 auto monitor = &monitors[i];
222 layout.Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
223 layout.Left = monitor->x;
224 layout.Top = monitor->y;
225 layout.Width = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
226 layout.Height = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
227 layout.Orientation = ORIENTATION_LANDSCAPE;
228 layout.PhysicalWidth = monitor->attributes.physicalWidth;
229 layout.PhysicalHeight = monitor->attributes.physicalHeight;
231 switch (monitor->attributes.orientation)
233 case ORIENTATION_PORTRAIT:
234 layout.Orientation = ORIENTATION_PORTRAIT;
237 case ORIENTATION_LANDSCAPE_FLIPPED:
238 layout.Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
241 case ORIENTATION_PORTRAIT_FLIPPED:
242 layout.Orientation = ORIENTATION_PORTRAIT_FLIPPED;
245 case ORIENTATION_LANDSCAPE:
254 layout.Orientation = ORIENTATION_LANDSCAPE;
258 layout.DesktopScaleFactor = monitor->attributes.desktopScaleFactor;
259 layout.DeviceScaleFactor = monitor->attributes.deviceScaleFactor;
262 if ((mask & FREERDP_MONITOR_OVERRIDE_ORIENTATION) != 0)
264 if ((mask & FREERDP_MONITOR_OVERRIDE_DESKTOP_SCALE) != 0)
265 layout.DesktopScaleFactor =
267 if ((mask & FREERDP_MONITOR_OVERRIDE_DEVICE_SCALE) != 0)
268 layout.DeviceScaleFactor =
270 layouts.emplace_back(layout);
273 if (!settings_changed(layouts))
277 const size_t len = layouts.size();
278 WINPR_ASSERT(len <= UINT32_MAX);
279 const auto ret = IFCALLRESULT(CHANNEL_RC_OK, _disp->SendMonitorLayout, _disp,
280 static_cast<UINT32
>(len), layouts.data());
281 if (ret != CHANNEL_RC_OK)
283 _last_sent_layout = std::move(layouts);
287bool sdlDispContext::addTimer()
289 if (SDL_WasInit(SDL_INIT_EVENTS) == 0)
292 SDL_RemoveTimer(_timer);
293 WLog_Print(_sdl->getWLog(), WLOG_TRACE,
"adding new display check timer");
298 _timer = SDL_AddTimer(1000, sdlDispContext::OnTimer,
this);
302bool sdlDispContext::updateMonitor(SDL_WindowID
id)
307 if (!_sdl->updateWindow(
id))
310 if (!_sdl->updateWindowList())
316bool sdlDispContext::updateMonitors(SDL_EventType type, SDL_DisplayID displayID)
318 auto settings = _sdl->context()->settings;
327 case SDL_EVENT_DISPLAY_ADDED:
328 if (!_sdl->addDisplayWindow(displayID))
331 case SDL_EVENT_DISPLAY_REMOVED:
332 if (!_sdl->removeDisplayWindow(displayID))
339 if (!_sdl->updateWindowList())
344bool sdlDispContext::handleEvent(
const SDL_DisplayEvent& ev)
346 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
349 case SDL_EVENT_DISPLAY_ADDED:
350 SDL_LogDebug(cat,
"A new display with id %u was connected", ev.displayID);
351 return updateMonitors(ev.type, ev.displayID);
352 case SDL_EVENT_DISPLAY_REMOVED:
353 SDL_LogDebug(cat,
"The display with id %u was disconnected", ev.displayID);
354 return updateMonitors(ev.type, ev.displayID);
355 case SDL_EVENT_DISPLAY_ORIENTATION:
356 SDL_LogDebug(cat,
"The orientation of display with id %u was changed", ev.displayID);
357 return updateMonitors(ev.type, ev.displayID);
358 case SDL_EVENT_DISPLAY_MOVED:
359 SDL_LogDebug(cat,
"The display with id %u was moved", ev.displayID);
360 return updateMonitors(ev.type, ev.displayID);
361 case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
362 SDL_LogDebug(cat,
"The display with id %u changed scale", ev.displayID);
363 return updateMonitors(ev.type, ev.displayID);
364#if SDL_VERSION_ATLEAST(3, 3, 2)
365 case SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED:
366 SDL_LogDebug(cat,
"The display with id %u changed usable bounds", ev.displayID);
367 return updateMonitors(ev.type, ev.displayID);
369 case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED:
370 SDL_LogDebug(cat,
"The display with id %u changed mode", ev.displayID);
371 return updateMonitors(ev.type, ev.displayID);
372 case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED:
373 SDL_LogDebug(cat,
"The display with id %u changed desktop mode", ev.displayID);
374 return updateMonitors(ev.type, ev.displayID);
380bool sdlDispContext::handleEvent(
const SDL_WindowEvent& ev)
382 auto window = _sdl->getWindowForId(ev.windowID);
387 window->setBordered(bordered);
391 case SDL_EVENT_WINDOW_HIDDEN:
392 case SDL_EVENT_WINDOW_MINIMIZED:
394 if (!_sdl->allWindowsHidden())
396 return _sdl->redraw(
true);
397 case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
398 return updateMonitor(ev.windowID);
399 case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
400 return updateMonitor(ev.windowID);
402 case SDL_EVENT_WINDOW_EXPOSED:
403 case SDL_EVENT_WINDOW_SHOWN:
404 case SDL_EVENT_WINDOW_MAXIMIZED:
405 case SDL_EVENT_WINDOW_RESTORED:
411 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
412 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
413 case SDL_EVENT_WINDOW_RESIZED:
414 return updateMonitor(ev.windowID);
415 case SDL_EVENT_WINDOW_MOUSE_LEAVE:
417 return _sdl->getInputChannelContext().keyboard_grab(ev.windowID,
false);
418 case SDL_EVENT_WINDOW_MOUSE_ENTER:
420 if (!_sdl->getInputChannelContext().keyboard_grab(ev.windowID,
true))
422 return _sdl->getInputChannelContext().keyboard_focus_in();
423 case SDL_EVENT_WINDOW_FOCUS_GAINED:
424 return _sdl->getInputChannelContext().keyboard_focus_in();
431UINT sdlDispContext::DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
432 UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
438 return sdlDisp->DisplayControlCaps(maxNumMonitors, maxMonitorAreaFactorA,
439 maxMonitorAreaFactorB);
442UINT sdlDispContext::DisplayControlCaps(UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA,
443 UINT32 maxMonitorAreaFactorB)
445 auto settings = _sdl->context()->settings;
446 WINPR_ASSERT(settings);
449 "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32
" MaxMonitorAreaFactorA: %" PRIu32
450 " MaxMonitorAreaFactorB: %" PRIu32
"",
451 maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
458 _last_sent_layout.clear();
462 _lastSentDate = GetTickCount64();
468 std::ignore = addTimer();
469 return CHANNEL_RC_OK;
472 WLog_DBG(TAG,
"DisplayControlCapsPdu: setting the window as resizable");
473 if (!setWindowResizeable())
474 return CHANNEL_RC_NO_MEMORY;
475 std::ignore = addTimer();
476 return CHANNEL_RC_OK;
479bool sdlDispContext::init(DispClientContext* disp)
484 auto settings = _sdl->context()->settings;
494 disp->DisplayControlCaps = sdlDispContext::DisplayControlCaps;
497 return _sdl->setResizeable(
true);
500bool sdlDispContext::uninit(DispClientContext* disp)
506 return _sdl->setResizeable(
false);
512 WINPR_ASSERT(_sdl->context()->settings);
513 WINPR_ASSERT(_sdl->context()->pubSub);
515 auto pubSub = _sdl->context()->pubSub;
517 if (PubSub_SubscribeActivated(pubSub, sdlDispContext::OnActivated) < 0)
518 throw std::exception();
519 if (PubSub_SubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset) < 0)
520 throw std::exception();
521 std::ignore = addTimer();
524sdlDispContext::~sdlDispContext()
526 wPubSub* pubSub = _sdl->context()->pubSub;
527 WINPR_ASSERT(pubSub);
529 PubSub_UnsubscribeActivated(pubSub, sdlDispContext::OnActivated);
530 PubSub_UnsubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
531 SDL_RemoveTimer(_timer);
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 UINT64 freerdp_settings_get_uint64(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt64 id)
Returns a UINT64 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.