23#include "sdl_context.hpp"
24#include "sdl_config.hpp"
25#include "sdl_channels.hpp"
26#include "sdl_monitor.hpp"
27#include "sdl_pointer.hpp"
28#include "sdl_touch.hpp"
30#include <sdl_common_utils.hpp>
31#include <scoped_guard.hpp>
33#include "dialogs/sdl_dialogs.hpp"
35#if defined(WITH_WEBVIEW)
36#include <aad/sdl_webview.hpp>
39static constexpr auto sdl_allow_screensaver =
"sdl-allow-screensaver";
41static void sdl_PointerFreeCopyAll(rdpPointer* pointer)
43 sdl_Pointer_FreeCopy(pointer);
47SdlContext::SdlContext(rdpContext* context)
48 : _context(context), _log(WLog_Get(CLIENT_TAG(
"SDL"))), _cursor(nullptr, sdl_Pointer_FreeCopy),
49 _rdpThreadRunning(false), _primary(nullptr, SDL_DestroySurface), _disp(this), _input(this),
50 _clip(this), _dialog(_log)
52 WINPR_ASSERT(context);
55 auto instance = _context->instance;
56 WINPR_ASSERT(instance);
58 instance->PreConnect = preConnect;
59 instance->PostConnect = postConnect;
60 instance->PostDisconnect = postDisconnect;
61 instance->PostFinalDisconnect = postFinalDisconnect;
62 instance->AuthenticateEx = sdl_authenticate_ex;
63 instance->VerifyCertificateEx = sdl_verify_certificate_ex;
64 instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
65 instance->LogonErrorInfo = sdl_logon_error_info;
66 instance->PresentGatewayMessage = sdl_present_gateway_message;
67 instance->ChooseSmartcard = sdl_choose_smartcard;
68 instance->RetryDialog = sdl_retry_dialog;
71 instance->GetAccessToken = sdl_webview_get_access_token;
73 instance->GetAccessToken = client_cli_get_access_token;
77 _args.push_back({ sdl_allow_screensaver, COMMAND_LINE_VALUE_BOOL,
nullptr, BoolValueFalse,
78 nullptr, -1,
nullptr,
"Allow local screensaver to activate" });
81 _args.push_back({
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
nullptr });
84void SdlContext::setHasCursor(
bool val)
86 this->_cursor_visible = val;
89bool SdlContext::hasCursor()
const
91 return _cursor_visible;
94void SdlContext::setMetadata()
97 if (!wmclass || (strlen(wmclass) == 0))
98 wmclass = SDL_CLIENT_UUID;
100 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, wmclass);
101 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, SDL_CLIENT_NAME);
102 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, SDL_CLIENT_VERSION);
103 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, SDL_CLIENT_VENDOR);
104 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, SDL_CLIENT_COPYRIGHT);
105 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, SDL_CLIENT_URL);
106 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, SDL_CLIENT_TYPE);
109int SdlContext::start()
111 _thread = std::thread(rdpThreadRun,
this);
115int SdlContext::join()
119 HANDLE
event = freerdp_abort_event(context());
120 if (!SetEvent(event))
127void SdlContext::cleanup()
129 std::unique_lock lock(_critical);
135bool SdlContext::shallAbort(
bool ignoreDialogs)
137 std::unique_lock lock(_critical);
138 if (freerdp_shall_disconnect_context(context()))
142 if (_rdpThreadRunning)
144 return !getDialog().isRunning();
151BOOL SdlContext::preConnect(freerdp* instance)
153 WINPR_ASSERT(instance);
154 WINPR_ASSERT(instance->context);
156 auto sdl = get_context(instance->context);
158 auto settings = instance->context->settings;
159 WINPR_ASSERT(settings);
174 if (PubSub_SubscribeChannelConnected(instance->context->pubSub,
175 sdl_OnChannelConnectedEventHandler) < 0)
177 if (PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
178 sdl_OnChannelDisconnectedEventHandler) < 0)
180 if (PubSub_SubscribeUserNotification(instance->context->pubSub,
181 sdl_OnUserNotificationEventHandler) < 0)
187 UINT32 maxHeight = 0;
189 if (!sdl_detect_monitors(
sdl, &maxWidth, &maxHeight))
192 if ((maxWidth != 0) && (maxHeight != 0) &&
195 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Update size to %ux%u", maxWidth, maxHeight);
210 if (sm && (sw > 0) && (sh > 0))
214 WLog_Print(
sdl->getWLog(), WLOG_WARN,
215 "/smart-sizing and /multimon are currently not supported, ignoring "
234 WLog_Print(
sdl->getWLog(), WLOG_INFO,
235 "auth-only, but no password set. Please provide one.");
242 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect SDL.");
245 if (!
sdl->getInputChannelContext().initialize())
248 sdl->_credentialsRead =
false;
261BOOL SdlContext::postConnect(freerdp* instance)
263 WINPR_ASSERT(instance);
265 auto context = instance->context;
266 WINPR_ASSERT(context);
268 auto sdl = get_context(context);
271 sdl->getDialog().show(
false);
278 WLog_Print(
sdl->getWLog(), WLOG_INFO,
279 "auth-only, but no password set. Please provide one.");
283 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect to X.");
287 if (!
sdl->waitForWindowsCreated())
290 sdl->_sdlPixelFormat = SDL_PIXELFORMAT_BGRA32;
291 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
294 if (!
sdl->createPrimary())
297 if (!sdl_register_pointer(instance->context->graphics))
300 WINPR_ASSERT(context->update);
302 context->update->BeginPaint = beginPaint;
303 context->update->EndPaint = endPaint;
304 context->update->PlaySound = playSound;
305 context->update->DesktopResize = desktopResize;
306 context->update->SetKeyboardIndicators = sdlInput::keyboard_set_indicators;
307 context->update->SetKeyboardImeStatus = sdlInput::keyboard_set_ime_status;
309 if (!
sdl->setResizeable(
false))
315 sdl->setConnected(
true);
322void SdlContext::postDisconnect(freerdp* instance)
327 if (!instance->context)
330 auto sdl = get_context(instance->context);
331 sdl->setConnected(
false);
336void SdlContext::postFinalDisconnect(freerdp* instance)
341 if (!instance->context)
344 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
345 sdl_OnChannelConnectedEventHandler);
346 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
347 sdl_OnChannelDisconnectedEventHandler);
348 PubSub_UnsubscribeUserNotification(instance->context->pubSub,
349 sdl_OnUserNotificationEventHandler);
353bool SdlContext::createPrimary()
355 auto gdi = context()->gdi;
358 _primary = SDLSurfacePtr(
359 SDL_CreateSurfaceFrom(
static_cast<int>(gdi->width),
static_cast<int>(gdi->height),
360 pixelFormat(), gdi->primary_buffer,
static_cast<int>(gdi->stride)),
365 SDL_SetSurfaceBlendMode(_primary.get(), SDL_BLENDMODE_NONE);
366 SDL_Rect surfaceRect = { 0, 0, gdi->width, gdi->height };
367 SDL_FillSurfaceRect(_primary.get(), &surfaceRect,
368 SDL_MapSurfaceRGBA(_primary.get(), 0, 0, 0, 0xff));
373bool SdlContext::createWindows()
375 auto settings = context()->settings;
376 const auto& title = windowTitle();
378 ScopeGuard guard1([&]() { _windowsCreatedEvent.set(); });
384 for (UINT32 x = 0; x < windowCount; x++)
386 auto id = monitorId(x);
391 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
393 originX = std::min<Sint32>(monitor->x, originX);
394 originY = std::min<Sint32>(monitor->y, originY);
397 for (UINT32 x = 0; x < windowCount; x++)
399 auto id = monitorId(x);
404 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
406 Uint32 w = WINPR_ASSERTING_INT_CAST(Uint32, monitor->width);
407 Uint32 h = WINPR_ASSERTING_INT_CAST(Uint32, monitor->height);
411 if (_windowWidth > 0)
416 if (_windowHeigth > 0)
422 Uint32 flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
427 flags |= SDL_WINDOW_FULLSCREEN;
432 flags |= SDL_WINDOW_BORDERLESS;
436 flags |= SDL_WINDOW_BORDERLESS;
438 auto did = WINPR_ASSERTING_INT_CAST(SDL_DisplayID,
id);
439 auto window = SdlWindow::create(did, title, flags, w, h);
443 window.setOffsetX(originX - monitor->x);
444 window.setOffsetY(originY - monitor->y);
447 _windows.insert({ window.id(), std::move(window) });
453bool SdlContext::updateWindowList()
455 std::vector<rdpMonitor> list;
456 list.reserve(_windows.size());
457 for (
const auto& win : _windows)
458 list.push_back(win.second.monitor(_windows.size() == 1));
464 std::none_of(list.cbegin(), list.cend(), [](
const rdpMonitor& m) { return m.is_primary; }))
465 list.at(0).is_primary =
true;
471bool SdlContext::updateWindow(SDL_WindowID
id)
477 auto& w = _windows.at(
id);
478 auto m = w.monitor(
true);
482 m.attributes.physicalWidth =
static_cast<UINT32
>(r.w);
483 m.attributes.physicalHeight =
static_cast<UINT32
>(r.h);
488std::string SdlContext::windowTitle()
const
490 const char* prefix =
"FreeRDP:";
498 const auto addPort = (port != 3389);
500 std::stringstream ss;
501 ss << prefix <<
" " << name;
509bool SdlContext::waitForWindowsCreated()
512 std::unique_lock<CriticalSection> lock(_critical);
513 _windowsCreatedEvent.clear();
514 if (!sdl_push_user_event(SDL_EVENT_USER_CREATE_WINDOWS,
this))
518 HANDLE handles[] = { _windowsCreatedEvent.handle(), freerdp_abort_event(context()) };
520 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
534BOOL SdlContext::endPaint(rdpContext* context)
536 auto sdl = get_context(context);
539 auto gdi = context->gdi;
541 WINPR_ASSERT(gdi->primary);
543 HGDI_DC hdc = gdi->primary->hdc;
549 WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
551 if (hwnd->invalid->null)
554 WINPR_ASSERT(hwnd->invalid);
555 if (gdi->suppressOutput || hwnd->invalid->null)
558 const INT32 ninvalid = hwnd->ninvalid;
559 const GDI_RGN* cinvalid = hwnd->cinvalid;
564 std::vector<SDL_Rect> rects;
565 for (INT32 x = 0; x < ninvalid; x++)
567 auto& rgn = cinvalid[x];
568 rects.push_back({ rgn.x, rgn.y, rgn.w, rgn.h });
571 sdl->push(std::move(rects));
572 return sdl_push_user_event(SDL_EVENT_USER_UPDATE);
575void SdlContext::sdl_client_cleanup(
int exit_code,
const std::string& error_msg)
577 rdpSettings* settings = context()->settings;
578 WINPR_ASSERT(settings);
580 _rdpThreadRunning =
false;
581 bool showError =
false;
583 WLog_Print(getWLog(), WLOG_INFO,
"Authentication only, exit status %s [%" PRId32
"]",
584 sdl::error::exitCodeToTag(exit_code), exit_code);
589 case sdl::error::SUCCESS:
590 case sdl::error::DISCONNECT:
591 case sdl::error::LOGOFF:
592 case sdl::error::DISCONNECT_BY_USER:
593 case sdl::error::CONNECT_CANCELLED:
597 getDialog().showError(error_msg);
604 getDialog().show(
false);
606 _exitCode = exit_code;
607 std::ignore = sdl_push_user_event(SDL_EVENT_USER_QUIT);
611int SdlContext::sdl_client_thread_connect(std::string& error_msg)
613 auto instance = context()->instance;
614 WINPR_ASSERT(instance);
616 _rdpThreadRunning =
true;
617 BOOL rc = freerdp_connect(instance);
619 rdpSettings* settings = context()->settings;
620 WINPR_ASSERT(settings);
622 int exit_code = sdl::error::SUCCESS;
625 UINT32 error = freerdp_get_last_error(context());
626 exit_code = sdl::error::errorToExitCode(error);
631 DWORD code = freerdp_get_last_error(context());
632 freerdp_abort_connect_context(context());
633 WLog_Print(getWLog(), WLOG_ERROR,
"Authentication only, %s [0x%08" PRIx32
"] %s",
634 freerdp_get_last_error_name(code), code, freerdp_get_last_error_string(code));
640 DWORD code = freerdp_error_info(instance);
641 if (exit_code == sdl::error::SUCCESS)
645 exit_code = error_info_to_error(&code, &msg, &len);
651 auto last = freerdp_get_last_error(context());
652 if (error_msg.empty())
656 winpr_asprintf(&msg, &len,
"%s [0x%08" PRIx32
"]\n%s",
657 freerdp_get_last_error_name(last), last,
658 freerdp_get_last_error_string(last));
664 if (exit_code == sdl::error::SUCCESS)
666 if (last == FREERDP_ERROR_AUTHENTICATION_FAILED)
667 exit_code = sdl::error::AUTH_FAILURE;
668 else if (code == ERRINFO_SUCCESS)
669 exit_code = sdl::error::CONN_FAILED;
672 getDialog().show(
false);
678int SdlContext::sdl_client_thread_run(std::string& error_msg)
680 auto instance = context()->instance;
681 WINPR_ASSERT(instance);
683 int exit_code = sdl::error::SUCCESS;
684 while (!freerdp_shall_disconnect_context(context()))
686 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
692 if (freerdp_focus_required(instance))
694 auto ctx = get_context(context());
697 auto& input = ctx->getInputChannelContext();
698 if (!input.keyboard_focus_in())
700 if (!input.keyboard_focus_in())
704 const DWORD nCount = freerdp_get_event_handles(context(), handles, ARRAYSIZE(handles));
708 WLog_Print(getWLog(), WLOG_ERROR,
"freerdp_get_event_handles failed");
712 const DWORD status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
714 if (status == WAIT_FAILED)
716 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects WAIT_FAILED");
720 if (!freerdp_check_event_handles(context()))
722 if (client_auto_reconnect(instance))
725 getDialog().show(
false);
734 if (freerdp_error_info(instance) == 0)
735 exit_code = sdl::error::CONN_FAILED;
738 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
739 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects failed with %" PRIu32
"",
741 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
742 WLog_Print(getWLog(), WLOG_ERROR,
"Failed to check FreeRDP event handles");
747 if (exit_code == sdl::error::SUCCESS)
751 char* emsg =
nullptr;
753 exit_code = error_info_to_error(&code, &emsg, &elen);
759 if ((code == ERRINFO_LOGOFF_BY_USER) &&
760 (freerdp_get_disconnect_ultimatum(context()) == Disconnect_Ultimatum_user_requested))
762 const char* msg =
"Error info says user did not initiate but disconnect ultimatum says "
763 "they did; treat this as a user logoff";
765 char* emsg =
nullptr;
767 winpr_asprintf(&emsg, &elen,
"%s", msg);
773 WLog_Print(getWLog(), WLOG_INFO,
"%s", msg);
774 exit_code = sdl::error::LOGOFF;
778 freerdp_disconnect(instance);
790 std::string error_msg;
791 int exit_code =
sdl->sdl_client_thread_connect(error_msg);
792 if (exit_code == sdl::error::SUCCESS)
793 exit_code =
sdl->sdl_client_thread_run(error_msg);
794 sdl->sdl_client_cleanup(exit_code, error_msg);
796 return static_cast<DWORD
>(exit_code);
799int SdlContext::error_info_to_error(DWORD* pcode,
char** msg,
size_t* len)
const
801 const DWORD code = freerdp_error_info(context()->instance);
802 const char* name = freerdp_get_error_info_name(code);
803 const char* str = freerdp_get_error_info_string(code);
804 const int exit_code = sdl::error::errorToExitCode(code);
806 winpr_asprintf(msg, len,
"Terminate with %s due to ERROR_INFO %s [0x%08" PRIx32
"]: %s",
807 sdl::error::errorToExitCodeTag(code), name, code, str);
808 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%s", *msg);
814void SdlContext::applyMonitorOffset(SDL_WindowID window,
float& x,
float& y)
const
819 auto w = getWindowForId(window);
820 x -=
static_cast<float>(w->offsetX());
821 y -=
static_cast<float>(w->offsetY());
824static bool alignX(
const SDL_Rect& a,
const SDL_Rect& b)
826 if (a.x + a.w == b.x)
828 if (b.x + b.w == a.x)
833static bool alignY(
const SDL_Rect& a,
const SDL_Rect& b)
835 if (a.y + a.h == b.y)
837 if (b.y + b.h == a.y)
842std::vector<SDL_DisplayID>
843SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID
id,
844 const std::vector<SDL_DisplayID>& ignore)
846 auto first = _offsets.at(
id);
847 std::vector<SDL_DisplayID> neighbours;
849 for (
auto& entry : _offsets)
851 if (entry.first ==
id)
853 if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end())
856 bool neighbor =
false;
857 if (alignX(entry.second.first, first.first))
859 if (entry.second.first.x < first.first.x)
860 entry.second.second.x = first.second.x - entry.second.second.w;
862 entry.second.second.x = first.second.x + first.second.w;
865 if (alignY(entry.second.first, first.first))
867 if (entry.second.first.y < first.first.y)
868 entry.second.second.y = first.second.y - entry.second.second.h;
870 entry.second.second.y = first.second.y + first.second.h;
875 neighbours.push_back(entry.first);
880void SdlContext::updateMonitorDataFromOffsets()
882 for (
auto& entry : _displays)
884 auto offsets = _offsets.at(entry.first);
885 entry.second.x = offsets.second.x;
886 entry.second.y = offsets.second.y;
889 for (
auto& entry : _windows)
891 const auto& monitor = _displays.at(entry.first);
892 entry.second.setMonitor(monitor);
896bool SdlContext::drawToWindow(
SdlWindow& window,
const std::vector<SDL_Rect>& rects)
901 auto gdi = context()->gdi;
904 auto size = window.rect();
906 std::unique_lock lock(_critical);
907 auto surface = _primary.get();
911 window.setOffsetX(0);
912 window.setOffsetY(0);
913 if (gdi->width < size.w)
915 window.setOffsetX((size.w - gdi->width) / 2);
917 if (gdi->height < size.h)
919 window.setOffsetY((size.h - gdi->height) / 2);
922 _localScale = {
static_cast<float>(size.w) /
static_cast<float>(gdi->width),
923 static_cast<float>(size.h) /
static_cast<float>(gdi->height) };
924 if (!window.drawScaledRects(surface, _localScale, rects))
929 SDL_Point offset{ 0, 0 };
931 offset = { window.offsetX(), window.offsetY() };
932 if (!window.drawRects(surface, offset, rects))
936 window.updateSurface();
940bool SdlContext::minimizeAllWindows()
942 for (
auto& w : _windows)
947int SdlContext::exitCode()
const
952SDL_PixelFormat SdlContext::pixelFormat()
const
954 return _sdlPixelFormat;
957bool SdlContext::addDisplayWindow(SDL_DisplayID
id)
960 SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
961 auto title = sdl::utils::windowTitle(context()->settings);
962 auto w = SdlWindow::create(
id, title, flags);
963 _windows.emplace(w.id(), std::move(w));
967bool SdlContext::removeDisplayWindow(SDL_DisplayID
id)
969 for (
auto& w : _windows)
971 if (w.second.displayIndex() ==
id)
972 _windows.erase(w.first);
977bool SdlContext::detectDisplays()
980 auto display = SDL_GetDisplays(&count);
983 for (
int x = 0; x < count; x++)
985 const auto id = display[x];
986 addOrUpdateDisplay(
id);
992rdpMonitor SdlContext::getDisplay(SDL_DisplayID
id)
const
994 return _displays.at(
id);
997std::vector<SDL_DisplayID> SdlContext::getDisplayIds()
const
999 std::vector<SDL_DisplayID> keys;
1000 keys.reserve(_displays.size());
1001 for (
const auto& entry : _displays)
1003 keys.push_back(entry.first);
1008const SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
const
1010 auto it = _windows.find(
id);
1011 if (it == _windows.end())
1016SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
1018 auto it = _windows.find(
id);
1019 if (it == _windows.end())
1026 if (_windows.empty())
1028 return &_windows.begin()->second;
1036sdlInput& SdlContext::getInputChannelContext()
1041sdlClip& SdlContext::getClipboardChannelContext()
1051wLog* SdlContext::getWLog()
1056bool SdlContext::moveMouseTo(
const SDL_FPoint& pos)
1058 auto window = SDL_GetMouseFocus();
1062 const auto id = SDL_GetWindowID(window);
1063 const auto spos = pixelToScreen(
id, pos);
1064 SDL_WarpMouseInWindow(window, spos.x, spos.y);
1068bool SdlContext::handleEvent(
const SDL_MouseMotionEvent& ev)
1070 if (!getWindowForId(ev.windowID))
1074 if (!eventToPixelCoordinates(ev.windowID, copy))
1076 removeLocalScaling(copy.motion.x, copy.motion.y);
1077 removeLocalScaling(copy.motion.xrel, copy.motion.yrel);
1078 applyMonitorOffset(copy.motion.windowID, copy.motion.x, copy.motion.y);
1080 return SdlTouch::handleEvent(
this, copy.motion);
1083bool SdlContext::handleEvent(
const SDL_MouseWheelEvent& ev)
1085 if (!getWindowForId(ev.windowID))
1089 if (!eventToPixelCoordinates(ev.windowID, copy))
1091 removeLocalScaling(copy.wheel.mouse_x, copy.wheel.mouse_y);
1092 return SdlTouch::handleEvent(
this, copy.wheel);
1095bool SdlContext::handleEvent(
const SDL_WindowEvent& ev)
1097 if (!getDisplayChannelContext().handleEvent(ev))
1100 auto window = getWindowForId(ev.windowID);
1105 const auto& r = window->rect();
1106 const auto& b = window->bounds();
1107 const auto& scale = window->scale();
1108 const auto& orientation = window->orientation();
1109 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1110 "%s: [%u] %dx%d-%dx%d {%dx%d-%dx%d}{scale=%f,orientation=%s}",
1111 sdl::utils::toString(ev.type).c_str(), ev.windowID, r.x, r.y, r.w, r.h, b.x,
1112 b.y, b.w, b.h,
static_cast<double>(scale),
1113 sdl::utils::toString(orientation).c_str());
1118 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1119 return restoreCursor();
1120 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1121 if (!resizeToScale(window))
1125 if (!window->fill())
1127 if (!drawToWindow(*window))
1129 if (!restoreCursor())
1133 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1134 if (!resizeToScale(window))
1136 if (!window->fill())
1138 if (!drawToWindow(*window))
1140 if (!restoreCursor())
1143 case SDL_EVENT_WINDOW_MOVED:
1145 auto r = window->rect();
1146 auto id = window->id();
1147 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%u: %dx%d-%dx%d",
id, r.x, r.y, r.w, r.h);
1150 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1152 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"Window closed, terminating RDP session...");
1153 freerdp_abort_connect_context(context());
1162bool SdlContext::handleEvent(
const SDL_DisplayEvent& ev)
1164 if (!getDisplayChannelContext().handleEvent(ev))
1169 case SDL_EVENT_DISPLAY_REMOVED:
1174 if (!SDL_GetDisplayBounds(ev.displayID, &r))
1176 const auto name = SDL_GetDisplayName(ev.displayID);
1179 const auto orientation = SDL_GetCurrentDisplayOrientation(ev.displayID);
1180 const auto scale = SDL_GetDisplayContentScale(ev.displayID);
1181 const auto mode = SDL_GetCurrentDisplayMode(ev.displayID);
1185 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1186 "%s: [%u, %s] %dx%d-%dx%d {orientation=%s, scale=%f}%s",
1187 sdl::utils::toString(ev.type).c_str(), ev.displayID, name, r.x, r.y, r.w,
1188 r.h, sdl::utils::toString(orientation).c_str(),
static_cast<double>(scale),
1189 sdl::utils::toString(mode).c_str());
1196bool SdlContext::handleEvent(
const SDL_MouseButtonEvent& ev)
1198 if (!getWindowForId(ev.windowID))
1200 SDL_Event copy = {};
1202 if (!eventToPixelCoordinates(ev.windowID, copy))
1204 removeLocalScaling(copy.button.x, copy.button.y);
1205 applyMonitorOffset(copy.button.windowID, copy.button.x, copy.button.y);
1206 return SdlTouch::handleEvent(
this, copy.button);
1209bool SdlContext::handleEvent(
const SDL_TouchFingerEvent& ev)
1211 if (!getWindowForId(ev.windowID))
1215 if (!eventToPixelCoordinates(ev.windowID, copy))
1217 removeLocalScaling(copy.tfinger.dx, copy.tfinger.dy);
1218 removeLocalScaling(copy.tfinger.x, copy.tfinger.y);
1219 applyMonitorOffset(copy.tfinger.windowID, copy.tfinger.x, copy.tfinger.y);
1220 return SdlTouch::handleEvent(
this, copy.tfinger);
1223void SdlContext::addOrUpdateDisplay(SDL_DisplayID
id)
1225 auto monitor = SdlWindow::query(
id,
false);
1226 _displays.emplace(
id, monitor);
1236 for (
auto& entry : _displays)
1239 std::ignore = SDL_GetDisplayBounds(entry.first, &bounds);
1242 pixel.w = entry.second.width;
1243 pixel.h = entry.second.height;
1244 _offsets.emplace(entry.first, std::pair{ bounds, pixel });
1251 const auto primary = SDL_GetPrimaryDisplay();
1252 std::vector<SDL_DisplayID> handled;
1253 handled.push_back(primary);
1255 auto neighbors = updateDisplayOffsetsForNeighbours(primary);
1256 while (!neighbors.empty())
1258 auto neighbor = neighbors.front();
1259 neighbors.erase(neighbors.begin());
1261 if (std::find(handled.begin(), handled.end(), neighbor) != handled.end())
1263 handled.push_back(neighbor);
1265 auto next = updateDisplayOffsetsForNeighbours(neighbor, handled);
1266 neighbors.insert(neighbors.end(), next.begin(), next.end());
1268 updateMonitorDataFromOffsets();
1271void SdlContext::deleteDisplay(SDL_DisplayID
id)
1273 _displays.erase(
id);
1276bool SdlContext::eventToPixelCoordinates(SDL_WindowID
id, SDL_Event& ev)
1278 auto w = getWindowForId(
id);
1283 auto renderer = w->renderer();
1286 return SDL_ConvertEventToRenderCoordinates(renderer, &ev);
1289SDL_FPoint SdlContext::applyLocalScaling(
const SDL_FPoint& val)
const
1291 if (!useLocalScale())
1295 rval.x *= _localScale.x;
1296 rval.y *= _localScale.y;
1300void SdlContext::removeLocalScaling(
float& x,
float& y)
const
1302 if (!useLocalScale())
1308SDL_FPoint SdlContext::screenToPixel(SDL_WindowID
id,
const SDL_FPoint& pos)
1310 auto w = getWindowForId(
id);
1315 auto renderer = w->renderer();
1320 if (!SDL_RenderCoordinatesFromWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1322 removeLocalScaling(rpos.x, rpos.y);
1326SDL_FPoint SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FPoint& pos)
1328 auto w = getWindowForId(
id);
1333 auto renderer = w->renderer();
1338 if (!SDL_RenderCoordinatesToWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1340 return applyLocalScaling(rpos);
1343SDL_FRect SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FRect& pos,
bool round)
1345 const auto fpos = pixelToScreen(
id, SDL_FPoint{ pos.x, pos.y });
1346 const auto size = pixelToScreen(
id, SDL_FPoint{ pos.w, pos.h });
1347 SDL_FRect r{ fpos.x, fpos.y, size.x, size.y };
1350 r.w = std::ceil(r.w);
1351 r.h = std::ceil(r.h);
1352 r.x = std::floor(r.x);
1353 r.y = std::floor(r.y);
1358bool SdlContext::handleEvent(
const SDL_Event& ev)
1360 if ((ev.type >= SDL_EVENT_DISPLAY_FIRST) && (ev.type <= SDL_EVENT_DISPLAY_LAST))
1362 const auto& dev = ev.display;
1363 return handleEvent(dev);
1365 if ((ev.type >= SDL_EVENT_WINDOW_FIRST) && (ev.type <= SDL_EVENT_WINDOW_LAST))
1367 const auto& wev = ev.window;
1368 return handleEvent(wev);
1372 case SDL_EVENT_RENDER_TARGETS_RESET:
1373 case SDL_EVENT_RENDER_DEVICE_RESET:
1374 case SDL_EVENT_WILL_ENTER_FOREGROUND:
1385 case SDL_EVENT_FINGER_DOWN:
1386 case SDL_EVENT_FINGER_UP:
1387 case SDL_EVENT_FINGER_MOTION:
1389 const auto& cev = ev.tfinger;
1390 return handleEvent(cev);
1392 case SDL_EVENT_MOUSE_MOTION:
1395 const auto& cev = ev.motion;
1396 return handleEvent(cev);
1398 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1399 case SDL_EVENT_MOUSE_BUTTON_UP:
1401 const auto& cev = ev.button;
1402 return handleEvent(cev);
1404 case SDL_EVENT_MOUSE_WHEEL:
1406 const auto& cev = ev.wheel;
1407 return handleEvent(cev);
1409 case SDL_EVENT_CLIPBOARD_UPDATE:
1411 const auto& cev = ev.clipboard;
1412 return getClipboardChannelContext().handleEvent(cev);
1414 case SDL_EVENT_KEY_DOWN:
1415 case SDL_EVENT_KEY_UP:
1417 const auto& cev = ev.key;
1418 return getInputChannelContext().handleEvent(cev);
1427 return _args.data();
1430size_t SdlContext::argsCount()
const
1432 if (_args.size() <= 1)
1434 return _args.size() - 1;
1445 if (strcmp(arg->Name, sdl_allow_screensaver) == 0)
1447 if (arg->Value !=
nullptr)
1449 if (!SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER,
"1"))
1451 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
1452 "SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER) failed with %s",
1467std::vector<rdpPointer*>& SdlContext::pointers()
1469 return _valid_pointers;
1472bool SdlContext::contains(
const rdpPointer* ptr)
const
1474 for (
const auto& cur : _valid_pointers)
1482bool SdlContext::credentialsRead()
const
1484 return _credentialsRead;
1487void SdlContext::setCredentialsRead()
1489 _credentialsRead =
true;
1492bool SdlContext::resizeToScale(
SdlWindow* window)
1496 if (!useLocalScale())
1500 return window->resizeToScale();
1503bool SdlContext::useLocalScale()
const
1508 const auto dynResize =
1512 return !dynResize && !fs && !multimon;
1515bool SdlContext::drawToWindows(
const std::vector<SDL_Rect>& rects)
1517 for (
auto& window : _windows)
1519 if (!drawToWindow(window.second, rects))
1526BOOL SdlContext::desktopResize(rdpContext* context)
1528 rdpGdi* gdi =
nullptr;
1529 rdpSettings* settings =
nullptr;
1530 auto sdl = get_context(context);
1533 WINPR_ASSERT(context);
1535 settings = context->settings;
1536 WINPR_ASSERT(settings);
1538 std::unique_lock lock(
sdl->_critical);
1543 return sdl->createPrimary();
1547BOOL SdlContext::playSound(rdpContext* context,
const PLAY_SOUND_UPDATE* play_sound)
1550 WINPR_UNUSED(context);
1551 WINPR_UNUSED(play_sound);
1557BOOL SdlContext::beginPaint(rdpContext* context)
1559 auto gdi = context->gdi;
1561 WINPR_ASSERT(gdi->primary);
1563 HGDI_DC hdc = gdi->primary->hdc;
1569 WINPR_ASSERT(hwnd->invalid);
1570 hwnd->invalid->null = TRUE;
1576bool SdlContext::redraw(
bool suppress)
const
1581 auto gdi = context()->gdi;
1583 return gdi_send_suppress_output(gdi, suppress);
1586void SdlContext::setConnected(
bool val)
1591bool SdlContext::isConnected()
const
1596rdpContext* SdlContext::context()
const
1598 WINPR_ASSERT(_context);
1602rdpClientContext* SdlContext::common()
const
1604 return reinterpret_cast<rdpClientContext*
>(context());
1607bool SdlContext::setCursor(CursorType type)
1610 return restoreCursor();
1613bool SdlContext::setCursor(
const rdpPointer* cursor)
1615 std::unique_lock lock(_critical);
1616 if (!contains(cursor))
1619 _cursor = { sdl_Pointer_Copy(cursor), sdl_PointerFreeCopyAll };
1620 return setCursor(CURSOR_IMAGE);
1623rdpPointer* SdlContext::cursor()
const
1625 return _cursor.get();
1628bool SdlContext::restoreCursor()
1630 WLog_Print(getWLog(), WLOG_DEBUG,
"restore cursor: %d", _cursorType);
1631 switch (_cursorType)
1634 if (!SDL_HideCursor())
1636 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_HideCursor failed");
1640 setHasCursor(
false);
1643 case CURSOR_DEFAULT:
1645 auto def = SDL_GetDefaultCursor();
1646 if (!SDL_SetCursor(def))
1648 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_SetCursor(default=%p) failed",
1649 static_cast<void*
>(def));
1652 if (!SDL_ShowCursor())
1654 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_ShowCursor failed");
1662 return sdl_Pointer_Set_Process(
this);
1664 WLog_Print(getWLog(), WLOG_ERROR,
"Unknown cursorType %s",
1665 sdl::utils::toString(_cursorType).c_str());
1670void SdlContext::setMonitorIds(
const std::vector<SDL_DisplayID>& ids)
1672 _monitorIds.clear();
1675 _monitorIds.push_back(
id);
1679const std::vector<SDL_DisplayID>& SdlContext::monitorIds()
const
1684int64_t SdlContext::monitorId(uint32_t index)
const
1686 if (index >= _monitorIds.size())
1690 return _monitorIds.at(index);
1693void SdlContext::push(std::vector<SDL_Rect>&& rects)
1695 std::unique_lock lock(_queue_mux);
1696 _queue.emplace(std::move(rects));
1699std::vector<SDL_Rect> SdlContext::pop()
1701 std::unique_lock lock(_queue_mux);
1706 auto val = std::move(_queue.front());
1711bool SdlContext::setFullscreen(
bool enter,
bool forceOriginalDisplay)
1713 for (
const auto& window : _windows)
1715 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_FULLSCREEN, &window.second, enter,
1716 forceOriginalDisplay))
1719 _fullscreen = enter;
1723bool SdlContext::setMinimized()
1725 return sdl_push_user_event(SDL_EVENT_USER_WINDOW_MINIMIZE);
1728bool SdlContext::grabMouse()
const
1733bool SdlContext::toggleGrabMouse()
1735 return setGrabMouse(!grabMouse());
1738bool SdlContext::setGrabMouse(
bool enter)
1744bool SdlContext::grabKeyboard()
const
1746 return _grabKeyboard;
1749bool SdlContext::toggleGrabKeyboard()
1751 return setGrabKeyboard(!grabKeyboard());
1754bool SdlContext::setGrabKeyboard(
bool enter)
1756 _grabKeyboard = enter;
1760bool SdlContext::setResizeable(
bool enable)
1762 const auto settings = context()->settings;
1765 bool use = (dyn && enable) || smart;
1767 for (
const auto& window : _windows)
1769 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_RESIZEABLE, &window.second, use))
1777bool SdlContext::resizeable()
const
1782bool SdlContext::toggleResizeable()
1784 return setResizeable(!resizeable());
1787bool SdlContext::fullscreen()
const
1792bool SdlContext::toggleFullscreen()
1794 return setFullscreen(!fullscreen());
object that handles clipboard context for the SDL3 client
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_server_name(const rdpSettings *settings)
A helper function to return the correct server name.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 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_set_monitor_def_array_sorted(rdpSettings *settings, const rdpMonitor *monitors, size_t count)
Sort monitor array according to:
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.