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>
40 : _context(context), _log(WLog_Get(CLIENT_TAG(
"SDL"))), _cursor(nullptr, sdl_Pointer_FreeCopy),
41 _rdpThreadRunning(false), _primary(nullptr, SDL_DestroySurface), _disp(this), _input(this),
42 _clip(this), _dialog(_log)
44 WINPR_ASSERT(context);
47 auto instance = _context->instance;
48 WINPR_ASSERT(instance);
50 instance->PreConnect = preConnect;
51 instance->PostConnect = postConnect;
52 instance->PostDisconnect = postDisconnect;
53 instance->PostFinalDisconnect = postFinalDisconnect;
54 instance->AuthenticateEx = sdl_authenticate_ex;
55 instance->VerifyCertificateEx = sdl_verify_certificate_ex;
56 instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
57 instance->LogonErrorInfo = sdl_logon_error_info;
58 instance->PresentGatewayMessage = sdl_present_gateway_message;
59 instance->ChooseSmartcard = sdl_choose_smartcard;
60 instance->RetryDialog = sdl_retry_dialog;
63 instance->GetAccessToken = sdl_webview_get_access_token;
65 instance->GetAccessToken = client_cli_get_access_token;
70void SdlContext::setHasCursor(
bool val)
72 this->_cursor_visible = val;
75bool SdlContext::hasCursor()
const
77 return _cursor_visible;
80void SdlContext::setMetadata()
83 if (!wmclass || (strlen(wmclass) == 0))
84 wmclass = SDL_CLIENT_UUID;
86 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, wmclass);
87 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, SDL_CLIENT_NAME);
88 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, SDL_CLIENT_VERSION);
89 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, SDL_CLIENT_VENDOR);
90 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, SDL_CLIENT_COPYRIGHT);
91 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, SDL_CLIENT_URL);
92 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, SDL_CLIENT_TYPE);
95int SdlContext::start()
97 _thread = std::thread(rdpThreadRun,
this);
101int SdlContext::join()
105 HANDLE
event = freerdp_abort_event(context());
106 if (!SetEvent(event))
113void SdlContext::cleanup()
115 std::unique_lock lock(_critical);
121bool SdlContext::shallAbort(
bool ignoreDialogs)
123 std::unique_lock lock(_critical);
124 if (freerdp_shall_disconnect_context(context()))
128 if (_rdpThreadRunning)
130 return !getDialog().isRunning();
137BOOL SdlContext::preConnect(freerdp* instance)
139 WINPR_ASSERT(instance);
140 WINPR_ASSERT(instance->context);
142 auto sdl = get_context(instance->context);
144 auto settings = instance->context->settings;
145 WINPR_ASSERT(settings);
160 if (PubSub_SubscribeChannelConnected(instance->context->pubSub,
161 sdl_OnChannelConnectedEventHandler) < 0)
163 if (PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
164 sdl_OnChannelDisconnectedEventHandler) < 0)
166 if (PubSub_SubscribeUserNotification(instance->context->pubSub,
167 sdl_OnUserNotificationEventHandler) < 0)
173 UINT32 maxHeight = 0;
175 if (!sdl_detect_monitors(sdl, &maxWidth, &maxHeight))
178 if ((maxWidth != 0) && (maxHeight != 0) &&
181 WLog_Print(sdl->getWLog(), WLOG_INFO,
"Update size to %ux%u", maxWidth, maxHeight);
196 if (sm && (sw > 0) && (sh > 0))
200 WLog_Print(sdl->getWLog(), WLOG_WARN,
201 "/smart-sizing and /multimon are currently not supported, ignoring "
220 WLog_Print(sdl->getWLog(), WLOG_INFO,
221 "auth-only, but no password set. Please provide one.");
228 WLog_Print(sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect SDL.");
231 if (!sdl->getInputChannelContext().initialize())
246BOOL SdlContext::postConnect(freerdp* instance)
248 WINPR_ASSERT(instance);
250 auto context = instance->context;
251 WINPR_ASSERT(context);
253 auto sdl = get_context(context);
256 sdl->getDialog().show(
false);
263 WLog_Print(sdl->getWLog(), WLOG_INFO,
264 "auth-only, but no password set. Please provide one.");
268 WLog_Print(sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect to X.");
272 if (!sdl->waitForWindowsCreated())
275 sdl->_sdlPixelFormat = SDL_PIXELFORMAT_BGRA32;
276 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
279 if (!sdl->createPrimary())
282 if (!sdl_register_pointer(instance->context->graphics))
285 WINPR_ASSERT(context->update);
287 context->update->BeginPaint = beginPaint;
288 context->update->EndPaint = endPaint;
289 context->update->PlaySound = playSound;
290 context->update->DesktopResize = desktopResize;
291 context->update->SetKeyboardIndicators = sdlInput::keyboard_set_indicators;
292 context->update->SetKeyboardImeStatus = sdlInput::keyboard_set_ime_status;
294 if (!sdl->setResizeable(
false))
300 sdl->setConnected(
true);
307void SdlContext::postDisconnect(freerdp* instance)
312 if (!instance->context)
315 auto sdl = get_context(instance->context);
316 sdl->setConnected(
false);
321void SdlContext::postFinalDisconnect(freerdp* instance)
326 if (!instance->context)
329 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
330 sdl_OnChannelConnectedEventHandler);
331 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
332 sdl_OnChannelDisconnectedEventHandler);
333 PubSub_UnsubscribeUserNotification(instance->context->pubSub,
334 sdl_OnUserNotificationEventHandler);
338bool SdlContext::createPrimary()
340 auto gdi = context()->gdi;
343 _primary = SDLSurfacePtr(
344 SDL_CreateSurfaceFrom(
static_cast<int>(gdi->width),
static_cast<int>(gdi->height),
345 pixelFormat(), gdi->primary_buffer,
static_cast<int>(gdi->stride)),
350 SDL_SetSurfaceBlendMode(_primary.get(), SDL_BLENDMODE_NONE);
351 SDL_Rect surfaceRect = { 0, 0, gdi->width, gdi->height };
352 SDL_FillSurfaceRect(_primary.get(), &surfaceRect,
353 SDL_MapSurfaceRGBA(_primary.get(), 0, 0, 0, 0xff));
358bool SdlContext::createWindows()
360 auto settings = context()->settings;
361 const auto& title = windowTitle();
363 ScopeGuard guard1([&]() { _windowsCreatedEvent.set(); });
369 for (UINT32 x = 0; x < windowCount; x++)
371 auto id = monitorId(x);
376 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
378 originX = std::min<Sint32>(monitor->x, originX);
379 originY = std::min<Sint32>(monitor->y, originY);
382 for (UINT32 x = 0; x < windowCount; x++)
384 auto id = monitorId(x);
389 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
391 Uint32 w = WINPR_ASSERTING_INT_CAST(Uint32, monitor->width);
392 Uint32 h = WINPR_ASSERTING_INT_CAST(Uint32, monitor->height);
396 if (_windowWidth > 0)
401 if (_windowHeigth > 0)
407 Uint32 flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
412 flags |= SDL_WINDOW_FULLSCREEN;
417 flags |= SDL_WINDOW_BORDERLESS;
421 flags |= SDL_WINDOW_BORDERLESS;
423 auto did = WINPR_ASSERTING_INT_CAST(SDL_DisplayID,
id);
424 auto window = SdlWindow::create(did, title, flags, w, h);
428 window.setOffsetX(originX - monitor->x);
429 window.setOffsetY(originY - monitor->y);
432 _windows.insert({ window.id(), std::move(window) });
438bool SdlContext::updateWindowList()
440 std::vector<rdpMonitor> list;
441 list.reserve(_windows.size());
442 for (
const auto& win : _windows)
443 list.push_back(win.second.monitor(_windows.size() == 1));
449bool SdlContext::updateWindow(SDL_WindowID
id)
455 auto& w = _windows.at(
id);
456 auto m = w.monitor(
true);
460 m.attributes.physicalWidth =
static_cast<UINT32
>(r.w);
461 m.attributes.physicalHeight =
static_cast<UINT32
>(r.h);
466std::string SdlContext::windowTitle()
const
468 const char* prefix =
"FreeRDP:";
476 const auto addPort = (port != 3389);
478 std::stringstream ss;
479 ss << prefix <<
" " << name;
487bool SdlContext::waitForWindowsCreated()
490 std::unique_lock<CriticalSection> lock(_critical);
491 _windowsCreatedEvent.clear();
492 if (!sdl_push_user_event(SDL_EVENT_USER_CREATE_WINDOWS,
this))
496 HANDLE handles[] = { _windowsCreatedEvent.handle(), freerdp_abort_event(context()) };
498 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
512BOOL SdlContext::endPaint(rdpContext* context)
514 auto sdl = get_context(context);
517 auto gdi = context->gdi;
519 WINPR_ASSERT(gdi->primary);
521 HGDI_DC hdc = gdi->primary->hdc;
527 WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
529 if (hwnd->invalid->null)
532 WINPR_ASSERT(hwnd->invalid);
533 if (gdi->suppressOutput || hwnd->invalid->null)
536 const INT32 ninvalid = hwnd->ninvalid;
537 const GDI_RGN* cinvalid = hwnd->cinvalid;
542 std::vector<SDL_Rect> rects;
543 for (INT32 x = 0; x < ninvalid; x++)
545 auto& rgn = cinvalid[x];
546 rects.push_back({ rgn.x, rgn.y, rgn.w, rgn.h });
549 sdl->push(std::move(rects));
550 return sdl_push_user_event(SDL_EVENT_USER_UPDATE);
553void SdlContext::sdl_client_cleanup(
int exit_code,
const std::string& error_msg)
555 rdpSettings* settings = context()->settings;
556 WINPR_ASSERT(settings);
558 _rdpThreadRunning =
false;
559 bool showError =
false;
561 WLog_Print(getWLog(), WLOG_INFO,
"Authentication only, exit status %s [%" PRId32
"]",
562 sdl::error::exitCodeToTag(exit_code), exit_code);
567 case sdl::error::SUCCESS:
568 case sdl::error::DISCONNECT:
569 case sdl::error::LOGOFF:
570 case sdl::error::DISCONNECT_BY_USER:
571 case sdl::error::CONNECT_CANCELLED:
575 getDialog().showError(error_msg);
582 getDialog().show(
false);
584 _exitCode = exit_code;
585 std::ignore = sdl_push_user_event(SDL_EVENT_USER_QUIT);
589int SdlContext::sdl_client_thread_connect(std::string& error_msg)
591 auto instance = context()->instance;
592 WINPR_ASSERT(instance);
594 _rdpThreadRunning =
true;
595 BOOL rc = freerdp_connect(instance);
597 rdpSettings* settings = context()->settings;
598 WINPR_ASSERT(settings);
600 int exit_code = sdl::error::SUCCESS;
603 UINT32 error = freerdp_get_last_error(context());
604 exit_code = sdl::error::errorToExitCode(error);
609 DWORD code = freerdp_get_last_error(context());
610 freerdp_abort_connect_context(context());
611 WLog_Print(getWLog(), WLOG_ERROR,
"Authentication only, %s [0x%08" PRIx32
"] %s",
612 freerdp_get_last_error_name(code), code, freerdp_get_last_error_string(code));
618 DWORD code = freerdp_error_info(instance);
619 if (exit_code == sdl::error::SUCCESS)
623 exit_code = error_info_to_error(&code, &msg, &len);
629 auto last = freerdp_get_last_error(context());
630 if (error_msg.empty())
634 winpr_asprintf(&msg, &len,
"%s [0x%08" PRIx32
"]\n%s",
635 freerdp_get_last_error_name(last), last,
636 freerdp_get_last_error_string(last));
642 if (exit_code == sdl::error::SUCCESS)
644 if (last == FREERDP_ERROR_AUTHENTICATION_FAILED)
645 exit_code = sdl::error::AUTH_FAILURE;
646 else if (code == ERRINFO_SUCCESS)
647 exit_code = sdl::error::CONN_FAILED;
650 getDialog().show(
false);
656int SdlContext::sdl_client_thread_run(std::string& error_msg)
658 auto instance = context()->instance;
659 WINPR_ASSERT(instance);
661 int exit_code = sdl::error::SUCCESS;
662 while (!freerdp_shall_disconnect_context(context()))
664 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
670 if (freerdp_focus_required(instance))
672 auto ctx = get_context(context());
675 auto& input = ctx->getInputChannelContext();
676 if (!input.keyboard_focus_in())
678 if (!input.keyboard_focus_in())
682 const DWORD nCount = freerdp_get_event_handles(context(), handles, ARRAYSIZE(handles));
686 WLog_Print(getWLog(), WLOG_ERROR,
"freerdp_get_event_handles failed");
690 const DWORD status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
692 if (status == WAIT_FAILED)
694 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects WAIT_FAILED");
698 if (!freerdp_check_event_handles(context()))
700 if (client_auto_reconnect(instance))
703 getDialog().show(
false);
712 if (freerdp_error_info(instance) == 0)
713 exit_code = sdl::error::CONN_FAILED;
716 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
717 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects failed with %" PRIu32
"",
719 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
720 WLog_Print(getWLog(), WLOG_ERROR,
"Failed to check FreeRDP event handles");
725 if (exit_code == sdl::error::SUCCESS)
729 char* emsg =
nullptr;
731 exit_code = error_info_to_error(&code, &emsg, &elen);
737 if ((code == ERRINFO_LOGOFF_BY_USER) &&
738 (freerdp_get_disconnect_ultimatum(context()) == Disconnect_Ultimatum_user_requested))
740 const char* msg =
"Error info says user did not initiate but disconnect ultimatum says "
741 "they did; treat this as a user logoff";
743 char* emsg =
nullptr;
745 winpr_asprintf(&emsg, &elen,
"%s", msg);
751 WLog_Print(getWLog(), WLOG_INFO,
"%s", msg);
752 exit_code = sdl::error::LOGOFF;
756 freerdp_disconnect(instance);
764DWORD SdlContext::rdpThreadRun(
SdlContext* sdl)
768 std::string error_msg;
769 int exit_code = sdl->sdl_client_thread_connect(error_msg);
770 if (exit_code == sdl::error::SUCCESS)
771 exit_code = sdl->sdl_client_thread_run(error_msg);
772 sdl->sdl_client_cleanup(exit_code, error_msg);
774 return static_cast<DWORD
>(exit_code);
777int SdlContext::error_info_to_error(DWORD* pcode,
char** msg,
size_t* len)
const
779 const DWORD code = freerdp_error_info(context()->instance);
780 const char* name = freerdp_get_error_info_name(code);
781 const char* str = freerdp_get_error_info_string(code);
782 const int exit_code = sdl::error::errorToExitCode(code);
784 winpr_asprintf(msg, len,
"Terminate with %s due to ERROR_INFO %s [0x%08" PRIx32
"]: %s",
785 sdl::error::errorToExitCodeTag(code), name, code, str);
786 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%s", *msg);
792void SdlContext::applyMonitorOffset(SDL_WindowID window,
float& x,
float& y)
const
797 auto w = getWindowForId(window);
798 x -=
static_cast<float>(w->offsetX());
799 y -=
static_cast<float>(w->offsetY());
802static bool alignX(
const SDL_Rect& a,
const SDL_Rect& b)
804 if (a.x + a.w == b.x)
806 if (b.x + b.w == a.x)
811static bool alignY(
const SDL_Rect& a,
const SDL_Rect& b)
813 if (a.y + a.h == b.y)
815 if (b.y + b.h == a.y)
820std::vector<SDL_DisplayID>
821SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID
id,
822 const std::vector<SDL_DisplayID>& ignore)
824 auto first = _offsets.at(
id);
825 std::vector<SDL_DisplayID> neighbours;
827 for (
auto& entry : _offsets)
829 if (entry.first ==
id)
831 if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end())
834 bool neighbor =
false;
835 if (alignX(entry.second.first, first.first))
837 if (entry.second.first.x < first.first.x)
838 entry.second.second.x = first.second.x - entry.second.second.w;
840 entry.second.second.x = first.second.x + first.second.w;
843 if (alignY(entry.second.first, first.first))
845 if (entry.second.first.y < first.first.y)
846 entry.second.second.y = first.second.y - entry.second.second.h;
848 entry.second.second.y = first.second.y + first.second.h;
853 neighbours.push_back(entry.first);
858void SdlContext::updateMonitorDataFromOffsets()
860 for (
auto& entry : _displays)
862 auto offsets = _offsets.at(entry.first);
863 entry.second.x = offsets.second.x;
864 entry.second.y = offsets.second.y;
867 for (
auto& entry : _windows)
869 const auto& monitor = _displays.at(entry.first);
870 entry.second.setMonitor(monitor);
874bool SdlContext::drawToWindow(
SdlWindow& window,
const std::vector<SDL_Rect>& rects)
879 auto gdi = context()->gdi;
882 auto size = window.rect();
884 std::unique_lock lock(_critical);
885 auto surface = _primary.get();
889 window.setOffsetX(0);
890 window.setOffsetY(0);
891 if (gdi->width < size.w)
893 window.setOffsetX((size.w - gdi->width) / 2);
895 if (gdi->height < size.h)
897 window.setOffsetY((size.h - gdi->height) / 2);
900 _localScale = {
static_cast<float>(size.w) /
static_cast<float>(gdi->width),
901 static_cast<float>(size.h) /
static_cast<float>(gdi->height) };
902 if (!window.drawScaledRects(surface, _localScale, rects))
907 SDL_Point offset{ 0, 0 };
909 offset = { window.offsetX(), window.offsetY() };
910 if (!window.drawRects(surface, offset, rects))
914 window.updateSurface();
918bool SdlContext::minimizeAllWindows()
920 for (
auto& w : _windows)
925int SdlContext::exitCode()
const
930SDL_PixelFormat SdlContext::pixelFormat()
const
932 return _sdlPixelFormat;
935bool SdlContext::addDisplayWindow(SDL_DisplayID
id)
938 SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
939 auto title = sdl::utils::windowTitle(context()->settings);
940 auto w = SdlWindow::create(
id, title, flags);
941 _windows.emplace(w.id(), std::move(w));
945bool SdlContext::removeDisplayWindow(SDL_DisplayID
id)
947 for (
auto& w : _windows)
949 if (w.second.displayIndex() ==
id)
950 _windows.erase(w.first);
955bool SdlContext::detectDisplays()
958 auto display = SDL_GetDisplays(&count);
961 for (
int x = 0; x < count; x++)
963 const auto id = display[x];
964 addOrUpdateDisplay(
id);
970rdpMonitor SdlContext::getDisplay(SDL_DisplayID
id)
const
972 return _displays.at(
id);
975std::vector<SDL_DisplayID> SdlContext::getDisplayIds()
const
977 std::vector<SDL_DisplayID> keys;
978 keys.reserve(_displays.size());
979 for (
const auto& entry : _displays)
981 keys.push_back(entry.first);
986const SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
const
988 auto it = _windows.find(
id);
989 if (it == _windows.end())
994SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
996 auto it = _windows.find(
id);
997 if (it == _windows.end())
1004 if (_windows.empty())
1006 return &_windows.begin()->second;
1014sdlInput& SdlContext::getInputChannelContext()
1019sdlClip& SdlContext::getClipboardChannelContext()
1029wLog* SdlContext::getWLog()
1034bool SdlContext::moveMouseTo(
const SDL_FPoint& pos)
1036 auto window = SDL_GetMouseFocus();
1040 const auto id = SDL_GetWindowID(window);
1041 const auto spos = pixelToScreen(
id, pos);
1042 SDL_WarpMouseInWindow(window, spos.x, spos.y);
1046bool SdlContext::handleEvent(
const SDL_MouseMotionEvent& ev)
1048 if (!getWindowForId(ev.windowID))
1052 if (!eventToPixelCoordinates(ev.windowID, copy))
1054 removeLocalScaling(copy.motion.x, copy.motion.y);
1055 removeLocalScaling(copy.motion.xrel, copy.motion.yrel);
1056 applyMonitorOffset(copy.motion.windowID, copy.motion.x, copy.motion.y);
1058 return SdlTouch::handleEvent(
this, copy.motion);
1061bool SdlContext::handleEvent(
const SDL_MouseWheelEvent& ev)
1063 if (!getWindowForId(ev.windowID))
1067 if (!eventToPixelCoordinates(ev.windowID, copy))
1069 removeLocalScaling(copy.wheel.mouse_x, copy.wheel.mouse_y);
1070 return SdlTouch::handleEvent(
this, copy.wheel);
1073bool SdlContext::handleEvent(
const SDL_WindowEvent& ev)
1075 if (!getDisplayChannelContext().handleEvent(ev))
1078 auto window = getWindowForId(ev.windowID);
1083 const auto& r = window->rect();
1084 const auto& b = window->bounds();
1085 const auto& scale = window->scale();
1086 const auto& orientation = window->orientation();
1087 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1088 "%s: [%u] %dx%d-%dx%d {%dx%d-%dx%d}{scale=%f,orientation=%s}",
1089 sdl::utils::toString(ev.type).c_str(), ev.windowID, r.x, r.y, r.w, r.h, b.x,
1090 b.y, b.w, b.h,
static_cast<double>(scale),
1091 sdl::utils::toString(orientation).c_str());
1096 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1097 return restoreCursor();
1098 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1101 if (!window->fill())
1103 if (!drawToWindow(*window))
1105 if (!restoreCursor())
1109 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1110 if (!window->fill())
1112 if (!drawToWindow(*window))
1114 if (!restoreCursor())
1117 case SDL_EVENT_WINDOW_MOVED:
1119 auto r = window->rect();
1120 auto id = window->id();
1121 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%u: %dx%d-%dx%d",
id, r.x, r.y, r.w, r.h);
1124 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1126 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"Window closed, terminating RDP session...");
1127 freerdp_abort_connect_context(context());
1136bool SdlContext::handleEvent(
const SDL_DisplayEvent& ev)
1138 if (!getDisplayChannelContext().handleEvent(ev))
1143 case SDL_EVENT_DISPLAY_REMOVED:
1148 if (!SDL_GetDisplayBounds(ev.displayID, &r))
1150 const auto name = SDL_GetDisplayName(ev.displayID);
1153 const auto orientation = SDL_GetCurrentDisplayOrientation(ev.displayID);
1154 const auto scale = SDL_GetDisplayContentScale(ev.displayID);
1155 const auto mode = SDL_GetCurrentDisplayMode(ev.displayID);
1159 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1160 "%s: [%u, %s] %dx%d-%dx%d {orientation=%s, scale=%f}%s",
1161 sdl::utils::toString(ev.type).c_str(), ev.displayID, name, r.x, r.y, r.w,
1162 r.h, sdl::utils::toString(orientation).c_str(),
static_cast<double>(scale),
1163 sdl::utils::toString(mode).c_str());
1170bool SdlContext::handleEvent(
const SDL_MouseButtonEvent& ev)
1172 if (!getWindowForId(ev.windowID))
1174 SDL_Event copy = {};
1176 if (!eventToPixelCoordinates(ev.windowID, copy))
1178 removeLocalScaling(copy.button.x, copy.button.y);
1179 applyMonitorOffset(copy.button.windowID, copy.button.x, copy.button.y);
1180 return SdlTouch::handleEvent(
this, copy.button);
1183bool SdlContext::handleEvent(
const SDL_TouchFingerEvent& ev)
1185 if (!getWindowForId(ev.windowID))
1189 if (!eventToPixelCoordinates(ev.windowID, copy))
1191 removeLocalScaling(copy.tfinger.dx, copy.tfinger.dy);
1192 removeLocalScaling(copy.tfinger.x, copy.tfinger.y);
1193 applyMonitorOffset(copy.tfinger.windowID, copy.tfinger.x, copy.tfinger.y);
1194 return SdlTouch::handleEvent(
this, copy.tfinger);
1197void SdlContext::addOrUpdateDisplay(SDL_DisplayID
id)
1199 auto monitor = SdlWindow::query(
id,
false);
1200 _displays.emplace(
id, monitor);
1210 for (
auto& entry : _displays)
1213 std::ignore = SDL_GetDisplayBounds(entry.first, &bounds);
1216 pixel.w = entry.second.width;
1217 pixel.h = entry.second.height;
1218 _offsets.emplace(entry.first, std::pair{ bounds, pixel });
1225 const auto primary = SDL_GetPrimaryDisplay();
1226 std::vector<SDL_DisplayID> handled;
1227 handled.push_back(primary);
1229 auto neighbors = updateDisplayOffsetsForNeighbours(primary);
1230 while (!neighbors.empty())
1232 auto neighbor = *neighbors.begin();
1233 neighbors.pop_back();
1235 if (std::find(handled.begin(), handled.end(), neighbor) != handled.end())
1237 handled.push_back(neighbor);
1239 auto next = updateDisplayOffsetsForNeighbours(neighbor, handled);
1240 neighbors.insert(neighbors.end(), next.begin(), next.end());
1242 updateMonitorDataFromOffsets();
1245void SdlContext::deleteDisplay(SDL_DisplayID
id)
1247 _displays.erase(
id);
1250bool SdlContext::eventToPixelCoordinates(SDL_WindowID
id, SDL_Event& ev)
1252 auto w = getWindowForId(
id);
1257 auto renderer = SDL_GetRenderer(w->window());
1260 return SDL_ConvertEventToRenderCoordinates(renderer, &ev);
1263SDL_FPoint SdlContext::applyLocalScaling(
const SDL_FPoint& val)
const
1265 if (!useLocalScale())
1269 rval.x *= _localScale.x;
1270 rval.y *= _localScale.y;
1274void SdlContext::removeLocalScaling(
float& x,
float& y)
const
1276 if (!useLocalScale())
1282SDL_FPoint SdlContext::screenToPixel(SDL_WindowID
id,
const SDL_FPoint& pos)
1284 auto w = getWindowForId(
id);
1289 auto renderer = SDL_GetRenderer(w->window());
1294 if (!SDL_RenderCoordinatesFromWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1296 removeLocalScaling(rpos.x, rpos.y);
1300SDL_FPoint SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FPoint& pos)
1302 auto w = getWindowForId(
id);
1307 auto renderer = SDL_GetRenderer(w->window());
1312 if (!SDL_RenderCoordinatesToWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1314 return applyLocalScaling(rpos);
1317SDL_FRect SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FRect& pos,
bool round)
1319 const auto fpos = pixelToScreen(
id, SDL_FPoint{ pos.x, pos.y });
1320 const auto size = pixelToScreen(
id, SDL_FPoint{ pos.w, pos.h });
1321 SDL_FRect r{ fpos.x, fpos.y, size.x, size.y };
1324 r.w = std::ceil(r.w);
1325 r.h = std::ceil(r.h);
1326 r.x = std::floor(r.x);
1327 r.y = std::floor(r.y);
1332bool SdlContext::handleEvent(
const SDL_Event& ev)
1334 if ((ev.type >= SDL_EVENT_DISPLAY_FIRST) && (ev.type <= SDL_EVENT_DISPLAY_LAST))
1336 const auto& dev = ev.display;
1337 return handleEvent(dev);
1339 if ((ev.type >= SDL_EVENT_WINDOW_FIRST) && (ev.type <= SDL_EVENT_WINDOW_LAST))
1341 const auto& wev = ev.window;
1342 return handleEvent(wev);
1346 case SDL_EVENT_RENDER_TARGETS_RESET:
1347 case SDL_EVENT_RENDER_DEVICE_RESET:
1348 case SDL_EVENT_WILL_ENTER_FOREGROUND:
1359 case SDL_EVENT_FINGER_DOWN:
1360 case SDL_EVENT_FINGER_UP:
1361 case SDL_EVENT_FINGER_MOTION:
1363 const auto& cev = ev.tfinger;
1364 return handleEvent(cev);
1366 case SDL_EVENT_MOUSE_MOTION:
1369 const auto& cev = ev.motion;
1370 return handleEvent(cev);
1372 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1373 case SDL_EVENT_MOUSE_BUTTON_UP:
1375 const auto& cev = ev.button;
1376 return handleEvent(cev);
1378 case SDL_EVENT_MOUSE_WHEEL:
1380 const auto& cev = ev.wheel;
1381 return handleEvent(cev);
1383 case SDL_EVENT_CLIPBOARD_UPDATE:
1385 const auto& cev = ev.clipboard;
1386 return getClipboardChannelContext().handleEvent(cev);
1388 case SDL_EVENT_KEY_DOWN:
1389 case SDL_EVENT_KEY_UP:
1391 const auto& cev = ev.key;
1392 return getInputChannelContext().handleEvent(cev);
1399bool SdlContext::useLocalScale()
const
1404 const auto dynResize =
1408 return !dynResize && !fs && !multimon;
1411bool SdlContext::drawToWindows(
const std::vector<SDL_Rect>& rects)
1413 for (
auto& window : _windows)
1415 if (!drawToWindow(window.second, rects))
1422BOOL SdlContext::desktopResize(rdpContext* context)
1424 rdpGdi* gdi =
nullptr;
1425 rdpSettings* settings =
nullptr;
1426 auto sdl = get_context(context);
1429 WINPR_ASSERT(context);
1431 settings = context->settings;
1432 WINPR_ASSERT(settings);
1434 std::unique_lock lock(sdl->_critical);
1439 return sdl->createPrimary();
1443BOOL SdlContext::playSound(rdpContext* context,
const PLAY_SOUND_UPDATE* play_sound)
1446 WINPR_UNUSED(context);
1447 WINPR_UNUSED(play_sound);
1453BOOL SdlContext::beginPaint(rdpContext* context)
1455 auto gdi = context->gdi;
1457 WINPR_ASSERT(gdi->primary);
1459 HGDI_DC hdc = gdi->primary->hdc;
1465 WINPR_ASSERT(hwnd->invalid);
1466 hwnd->invalid->null = TRUE;
1472bool SdlContext::redraw(
bool suppress)
const
1477 auto gdi = context()->gdi;
1479 return gdi_send_suppress_output(gdi, suppress);
1482void SdlContext::setConnected(
bool val)
1487bool SdlContext::isConnected()
const
1492rdpContext* SdlContext::context()
const
1494 WINPR_ASSERT(_context);
1498rdpClientContext* SdlContext::common()
const
1500 return reinterpret_cast<rdpClientContext*
>(context());
1503bool SdlContext::setCursor(CursorType type)
1506 return restoreCursor();
1509bool SdlContext::setCursor(
const rdpPointer* cursor)
1511 _cursor = { sdl_Pointer_Copy(cursor), sdl_Pointer_FreeCopy };
1512 return setCursor(CURSOR_IMAGE);
1515rdpPointer* SdlContext::cursor()
const
1517 return _cursor.get();
1520bool SdlContext::restoreCursor()
1522 WLog_Print(getWLog(), WLOG_DEBUG,
"restore cursor: %d", _cursorType);
1523 switch (_cursorType)
1526 if (!SDL_HideCursor())
1528 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_HideCursor failed");
1532 setHasCursor(
false);
1535 case CURSOR_DEFAULT:
1537 auto def = SDL_GetDefaultCursor();
1538 if (!SDL_SetCursor(def))
1540 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_SetCursor(default=%p) failed",
1541 static_cast<void*
>(def));
1544 if (!SDL_ShowCursor())
1546 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_ShowCursor failed");
1554 return sdl_Pointer_Set_Process(
this);
1556 WLog_Print(getWLog(), WLOG_ERROR,
"Unknown cursorType %s",
1557 sdl::utils::toString(_cursorType).c_str());
1562void SdlContext::setMonitorIds(
const std::vector<SDL_DisplayID>& ids)
1564 _monitorIds.clear();
1567 _monitorIds.push_back(
id);
1571const std::vector<SDL_DisplayID>& SdlContext::monitorIds()
const
1576int64_t SdlContext::monitorId(uint32_t index)
const
1578 if (index >= _monitorIds.size())
1582 return _monitorIds.at(index);
1585void SdlContext::push(std::vector<SDL_Rect>&& rects)
1587 std::unique_lock lock(_queue_mux);
1588 _queue.emplace(std::move(rects));
1591std::vector<SDL_Rect> SdlContext::pop()
1593 std::unique_lock lock(_queue_mux);
1598 auto val = std::move(_queue.front());
1603bool SdlContext::setFullscreen(
bool enter,
bool forceOriginalDisplay)
1605 for (
const auto& window : _windows)
1607 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_FULLSCREEN, &window.second, enter,
1608 forceOriginalDisplay))
1611 _fullscreen = enter;
1615bool SdlContext::setMinimized()
1617 return sdl_push_user_event(SDL_EVENT_USER_WINDOW_MINIMIZE);
1620bool SdlContext::grabMouse()
const
1625bool SdlContext::toggleGrabMouse()
1627 return setGrabMouse(!grabMouse());
1630bool SdlContext::setGrabMouse(
bool enter)
1636bool SdlContext::grabKeyboard()
const
1638 return _grabKeyboard;
1641bool SdlContext::toggleGrabKeyboard()
1643 return setGrabKeyboard(!grabKeyboard());
1646bool SdlContext::setGrabKeyboard(
bool enter)
1648 _grabKeyboard = enter;
1652bool SdlContext::setResizeable(
bool enable)
1654 const auto settings = context()->settings;
1657 bool use = (dyn && enable) || smart;
1659 for (
const auto& window : _windows)
1661 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_RESIZEABLE, &window.second, use))
1669bool SdlContext::resizeable()
const
1674bool SdlContext::toggleResizeable()
1676 return setResizeable(!resizeable());
1679bool SdlContext::fullscreen()
const
1684bool SdlContext::toggleFullscreen()
1686 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.