25#include "sdl_context.hpp"
26#include "sdl_config.hpp"
27#include "sdl_channels.hpp"
28#include "sdl_monitor.hpp"
29#include "sdl_pointer.hpp"
30#include "sdl_touch.hpp"
31#include "sdl_prefs.hpp"
33#include <sdl_common_utils.hpp>
34#include <scoped_guard.hpp>
36#include "dialogs/sdl_dialogs.hpp"
37#include <freerdp/client/aad_helper.h>
39static constexpr auto sdl_allow_screensaver =
"sdl-allow-screensaver";
40static constexpr auto sdl_connection_dialog =
"sdl-connection-dialog";
42SdlContext::SdlContext(rdpContext* context)
43 : _context(context), _log(WLog_Get(CLIENT_TAG(
"SDL"))), _cursor(nullptr, sdl_Pointer_FreeCopy),
44 _rdpThreadRunning(false), _primary(nullptr, SDL_DestroySurface), _disp(this), _input(this),
45 _clip(this), _rail(this), _dialog(_log)
47 WINPR_ASSERT(context);
50 auto instance = _context->instance;
51 WINPR_ASSERT(instance);
53 instance->PreConnect = preConnect;
54 instance->PostConnect = postConnect;
55 instance->PostDisconnect = postDisconnect;
56 instance->PostFinalDisconnect = postFinalDisconnect;
57 instance->AuthenticateEx = sdl_authenticate_ex;
58 instance->VerifyCertificateEx = sdl_verify_certificate_ex;
59 instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
60 instance->LogonErrorInfo = sdl_logon_error_info;
61 instance->PresentGatewayMessage = sdl_present_gateway_message;
62 instance->ChooseSmartcard = sdl_choose_smartcard;
63 instance->RetryDialog = sdl_retry_dialog;
64 instance->GetAccessToken = client_failsafe_get_access_token;
68 _args.push_back({ sdl_allow_screensaver, COMMAND_LINE_VALUE_BOOL,
nullptr, BoolValueFalse,
69 nullptr, -1,
nullptr,
"Allow local screensaver to activate" });
71 _args.push_back({ sdl_connection_dialog, COMMAND_LINE_VALUE_BOOL,
nullptr,
"on",
nullptr, -1,
72 nullptr,
"the connection dialog with progress and errors" });
75 _args.push_back({
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
nullptr });
78void SdlContext::setHasCursor(
bool val)
80 this->_cursor_visible = val;
83bool SdlContext::hasCursor()
const
85 return _cursor_visible;
88void SdlContext::setMetadata()
91 if (!wmclass || (strlen(wmclass) == 0))
92 wmclass = SDL_CLIENT_UUID;
94 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, wmclass);
95 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, SDL_CLIENT_NAME);
96 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, SDL_CLIENT_VERSION);
97 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, SDL_CLIENT_VENDOR);
98 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, SDL_CLIENT_COPYRIGHT);
99 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, SDL_CLIENT_URL);
100 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, SDL_CLIENT_TYPE);
103int SdlContext::start()
105 _thread = std::thread(rdpThreadRun,
this);
109int SdlContext::join()
113 HANDLE
event = freerdp_abort_event(context());
114 if (!SetEvent(event))
121void SdlContext::cleanup()
123 std::unique_lock lock(_critical);
129bool SdlContext::shallAbort(
bool ignoreDialogs)
131 std::unique_lock lock(_critical);
132 if (freerdp_shall_disconnect_context(context()))
136 if (_rdpThreadRunning)
138 return !getDialog().isRunning();
145BOOL SdlContext::preConnect(freerdp* instance)
147 WINPR_ASSERT(instance);
148 WINPR_ASSERT(instance->context);
150 auto sdl = get_context(instance->context);
152 auto settings = instance->context->settings;
153 WINPR_ASSERT(settings);
168 if (PubSub_SubscribeChannelConnected(instance->context->pubSub,
169 sdl_OnChannelConnectedEventHandler) < 0)
171 if (PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
172 sdl_OnChannelDisconnectedEventHandler) < 0)
174 if (PubSub_SubscribeUserNotification(instance->context->pubSub,
175 sdl_OnUserNotificationEventHandler) < 0)
181 UINT32 maxHeight = 0;
183 if (!sdl_detect_monitors(
sdl, &maxWidth, &maxHeight))
186 if ((maxWidth != 0) && (maxHeight != 0) &&
189 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Update size to %ux%u", maxWidth, maxHeight);
204 if (sm && (sw > 0) && (sh > 0))
208 WLog_Print(
sdl->getWLog(), WLOG_WARN,
209 "/smart-sizing and /multimon are currently not supported, ignoring "
228 WLog_Print(
sdl->getWLog(), WLOG_INFO,
229 "auth-only, but no password set. Please provide one.");
236 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect SDL.");
239 if (!
sdl->getInputChannelContext().initialize())
242 sdl->_credentialsRead =
false;
245 auto prefs = SdlPref::instance();
246 auto dialog = prefs->get_bool(
"SDL_ConnectionDialog",
true);
247 if (
sdl->_connectionDialog && prefs->is_commandline_override_allowed())
248 dialog = *
sdl->_connectionDialog;
251 sdl->getDialog().create(instance->context);
253 sdl->getDialog().showInfo(
"The connection is being established\n\nPlease wait...");
268BOOL SdlContext::postConnect(freerdp* instance)
270 WINPR_ASSERT(instance);
272 auto context = instance->context;
273 WINPR_ASSERT(context);
275 auto sdl = get_context(context);
278 sdl->getDialog().show(
false);
285 WLog_Print(
sdl->getWLog(), WLOG_INFO,
286 "auth-only, but no password set. Please provide one.");
290 WLog_Print(
sdl->getWLog(), WLOG_INFO,
"Authentication only. Don't connect to X.");
294 if (!
sdl->waitForWindowsCreated())
297 sdl->_sdlPixelFormat = SDL_PIXELFORMAT_BGRA32;
298 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
301 if (!
sdl->createPrimary())
304 if (!sdl_register_pointer(instance->context->graphics))
307 WINPR_ASSERT(context->update);
309 context->update->BeginPaint = beginPaint;
310 context->update->EndPaint = endPaint;
311 context->update->PlaySound = playSound;
312 context->update->DesktopResize = desktopResize;
313 context->update->SetKeyboardIndicators = sdlInput::keyboard_set_indicators;
314 context->update->SetKeyboardImeStatus = sdlInput::keyboard_set_ime_status;
316 if (!
sdl->setResizeable(
false))
322 sdl->setConnected(
true);
329void SdlContext::postDisconnect(freerdp* instance)
334 if (!instance->context)
337 auto sdl = get_context(instance->context);
338 sdl->setConnected(
false);
343void SdlContext::postFinalDisconnect(freerdp* instance)
348 if (!instance->context)
351 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
352 sdl_OnChannelConnectedEventHandler);
353 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
354 sdl_OnChannelDisconnectedEventHandler);
355 PubSub_UnsubscribeUserNotification(instance->context->pubSub,
356 sdl_OnUserNotificationEventHandler);
360bool SdlContext::createPrimary()
362 auto gdi = context()->gdi;
365 _primary = SDLSurfacePtr(
366 SDL_CreateSurfaceFrom(
static_cast<int>(gdi->width),
static_cast<int>(gdi->height),
367 pixelFormat(), gdi->primary_buffer,
static_cast<int>(gdi->stride)),
372 SDL_SetSurfaceBlendMode(_primary.get(), SDL_BLENDMODE_NONE);
373 SDL_Rect surfaceRect = { 0, 0, gdi->width, gdi->height };
374 SDL_FillSurfaceRect(_primary.get(), &surfaceRect,
375 SDL_MapSurfaceRGBA(_primary.get(), 0, 0, 0, 0xff));
380bool SdlContext::createWindows()
382 auto settings = context()->settings;
383 const auto& title = windowTitle();
385 ScopeGuard guard1([&]() { _windowsCreatedEvent.set(); });
391 auto monitorFor = [&](UINT32 index, int64_t id)
393 for (UINT32 y = 0; y < windowCount; y++)
396 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, y));
397 if (cur && (
static_cast<int64_t
>(cur->orig_screen) ==
id))
401 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, index));
406 for (UINT32 x = 0; x < windowCount; x++)
408 auto id = monitorId(x);
412 auto monitor = monitorFor(x,
id);
414 originX = std::min<Sint32>(monitor->x, originX);
415 originY = std::min<Sint32>(monitor->y, originY);
418 for (UINT32 x = 0; x < windowCount; x++)
420 auto id = monitorId(x);
424 auto monitor = monitorFor(x,
id);
426 Uint32 w = WINPR_ASSERTING_INT_CAST(Uint32, monitor->width);
427 Uint32 h = WINPR_ASSERTING_INT_CAST(Uint32, monitor->height);
431 if (_windowWidth > 0)
436 if (_windowHeight > 0)
442 Uint32 flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
447 flags |= SDL_WINDOW_FULLSCREEN;
452 flags |= SDL_WINDOW_BORDERLESS;
456 flags |= SDL_WINDOW_BORDERLESS;
460 flags |= SDL_WINDOW_HIDDEN;
462 auto did = WINPR_ASSERTING_INT_CAST(SDL_DisplayID,
id);
463 auto window = SdlWindow::create(did, title, flags, w, h);
467 window.setOffsetX(originX - monitor->x);
468 window.setOffsetY(originY - monitor->y);
471 _windows.insert({ window.id(), std::move(window) });
477#if defined(__EMSCRIPTEN__)
479extern "C" int freerdp_web_monitors_js(int32_t* edges,
int max);
485static std::vector<rdpMonitor> sdl_web_monitors(
const rdpMonitor& canvas)
487 constexpr int max = 16;
488 std::array<int32_t, 5 * max> edges{};
489 const auto n = freerdp_web_monitors_js(edges.data(), max);
490 if ((n < 2) || (n > max))
493 const auto at = [](int32_t edge, int32_t size)
494 {
return static_cast<INT32
>(
static_cast<int64_t
>(edge) * size / 10000); };
495 std::vector<rdpMonitor> list;
496 for (
int i = 0; i < n; i++)
498 const int32_t* e = &edges.at(5 *
static_cast<size_t>(i));
499 const auto left = at(e[0], canvas.width);
500 const auto top = at(e[1], canvas.height);
501 const auto right = at(e[2], canvas.width);
502 const auto bottom = at(e[3], canvas.height);
503 if ((right <= left) || (bottom <= top))
509 m.width = right - left;
510 m.height = bottom - top;
511 m.is_primary = (e[4] != 0);
512 m.attributes.physicalWidth =
513 static_cast<UINT32
>(
static_cast<uint64_t
>(canvas.attributes.physicalWidth) *
514 static_cast<UINT32
>(m.width) /
static_cast<UINT32
>(canvas.width));
515 m.attributes.physicalHeight =
516 static_cast<UINT32
>(
static_cast<uint64_t
>(canvas.attributes.physicalHeight) *
517 static_cast<UINT32
>(m.height) /
static_cast<UINT32
>(canvas.height));
524bool SdlContext::updateWindowList()
526 std::vector<rdpMonitor> list;
527 list.reserve(_windows.size());
528 for (
const auto& win : _windows)
529 list.push_back(win.second.monitor(_windows.size() == 1));
531#if defined(__EMSCRIPTEN__)
532 if ((list.size() == 1) && (list.at(0).width > 0) && (list.at(0).height > 0))
534 auto split = sdl_web_monitors(list.at(0));
536 list = std::move(split);
544 std::none_of(list.cbegin(), list.cend(), [](
const rdpMonitor& m) { return m.is_primary; }))
545 list.at(0).is_primary =
true;
551bool SdlContext::updateWindow(SDL_WindowID
id)
559 auto& w = _windows.at(
id);
560 auto m = w.monitor(
true);
568std::string SdlContext::windowTitle()
const
570 const char* prefix =
"FreeRDP:";
578 const auto addPort = (port != 3389);
580 std::stringstream ss;
581 ss << prefix <<
" " << name;
589bool SdlContext::waitForWindowsCreated()
592 std::unique_lock<CriticalSection> lock(_critical);
593 _windowsCreatedEvent.clear();
594 if (!sdl_push_user_event(SDL_EVENT_USER_CREATE_WINDOWS,
this))
598 HANDLE handles[] = { _windowsCreatedEvent.handle(), freerdp_abort_event(context()) };
600 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
614BOOL SdlContext::endPaint(rdpContext* context)
616 auto sdl = get_context(context);
619 auto gdi = context->gdi;
621 WINPR_ASSERT(gdi->primary);
623 HGDI_DC hdc = gdi->primary->hdc;
629 WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
631 if (hwnd->invalid->null)
634 WINPR_ASSERT(hwnd->invalid);
635 if (gdi->suppressOutput || hwnd->invalid->null)
638 const INT32 ninvalid = hwnd->ninvalid;
639 const GDI_RGN* cinvalid = hwnd->cinvalid;
644 std::vector<SDL_Rect> rects;
645 for (INT32 x = 0; x < ninvalid; x++)
647 auto& rgn = cinvalid[x];
648 rects.push_back({ rgn.x, rgn.y, rgn.w, rgn.h });
651 sdl->push(std::move(rects));
652 return sdl_push_user_event(SDL_EVENT_USER_UPDATE);
655void SdlContext::sdl_client_cleanup(
int exit_code,
const std::string& error_msg)
657 rdpSettings* settings = context()->settings;
658 WINPR_ASSERT(settings);
660 _rdpThreadRunning =
false;
661 bool showError =
false;
663 WLog_Print(getWLog(), WLOG_INFO,
"Authentication only, exit status %s [%" PRId32
"]",
664 sdl::error::exitCodeToTag(exit_code), exit_code);
669 case sdl::error::SUCCESS:
670 case sdl::error::DISCONNECT:
671 case sdl::error::LOGOFF:
672 case sdl::error::DISCONNECT_BY_USER:
673 case sdl::error::CONNECT_CANCELLED:
678 getDialog().showError(error_msg);
685 getDialog().show(
false);
687 _exitCode = exit_code;
688 std::ignore = sdl_push_user_event(SDL_EVENT_USER_QUIT);
692int SdlContext::sdl_client_thread_connect(std::string& error_msg)
694 auto instance = context()->instance;
695 WINPR_ASSERT(instance);
697 _rdpThreadRunning =
true;
698 BOOL rc = freerdp_connect(instance);
700 rdpSettings* settings = context()->settings;
701 WINPR_ASSERT(settings);
703 int exit_code = sdl::error::SUCCESS;
706 UINT32 error = freerdp_get_last_error(context());
707 exit_code = sdl::error::errorToExitCode(error);
712 DWORD code = freerdp_get_last_error(context());
713 freerdp_abort_connect_context(context());
714 WLog_Print(getWLog(), WLOG_ERROR,
"Authentication only, %s [0x%08" PRIx32
"] %s",
715 freerdp_get_last_error_name(code), code, freerdp_get_last_error_string(code));
721 DWORD code = freerdp_error_info(instance);
722 if (exit_code == sdl::error::SUCCESS)
726 exit_code = error_info_to_error(&code, &msg, &len);
732 auto last = freerdp_get_last_error(context());
733 if (error_msg.empty())
737 winpr_asprintf(&msg, &len,
"%s [0x%08" PRIx32
"]\n%s",
738 freerdp_get_last_error_name(last), last,
739 freerdp_get_last_error_string(last));
745 if (exit_code == sdl::error::SUCCESS)
747 if (last == FREERDP_ERROR_AUTHENTICATION_FAILED)
748 exit_code = sdl::error::AUTH_FAILURE;
749 else if (code == ERRINFO_SUCCESS)
750 exit_code = sdl::error::CONN_FAILED;
753 getDialog().show(
false);
759int SdlContext::sdl_client_thread_run(std::string& error_msg)
761 auto instance = context()->instance;
762 WINPR_ASSERT(instance);
764 int exit_code = sdl::error::SUCCESS;
765 while (!freerdp_shall_disconnect_context(context()))
767 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
773 if (freerdp_focus_required(instance))
775 auto ctx = get_context(context());
778 auto& input = ctx->getInputChannelContext();
779 if (!input.keyboard_focus_in())
781 if (!input.keyboard_focus_in())
785 const DWORD nCount = freerdp_get_event_handles(context(), handles, ARRAYSIZE(handles));
789 WLog_Print(getWLog(), WLOG_ERROR,
"freerdp_get_event_handles failed");
793 const DWORD status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
795 if (status == WAIT_FAILED)
797 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects WAIT_FAILED");
801 if (!freerdp_check_event_handles(context()))
803 if (client_auto_reconnect(instance))
806 getDialog().show(
false);
815 if (freerdp_error_info(instance) == 0)
816 exit_code = sdl::error::errorToExitCode(freerdp_get_last_error(context()));
819 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
820 WLog_Print(getWLog(), WLOG_ERROR,
"WaitForMultipleObjects failed with %" PRIu32
"",
822 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
823 WLog_Print(getWLog(), WLOG_ERROR,
"Failed to check FreeRDP event handles");
828 if (exit_code == sdl::error::SUCCESS)
832 char* emsg =
nullptr;
834 exit_code = error_info_to_error(&code, &emsg, &elen);
840 if ((code == ERRINFO_LOGOFF_BY_USER) &&
841 (freerdp_get_disconnect_ultimatum(context()) == Disconnect_Ultimatum_user_requested))
843 const char* msg =
"Error info says user did not initiate but disconnect ultimatum says "
844 "they did; treat this as a user logoff";
846 char* emsg =
nullptr;
848 winpr_asprintf(&emsg, &elen,
"%s", msg);
854 WLog_Print(getWLog(), WLOG_INFO,
"%s", msg);
855 exit_code = sdl::error::LOGOFF;
859 if ((exit_code != sdl::error::SUCCESS) && error_msg.empty())
861 const auto last = freerdp_get_last_error(context());
864 winpr_asprintf(&msg, &len,
"%s [0x%08" PRIx32
"]\n%s", freerdp_get_last_error_name(last),
865 last, freerdp_get_last_error_string(last));
871 freerdp_disconnect(instance);
883 std::string error_msg;
884 int exit_code =
sdl->sdl_client_thread_connect(error_msg);
885 if (exit_code == sdl::error::SUCCESS)
886 exit_code =
sdl->sdl_client_thread_run(error_msg);
887 sdl->sdl_client_cleanup(exit_code, error_msg);
889 return static_cast<DWORD
>(exit_code);
892int SdlContext::error_info_to_error(DWORD* pcode,
char** msg,
size_t* len)
const
894 const DWORD code = freerdp_error_info(context()->instance);
895 const char* name = freerdp_get_error_info_name(code);
896 const char* str = freerdp_get_error_info_string(code);
897 const int exit_code = sdl::error::errorToExitCode(code);
899 winpr_asprintf(msg, len,
"Terminate with %s due to ERROR_INFO %s [0x%08" PRIx32
"]: %s",
900 sdl::error::errorToExitCodeTag(code), name, code, str);
901 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%s", *msg);
907void SdlContext::applyMonitorOffset(SDL_WindowID window,
float& x,
float& y)
const
912 auto w = getWindowForId(window);
913 x -=
static_cast<float>(w->offsetX());
914 y -=
static_cast<float>(w->offsetY());
917static bool alignX(
const SDL_Rect& a,
const SDL_Rect& b)
919 if (a.x + a.w == b.x)
921 if (b.x + b.w == a.x)
926static bool alignY(
const SDL_Rect& a,
const SDL_Rect& b)
928 if (a.y + a.h == b.y)
930 if (b.y + b.h == a.y)
935std::vector<SDL_DisplayID>
936SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID
id,
937 const std::vector<SDL_DisplayID>& ignore)
939 auto first = _offsets.at(
id);
940 std::vector<SDL_DisplayID> neighbours;
942 for (
auto& entry : _offsets)
944 if (entry.first ==
id)
946 if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end())
949 bool neighbor =
false;
950 const bool ax = alignX(entry.second.first, first.first);
951 const bool ay = alignY(entry.second.first, first.first);
959 auto crossOffset = [](
int ePos,
int fPos,
int fLogical,
int fPixel) ->
int
963 return static_cast<int>(
964 std::lround(
static_cast<double>(ePos - fPos) * fPixel / fLogical));
969 if (entry.second.first.x < first.first.x)
970 entry.second.second.x = first.second.x - entry.second.second.w;
972 entry.second.second.x = first.second.x + first.second.w;
974 entry.second.second.y =
976 crossOffset(entry.second.first.y, first.first.y, first.first.h, first.second.h);
981 if (entry.second.first.y < first.first.y)
982 entry.second.second.y = first.second.y - entry.second.second.h;
984 entry.second.second.y = first.second.y + first.second.h;
986 entry.second.second.x =
988 crossOffset(entry.second.first.x, first.first.x, first.first.w, first.second.w);
993 neighbours.push_back(entry.first);
998void SdlContext::updateMonitorDataFromOffsets()
1000 for (
auto& entry : _displays)
1002 auto offsets = _offsets.at(entry.first);
1003 entry.second.x = offsets.second.x;
1004 entry.second.y = offsets.second.y;
1007 for (
auto& entry : _windows)
1009 const auto& monitor = _displays.at(entry.first);
1010 entry.second.setMonitor(monitor);
1014bool SdlContext::drawToWindow(
SdlWindow& window,
const std::vector<SDL_Rect>& rects)
1019 auto gdi = context()->gdi;
1022 auto size = window.rect();
1024 std::unique_lock lock(_critical);
1025 auto surface = _primary.get();
1027 if (useLocalScale())
1029 window.setOffsetX(0);
1030 window.setOffsetY(0);
1031 if (gdi->width < size.w)
1033 window.setOffsetX((size.w - gdi->width) / 2);
1035 if (gdi->height < size.h)
1037 window.setOffsetY((size.h - gdi->height) / 2);
1040 _localScale = {
static_cast<float>(size.w) /
static_cast<float>(gdi->width),
1041 static_cast<float>(size.h) /
static_cast<float>(gdi->height) };
1042 if (!window.drawScaledRects(surface, _localScale, rects))
1047 SDL_Point offset{ 0, 0 };
1049 offset = { window.offsetX(), window.offsetY() };
1050 if (!window.drawRects(surface, offset, rects))
1054 window.updateSurface();
1058bool SdlContext::minimizeAllWindows()
1060 for (
auto& w : _windows)
1061 w.second.minimize();
1065int SdlContext::exitCode()
const
1070SDL_PixelFormat SdlContext::pixelFormat()
const
1072 return _sdlPixelFormat;
1075bool SdlContext::addDisplayWindow(SDL_DisplayID
id)
1078 SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
1079 auto title = sdl::utils::windowTitle(context()->settings);
1080 auto w = SdlWindow::create(
id, title, flags);
1081 _windows.emplace(w.id(), std::move(w));
1085bool SdlContext::removeDisplayWindow(SDL_DisplayID
id)
1087 for (
auto& w : _windows)
1089 if (w.second.displayIndex() ==
id)
1090 _windows.erase(w.first);
1095bool SdlContext::detectDisplays()
1098 auto display = SDL_GetDisplays(&count);
1101 for (
int x = 0; x < count; x++)
1103 const auto id = display[x];
1104 addOrUpdateDisplay(
id);
1110rdpMonitor SdlContext::getDisplay(SDL_DisplayID
id)
const
1112 return _displays.at(
id);
1115std::vector<SDL_DisplayID> SdlContext::getDisplayIds()
const
1117 std::vector<SDL_DisplayID> keys;
1118 keys.reserve(_displays.size());
1119 for (
const auto& entry : _displays)
1121 keys.push_back(entry.first);
1126const SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
const
1128 auto it = _windows.find(
id);
1129 if (it == _windows.end())
1134SdlWindow* SdlContext::getWindowForId(SDL_WindowID
id)
1136 auto it = _windows.find(
id);
1137 if (it == _windows.end())
1142bool SdlContext::allWindowsHidden()
const
1144 return std::all_of(_windows.begin(), _windows.end(),
1145 [](
const auto& entry)
1147 const auto flags = SDL_GetWindowFlags(entry.second.window());
1148 return (flags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED)) != 0;
1154 if (_windows.empty())
1156 return &_windows.begin()->second;
1164sdlInput& SdlContext::getInputChannelContext()
1169sdlClip& SdlContext::getClipboardChannelContext()
1174SdlRail& SdlContext::getRailChannelContext()
1184wLog* SdlContext::getWLog()
1189bool SdlContext::moveMouseTo(
const SDL_FPoint& pos)
1191 auto window = SDL_GetMouseFocus();
1195 const auto id = SDL_GetWindowID(window);
1196 const auto spos = pixelToScreen(
id, pos);
1197 SDL_WarpMouseInWindow(window, spos.x, spos.y);
1201static bool onWindowEdge(SDL_WindowID
id,
float x,
float y)
1205 auto* window = SDL_GetWindowFromID(
id);
1206 if (!window || !SDL_GetWindowSize(window, &w, &h))
1208 return (x <= 0.0f) || (y <= 0.0f) || (x >=
static_cast<float>(w - 1)) ||
1209 (y >=
static_cast<float>(h - 1));
1212bool SdlContext::handleEvent(
const SDL_MouseMotionEvent& ev)
1217 if (_rail.enabled() && (ev.state == 0) && (ev.windowID == _lastRelease.window) &&
1218 (ev.timestamp - _lastRelease.time < SDL_MS_TO_NS(2)) &&
1219 onWindowEdge(ev.windowID, ev.x, ev.y))
1222 if (_rail.enabled() && _rail.suppressServerMotion(ev.windowID))
1224 if (!(SDL_GetGlobalMouseState(
nullptr,
nullptr) & SDL_BUTTON_LMASK))
1225 _rail.completeLocalMoveIfPending();
1228 if (_rail.enabled() && _rail.translateToServer(ev.windowID, copy.motion.x, copy.motion.y))
1230 _lastMotion = { ev.windowID, { ev.x, ev.y }, { copy.motion.x, copy.motion.y } };
1231 _rail.noteServerPointer(copy.motion.x, copy.motion.y);
1232 return SdlTouch::handleEvent(
this, copy.motion);
1235 if (!getWindowForId(ev.windowID))
1237 if (!eventToPixelCoordinates(ev.windowID, copy))
1239 removeLocalScaling(copy.motion.x, copy.motion.y);
1240 removeLocalScaling(copy.motion.xrel, copy.motion.yrel);
1241 applyMonitorOffset(copy.motion.windowID, copy.motion.x, copy.motion.y);
1243 return SdlTouch::handleEvent(
this, copy.motion);
1246bool SdlContext::handleEvent(
const SDL_MouseWheelEvent& ev)
1250 if (_rail.enabled() &&
1251 _rail.translateToServer(ev.windowID, copy.wheel.mouse_x, copy.wheel.mouse_y))
1252 return SdlTouch::handleEvent(
this, copy.wheel);
1254 if (!getWindowForId(ev.windowID))
1256 if (!eventToPixelCoordinates(ev.windowID, copy))
1258 removeLocalScaling(copy.wheel.mouse_x, copy.wheel.mouse_y);
1259 return SdlTouch::handleEvent(
this, copy.wheel);
1262bool SdlContext::handleEvent(
const SDL_WindowEvent& ev)
1264 if (!getDisplayChannelContext().handleEvent(ev))
1267 auto window = getWindowForId(ev.windowID);
1271 if (_rail.enabled() && _rail.ownsWindow(ev.windowID))
1275 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1277 if (!(SDL_GetGlobalMouseState(
nullptr,
nullptr) & SDL_BUTTON_LMASK))
1278 _rail.completeLocalMoveIfPending();
1279 _rail.completeWaylandResize();
1281 return restoreCursor();
1282 case SDL_EVENT_WINDOW_MOUSE_LEAVE:
1284 _rail.noteResizeGrab(ev.windowID);
1286 case SDL_EVENT_WINDOW_EXPOSED:
1289 if (!_rail.dragActive(ev.windowID))
1290 _rail.invalidateWindow(ev.windowID);
1292 case SDL_EVENT_WINDOW_MAXIMIZED:
1294 _rail.completeLocalMoveIfPending();
1295 _rail.handleMaximized(ev.windowID);
1297 case SDL_EVENT_WINDOW_MINIMIZED:
1298 _rail.handleMinimized(ev.windowID);
1300 case SDL_EVENT_WINDOW_RESTORED:
1301 _rail.handleRestored(ev.windowID);
1303 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1305 _rail.handleClose(ev.windowID);
1307 case SDL_EVENT_WINDOW_FOCUS_GAINED:
1308 _rail.handleFocus(ev.windowID,
true);
1310 case SDL_EVENT_WINDOW_FOCUS_LOST:
1311 _rail.handleFocus(ev.windowID,
false);
1313 case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
1314 _rail.handleDisplayChanged(ev.windowID,
static_cast<SDL_DisplayID
>(ev.data1));
1316 case SDL_EVENT_WINDOW_MOVED:
1317 _rail.notePopupMoved(ev.windowID);
1318 if (!_rail.dragActive(ev.windowID))
1319 _rail.syncGeometry(ev.windowID);
1321 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1324 _rail.noteDragResize(ev.windowID, ev.data1, ev.data2);
1325 _rail.syncGeometry(ev.windowID);
1326 _rail.noteResizeGrab(ev.windowID);
1327 std::ignore = drawToWindows({});
1337 const auto& r = window->rect();
1338 const auto& b = window->bounds();
1339 const auto& scale = window->scale();
1340 const auto& orientation = window->orientation();
1341 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1342 "%s: [%u] %dx%d-%dx%d {%dx%d-%dx%d}{scale=%f,orientation=%s}",
1343 sdl::utils::toString(ev.type).c_str(), ev.windowID, r.x, r.y, r.w, r.h, b.x,
1344 b.y, b.w, b.h,
static_cast<double>(scale),
1345 sdl::utils::toString(orientation).c_str());
1350 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1351 return restoreCursor();
1352 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1353 if (!resizeToScale(window))
1357 if (!window->fill())
1359 if (!drawToWindow(*window))
1361 if (!restoreCursor())
1365 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1366 if (!resizeToScale(window))
1368 if (!window->fill())
1370 if (!drawToWindow(*window))
1372 if (!restoreCursor())
1375 case SDL_EVENT_WINDOW_MOVED:
1377 auto r = window->rect();
1378 auto id = window->id();
1379 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"%u: %dx%d-%dx%d",
id, r.x, r.y, r.w, r.h);
1382 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1384 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"Window closed, terminating RDP session...");
1385 freerdp_abort_connect_context(context());
1394bool SdlContext::handleEvent(
const SDL_DisplayEvent& ev)
1396 if (!getDisplayChannelContext().handleEvent(ev))
1401 case SDL_EVENT_DISPLAY_REMOVED:
1406 if (!SDL_GetDisplayBounds(ev.displayID, &r))
1408 const auto name = SDL_GetDisplayName(ev.displayID);
1411 const auto orientation = SDL_GetCurrentDisplayOrientation(ev.displayID);
1412 const auto scale = SDL_GetDisplayContentScale(ev.displayID);
1413 const auto mode = SDL_GetCurrentDisplayMode(ev.displayID);
1417 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1418 "%s: [%u, %s] %dx%d-%dx%d {orientation=%s, scale=%f}%s",
1419 sdl::utils::toString(ev.type).c_str(), ev.displayID, name, r.x, r.y, r.w,
1420 r.h, sdl::utils::toString(orientation).c_str(),
static_cast<double>(scale),
1421 sdl::utils::toString(mode).c_str());
1428bool SdlContext::handleEvent(
const SDL_MouseButtonEvent& ev)
1430 SDL_Event copy = {};
1432 if (_rail.enabled())
1435 const bool suppress = _rail.dragActive(ev.windowID);
1436 if (ev.type == SDL_EVENT_MOUSE_BUTTON_UP)
1437 _rail.completeLocalMoveIfPending();
1440 if (ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
1441 _rail.completeWaylandResize(
true);
1443 if (ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
1444 _rail.ensureActive(ev.windowID);
1447 if (ev.type == SDL_EVENT_MOUSE_BUTTON_UP)
1449 _lastRelease = { ev.windowID, ev.timestamp };
1451 if (_rail.translateToServer(ev.windowID, copy.button.x, copy.button.y))
1454 if ((ev.windowID == _lastMotion.window) &&
1455 (fabsf(ev.x - _lastMotion.local.x) < FLT_EPSILON) &&
1456 (fabsf(ev.y - _lastMotion.local.y) < FLT_EPSILON))
1458 copy.button.x = _lastMotion.server.x;
1459 copy.button.y = _lastMotion.server.y;
1463 if ((ev.type == SDL_EVENT_MOUSE_BUTTON_DOWN) && (ev.button == SDL_BUTTON_LEFT))
1464 _rail.noteLeftPress(copy.button.x, copy.button.y);
1465 return SdlTouch::handleEvent(
this, copy.button);
1469 if (!getWindowForId(ev.windowID))
1471 if (!eventToPixelCoordinates(ev.windowID, copy))
1473 removeLocalScaling(copy.button.x, copy.button.y);
1474 applyMonitorOffset(copy.button.windowID, copy.button.x, copy.button.y);
1475 return SdlTouch::handleEvent(
this, copy.button);
1478bool SdlContext::handleEvent(
const SDL_TouchFingerEvent& ev)
1480 if (!getWindowForId(ev.windowID))
1484 if (!eventToPixelCoordinates(ev.windowID, copy))
1486 removeLocalScaling(copy.tfinger.dx, copy.tfinger.dy);
1487 removeLocalScaling(copy.tfinger.x, copy.tfinger.y);
1488 applyMonitorOffset(copy.tfinger.windowID, copy.tfinger.x, copy.tfinger.y);
1489 return SdlTouch::handleEvent(
this, copy.tfinger);
1492void SdlContext::addOrUpdateDisplay(SDL_DisplayID
id)
1494 auto monitor = SdlWindow::query(
id,
false);
1495 _displays.emplace(
id, monitor);
1505 for (
auto& entry : _displays)
1508 std::ignore = SDL_GetDisplayBounds(entry.first, &bounds);
1511 pixel.w = entry.second.width;
1512 pixel.h = entry.second.height;
1513 _offsets.emplace(entry.first, std::pair{ bounds, pixel });
1520 const auto primary = SDL_GetPrimaryDisplay();
1522 auto it = _offsets.find(primary);
1523 if (it != _offsets.end())
1525 it->second.second.x = it->second.first.x;
1526 it->second.second.y = it->second.first.y;
1528 std::vector<SDL_DisplayID> handled;
1529 handled.push_back(primary);
1531 auto neighbors = updateDisplayOffsetsForNeighbours(primary);
1532 while (!neighbors.empty())
1534 auto neighbor = neighbors.front();
1535 neighbors.erase(neighbors.begin());
1537 if (std::find(handled.begin(), handled.end(), neighbor) != handled.end())
1539 handled.push_back(neighbor);
1541 auto next = updateDisplayOffsetsForNeighbours(neighbor, handled);
1542 neighbors.insert(neighbors.end(), next.begin(), next.end());
1544 updateMonitorDataFromOffsets();
1547void SdlContext::deleteDisplay(SDL_DisplayID
id)
1549 _displays.erase(
id);
1552bool SdlContext::eventToPixelCoordinates(SDL_WindowID
id, SDL_Event& ev)
1554 auto w = getWindowForId(
id);
1559 auto renderer = w->renderer();
1562 return SDL_ConvertEventToRenderCoordinates(renderer, &ev);
1565SDL_FPoint SdlContext::applyLocalScaling(
const SDL_FPoint& val)
const
1567 if (!useLocalScale())
1571 rval.x *= _localScale.x;
1572 rval.y *= _localScale.y;
1576void SdlContext::removeLocalScaling(
float& x,
float& y)
const
1578 if (!useLocalScale())
1584SDL_FPoint SdlContext::screenToPixel(SDL_WindowID
id,
const SDL_FPoint& pos)
1586 auto w = getWindowForId(
id);
1590 if (_rail.enabled() && _rail.ownsWindow(
id))
1596 auto renderer = w->renderer();
1601 if (!SDL_RenderCoordinatesFromWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1603 removeLocalScaling(rpos.x, rpos.y);
1607SDL_FPoint SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FPoint& pos)
1609 auto w = getWindowForId(
id);
1612 if (_rail.enabled() && _rail.ownsWindow(
id))
1618 auto renderer = w->renderer();
1623 if (!SDL_RenderCoordinatesToWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1625 return applyLocalScaling(rpos);
1628SDL_FRect SdlContext::pixelToScreen(SDL_WindowID
id,
const SDL_FRect& pos,
bool round)
1630 const auto fpos = pixelToScreen(
id, SDL_FPoint{ pos.x, pos.y });
1631 const auto size = pixelToScreen(
id, SDL_FPoint{ pos.w, pos.h });
1632 SDL_FRect r{ fpos.x, fpos.y, size.x, size.y };
1635 r.w = std::ceil(r.w);
1636 r.h = std::ceil(r.h);
1637 r.x = std::floor(r.x);
1638 r.y = std::floor(r.y);
1643bool SdlContext::handleEvent(
const SDL_Event& ev)
1645 if ((ev.type >= SDL_EVENT_DISPLAY_FIRST) && (ev.type <= SDL_EVENT_DISPLAY_LAST))
1647 const auto& dev = ev.display;
1648 return handleEvent(dev);
1650 if ((ev.type >= SDL_EVENT_WINDOW_FIRST) && (ev.type <= SDL_EVENT_WINDOW_LAST))
1652 const auto& wev = ev.window;
1653 return handleEvent(wev);
1657 case SDL_EVENT_RENDER_TARGETS_RESET:
1658 case SDL_EVENT_RENDER_DEVICE_RESET:
1659 case SDL_EVENT_WILL_ENTER_FOREGROUND:
1670 case SDL_EVENT_FINGER_DOWN:
1671 case SDL_EVENT_FINGER_UP:
1672 case SDL_EVENT_FINGER_MOTION:
1674 const auto& cev = ev.tfinger;
1675 return handleEvent(cev);
1677 case SDL_EVENT_MOUSE_MOTION:
1680 const auto& cev = ev.motion;
1681 return handleEvent(cev);
1683 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1684 case SDL_EVENT_MOUSE_BUTTON_UP:
1686 const auto& cev = ev.button;
1687 return handleEvent(cev);
1689 case SDL_EVENT_MOUSE_WHEEL:
1691 const auto& cev = ev.wheel;
1692 return handleEvent(cev);
1694 case SDL_EVENT_CLIPBOARD_UPDATE:
1696 const auto& cev = ev.clipboard;
1697 return getClipboardChannelContext().handleEvent(cev);
1699 case SDL_EVENT_KEY_DOWN:
1700 case SDL_EVENT_KEY_UP:
1702 const auto& cev = ev.key;
1703 return getInputChannelContext().handleEvent(cev);
1712 return _args.data();
1715size_t SdlContext::argsCount()
const
1717 if (_args.size() <= 1)
1719 return _args.size() - 1;
1730 if (strcmp(arg->Name, sdl_allow_screensaver) == 0)
1732 if (arg->Value !=
nullptr)
1734 if (!SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER,
"1"))
1736 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
1737 "SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER) failed with %s",
1743 else if (strcmp(arg->Name, sdl_connection_dialog) == 0)
1744 sdl->_connectionDialog = (arg->Value !=
nullptr);
1754std::vector<rdpPointer*>& SdlContext::pointers()
1756 return _valid_pointers;
1759bool SdlContext::contains(
const rdpPointer* ptr)
const
1761 for (
const auto& cur : _valid_pointers)
1769bool SdlContext::credentialsRead()
const
1771 return _credentialsRead;
1774void SdlContext::setCredentialsRead()
1776 _credentialsRead =
true;
1779bool SdlContext::resizeToScale(
SdlWindow* window)
1783 if (!useLocalScale())
1787 return window->resizeToScale();
1790bool SdlContext::useLocalScale()
const
1795 const auto dynResize =
1799 return !dynResize && !fs && !multimon;
1802bool SdlContext::drawToWindows(
const std::vector<SDL_Rect>& rects)
1805 if (_rail.enabled())
1807 for (
auto& window : _windows)
1809 if ((SDL_GetWindowFlags(window.second.window()) & SDL_WINDOW_HIDDEN) == 0)
1810 SDL_HideWindow(window.second.window());
1813 std::unique_lock lock(_critical);
1814 _rail.paint(_primary.get(), pixelFormat(), rects);
1821 !_rail.desktopMayShow())
1823 bool firstShow =
false;
1824 for (
auto& window : _windows)
1826 if (SDL_GetWindowFlags(window.second.window()) & SDL_WINDOW_HIDDEN)
1828 SDL_ShowWindow(window.second.window());
1829 SDL_RaiseWindow(window.second.window());
1834 if (rects.empty() && !firstShow)
1837 std::vector<SDL_Rect> drawRects = rects;
1839 std::unique_lock lock(_critical);
1840 if ((drawRects.empty() || firstShow) && _primary)
1841 drawRects = { { 0, 0, _primary->w, _primary->h } };
1844 for (
auto& window : _windows)
1846 if (!drawToWindow(window.second, drawRects))
1853BOOL SdlContext::desktopResize(rdpContext* context)
1855 rdpGdi* gdi =
nullptr;
1856 rdpSettings* settings =
nullptr;
1857 auto sdl = get_context(context);
1860 WINPR_ASSERT(context);
1862 settings = context->settings;
1863 WINPR_ASSERT(settings);
1865 std::unique_lock lock(
sdl->_critical);
1870 return sdl->createPrimary();
1874BOOL SdlContext::playSound(rdpContext* context,
const PLAY_SOUND_UPDATE* play_sound)
1877 WINPR_UNUSED(context);
1878 WINPR_UNUSED(play_sound);
1884BOOL SdlContext::beginPaint(rdpContext* context)
1886 auto gdi = context->gdi;
1888 WINPR_ASSERT(gdi->primary);
1890 HGDI_DC hdc = gdi->primary->hdc;
1896 WINPR_ASSERT(hwnd->invalid);
1897 hwnd->invalid->null = TRUE;
1903bool SdlContext::redraw(
bool suppress)
const
1912 auto gdi = context()->gdi;
1914 return gdi_send_suppress_output(gdi, suppress);
1917void SdlContext::setConnected(
bool val)
1922bool SdlContext::isConnected()
const
1927rdpContext* SdlContext::context()
const
1929 WINPR_ASSERT(_context);
1933rdpClientContext* SdlContext::common()
const
1935 return reinterpret_cast<rdpClientContext*
>(context());
1938bool SdlContext::setCursor(CursorType type)
1941 return restoreCursor();
1944bool SdlContext::setCursor(
const rdpPointer* cursor)
1946 std::unique_lock lock(_critical);
1947 if (!contains(cursor))
1950 _cursor = { sdl_Pointer_Copy(cursor), sdl_PointerFreeCopyAll };
1951 return setCursor(CURSOR_IMAGE);
1954rdpPointer* SdlContext::cursor()
const
1956 return _cursor.get();
1959bool SdlContext::restoreCursor()
1961 WLog_Print(getWLog(), WLOG_TRACE,
"restore cursor: %d", _cursorType);
1962 switch (_cursorType)
1965 if (!SDL_HideCursor())
1967 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_HideCursor failed");
1971 setHasCursor(
false);
1974 case CURSOR_DEFAULT:
1976 auto def = SDL_GetDefaultCursor();
1977 if (!SDL_SetCursor(def))
1979 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_SetCursor(default=%p) failed",
1980 static_cast<void*
>(def));
1983 if (!SDL_ShowCursor())
1985 WLog_Print(getWLog(), WLOG_ERROR,
"SDL_ShowCursor failed");
1993 return sdl_Pointer_Set_Process(
this);
1995 WLog_Print(getWLog(), WLOG_ERROR,
"Unknown cursorType %s",
1996 sdl::utils::toString(_cursorType).c_str());
2001void SdlContext::setMonitorIds(
const std::vector<SDL_DisplayID>& ids)
2003 _monitorIds.clear();
2006 _monitorIds.push_back(
id);
2010const std::vector<SDL_DisplayID>& SdlContext::monitorIds()
const
2015int64_t SdlContext::monitorId(uint32_t index)
const
2017 if (index >= _monitorIds.size())
2021 return _monitorIds.at(index);
2024void SdlContext::push(std::vector<SDL_Rect>&& rects)
2026 std::unique_lock lock(_queue_mux);
2027 _queue.emplace(std::move(rects));
2030std::vector<SDL_Rect> SdlContext::pop()
2032 std::unique_lock lock(_queue_mux);
2037 auto val = std::move(_queue.front());
2042bool SdlContext::setFullscreen(
bool enter,
bool forceOriginalDisplay)
2044 for (
const auto& window : _windows)
2046 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_FULLSCREEN, &window.second, enter,
2047 forceOriginalDisplay))
2050 _fullscreen = enter;
2054bool SdlContext::setMinimized()
2056 return sdl_push_user_event(SDL_EVENT_USER_WINDOW_MINIMIZE);
2059bool SdlContext::grabMouse()
const
2064bool SdlContext::toggleGrabMouse()
2066 return setGrabMouse(!grabMouse());
2069bool SdlContext::setGrabMouse(
bool enter)
2075bool SdlContext::grabKeyboard()
const
2077 return _grabKeyboard;
2080bool SdlContext::toggleGrabKeyboard()
2082 return setGrabKeyboard(!grabKeyboard());
2085bool SdlContext::setGrabKeyboard(
bool enter)
2087 _grabKeyboard = enter;
2091bool SdlContext::setResizeable(
bool enable)
2093 const auto settings = context()->settings;
2096 bool use = (dyn && enable) || smart;
2098 for (
const auto& window : _windows)
2100 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_RESIZEABLE, &window.second, use))
2108bool SdlContext::resizeable()
const
2113bool SdlContext::toggleResizeable()
2115 return setResizeable(!resizeable());
2118bool SdlContext::fullscreen()
const
2123bool SdlContext::toggleFullscreen()
2125 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.