22#include "sdl_context.hpp"
23#include "sdl_config.hpp"
24#include "sdl_channels.hpp"
25#include "sdl_monitor.hpp"
26#include "sdl_pointer.hpp"
27#include "sdl_touch.hpp"
29#include <sdl_common_utils.hpp>
30#include <scoped_guard.hpp>
32#include "dialogs/sdl_dialogs.hpp"
34#if defined(WITH_WEBVIEW)
35#include <aad/sdl_webview.hpp>
39 : _context(context), _log(WLog_Get(CLIENT_TAG(
"SDL"))), _cursor(nullptr, sdl_Pointer_FreeCopy),
40 _rdpThreadRunning(false), _primary(nullptr, SDL_DestroySurface), _disp(this), _input(this),
41 _clip(this), _dialog(_log)
43 WINPR_ASSERT(context);
46 auto instance = _context->instance;
47 WINPR_ASSERT(instance);
49 instance->PreConnect = preConnect;
50 instance->PostConnect = postConnect;
51 instance->PostDisconnect = postDisconnect;
52 instance->PostFinalDisconnect = postFinalDisconnect;
53 instance->AuthenticateEx = sdl_authenticate_ex;
54 instance->VerifyCertificateEx = sdl_verify_certificate_ex;
55 instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
56 instance->LogonErrorInfo = sdl_logon_error_info;
57 instance->PresentGatewayMessage = sdl_present_gateway_message;
58 instance->ChooseSmartcard = sdl_choose_smartcard;
59 instance->RetryDialog = sdl_retry_dialog;
62 instance->GetAccessToken = sdl_webview_get_access_token;
64 instance->GetAccessToken = client_cli_get_access_token;
69void SdlContext::setHasCursor(
bool val)
71 this->_cursor_visible = val;
74bool SdlContext::hasCursor()
const
76 return _cursor_visible;
79void SdlContext::setMetadata()
82 if (!wmclass || (strlen(wmclass) == 0))
83 wmclass = SDL_CLIENT_UUID;
85 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, wmclass);
86 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, SDL_CLIENT_NAME);
87 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, SDL_CLIENT_VERSION);
88 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, SDL_CLIENT_VENDOR);
89 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, SDL_CLIENT_COPYRIGHT);
90 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, SDL_CLIENT_URL);
91 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, SDL_CLIENT_TYPE);
94int SdlContext::start()
96 _thread = std::thread(rdpThreadRun,
this);
100int SdlContext::join()
104 HANDLE
event = freerdp_abort_event(context());
105 if (!SetEvent(event))
112void SdlContext::cleanup()
114 std::unique_lock lock(_critical);
120bool SdlContext::shallAbort(
bool ignoreDialogs)
122 std::unique_lock lock(_critical);
123 if (freerdp_shall_disconnect_context(context()))
127 if (_rdpThreadRunning)
129 return !getDialog().isRunning();
136BOOL SdlContext::preConnect(freerdp* instance)
138 WINPR_ASSERT(instance);
139 WINPR_ASSERT(instance->context);
141 auto sdl = get_context(instance->context);
143 auto settings = instance->context->settings;
144 WINPR_ASSERT(settings);
159 if (PubSub_SubscribeChannelConnected(instance->context->pubSub,
160 sdl_OnChannelConnectedEventHandler) < 0)
162 if (PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
163 sdl_OnChannelDisconnectedEventHandler) < 0)
169 UINT32 maxHeight = 0;
171 if (!sdl_detect_monitors(sdl, &maxWidth, &maxHeight))
174 if ((maxWidth != 0) && (maxHeight != 0) &&
177 WLog_Print(sdl->getWLog(), WLOG_INFO,
"Update size to %ux%u", maxWidth, maxHeight);
192 if (sm && (sw > 0) && (sh > 0))
196 WLog_Print(sdl->getWLog(), WLOG_WARN,
197 "/smart-sizing and /multimon are currently not supported, ignoring "
216 WLog_Print(sdl->getWLog(), WLOG_INFO,
217 "auth-only, but no password set. Please provide one.");
224 WLog_Print(sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect SDL.");
227 if (!sdl->getInputChannelContext().initialize())
242BOOL SdlContext::postConnect(freerdp* instance)
244 WINPR_ASSERT(instance);
246 auto context = instance->context;
247 WINPR_ASSERT(context);
249 auto sdl = get_context(context);
252 sdl->getDialog().show(
false);
259 WLog_Print(sdl->getWLog(), WLOG_INFO,
260 "auth-only, but no password set. Please provide one.");
264 WLog_Print(sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect to X.");
268 if (!sdl->waitForWindowsCreated())
271 sdl->_sdlPixelFormat = SDL_PIXELFORMAT_BGRA32;
272 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
275 if (!sdl->createPrimary())
278 if (!sdl_register_pointer(instance->context->graphics))
281 WINPR_ASSERT(context->update);
283 context->update->BeginPaint = beginPaint;
284 context->update->EndPaint = endPaint;
285 context->update->PlaySound = playSound;
286 context->update->DesktopResize = desktopResize;
287 context->update->SetKeyboardIndicators = sdlInput::keyboard_set_indicators;
288 context->update->SetKeyboardImeStatus = sdlInput::keyboard_set_ime_status;
290 if (!sdl->setResizeable(
false))
296 sdl->setConnected(
true);
303void SdlContext::postDisconnect(freerdp* instance)
308 if (!instance->context)
311 auto sdl = get_context(instance->context);
312 sdl->setConnected(
false);
317void SdlContext::postFinalDisconnect(freerdp* instance)
322 if (!instance->context)
325 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
326 sdl_OnChannelConnectedEventHandler);
327 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
328 sdl_OnChannelDisconnectedEventHandler);
332bool SdlContext::createPrimary()
334 auto gdi = context()->gdi;
337 _primary = SDLSurfacePtr(
338 SDL_CreateSurfaceFrom(
static_cast<int>(gdi->width),
static_cast<int>(gdi->height),
339 pixelFormat(), gdi->primary_buffer,
static_cast<int>(gdi->stride)),
344 SDL_SetSurfaceBlendMode(_primary.get(), SDL_BLENDMODE_NONE);
345 SDL_Rect surfaceRect = { 0, 0, gdi->width, gdi->height };
346 SDL_FillSurfaceRect(_primary.get(), &surfaceRect,
347 SDL_MapSurfaceRGBA(_primary.get(), 0, 0, 0, 0xff));
352bool SdlContext::createWindows()
354 auto settings = context()->settings;
355 const auto& title = windowTitle();
357 ScopeGuard guard1([&]() { _windowsCreatedEvent.set(); });
363 for (UINT32 x = 0; x < windowCount; x++)
365 auto id = monitorId(x);
370 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
372 originX = std::min<Sint32>(monitor->x, originX);
373 originY = std::min<Sint32>(monitor->y, originY);
376 for (UINT32 x = 0; x < windowCount; x++)
378 auto id = monitorId(x);
383 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
385 Uint32 w = WINPR_ASSERTING_INT_CAST(Uint32, monitor->width);
386 Uint32 h = WINPR_ASSERTING_INT_CAST(Uint32, monitor->height);
390 if (_windowWidth > 0)
395 if (_windowHeigth > 0)
401 Uint32 flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
406 flags |= SDL_WINDOW_FULLSCREEN;
411 flags |= SDL_WINDOW_BORDERLESS;
415 flags |= SDL_WINDOW_BORDERLESS;
417 auto did = WINPR_ASSERTING_INT_CAST(SDL_DisplayID,
id);
418 auto window = SdlWindow::create(did, title, flags, w, h);
422 window.setOffsetX(originX - monitor->x);
423 window.setOffsetY(originY - monitor->y);
426 _windows.insert({ window.id(), std::move(window) });
432bool SdlContext::updateWindowList()
434 std::vector<rdpMonitor> list;
435 list.reserve(_windows.size());
436 for (
const auto& win : _windows)
437 list.push_back(win.second.monitor(_windows.size() == 1));
443bool SdlContext::updateWindow(SDL_WindowID
id)
449 auto& w = _windows.at(
id);
450 auto m = w.monitor(
true);
454 m.attributes.physicalWidth =
static_cast<UINT32
>(r.w);
455 m.attributes.physicalHeight =
static_cast<UINT32
>(r.h);
460std::string SdlContext::windowTitle()
const
462 const char* prefix =
"FreeRDP:";
470 const auto addPort = (port != 3389);
472 std::stringstream ss;
473 ss << prefix <<
" " << name;
481bool SdlContext::waitForWindowsCreated()
484 std::unique_lock<CriticalSection> lock(_critical);
485 _windowsCreatedEvent.clear();
486 if (!sdl_push_user_event(SDL_EVENT_USER_CREATE_WINDOWS,
this))
490 HANDLE handles[] = { _windowsCreatedEvent.handle(), freerdp_abort_event(context()) };
492 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
506BOOL SdlContext::endPaint(rdpContext* context)
508 auto sdl = get_context(context);
511 auto gdi = context->gdi;
513 WINPR_ASSERT(gdi->primary);
515 HGDI_DC hdc = gdi->primary->hdc;
521 WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
523 if (hwnd->invalid->null)
526 WINPR_ASSERT(hwnd->invalid);
527 if (gdi->suppressOutput || hwnd->invalid->null)
530 const INT32 ninvalid = hwnd->ninvalid;
531 const GDI_RGN* cinvalid = hwnd->cinvalid;
536 std::vector<SDL_Rect> rects;
537 for (INT32 x = 0; x < ninvalid; x++)
539 auto& rgn = cinvalid[x];
540 rects.push_back({ rgn.x, rgn.y, rgn.w, rgn.h });
543 sdl->push(std::move(rects));
544 return sdl_push_user_event(SDL_EVENT_USER_UPDATE);
547void SdlContext::sdl_client_cleanup(
int exit_code,
const std::string& error_msg)
549 rdpSettings* settings = context()->settings;
550 WINPR_ASSERT(settings);
552 _rdpThreadRunning =
false;
553 bool showError =
false;
555 WLog_Print(getWLog(), WLOG_INFO,
"Authentication only, exit status %s [%" PRId32
"]",
556 sdl::error::exitCodeToTag(exit_code), exit_code);
561 case sdl::error::SUCCESS:
562 case sdl::error::DISCONNECT:
563 case sdl::error::LOGOFF:
564 case sdl::error::DISCONNECT_BY_USER:
565 case sdl::error::CONNECT_CANCELLED:
569 getDialog().showError(error_msg);
576 getDialog().show(
false);
578 _exitCode = exit_code;
579 std::ignore = sdl_push_user_event(SDL_EVENT_USER_QUIT);
583int SdlContext::sdl_client_thread_connect(std::string& error_msg)
585 auto instance = context()->instance;
586 WINPR_ASSERT(instance);
588 _rdpThreadRunning =
true;
589 BOOL rc = freerdp_connect(instance);
591 rdpSettings* settings = context()->settings;
592 WINPR_ASSERT(settings);
594 int exit_code = sdl::error::SUCCESS;
597 UINT32 error = freerdp_get_last_error(context());
598 exit_code = sdl::error::errorToExitCode(error);
603 DWORD code = freerdp_get_last_error(context());
604 freerdp_abort_connect_context(context());
605 WLog_Print(getWLog(), WLOG_ERROR,
"Authentication only, %s [0x%08" PRIx32
"] %s",
606 freerdp_get_last_error_name(code), code, freerdp_get_last_error_string(code));
612 DWORD code = freerdp_error_info(instance);
613 if (exit_code == sdl::error::SUCCESS)
617 exit_code = error_info_to_error(&code, &msg, &len);
623 auto last = freerdp_get_last_error(context());
624 if (error_msg.empty())
628 winpr_asprintf(&msg, &len,
"%s [0x%08" PRIx32
"]\n%s",
629 freerdp_get_last_error_name(last), last,
630 freerdp_get_last_error_string(last));
636 if (exit_code == sdl::error::SUCCESS)
638 if (last == FREERDP_ERROR_AUTHENTICATION_FAILED)
639 exit_code = sdl::error::AUTH_FAILURE;
640 else if (code == ERRINFO_SUCCESS)
641 exit_code = sdl::error::CONN_FAILED;
644 getDialog().show(
false);
650int SdlContext::sdl_client_thread_run(std::string& error_msg)
652 auto instance = context()->instance;
653 WINPR_ASSERT(instance);
655 int exit_code = sdl::error::SUCCESS;
656 while (!freerdp_shall_disconnect_context(context()))
658 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
664 if (freerdp_focus_required(instance))
666 auto ctx = get_context(context());
669 auto& input = ctx->getInputChannelContext();
670 if (!input.keyboard_focus_in())
672 if (!input.keyboard_focus_in())
676 const DWORD nCount = freerdp_get_event_handles(context(), handles, ARRAYSIZE(handles));
680 WLog_Print(getWLog(), WLOG_ERROR,
"freerdp_get_event_handles failed");
684 const DWORD status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
686 if (status == WAIT_FAILED)
688 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects WAIT_FAILED");
692 if (!freerdp_check_event_handles(context()))
694 if (client_auto_reconnect(instance))
697 getDialog().show(
false);
706 if (freerdp_error_info(instance) == 0)
707 exit_code = sdl::error::CONN_FAILED;
710 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
711 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects failed with %" PRIu32
"",
713 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
714 WLog_Print(getWLog(), WLOG_ERROR,
"Failed to check FreeRDP event handles");
719 if (exit_code == sdl::error::SUCCESS)
723 char* emsg =
nullptr;
725 exit_code = error_info_to_error(&code, &emsg, &elen);
731 if ((code == ERRINFO_LOGOFF_BY_USER) &&
732 (freerdp_get_disconnect_ultimatum(context()) == Disconnect_Ultimatum_user_requested))
734 const char* msg =
"Error info says user did not initiate but disconnect ultimatum says "
735 "they did; treat this as a user logoff";
737 char* emsg =
nullptr;
739 winpr_asprintf(&emsg, &elen,
"%s", msg);
745 WLog_Print(getWLog(), WLOG_INFO,
"%s", msg);
746 exit_code = sdl::error::LOGOFF;
750 freerdp_disconnect(instance);
758DWORD SdlContext::rdpThreadRun(
SdlContext* sdl)
762 std::string error_msg;
763 int exit_code = sdl->sdl_client_thread_connect(error_msg);
764 if (exit_code == sdl::error::SUCCESS)
765 exit_code = sdl->sdl_client_thread_run(error_msg);
766 sdl->sdl_client_cleanup(exit_code, error_msg);
768 return static_cast<DWORD
>(exit_code);
771int SdlContext::error_info_to_error(DWORD* pcode,
char** msg,
size_t* len)
const
773 const DWORD code = freerdp_error_info(context()->instance);
774 const char* name = freerdp_get_error_info_name(code);
775 const char* str = freerdp_get_error_info_string(code);
776 const int exit_code = sdl::error::errorToExitCode(code);
778 winpr_asprintf(msg, len,
"Terminate with %s due to ERROR_INFO %s [0x%08" PRIx32
"]: %s",
779 sdl::error::errorToExitCodeTag(code), name, code, str);
780 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%s", *msg);
786void SdlContext::applyMonitorOffset(SDL_WindowID window,
float& x,
float& y)
const
791 auto w = getWindowForId(window);
792 x -=
static_cast<float>(w->offsetX());
793 y -=
static_cast<float>(w->offsetY());
796static bool alignX(
const SDL_Rect& a,
const SDL_Rect& b)
798 if (a.x + a.w == b.x)
800 if (b.x + b.w == a.x)
805static bool alignY(
const SDL_Rect& a,
const SDL_Rect& b)
807 if (a.y + a.h == b.y)
809 if (b.y + b.h == a.y)
814std::vector<SDL_DisplayID>
815SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID
id,
816 const std::vector<SDL_DisplayID>& ignore)
818 auto first = _offsets.at(
id);
819 std::vector<SDL_DisplayID> neighbours;
821 for (
auto& entry : _offsets)
823 if (entry.first ==
id)
825 if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end())
828 bool neighbor =
false;
829 if (alignX(entry.second.first, first.first))
831 if (entry.second.first.x < first.first.x)
832 entry.second.second.x = first.second.x - entry.second.second.w;
834 entry.second.second.x = first.second.x + first.second.w;
837 if (alignY(entry.second.first, first.first))
839 if (entry.second.first.y < first.first.y)
840 entry.second.second.y = first.second.y - entry.second.second.h;
842 entry.second.second.y = first.second.y + first.second.h;
847 neighbours.push_back(entry.first);
852void SdlContext::updateMonitorDataFromOffsets()
854 for (
auto& entry : _displays)
856 auto offsets = _offsets.at(entry.first);
857 entry.second.x = offsets.second.x;
858 entry.second.y = offsets.second.y;
861 for (
auto& entry : _windows)
863 const auto& monitor = _displays.at(entry.first);
864 entry.second.setMonitor(monitor);
868bool SdlContext::drawToWindow(
SdlWindow& window,
const std::vector<SDL_Rect>& rects)
873 auto gdi = context()->gdi;
876 auto size = window.rect();
878 std::unique_lock lock(_critical);
879 auto surface = _primary.get();
883 window.setOffsetX(0);
884 window.setOffsetY(0);
885 if (gdi->width < size.w)
887 window.setOffsetX((size.w - gdi->width) / 2);
889 if (gdi->height < size.h)
891 window.setOffsetY((size.h - gdi->height) / 2);
894 _localScale = {
static_cast<float>(size.w) /
static_cast<float>(gdi->width),
895 static_cast<float>(size.h) /
static_cast<float>(gdi->height) };
896 if (!window.drawScaledRects(surface, _localScale, rects))
901 SDL_Point offset{ 0, 0 };
903 offset = { window.offsetX(), window.offsetY() };
904 if (!window.drawRects(surface, offset, rects))
908 window.updateSurface();
912bool SdlContext::minimizeAllWindows()
914 for (
auto& w : _windows)
919int SdlContext::exitCode()
const
924SDL_PixelFormat SdlContext::pixelFormat()
const
926 return _sdlPixelFormat;
929bool SdlContext::addDisplayWindow(SDL_DisplayID
id)
932 SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
933 auto title = sdl::utils::windowTitle(context()->settings);
934 auto w = SdlWindow::create(
id, title, flags);
935 _windows.emplace(w.id(), std::move(w));
939bool SdlContext::removeDisplayWindow(SDL_DisplayID
id)
941 for (
auto& w : _windows)
943 if (w.second.displayIndex() ==
id)
944 _windows.erase(w.first);
949bool SdlContext::detectDisplays()
952 auto display = SDL_GetDisplays(&count);
955 for (
int x = 0; x < count; x++)
957 const auto id = display[x];
958 addOrUpdateDisplay(
id);
964rdpMonitor SdlContext::getDisplay(SDL_DisplayID
id)
const
966 return _displays.at(
id);
969std::vector<SDL_DisplayID> SdlContext::getDisplayIds()
const
971 std::vector<SDL_DisplayID> keys;
972 keys.reserve(_displays.size());
973 for (
const auto& entry : _displays)
975 keys.push_back(entry.first);
980const SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
const
982 auto it = _windows.find(
id);
983 if (it == _windows.end())
988SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
990 auto it = _windows.find(
id);
991 if (it == _windows.end())
998 if (_windows.empty())
1000 return &_windows.begin()->second;
1008sdlInput& SdlContext::getInputChannelContext()
1013sdlClip& SdlContext::getClipboardChannelContext()
1023wLog* SdlContext::getWLog()
1028bool SdlContext::moveMouseTo(
const SDL_FPoint& pos)
1030 auto window = SDL_GetMouseFocus();
1034 const auto id = SDL_GetWindowID(window);
1035 const auto spos = pixelToScreen(
id, pos);
1036 SDL_WarpMouseInWindow(window, spos.x, spos.y);
1040bool SdlContext::handleEvent(
const SDL_MouseMotionEvent& ev)
1044 if (!eventToPixelCoordinates(ev.windowID, copy))
1046 removeLocalScaling(copy.motion.x, copy.motion.y);
1047 removeLocalScaling(copy.motion.xrel, copy.motion.yrel);
1048 applyMonitorOffset(copy.motion.windowID, copy.motion.x, copy.motion.y);
1050 return SdlTouch::handleEvent(
this, copy.motion);
1053bool SdlContext::handleEvent(
const SDL_MouseWheelEvent& ev)
1057 if (!eventToPixelCoordinates(ev.windowID, copy))
1059 removeLocalScaling(copy.wheel.mouse_x, copy.wheel.mouse_y);
1060 return SdlTouch::handleEvent(
this, copy.wheel);
1063bool SdlContext::handleEvent(
const SDL_WindowEvent& ev)
1065 if (!getDisplayChannelContext().handleEvent(ev))
1068 auto window = getWindowForId(ev.windowID);
1073 const auto& r = window->rect();
1074 const auto& b = window->bounds();
1075 const auto& scale = window->scale();
1076 const auto& orientation = window->orientation();
1077 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1078 "%s: [%u] %dx%d-%dx%d {%dx%d-%dx%d}{scale=%f,orientation=%s}",
1079 sdl::utils::toString(ev.type).c_str(), ev.windowID, r.x, r.y, r.w, r.h, b.x,
1080 b.y, b.w, b.h,
static_cast<double>(scale),
1081 sdl::utils::toString(orientation).c_str());
1086 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1087 return restoreCursor();
1088 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1091 if (!window->fill())
1093 if (!drawToWindow(*window))
1095 if (!restoreCursor())
1099 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1100 if (!window->fill())
1102 if (!drawToWindow(*window))
1104 if (!restoreCursor())
1107 case SDL_EVENT_WINDOW_MOVED:
1109 auto r = window->rect();
1110 auto id = window->id();
1111 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%u: %dx%d-%dx%d",
id, r.x, r.y, r.w, r.h);
1114 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1116 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"Window closed, terminating RDP session...");
1117 freerdp_abort_connect_context(context());
1126bool SdlContext::handleEvent(
const SDL_DisplayEvent& ev)
1128 if (!getDisplayChannelContext().handleEvent(ev))
1133 case SDL_EVENT_DISPLAY_REMOVED:
1138 if (!SDL_GetDisplayBounds(ev.displayID, &r))
1140 const auto name = SDL_GetDisplayName(ev.displayID);
1143 const auto orientation = SDL_GetCurrentDisplayOrientation(ev.displayID);
1144 const auto scale = SDL_GetDisplayContentScale(ev.displayID);
1145 const auto mode = SDL_GetCurrentDisplayMode(ev.displayID);
1149 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1150 "%s: [%u, %s] %dx%d-%dx%d {orientation=%s, scale=%f}%s",
1151 sdl::utils::toString(ev.type).c_str(), ev.displayID, name, r.x, r.y, r.w,
1152 r.h, sdl::utils::toString(orientation).c_str(),
static_cast<double>(scale),
1153 sdl::utils::toString(mode).c_str());
1160bool SdlContext::handleEvent(
const SDL_MouseButtonEvent& ev)
1162 SDL_Event copy = {};
1164 if (!eventToPixelCoordinates(ev.windowID, copy))
1166 removeLocalScaling(copy.button.x, copy.button.y);
1167 applyMonitorOffset(copy.button.windowID, copy.button.x, copy.button.y);
1168 return SdlTouch::handleEvent(
this, copy.button);
1171bool SdlContext::handleEvent(
const SDL_TouchFingerEvent& ev)
1175 if (!eventToPixelCoordinates(ev.windowID, copy))
1177 removeLocalScaling(copy.tfinger.dx, copy.tfinger.dy);
1178 removeLocalScaling(copy.tfinger.x, copy.tfinger.y);
1179 applyMonitorOffset(copy.tfinger.windowID, copy.tfinger.x, copy.tfinger.y);
1180 return SdlTouch::handleEvent(
this, copy.tfinger);
1183void SdlContext::addOrUpdateDisplay(SDL_DisplayID
id)
1185 auto monitor = SdlWindow::query(
id,
false);
1186 _displays.emplace(
id, monitor);
1196 for (
auto& entry : _displays)
1199 std::ignore = SDL_GetDisplayBounds(entry.first, &bounds);
1202 pixel.w = entry.second.width;
1203 pixel.h = entry.second.height;
1204 _offsets.emplace(entry.first, std::pair{ bounds, pixel });
1211 const auto primary = SDL_GetPrimaryDisplay();
1212 std::vector<SDL_DisplayID> handled;
1213 handled.push_back(primary);
1215 auto neighbors = updateDisplayOffsetsForNeighbours(primary);
1216 while (!neighbors.empty())
1218 auto neighbor = *neighbors.begin();
1219 neighbors.pop_back();
1221 if (std::find(handled.begin(), handled.end(), neighbor) != handled.end())
1223 handled.push_back(neighbor);
1225 auto next = updateDisplayOffsetsForNeighbours(neighbor, handled);
1226 neighbors.insert(neighbors.end(), next.begin(), next.end());
1228 updateMonitorDataFromOffsets();
1231void SdlContext::deleteDisplay(SDL_DisplayID
id)
1233 _displays.erase(
id);
1236bool SdlContext::eventToPixelCoordinates(SDL_WindowID
id, SDL_Event& ev)
1238 auto w = getWindowForId(
id);
1243 auto renderer = SDL_GetRenderer(w->window());
1246 return SDL_ConvertEventToRenderCoordinates(renderer, &ev);
1249SDL_FPoint SdlContext::applyLocalScaling(
const SDL_FPoint& val)
const
1251 if (!useLocalScale())
1255 rval.x *= _localScale.x;
1256 rval.y *= _localScale.y;
1260void SdlContext::removeLocalScaling(
float& x,
float& y)
const
1262 if (!useLocalScale())
1268SDL_FPoint SdlContext::screenToPixel(SDL_WindowID
id,
const SDL_FPoint& pos)
1270 auto w = getWindowForId(
id);
1275 auto renderer = SDL_GetRenderer(w->window());
1280 if (!SDL_RenderCoordinatesFromWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1282 removeLocalScaling(rpos.x, rpos.y);
1286SDL_FPoint SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FPoint& pos)
1288 auto w = getWindowForId(
id);
1293 auto renderer = SDL_GetRenderer(w->window());
1298 if (!SDL_RenderCoordinatesToWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1300 return applyLocalScaling(rpos);
1303SDL_FRect SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FRect& pos)
1305 const auto fpos = pixelToScreen(
id, SDL_FPoint{ pos.x, pos.y });
1306 const auto size = pixelToScreen(
id, SDL_FPoint{ pos.w, pos.h });
1307 return { fpos.x, fpos.y, size.x, size.y };
1310bool SdlContext::handleEvent(
const SDL_Event& ev)
1312 if ((ev.type >= SDL_EVENT_DISPLAY_FIRST) && (ev.type <= SDL_EVENT_DISPLAY_LAST))
1314 const auto& dev = ev.display;
1315 return handleEvent(dev);
1317 if ((ev.type >= SDL_EVENT_WINDOW_FIRST) && (ev.type <= SDL_EVENT_WINDOW_LAST))
1319 const auto& wev = ev.window;
1320 return handleEvent(wev);
1324 case SDL_EVENT_RENDER_TARGETS_RESET:
1325 case SDL_EVENT_RENDER_DEVICE_RESET:
1326 case SDL_EVENT_WILL_ENTER_FOREGROUND:
1337 case SDL_EVENT_FINGER_DOWN:
1338 case SDL_EVENT_FINGER_UP:
1339 case SDL_EVENT_FINGER_MOTION:
1341 const auto& cev = ev.tfinger;
1342 return handleEvent(cev);
1344 case SDL_EVENT_MOUSE_MOTION:
1347 const auto& cev = ev.motion;
1348 return handleEvent(cev);
1350 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1351 case SDL_EVENT_MOUSE_BUTTON_UP:
1353 const auto& cev = ev.button;
1354 return handleEvent(cev);
1356 case SDL_EVENT_MOUSE_WHEEL:
1358 const auto& cev = ev.wheel;
1359 return handleEvent(cev);
1361 case SDL_EVENT_CLIPBOARD_UPDATE:
1363 const auto& cev = ev.clipboard;
1364 return getClipboardChannelContext().handleEvent(cev);
1366 case SDL_EVENT_KEY_DOWN:
1367 case SDL_EVENT_KEY_UP:
1369 const auto& cev = ev.key;
1370 return getInputChannelContext().handleEvent(cev);
1377bool SdlContext::useLocalScale()
const
1382 const auto dynResize =
1386 return !dynResize && !fs && !multimon;
1389bool SdlContext::drawToWindows(
const std::vector<SDL_Rect>& rects)
1391 for (
auto& window : _windows)
1393 if (!drawToWindow(window.second, rects))
1400BOOL SdlContext::desktopResize(rdpContext* context)
1402 rdpGdi* gdi =
nullptr;
1403 rdpSettings* settings =
nullptr;
1404 auto sdl = get_context(context);
1407 WINPR_ASSERT(context);
1409 settings = context->settings;
1410 WINPR_ASSERT(settings);
1412 std::unique_lock lock(sdl->_critical);
1417 return sdl->createPrimary();
1421BOOL SdlContext::playSound(rdpContext* context,
const PLAY_SOUND_UPDATE* play_sound)
1424 WINPR_UNUSED(context);
1425 WINPR_UNUSED(play_sound);
1431BOOL SdlContext::beginPaint(rdpContext* context)
1433 auto gdi = context->gdi;
1435 WINPR_ASSERT(gdi->primary);
1437 HGDI_DC hdc = gdi->primary->hdc;
1443 WINPR_ASSERT(hwnd->invalid);
1444 hwnd->invalid->null = TRUE;
1450bool SdlContext::redraw(
bool suppress)
const
1455 auto gdi = context()->gdi;
1457 return gdi_send_suppress_output(gdi, suppress);
1460void SdlContext::setConnected(
bool val)
1465bool SdlContext::isConnected()
const
1470rdpContext* SdlContext::context()
const
1472 WINPR_ASSERT(_context);
1476rdpClientContext* SdlContext::common()
const
1478 return reinterpret_cast<rdpClientContext*
>(context());
1481bool SdlContext::setCursor(CursorType type)
1484 return restoreCursor();
1487bool SdlContext::setCursor(
const rdpPointer* cursor)
1489 _cursor = { sdl_Pointer_Copy(cursor), sdl_Pointer_FreeCopy };
1490 return setCursor(CURSOR_IMAGE);
1493rdpPointer* SdlContext::cursor()
const
1495 return _cursor.get();
1498bool SdlContext::restoreCursor()
1500 WLog_Print(getWLog(), WLOG_DEBUG,
"restore cursor: %d", _cursorType);
1501 switch (_cursorType)
1504 if (!SDL_HideCursor())
1506 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_HideCursor failed");
1510 setHasCursor(
false);
1513 case CURSOR_DEFAULT:
1515 auto def = SDL_GetDefaultCursor();
1516 if (!SDL_SetCursor(def))
1518 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_SetCursor(default=%p) failed",
1519 static_cast<void*
>(def));
1522 if (!SDL_ShowCursor())
1524 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_ShowCursor failed");
1532 return sdl_Pointer_Set_Process(
this);
1534 WLog_Print(getWLog(), WLOG_ERROR,
"Unknown cursorType %s",
1535 sdl::utils::toString(_cursorType).c_str());
1540void SdlContext::setMonitorIds(
const std::vector<SDL_DisplayID>& ids)
1542 _monitorIds.clear();
1545 _monitorIds.push_back(
id);
1549const std::vector<SDL_DisplayID>& SdlContext::monitorIds()
const
1554int64_t SdlContext::monitorId(uint32_t index)
const
1556 if (index >= _monitorIds.size())
1560 return _monitorIds.at(index);
1563void SdlContext::push(std::vector<SDL_Rect>&& rects)
1565 std::unique_lock lock(_queue_mux);
1566 _queue.emplace(std::move(rects));
1569std::vector<SDL_Rect> SdlContext::pop()
1571 std::unique_lock lock(_queue_mux);
1576 auto val = std::move(_queue.front());
1581bool SdlContext::setFullscreen(
bool enter,
bool forceOriginalDisplay)
1583 for (
const auto& window : _windows)
1585 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_FULLSCREEN, &window.second, enter,
1586 forceOriginalDisplay))
1589 _fullscreen = enter;
1593bool SdlContext::setMinimized()
1595 return sdl_push_user_event(SDL_EVENT_USER_WINDOW_MINIMIZE);
1598bool SdlContext::grabMouse()
const
1603bool SdlContext::toggleGrabMouse()
1605 return setGrabMouse(!grabMouse());
1608bool SdlContext::setGrabMouse(
bool enter)
1614bool SdlContext::grabKeyboard()
const
1616 return _grabKeyboard;
1619bool SdlContext::toggleGrabKeyboard()
1621 return setGrabKeyboard(!grabKeyboard());
1624bool SdlContext::setGrabKeyboard(
bool enter)
1626 _grabKeyboard = enter;
1630bool SdlContext::setResizeable(
bool enable)
1632 const auto settings = context()->settings;
1635 bool use = (dyn && enable) || smart;
1637 for (
const auto& window : _windows)
1639 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_RESIZEABLE, &window.second, use))
1647bool SdlContext::resizeable()
const
1652bool SdlContext::toggleResizeable()
1654 return setResizeable(!resizeable());
1657bool SdlContext::fullscreen()
const
1662bool SdlContext::toggleFullscreen()
1664 return setFullscreen(!fullscreen());
SdlContext(rdpContext *context)
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.