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"
29#include "sdl_prefs.hpp"
31#include <sdl_common_utils.hpp>
32#include <scoped_guard.hpp>
34#include "dialogs/sdl_dialogs.hpp"
35#include <freerdp/client/aad_helper.h>
37static constexpr auto sdl_allow_screensaver =
"sdl-allow-screensaver";
38static constexpr auto sdl_connection_dialog =
"sdl-connection-dialog";
40SdlContext::SdlContext(rdpContext* context)
41 : _context(context), _log(WLog_Get(CLIENT_TAG(
"SDL"))), _cursor(nullptr, sdl_Pointer_FreeCopy),
42 _rdpThreadRunning(false), _primary(nullptr, SDL_DestroySurface), _disp(this), _input(this),
43 _clip(this), _rail(this), _dialog(_log)
45 WINPR_ASSERT(context);
48 auto instance = _context->instance;
49 WINPR_ASSERT(instance);
51 instance->PreConnect = preConnect;
52 instance->PostConnect = postConnect;
53 instance->PostDisconnect = postDisconnect;
54 instance->PostFinalDisconnect = postFinalDisconnect;
55 instance->AuthenticateEx = sdl_authenticate_ex;
56 instance->VerifyCertificateEx = sdl_verify_certificate_ex;
57 instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
58 instance->LogonErrorInfo = sdl_logon_error_info;
59 instance->PresentGatewayMessage = sdl_present_gateway_message;
60 instance->ChooseSmartcard = sdl_choose_smartcard;
61 instance->RetryDialog = sdl_retry_dialog;
62 instance->GetAccessToken = client_failsafe_get_access_token;
66 _args.push_back({ sdl_allow_screensaver, COMMAND_LINE_VALUE_BOOL,
nullptr, BoolValueFalse,
67 nullptr, -1,
nullptr,
"Allow local screensaver to activate" });
69 _args.push_back({ sdl_connection_dialog, COMMAND_LINE_VALUE_BOOL,
nullptr,
"on",
nullptr, -1,
70 nullptr,
"the connection dialog with progress and errors" });
73 _args.push_back({
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
nullptr });
76void SdlContext::setHasCursor(
bool val)
78 this->_cursor_visible = val;
81bool SdlContext::hasCursor()
const
83 return _cursor_visible;
86void SdlContext::setMetadata()
89 if (!wmclass || (strlen(wmclass) == 0))
90 wmclass = SDL_CLIENT_UUID;
92 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, wmclass);
93 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, SDL_CLIENT_NAME);
94 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, SDL_CLIENT_VERSION);
95 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, SDL_CLIENT_VENDOR);
96 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, SDL_CLIENT_COPYRIGHT);
97 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, SDL_CLIENT_URL);
98 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, SDL_CLIENT_TYPE);
101int SdlContext::start()
103 _thread = std::thread(rdpThreadRun,
this);
107int SdlContext::join()
111 HANDLE
event = freerdp_abort_event(context());
112 if (!SetEvent(event))
119void SdlContext::cleanup()
121 std::unique_lock lock(_critical);
127bool SdlContext::shallAbort(
bool ignoreDialogs)
129 std::unique_lock lock(_critical);
130 if (freerdp_shall_disconnect_context(context()))
134 if (_rdpThreadRunning)
136 return !getDialog().isRunning();
143BOOL SdlContext::preConnect(freerdp* instance)
145 WINPR_ASSERT(instance);
146 WINPR_ASSERT(instance->context);
148 auto sdl = get_context(instance->context);
150 auto settings = instance->context->settings;
151 WINPR_ASSERT(settings);
166 if (PubSub_SubscribeChannelConnected(instance->context->pubSub,
167 sdl_OnChannelConnectedEventHandler) < 0)
169 if (PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
170 sdl_OnChannelDisconnectedEventHandler) < 0)
172 if (PubSub_SubscribeUserNotification(instance->context->pubSub,
173 sdl_OnUserNotificationEventHandler) < 0)
179 UINT32 maxHeight = 0;
181 if (!sdl_detect_monitors(
sdl, &maxWidth, &maxHeight))
184 if ((maxWidth != 0) && (maxHeight != 0) &&
187 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Update size to %ux%u", maxWidth, maxHeight);
202 if (sm && (sw > 0) && (sh > 0))
206 WLog_Print(
sdl->getWLog(), WLOG_WARN,
207 "/smart-sizing and /multimon are currently not supported, ignoring "
226 WLog_Print(
sdl->getWLog(), WLOG_INFO,
227 "auth-only, but no password set. Please provide one.");
234 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect SDL.");
237 if (!
sdl->getInputChannelContext().initialize())
240 sdl->_credentialsRead =
false;
243 auto prefs = SdlPref::instance();
244 auto dialog = prefs->get_bool(
"SDL_ConnectionDialog",
true);
245 if (
sdl->_connectionDialog && prefs->is_commandline_override_allowed())
246 dialog = *
sdl->_connectionDialog;
249 sdl->getDialog().create(instance->context);
251 sdl->getDialog().showInfo(
"The connection is being established\n\nPlease wait...");
266BOOL SdlContext::postConnect(freerdp* instance)
268 WINPR_ASSERT(instance);
270 auto context = instance->context;
271 WINPR_ASSERT(context);
273 auto sdl = get_context(context);
276 sdl->getDialog().show(
false);
283 WLog_Print(
sdl->getWLog(), WLOG_INFO,
284 "auth-only, but no password set. Please provide one.");
288 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect to X.");
292 if (!
sdl->waitForWindowsCreated())
295 sdl->_sdlPixelFormat = SDL_PIXELFORMAT_BGRA32;
296 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
299 if (!
sdl->createPrimary())
302 if (!sdl_register_pointer(instance->context->graphics))
305 WINPR_ASSERT(context->update);
307 context->update->BeginPaint = beginPaint;
308 context->update->EndPaint = endPaint;
309 context->update->PlaySound = playSound;
310 context->update->DesktopResize = desktopResize;
311 context->update->SetKeyboardIndicators = sdlInput::keyboard_set_indicators;
312 context->update->SetKeyboardImeStatus = sdlInput::keyboard_set_ime_status;
314 if (!
sdl->setResizeable(
false))
320 sdl->setConnected(
true);
327void SdlContext::postDisconnect(freerdp* instance)
332 if (!instance->context)
335 auto sdl = get_context(instance->context);
336 sdl->setConnected(
false);
341void SdlContext::postFinalDisconnect(freerdp* instance)
346 if (!instance->context)
349 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
350 sdl_OnChannelConnectedEventHandler);
351 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
352 sdl_OnChannelDisconnectedEventHandler);
353 PubSub_UnsubscribeUserNotification(instance->context->pubSub,
354 sdl_OnUserNotificationEventHandler);
358bool SdlContext::createPrimary()
360 auto gdi = context()->gdi;
363 _primary = SDLSurfacePtr(
364 SDL_CreateSurfaceFrom(
static_cast<int>(gdi->width),
static_cast<int>(gdi->height),
365 pixelFormat(), gdi->primary_buffer,
static_cast<int>(gdi->stride)),
370 SDL_SetSurfaceBlendMode(_primary.get(), SDL_BLENDMODE_NONE);
371 SDL_Rect surfaceRect = { 0, 0, gdi->width, gdi->height };
372 SDL_FillSurfaceRect(_primary.get(), &surfaceRect,
373 SDL_MapSurfaceRGBA(_primary.get(), 0, 0, 0, 0xff));
378bool SdlContext::createWindows()
380 auto settings = context()->settings;
381 const auto& title = windowTitle();
383 ScopeGuard guard1([&]() { _windowsCreatedEvent.set(); });
389 for (UINT32 x = 0; x < windowCount; x++)
391 auto id = monitorId(x);
396 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
398 originX = std::min<Sint32>(monitor->x, originX);
399 originY = std::min<Sint32>(monitor->y, originY);
402 for (UINT32 x = 0; x < windowCount; x++)
404 auto id = monitorId(x);
409 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
411 Uint32 w = WINPR_ASSERTING_INT_CAST(Uint32, monitor->width);
412 Uint32 h = WINPR_ASSERTING_INT_CAST(Uint32, monitor->height);
416 if (_windowWidth > 0)
421 if (_windowHeight > 0)
427 Uint32 flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
432 flags |= SDL_WINDOW_FULLSCREEN;
437 flags |= SDL_WINDOW_BORDERLESS;
441 flags |= SDL_WINDOW_BORDERLESS;
443 auto did = WINPR_ASSERTING_INT_CAST(SDL_DisplayID,
id);
444 auto window = SdlWindow::create(did, title, flags, w, h);
448 window.setOffsetX(originX - monitor->x);
449 window.setOffsetY(originY - monitor->y);
452 _windows.insert({ window.id(), std::move(window) });
458bool SdlContext::updateWindowList()
460 std::vector<rdpMonitor> list;
461 list.reserve(_windows.size());
462 for (
const auto& win : _windows)
463 list.push_back(win.second.monitor(_windows.size() == 1));
469 std::none_of(list.cbegin(), list.cend(), [](
const rdpMonitor& m) { return m.is_primary; }))
470 list.at(0).is_primary =
true;
476bool SdlContext::updateWindow(SDL_WindowID
id)
484 auto& w = _windows.at(
id);
485 auto m = w.monitor(
true);
489 m.attributes.physicalWidth =
static_cast<UINT32
>(r.w);
490 m.attributes.physicalHeight =
static_cast<UINT32
>(r.h);
495std::string SdlContext::windowTitle()
const
497 const char* prefix =
"FreeRDP:";
505 const auto addPort = (port != 3389);
507 std::stringstream ss;
508 ss << prefix <<
" " << name;
516bool SdlContext::waitForWindowsCreated()
519 std::unique_lock<CriticalSection> lock(_critical);
520 _windowsCreatedEvent.clear();
521 if (!sdl_push_user_event(SDL_EVENT_USER_CREATE_WINDOWS,
this))
525 HANDLE handles[] = { _windowsCreatedEvent.handle(), freerdp_abort_event(context()) };
527 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
541BOOL SdlContext::endPaint(rdpContext* context)
543 auto sdl = get_context(context);
546 auto gdi = context->gdi;
548 WINPR_ASSERT(gdi->primary);
550 HGDI_DC hdc = gdi->primary->hdc;
556 WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
558 if (hwnd->invalid->null)
561 WINPR_ASSERT(hwnd->invalid);
562 if (gdi->suppressOutput || hwnd->invalid->null)
565 const INT32 ninvalid = hwnd->ninvalid;
566 const GDI_RGN* cinvalid = hwnd->cinvalid;
571 std::vector<SDL_Rect> rects;
572 for (INT32 x = 0; x < ninvalid; x++)
574 auto& rgn = cinvalid[x];
575 rects.push_back({ rgn.x, rgn.y, rgn.w, rgn.h });
578 sdl->push(std::move(rects));
579 return sdl_push_user_event(SDL_EVENT_USER_UPDATE);
582void SdlContext::sdl_client_cleanup(
int exit_code,
const std::string& error_msg)
584 rdpSettings* settings = context()->settings;
585 WINPR_ASSERT(settings);
587 _rdpThreadRunning =
false;
588 bool showError =
false;
590 WLog_Print(getWLog(), WLOG_INFO,
"Authentication only, exit status %s [%" PRId32
"]",
591 sdl::error::exitCodeToTag(exit_code), exit_code);
596 case sdl::error::SUCCESS:
597 case sdl::error::DISCONNECT:
598 case sdl::error::LOGOFF:
599 case sdl::error::DISCONNECT_BY_USER:
600 case sdl::error::CONNECT_CANCELLED:
605 getDialog().showError(error_msg);
612 getDialog().show(
false);
614 _exitCode = exit_code;
615 std::ignore = sdl_push_user_event(SDL_EVENT_USER_QUIT);
619int SdlContext::sdl_client_thread_connect(std::string& error_msg)
621 auto instance = context()->instance;
622 WINPR_ASSERT(instance);
624 _rdpThreadRunning =
true;
625 BOOL rc = freerdp_connect(instance);
627 rdpSettings* settings = context()->settings;
628 WINPR_ASSERT(settings);
630 int exit_code = sdl::error::SUCCESS;
633 UINT32 error = freerdp_get_last_error(context());
634 exit_code = sdl::error::errorToExitCode(error);
639 DWORD code = freerdp_get_last_error(context());
640 freerdp_abort_connect_context(context());
641 WLog_Print(getWLog(), WLOG_ERROR,
"Authentication only, %s [0x%08" PRIx32
"] %s",
642 freerdp_get_last_error_name(code), code, freerdp_get_last_error_string(code));
648 DWORD code = freerdp_error_info(instance);
649 if (exit_code == sdl::error::SUCCESS)
653 exit_code = error_info_to_error(&code, &msg, &len);
659 auto last = freerdp_get_last_error(context());
660 if (error_msg.empty())
664 winpr_asprintf(&msg, &len,
"%s [0x%08" PRIx32
"]\n%s",
665 freerdp_get_last_error_name(last), last,
666 freerdp_get_last_error_string(last));
672 if (exit_code == sdl::error::SUCCESS)
674 if (last == FREERDP_ERROR_AUTHENTICATION_FAILED)
675 exit_code = sdl::error::AUTH_FAILURE;
676 else if (code == ERRINFO_SUCCESS)
677 exit_code = sdl::error::CONN_FAILED;
680 getDialog().show(
false);
686int SdlContext::sdl_client_thread_run(std::string& error_msg)
688 auto instance = context()->instance;
689 WINPR_ASSERT(instance);
691 int exit_code = sdl::error::SUCCESS;
692 while (!freerdp_shall_disconnect_context(context()))
694 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
700 if (freerdp_focus_required(instance))
702 auto ctx = get_context(context());
705 auto& input = ctx->getInputChannelContext();
706 if (!input.keyboard_focus_in())
708 if (!input.keyboard_focus_in())
712 const DWORD nCount = freerdp_get_event_handles(context(), handles, ARRAYSIZE(handles));
716 WLog_Print(getWLog(), WLOG_ERROR,
"freerdp_get_event_handles failed");
720 const DWORD status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
722 if (status == WAIT_FAILED)
724 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects WAIT_FAILED");
728 if (!freerdp_check_event_handles(context()))
730 if (client_auto_reconnect(instance))
733 getDialog().show(
false);
742 if (freerdp_error_info(instance) == 0)
743 exit_code = sdl::error::errorToExitCode(freerdp_get_last_error(context()));
746 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
747 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects failed with %" PRIu32
"",
749 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
750 WLog_Print(getWLog(), WLOG_ERROR,
"Failed to check FreeRDP event handles");
755 if (exit_code == sdl::error::SUCCESS)
759 char* emsg =
nullptr;
761 exit_code = error_info_to_error(&code, &emsg, &elen);
767 if ((code == ERRINFO_LOGOFF_BY_USER) &&
768 (freerdp_get_disconnect_ultimatum(context()) == Disconnect_Ultimatum_user_requested))
770 const char* msg =
"Error info says user did not initiate but disconnect ultimatum says "
771 "they did; treat this as a user logoff";
773 char* emsg =
nullptr;
775 winpr_asprintf(&emsg, &elen,
"%s", msg);
781 WLog_Print(getWLog(), WLOG_INFO,
"%s", msg);
782 exit_code = sdl::error::LOGOFF;
786 if ((exit_code != sdl::error::SUCCESS) && error_msg.empty())
788 const auto last = freerdp_get_last_error(context());
791 winpr_asprintf(&msg, &len,
"%s [0x%08" PRIx32
"]\n%s", freerdp_get_last_error_name(last),
792 last, freerdp_get_last_error_string(last));
798 freerdp_disconnect(instance);
810 std::string error_msg;
811 int exit_code =
sdl->sdl_client_thread_connect(error_msg);
812 if (exit_code == sdl::error::SUCCESS)
813 exit_code =
sdl->sdl_client_thread_run(error_msg);
814 sdl->sdl_client_cleanup(exit_code, error_msg);
816 return static_cast<DWORD
>(exit_code);
819int SdlContext::error_info_to_error(DWORD* pcode,
char** msg,
size_t* len)
const
821 const DWORD code = freerdp_error_info(context()->instance);
822 const char* name = freerdp_get_error_info_name(code);
823 const char* str = freerdp_get_error_info_string(code);
824 const int exit_code = sdl::error::errorToExitCode(code);
826 winpr_asprintf(msg, len,
"Terminate with %s due to ERROR_INFO %s [0x%08" PRIx32
"]: %s",
827 sdl::error::errorToExitCodeTag(code), name, code, str);
828 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%s", *msg);
834void SdlContext::applyMonitorOffset(SDL_WindowID window,
float& x,
float& y)
const
839 auto w = getWindowForId(window);
840 x -=
static_cast<float>(w->offsetX());
841 y -=
static_cast<float>(w->offsetY());
844static bool alignX(
const SDL_Rect& a,
const SDL_Rect& b)
846 if (a.x + a.w == b.x)
848 if (b.x + b.w == a.x)
853static bool alignY(
const SDL_Rect& a,
const SDL_Rect& b)
855 if (a.y + a.h == b.y)
857 if (b.y + b.h == a.y)
862std::vector<SDL_DisplayID>
863SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID
id,
864 const std::vector<SDL_DisplayID>& ignore)
866 auto first = _offsets.at(
id);
867 std::vector<SDL_DisplayID> neighbours;
869 for (
auto& entry : _offsets)
871 if (entry.first ==
id)
873 if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end())
876 bool neighbor =
false;
877 const bool ax = alignX(entry.second.first, first.first);
878 const bool ay = alignY(entry.second.first, first.first);
886 auto crossOffset = [](
int ePos,
int fPos,
int fLogical,
int fPixel) ->
int
890 return static_cast<int>(
891 std::lround(
static_cast<double>(ePos - fPos) * fPixel / fLogical));
896 if (entry.second.first.x < first.first.x)
897 entry.second.second.x = first.second.x - entry.second.second.w;
899 entry.second.second.x = first.second.x + first.second.w;
901 entry.second.second.y =
903 crossOffset(entry.second.first.y, first.first.y, first.first.h, first.second.h);
908 if (entry.second.first.y < first.first.y)
909 entry.second.second.y = first.second.y - entry.second.second.h;
911 entry.second.second.y = first.second.y + first.second.h;
913 entry.second.second.x =
915 crossOffset(entry.second.first.x, first.first.x, first.first.w, first.second.w);
920 neighbours.push_back(entry.first);
925void SdlContext::updateMonitorDataFromOffsets()
927 for (
auto& entry : _displays)
929 auto offsets = _offsets.at(entry.first);
930 entry.second.x = offsets.second.x;
931 entry.second.y = offsets.second.y;
934 for (
auto& entry : _windows)
936 const auto& monitor = _displays.at(entry.first);
937 entry.second.setMonitor(monitor);
941bool SdlContext::drawToWindow(
SdlWindow& window,
const std::vector<SDL_Rect>& rects)
946 auto gdi = context()->gdi;
949 auto size = window.rect();
951 std::unique_lock lock(_critical);
952 auto surface = _primary.get();
956 window.setOffsetX(0);
957 window.setOffsetY(0);
958 if (gdi->width < size.w)
960 window.setOffsetX((size.w - gdi->width) / 2);
962 if (gdi->height < size.h)
964 window.setOffsetY((size.h - gdi->height) / 2);
967 _localScale = {
static_cast<float>(size.w) /
static_cast<float>(gdi->width),
968 static_cast<float>(size.h) /
static_cast<float>(gdi->height) };
969 if (!window.drawScaledRects(surface, _localScale, rects))
974 SDL_Point offset{ 0, 0 };
976 offset = { window.offsetX(), window.offsetY() };
977 if (!window.drawRects(surface, offset, rects))
981 window.updateSurface();
985bool SdlContext::minimizeAllWindows()
987 for (
auto& w : _windows)
992int SdlContext::exitCode()
const
997SDL_PixelFormat SdlContext::pixelFormat()
const
999 return _sdlPixelFormat;
1002bool SdlContext::addDisplayWindow(SDL_DisplayID
id)
1005 SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
1006 auto title = sdl::utils::windowTitle(context()->settings);
1007 auto w = SdlWindow::create(
id, title, flags);
1008 _windows.emplace(w.id(), std::move(w));
1012bool SdlContext::removeDisplayWindow(SDL_DisplayID
id)
1014 for (
auto& w : _windows)
1016 if (w.second.displayIndex() ==
id)
1017 _windows.erase(w.first);
1022bool SdlContext::detectDisplays()
1025 auto display = SDL_GetDisplays(&count);
1028 for (
int x = 0; x < count; x++)
1030 const auto id = display[x];
1031 addOrUpdateDisplay(
id);
1037rdpMonitor SdlContext::getDisplay(SDL_DisplayID
id)
const
1039 return _displays.at(
id);
1042std::vector<SDL_DisplayID> SdlContext::getDisplayIds()
const
1044 std::vector<SDL_DisplayID> keys;
1045 keys.reserve(_displays.size());
1046 for (
const auto& entry : _displays)
1048 keys.push_back(entry.first);
1053const SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
const
1055 auto it = _windows.find(
id);
1056 if (it == _windows.end())
1061SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
1063 auto it = _windows.find(
id);
1064 if (it == _windows.end())
1071 if (_windows.empty())
1073 return &_windows.begin()->second;
1081sdlInput& SdlContext::getInputChannelContext()
1086sdlClip& SdlContext::getClipboardChannelContext()
1091SdlRail& SdlContext::getRailChannelContext()
1101wLog* SdlContext::getWLog()
1106bool SdlContext::moveMouseTo(
const SDL_FPoint& pos)
1108 auto window = SDL_GetMouseFocus();
1112 const auto id = SDL_GetWindowID(window);
1113 const auto spos = pixelToScreen(
id, pos);
1114 SDL_WarpMouseInWindow(window, spos.x, spos.y);
1118bool SdlContext::handleEvent(
const SDL_MouseMotionEvent& ev)
1123 if (_rail.enabled() && _rail.suppressServerMotion(ev.windowID))
1125 if (!(SDL_GetGlobalMouseState(
nullptr,
nullptr) & SDL_BUTTON_LMASK))
1126 _rail.completeLocalMoveIfPending();
1129 if (_rail.enabled() && _rail.translateToServer(ev.windowID, copy.motion.x, copy.motion.y))
1131 _rail.noteServerPointer(copy.motion.x, copy.motion.y);
1132 return SdlTouch::handleEvent(
this, copy.motion);
1135 if (!getWindowForId(ev.windowID))
1137 if (!eventToPixelCoordinates(ev.windowID, copy))
1139 removeLocalScaling(copy.motion.x, copy.motion.y);
1140 removeLocalScaling(copy.motion.xrel, copy.motion.yrel);
1141 applyMonitorOffset(copy.motion.windowID, copy.motion.x, copy.motion.y);
1143 return SdlTouch::handleEvent(
this, copy.motion);
1146bool SdlContext::handleEvent(
const SDL_MouseWheelEvent& ev)
1150 if (_rail.enabled() &&
1151 _rail.translateToServer(ev.windowID, copy.wheel.mouse_x, copy.wheel.mouse_y))
1152 return SdlTouch::handleEvent(
this, copy.wheel);
1154 if (!getWindowForId(ev.windowID))
1156 if (!eventToPixelCoordinates(ev.windowID, copy))
1158 removeLocalScaling(copy.wheel.mouse_x, copy.wheel.mouse_y);
1159 return SdlTouch::handleEvent(
this, copy.wheel);
1162bool SdlContext::handleEvent(
const SDL_WindowEvent& ev)
1164 if (!getDisplayChannelContext().handleEvent(ev))
1167 auto window = getWindowForId(ev.windowID);
1171 if (_rail.enabled() && _rail.ownsWindow(ev.windowID))
1175 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1177 if (!(SDL_GetGlobalMouseState(
nullptr,
nullptr) & SDL_BUTTON_LMASK))
1178 _rail.completeLocalMoveIfPending();
1179 _rail.completeWaylandResize();
1181 return restoreCursor();
1182 case SDL_EVENT_WINDOW_MOUSE_LEAVE:
1184 _rail.noteResizeGrab(ev.windowID);
1186 case SDL_EVENT_WINDOW_EXPOSED:
1189 if (!_rail.suppressServerInput(ev.windowID))
1190 _rail.invalidateWindow(ev.windowID);
1192 case SDL_EVENT_WINDOW_MAXIMIZED:
1194 _rail.completeLocalMoveIfPending();
1195 _rail.handleMaximized(ev.windowID);
1197 case SDL_EVENT_WINDOW_MINIMIZED:
1198 _rail.handleMinimized(ev.windowID);
1200 case SDL_EVENT_WINDOW_RESTORED:
1201 _rail.handleRestored(ev.windowID);
1203 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1205 _rail.handleClose(ev.windowID);
1207 case SDL_EVENT_WINDOW_FOCUS_GAINED:
1208 _rail.handleFocus(ev.windowID,
true);
1210 case SDL_EVENT_WINDOW_FOCUS_LOST:
1211 _rail.handleFocus(ev.windowID,
false);
1213 case SDL_EVENT_WINDOW_MOVED:
1214 if (!_rail.suppressServerInput(ev.windowID))
1215 _rail.syncGeometry(ev.windowID);
1217 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1220 _rail.noteDragResize(ev.windowID, ev.data1, ev.data2);
1221 _rail.syncGeometry(ev.windowID);
1222 _rail.handleWaylandResize(ev.windowID);
1223 std::ignore = drawToWindows({});
1233 const auto& r = window->rect();
1234 const auto& b = window->bounds();
1235 const auto& scale = window->scale();
1236 const auto& orientation = window->orientation();
1237 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1238 "%s: [%u] %dx%d-%dx%d {%dx%d-%dx%d}{scale=%f,orientation=%s}",
1239 sdl::utils::toString(ev.type).c_str(), ev.windowID, r.x, r.y, r.w, r.h, b.x,
1240 b.y, b.w, b.h,
static_cast<double>(scale),
1241 sdl::utils::toString(orientation).c_str());
1246 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1247 return restoreCursor();
1248 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1249 if (!resizeToScale(window))
1253 if (!window->fill())
1255 if (!drawToWindow(*window))
1257 if (!restoreCursor())
1261 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1262 if (!resizeToScale(window))
1264 if (!window->fill())
1266 if (!drawToWindow(*window))
1268 if (!restoreCursor())
1271 case SDL_EVENT_WINDOW_MOVED:
1273 auto r = window->rect();
1274 auto id = window->id();
1275 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%u: %dx%d-%dx%d",
id, r.x, r.y, r.w, r.h);
1278 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1280 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"Window closed, terminating RDP session...");
1281 freerdp_abort_connect_context(context());
1290bool SdlContext::handleEvent(
const SDL_DisplayEvent& ev)
1292 if (!getDisplayChannelContext().handleEvent(ev))
1297 case SDL_EVENT_DISPLAY_REMOVED:
1302 if (!SDL_GetDisplayBounds(ev.displayID, &r))
1304 const auto name = SDL_GetDisplayName(ev.displayID);
1307 const auto orientation = SDL_GetCurrentDisplayOrientation(ev.displayID);
1308 const auto scale = SDL_GetDisplayContentScale(ev.displayID);
1309 const auto mode = SDL_GetCurrentDisplayMode(ev.displayID);
1313 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1314 "%s: [%u, %s] %dx%d-%dx%d {orientation=%s, scale=%f}%s",
1315 sdl::utils::toString(ev.type).c_str(), ev.displayID, name, r.x, r.y, r.w,
1316 r.h, sdl::utils::toString(orientation).c_str(),
static_cast<double>(scale),
1317 sdl::utils::toString(mode).c_str());
1324bool SdlContext::handleEvent(
const SDL_MouseButtonEvent& ev)
1326 SDL_Event copy = {};
1328 if (_rail.enabled())
1331 const bool suppress = _rail.suppressServerInput(ev.windowID);
1332 if (ev.type == SDL_EVENT_MOUSE_BUTTON_UP)
1333 _rail.completeLocalMoveIfPending();
1336 if (ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
1337 _rail.completeWaylandResize(
true);
1339 if (ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
1340 _rail.ensureActive(ev.windowID);
1343 if (_rail.translateToServer(ev.windowID, copy.button.x, copy.button.y))
1347 if ((ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN) && (ev.button == SDL_BUTTON_LEFT))
1348 _rail.noteLeftPress(copy.button.x, copy.button.y);
1349 return SdlTouch::handleEvent(
this, copy.button);
1353 if (!getWindowForId(ev.windowID))
1355 if (!eventToPixelCoordinates(ev.windowID, copy))
1357 removeLocalScaling(copy.button.x, copy.button.y);
1358 applyMonitorOffset(copy.button.windowID, copy.button.x, copy.button.y);
1359 return SdlTouch::handleEvent(
this, copy.button);
1362bool SdlContext::handleEvent(
const SDL_TouchFingerEvent& ev)
1364 if (!getWindowForId(ev.windowID))
1368 if (!eventToPixelCoordinates(ev.windowID, copy))
1370 removeLocalScaling(copy.tfinger.dx, copy.tfinger.dy);
1371 removeLocalScaling(copy.tfinger.x, copy.tfinger.y);
1372 applyMonitorOffset(copy.tfinger.windowID, copy.tfinger.x, copy.tfinger.y);
1373 return SdlTouch::handleEvent(
this, copy.tfinger);
1376void SdlContext::addOrUpdateDisplay(SDL_DisplayID
id)
1378 auto monitor = SdlWindow::query(
id,
false);
1379 _displays.emplace(
id, monitor);
1389 for (
auto& entry : _displays)
1392 std::ignore = SDL_GetDisplayBounds(entry.first, &bounds);
1395 pixel.w = entry.second.width;
1396 pixel.h = entry.second.height;
1397 _offsets.emplace(entry.first, std::pair{ bounds, pixel });
1404 const auto primary = SDL_GetPrimaryDisplay();
1405 std::vector<SDL_DisplayID> handled;
1406 handled.push_back(primary);
1408 auto neighbors = updateDisplayOffsetsForNeighbours(primary);
1409 while (!neighbors.empty())
1411 auto neighbor = neighbors.front();
1412 neighbors.erase(neighbors.begin());
1414 if (std::find(handled.begin(), handled.end(), neighbor) != handled.end())
1416 handled.push_back(neighbor);
1418 auto next = updateDisplayOffsetsForNeighbours(neighbor, handled);
1419 neighbors.insert(neighbors.end(), next.begin(), next.end());
1421 updateMonitorDataFromOffsets();
1424void SdlContext::deleteDisplay(SDL_DisplayID
id)
1426 _displays.erase(
id);
1429bool SdlContext::eventToPixelCoordinates(SDL_WindowID
id, SDL_Event& ev)
1431 auto w = getWindowForId(
id);
1436 auto renderer = w->renderer();
1439 return SDL_ConvertEventToRenderCoordinates(renderer, &ev);
1442SDL_FPoint SdlContext::applyLocalScaling(
const SDL_FPoint& val)
const
1444 if (!useLocalScale())
1448 rval.x *= _localScale.x;
1449 rval.y *= _localScale.y;
1453void SdlContext::removeLocalScaling(
float& x,
float& y)
const
1455 if (!useLocalScale())
1461SDL_FPoint SdlContext::screenToPixel(SDL_WindowID
id,
const SDL_FPoint& pos)
1463 auto w = getWindowForId(
id);
1467 if (_rail.enabled() && _rail.ownsWindow(
id))
1473 auto renderer = w->renderer();
1478 if (!SDL_RenderCoordinatesFromWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1480 removeLocalScaling(rpos.x, rpos.y);
1484SDL_FPoint SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FPoint& pos)
1486 auto w = getWindowForId(
id);
1489 if (_rail.enabled() && _rail.ownsWindow(
id))
1495 auto renderer = w->renderer();
1500 if (!SDL_RenderCoordinatesToWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1502 return applyLocalScaling(rpos);
1505SDL_FRect SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FRect& pos,
bool round)
1507 const auto fpos = pixelToScreen(
id, SDL_FPoint{ pos.x, pos.y });
1508 const auto size = pixelToScreen(
id, SDL_FPoint{ pos.w, pos.h });
1509 SDL_FRect r{ fpos.x, fpos.y, size.x, size.y };
1512 r.w = std::ceil(r.w);
1513 r.h = std::ceil(r.h);
1514 r.x = std::floor(r.x);
1515 r.y = std::floor(r.y);
1520bool SdlContext::handleEvent(
const SDL_Event& ev)
1522 if ((ev.type >= SDL_EVENT_DISPLAY_FIRST) && (ev.type <= SDL_EVENT_DISPLAY_LAST))
1524 const auto& dev = ev.display;
1525 return handleEvent(dev);
1527 if ((ev.type >= SDL_EVENT_WINDOW_FIRST) && (ev.type <= SDL_EVENT_WINDOW_LAST))
1529 const auto& wev = ev.window;
1530 return handleEvent(wev);
1534 case SDL_EVENT_RENDER_TARGETS_RESET:
1535 case SDL_EVENT_RENDER_DEVICE_RESET:
1536 case SDL_EVENT_WILL_ENTER_FOREGROUND:
1547 case SDL_EVENT_FINGER_DOWN:
1548 case SDL_EVENT_FINGER_UP:
1549 case SDL_EVENT_FINGER_MOTION:
1551 const auto& cev = ev.tfinger;
1552 return handleEvent(cev);
1554 case SDL_EVENT_MOUSE_MOTION:
1557 const auto& cev = ev.motion;
1558 return handleEvent(cev);
1560 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1561 case SDL_EVENT_MOUSE_BUTTON_UP:
1563 const auto& cev = ev.button;
1564 return handleEvent(cev);
1566 case SDL_EVENT_MOUSE_WHEEL:
1568 const auto& cev = ev.wheel;
1569 return handleEvent(cev);
1571 case SDL_EVENT_CLIPBOARD_UPDATE:
1573 const auto& cev = ev.clipboard;
1574 return getClipboardChannelContext().handleEvent(cev);
1576 case SDL_EVENT_KEY_DOWN:
1577 case SDL_EVENT_KEY_UP:
1579 const auto& cev = ev.key;
1580 return getInputChannelContext().handleEvent(cev);
1589 return _args.data();
1592size_t SdlContext::argsCount()
const
1594 if (_args.size() <= 1)
1596 return _args.size() - 1;
1607 if (strcmp(arg->Name, sdl_allow_screensaver) == 0)
1609 if (arg->Value !=
nullptr)
1611 if (!SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER,
"1"))
1613 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
1614 "SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER) failed with %s",
1620 else if (strcmp(arg->Name, sdl_connection_dialog) == 0)
1621 sdl->_connectionDialog = (arg->Value !=
nullptr);
1631std::vector<rdpPointer*>& SdlContext::pointers()
1633 return _valid_pointers;
1636bool SdlContext::contains(
const rdpPointer* ptr)
const
1638 for (
const auto& cur : _valid_pointers)
1646bool SdlContext::credentialsRead()
const
1648 return _credentialsRead;
1651void SdlContext::setCredentialsRead()
1653 _credentialsRead =
true;
1656bool SdlContext::resizeToScale(
SdlWindow* window)
1660 if (!useLocalScale())
1664 return window->resizeToScale();
1667bool SdlContext::useLocalScale()
const
1672 const auto dynResize =
1676 return !dynResize && !fs && !multimon;
1679bool SdlContext::drawToWindows(
const std::vector<SDL_Rect>& rects)
1682 if (_rail.enabled())
1684 for (
auto& window : _windows)
1686 if ((SDL_GetWindowFlags(window.second.window()) & SDL_WINDOW_HIDDEN) == 0)
1687 SDL_HideWindow(window.second.window());
1690 std::unique_lock lock(_critical);
1691 _rail.paint(_primary.get(), pixelFormat(), rects);
1697 bool firstShow =
false;
1698 for (
auto& window : _windows)
1700 if (SDL_GetWindowFlags(window.second.window()) & SDL_WINDOW_HIDDEN)
1702 SDL_ShowWindow(window.second.window());
1703 SDL_RaiseWindow(window.second.window());
1708 if (rects.empty() && !firstShow)
1711 std::vector<SDL_Rect> drawRects = rects;
1713 std::unique_lock lock(_critical);
1714 if ((drawRects.empty() || firstShow) && _primary)
1715 drawRects = { { 0, 0, _primary->w, _primary->h } };
1718 for (
auto& window : _windows)
1720 if (!drawToWindow(window.second, drawRects))
1727BOOL SdlContext::desktopResize(rdpContext* context)
1729 rdpGdi* gdi =
nullptr;
1730 rdpSettings* settings =
nullptr;
1731 auto sdl = get_context(context);
1734 WINPR_ASSERT(context);
1736 settings = context->settings;
1737 WINPR_ASSERT(settings);
1739 std::unique_lock lock(
sdl->_critical);
1744 return sdl->createPrimary();
1748BOOL SdlContext::playSound(rdpContext* context,
const PLAY_SOUND_UPDATE* play_sound)
1751 WINPR_UNUSED(context);
1752 WINPR_UNUSED(play_sound);
1758BOOL SdlContext::beginPaint(rdpContext* context)
1760 auto gdi = context->gdi;
1762 WINPR_ASSERT(gdi->primary);
1764 HGDI_DC hdc = gdi->primary->hdc;
1770 WINPR_ASSERT(hwnd->invalid);
1771 hwnd->invalid->null = TRUE;
1777bool SdlContext::redraw(
bool suppress)
const
1783 if (suppress && _rail.enabled())
1786 auto gdi = context()->gdi;
1788 return gdi_send_suppress_output(gdi, suppress);
1791void SdlContext::setConnected(
bool val)
1796bool SdlContext::isConnected()
const
1801rdpContext* SdlContext::context()
const
1803 WINPR_ASSERT(_context);
1807rdpClientContext* SdlContext::common()
const
1809 return reinterpret_cast<rdpClientContext*
>(context());
1812bool SdlContext::setCursor(CursorType type)
1815 return restoreCursor();
1818bool SdlContext::setCursor(
const rdpPointer* cursor)
1820 std::unique_lock lock(_critical);
1821 if (!contains(cursor))
1824 _cursor = { sdl_Pointer_Copy(cursor), sdl_PointerFreeCopyAll };
1825 return setCursor(CURSOR_IMAGE);
1828rdpPointer* SdlContext::cursor()
const
1830 return _cursor.get();
1833bool SdlContext::restoreCursor()
1835 WLog_Print(getWLog(), WLOG_TRACE,
"restore cursor: %d", _cursorType);
1836 switch (_cursorType)
1839 if (!SDL_HideCursor())
1841 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_HideCursor failed");
1845 setHasCursor(
false);
1848 case CURSOR_DEFAULT:
1850 auto def = SDL_GetDefaultCursor();
1851 if (!SDL_SetCursor(def))
1853 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_SetCursor(default=%p) failed",
1854 static_cast<void*
>(def));
1857 if (!SDL_ShowCursor())
1859 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_ShowCursor failed");
1867 return sdl_Pointer_Set_Process(
this);
1869 WLog_Print(getWLog(), WLOG_ERROR,
"Unknown cursorType %s",
1870 sdl::utils::toString(_cursorType).c_str());
1875void SdlContext::setMonitorIds(
const std::vector<SDL_DisplayID>& ids)
1877 _monitorIds.clear();
1880 _monitorIds.push_back(
id);
1884const std::vector<SDL_DisplayID>& SdlContext::monitorIds()
const
1889int64_t SdlContext::monitorId(uint32_t index)
const
1891 if (index >= _monitorIds.size())
1895 return _monitorIds.at(index);
1898void SdlContext::push(std::vector<SDL_Rect>&& rects)
1900 std::unique_lock lock(_queue_mux);
1901 _queue.emplace(std::move(rects));
1904std::vector<SDL_Rect> SdlContext::pop()
1906 std::unique_lock lock(_queue_mux);
1911 auto val = std::move(_queue.front());
1916bool SdlContext::setFullscreen(
bool enter,
bool forceOriginalDisplay)
1918 for (
const auto& window : _windows)
1920 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_FULLSCREEN, &window.second, enter,
1921 forceOriginalDisplay))
1924 _fullscreen = enter;
1928bool SdlContext::setMinimized()
1930 return sdl_push_user_event(SDL_EVENT_USER_WINDOW_MINIMIZE);
1933bool SdlContext::grabMouse()
const
1938bool SdlContext::toggleGrabMouse()
1940 return setGrabMouse(!grabMouse());
1943bool SdlContext::setGrabMouse(
bool enter)
1949bool SdlContext::grabKeyboard()
const
1951 return _grabKeyboard;
1954bool SdlContext::toggleGrabKeyboard()
1956 return setGrabKeyboard(!grabKeyboard());
1959bool SdlContext::setGrabKeyboard(
bool enter)
1961 _grabKeyboard = enter;
1965bool SdlContext::setResizeable(
bool enable)
1967 const auto settings = context()->settings;
1970 bool use = (dyn && enable) || smart;
1972 for (
const auto& window : _windows)
1974 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_RESIZEABLE, &window.second, use))
1982bool SdlContext::resizeable()
const
1987bool SdlContext::toggleResizeable()
1989 return setResizeable(!resizeable());
1992bool SdlContext::fullscreen()
const
1997bool SdlContext::toggleFullscreen()
1999 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.