FreeRDP
Loading...
Searching...
No Matches
SDL3/sdl_freerdp.cpp
1
20#include <iostream>
21#include <memory>
22#include <mutex>
23
24#include <freerdp/config.h>
25
26#if defined(__has_include)
27#if __has_include(<version>)
28#include <version>
29#endif
30#endif
31
32#include <cerrno>
33#include <cstdio>
34#include <cstring>
35
36#if defined(__cpp_lib_source_location) && (__cpp_lib_source_location >= 201907L)
37#include <source_location>
38#endif
39
40#include <freerdp/constants.h>
41#include <freerdp/freerdp.h>
42#include <freerdp/gdi/gdi.h>
43#include <freerdp/streamdump.h>
44#include <freerdp/utils/signal.h>
45
46#include <freerdp/channels/channels.h>
47#include <freerdp/client/channels.h>
48#include <freerdp/client/cliprdr.h>
49#include <freerdp/client/cmdline.h>
50#include <freerdp/client/file.h>
51
52#include <freerdp/log.h>
53#include <winpr/assert.h>
54#include <winpr/config.h>
55#include <winpr/crt.h>
56#include <winpr/synch.h>
57
58#include <SDL3/SDL.h>
59#if !defined(__MINGW32__)
60#include <SDL3/SDL_main.h>
61#endif
62#include <SDL3/SDL_hints.h>
63#include <SDL3/SDL_oldnames.h>
64#include <SDL3/SDL_video.h>
65
66#include <sdl_config.hpp>
67
68#include "dialogs/sdl_connection_dialog_hider.hpp"
69#include "dialogs/sdl_dialogs.hpp"
70#include "scoped_guard.hpp"
71#include "sdl_channels.hpp"
72#include "sdl_freerdp.hpp"
73#include "sdl_context.hpp"
74#include "sdl_monitor.hpp"
75#include "sdl_pointer.hpp"
76#include "sdl_prefs.hpp"
77#if defined(_WIN32)
78#include "sdl_win32_console.hpp"
79#endif
80#include "sdl_utils.hpp"
81
82#if defined(_WIN32)
83#define SDL_TAG CLIENT_TAG("SDL")
84#endif
85
86class ErrorMsg : public std::exception
87{
88 public:
89 ErrorMsg(int rc, Uint32 type, const std::string& msg,
90 const std::string& sdlError = SDL_GetError()
91#if defined(__cpp_lib_source_location) && (__cpp_lib_source_location >= 201907L)
92 ,
93 std::source_location loc = std::source_location::current()
94#elif defined(__GNUC__)
95 ,
96 const std::string& file = __builtin_FILE(),
97 const std::string& fkt = __builtin_FUNCTION(), size_t line = __builtin_LINE(),
98#if defined(__clang__)
99 size_t column = __builtin_COLUMN()
100#else
101 size_t column = 0
102#endif
103#endif
104 );
105
106 [[nodiscard]] int rc() const;
107 [[nodiscard]] const char* what() const noexcept override;
108
109 private:
110 int _rc;
111 Uint32 _type;
112 std::string _msg;
113 std::string _sdlError;
114 std::string _buffer;
115
116 std::string _file;
117 std::string _fkt;
118 size_t _line = 0;
119 size_t _column = 0;
120};
121const char* ErrorMsg::what() const noexcept
122{
123 return _buffer.c_str();
124}
125
126int ErrorMsg::rc() const
127{
128 return _rc;
129}
130
131ErrorMsg::ErrorMsg(int rc, Uint32 type, const std::string& msg, const std::string& sdlError
132#if defined(__cpp_lib_source_location) && (__cpp_lib_source_location >= 201907L)
133 ,
134 std::source_location loc
135#elif defined(__GNUC__)
136 ,
137 const std::string& file, const std::string& fkt, size_t line, size_t column
138#endif
139 )
140 : _rc(rc), _type(type), _msg(msg), _sdlError(sdlError)
141#if defined(__cpp_lib_source_location) && (__cpp_lib_source_location >= 201907L)
142 ,
143 _file(loc.file_name()), _fkt(loc.function_name()), _line(loc.line()), _column(loc.column())
144#elif defined(__GNUC__)
145 ,
146 _file(file), _fkt(fkt), _line(line), _column(column)
147#endif
148{
149 std::stringstream ss;
150 ss << _msg << " {" << sdl::utils::toString(_type) << "}{SDL:" << sdlError << "}";
151 ss << "{" << _fkt << " [" << _file << ":" << _line << "-" << _column << "]}";
152 _buffer = ss.str();
153}
154
155static void sdl_term_handler([[maybe_unused]] int signum, [[maybe_unused]] const char* signame,
156 [[maybe_unused]] void* context)
157{
158 std::ignore = sdl_push_quit();
159}
160
161[[nodiscard]] static int sdl_run(SdlContext* sdl)
162{
163 int rc = -1;
164 WINPR_ASSERT(sdl);
165
166 try
167 {
168 while (!sdl->shallAbort())
169 {
170 SDL_Event windowEvent = {};
171 while (!sdl->shallAbort() && SDL_WaitEventTimeout(nullptr, 1000))
172 {
173 /* Only poll standard SDL events and SDL_EVENT_USERS meant to create
174 * dialogs. do not process the dialog return value events here.
175 */
176 const int prc = SDL_PeepEvents(&windowEvent, 1, SDL_GETEVENT, SDL_EVENT_FIRST,
177 SDL_EVENT_USER_RETRY_DIALOG);
178 if (prc < 0)
179 {
180 if (sdl_log_error(prc, sdl->getWLog(), "SDL_PeepEvents"))
181 continue;
182 }
183
184 WLog_Print(sdl->getWLog(), WLOG_TRACE, "got event %s [0x%08" PRIx32 "]",
185 sdl::utils::toString(windowEvent.type).c_str(), windowEvent.type);
186 /* The connection dialog paints on window events, so let it see them even
187 * after the session is gone: it may still show an error message. */
188 if (sdl->getDialog().handleEvent(windowEvent))
189 continue;
190
191 if (sdl->shallAbort(true))
192 continue;
193
194 if (!sdl->handleEvent(windowEvent))
195 throw ErrorMsg{ -1, windowEvent.type, "sdl->handleEvent" };
196
197 switch (windowEvent.type)
198 {
199 case SDL_EVENT_QUIT:
200 std::ignore = freerdp_abort_connect_context(sdl->context());
201 break;
202 case SDL_EVENT_USER_CERT_DIALOG:
203 {
205 auto title = static_cast<const char*>(windowEvent.user.data1);
206 auto msg = static_cast<const char*>(windowEvent.user.data2);
207 if (!sdl_cert_dialog_show(title, msg))
208 throw ErrorMsg{ -1, windowEvent.type, "sdl_cert_dialog_show" };
209 }
210 break;
211 case SDL_EVENT_USER_SHOW_DIALOG:
212 {
214 auto title = static_cast<const char*>(windowEvent.user.data1);
215 auto msg = static_cast<const char*>(windowEvent.user.data2);
216 if (!sdl_message_dialog_show(title, msg, windowEvent.user.code))
217 throw ErrorMsg{ -1, windowEvent.type, "sdl_message_dialog_show" };
218 }
219 break;
220 case SDL_EVENT_USER_SCARD_DIALOG:
221 {
223 auto title = static_cast<const char*>(windowEvent.user.data1);
224 auto msg = static_cast<const char**>(windowEvent.user.data2);
225 if (!sdl_scard_dialog_show(title, windowEvent.user.code, msg))
226 throw ErrorMsg{ -1, windowEvent.type, "sdl_scard_dialog_show" };
227 }
228 break;
229 case SDL_EVENT_USER_AUTH_DIALOG:
230 {
232 if (!sdl_auth_dialog_show(
233 reinterpret_cast<const SDL_UserAuthArg*>(windowEvent.padding)))
234 throw ErrorMsg{ -1, windowEvent.type, "sdl_auth_dialog_show" };
235 }
236 break;
237 case SDL_EVENT_USER_UPDATE:
238 {
239 std::vector<SDL_Rect> rectangles;
240 do
241 {
242 rectangles = sdl->pop();
243 if (!sdl->drawToWindows(rectangles))
244 throw ErrorMsg{ -1, windowEvent.type, "sdl->drawToWindows" };
245 } while (!rectangles.empty());
246 }
247 break;
248 case SDL_EVENT_USER_CREATE_WINDOWS:
249 {
250 auto ctx = static_cast<SdlContext*>(windowEvent.user.data1);
251 if (!ctx->createWindows())
252 throw ErrorMsg{ -1, windowEvent.type, "sdl->createWindows" };
253 }
254 break;
255 case SDL_EVENT_USER_WINDOW_RESIZEABLE:
256 {
257 auto window = static_cast<SdlWindow*>(windowEvent.user.data1);
258 const bool use = windowEvent.user.code != 0;
259 if (window)
260 window->resizeable(use);
261 }
262 break;
263 case SDL_EVENT_USER_WINDOW_FULLSCREEN:
264 {
265 auto window = static_cast<SdlWindow*>(windowEvent.user.data1);
266 const bool enter = windowEvent.user.code != 0;
267 const bool forceOriginalDisplay = windowEvent.user.data2 != nullptr;
268 if (window)
269 window->fullscreen(enter, forceOriginalDisplay);
270 }
271 break;
272 case SDL_EVENT_USER_WINDOW_MINIMIZE:
273 if (!sdl->minimizeAllWindows())
274 throw ErrorMsg{ -1, windowEvent.type, "sdl->minimizeAllWindows" };
275 break;
276 case SDL_EVENT_USER_POINTER_NULL:
277 if (!sdl->setCursor(SdlContext::CURSOR_NULL))
278 throw ErrorMsg{ -1, windowEvent.type, "sdl->setCursor" };
279 break;
280 case SDL_EVENT_USER_POINTER_DEFAULT:
281 if (!sdl->setCursor(SdlContext::CURSOR_DEFAULT))
282 throw ErrorMsg{ -1, windowEvent.type, "sdl->setCursor" };
283 break;
284 case SDL_EVENT_USER_POINTER_POSITION:
285 {
286 const auto x =
287 static_cast<INT32>(reinterpret_cast<uintptr_t>(windowEvent.user.data1));
288 const auto y =
289 static_cast<INT32>(reinterpret_cast<uintptr_t>(windowEvent.user.data2));
290 if (!sdl->moveMouseTo(
291 { static_cast<float>(x) * 1.0f, static_cast<float>(y) * 1.0f }))
292 throw ErrorMsg{ -1, windowEvent.type, "sdl->moveMouseTo" };
293 }
294 break;
295 case SDL_EVENT_USER_POINTER_SET:
296 if (!sdl->setCursor(static_cast<rdpPointer*>(windowEvent.user.data1)))
297 throw ErrorMsg{ -1, windowEvent.type, "sdl->setCursor" };
298 break;
299 case SDL_EVENT_USER_RAIL_MOVE:
300 {
301 const auto id = static_cast<uint32_t>(
302 reinterpret_cast<uintptr_t>(windowEvent.user.data1));
303 const auto moveType = static_cast<uint16_t>(windowEvent.user.code);
304 sdl->getRailChannelContext().handleLocalMoveRequested(id, moveType);
305 }
306 break;
307 case SDL_EVENT_USER_QUIT:
308 default:
309 break;
310 }
311 }
312 }
313 rc = 1;
314 }
315 catch (ErrorMsg& msg)
316 {
317 WLog_Print(sdl->getWLog(), WLOG_ERROR, "[exception] %s", msg.what());
318 rc = msg.rc();
319 }
320
321 return rc;
322}
323
324/* Optional global initializer.
325 * Here we just register a signal handler to print out stack traces
326 * if available. */
327[[nodiscard]] static BOOL sdl_client_global_init()
328{
329#if defined(_WIN32)
330 WSADATA wsaData = {};
331 const DWORD wVersionRequested = MAKEWORD(1, 1);
332 const int rc = WSAStartup(wVersionRequested, &wsaData);
333 if (rc != 0)
334 {
335 WLog_ERR(SDL_TAG, "WSAStartup failed with %s [%d]", gai_strerrorA(rc), rc);
336 return FALSE;
337 }
338#endif
339
340 return (freerdp_handle_signals() == 0);
341}
342
343/* Optional global tear down */
344static void sdl_client_global_uninit()
345{
346#if defined(_WIN32)
347 WSACleanup();
348#endif
349}
350
351[[nodiscard]] static BOOL sdl_client_new(freerdp* instance, rdpContext* context)
352{
353 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
354
355 if (!instance || !context)
356 return FALSE;
357
358 sdl->sdl = new SdlContext(context);
359 return sdl->sdl != nullptr;
360}
361
362static void sdl_client_free([[maybe_unused]] freerdp* instance, rdpContext* context)
363{
364 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
365
366 if (!context)
367 return;
368
369 delete sdl->sdl;
370}
371
372[[nodiscard]] static int sdl_client_start(rdpContext* context)
373{
374 auto sdl = get_context(context);
375 WINPR_ASSERT(sdl);
376 return sdl->start();
377}
378
379[[nodiscard]] static int sdl_client_stop(rdpContext* context)
380{
381 auto sdl = get_context(context);
382 WINPR_ASSERT(sdl);
383 return sdl->join();
384}
385
386static void RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
387{
388 WINPR_ASSERT(pEntryPoints);
389
390 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
391 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
392 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
393 pEntryPoints->GlobalInit = sdl_client_global_init;
394 pEntryPoints->GlobalUninit = sdl_client_global_uninit;
395 pEntryPoints->ContextSize = sizeof(sdl_rdp_context);
396 pEntryPoints->ClientNew = sdl_client_new;
397 pEntryPoints->ClientFree = sdl_client_free;
398 pEntryPoints->ClientStart = sdl_client_start;
399 pEntryPoints->ClientStop = sdl_client_stop;
400}
401
402static void context_free(sdl_rdp_context* sdl)
403{
404 if (sdl)
405 freerdp_client_context_free(&sdl->common.context);
406}
407
408[[nodiscard]] static const char* category2str(int category)
409{
410 switch (category)
411 {
412 case SDL_LOG_CATEGORY_APPLICATION:
413 return "SDL_LOG_CATEGORY_APPLICATION";
414 case SDL_LOG_CATEGORY_ERROR:
415 return "SDL_LOG_CATEGORY_ERROR";
416 case SDL_LOG_CATEGORY_ASSERT:
417 return "SDL_LOG_CATEGORY_ASSERT";
418 case SDL_LOG_CATEGORY_SYSTEM:
419 return "SDL_LOG_CATEGORY_SYSTEM";
420 case SDL_LOG_CATEGORY_AUDIO:
421 return "SDL_LOG_CATEGORY_AUDIO";
422 case SDL_LOG_CATEGORY_VIDEO:
423 return "SDL_LOG_CATEGORY_VIDEO";
424 case SDL_LOG_CATEGORY_RENDER:
425 return "SDL_LOG_CATEGORY_RENDER";
426 case SDL_LOG_CATEGORY_INPUT:
427 return "SDL_LOG_CATEGORY_INPUT";
428 case SDL_LOG_CATEGORY_TEST:
429 return "SDL_LOG_CATEGORY_TEST";
430 case SDL_LOG_CATEGORY_GPU:
431 return "SDL_LOG_CATEGORY_GPU";
432 case SDL_LOG_CATEGORY_RESERVED2:
433 return "SDL_LOG_CATEGORY_RESERVED2";
434 case SDL_LOG_CATEGORY_RESERVED3:
435 return "SDL_LOG_CATEGORY_RESERVED3";
436 case SDL_LOG_CATEGORY_RESERVED4:
437 return "SDL_LOG_CATEGORY_RESERVED4";
438 case SDL_LOG_CATEGORY_RESERVED5:
439 return "SDL_LOG_CATEGORY_RESERVED5";
440 case SDL_LOG_CATEGORY_RESERVED6:
441 return "SDL_LOG_CATEGORY_RESERVED6";
442 case SDL_LOG_CATEGORY_RESERVED7:
443 return "SDL_LOG_CATEGORY_RESERVED7";
444 case SDL_LOG_CATEGORY_RESERVED8:
445 return "SDL_LOG_CATEGORY_RESERVED8";
446 case SDL_LOG_CATEGORY_RESERVED9:
447 return "SDL_LOG_CATEGORY_RESERVED9";
448 case SDL_LOG_CATEGORY_RESERVED10:
449 return "SDL_LOG_CATEGORY_RESERVED10";
450 case SDL_LOG_CATEGORY_CUSTOM:
451 default:
452 return "SDL_LOG_CATEGORY_CUSTOM";
453 }
454}
455
456[[nodiscard]] static SDL_LogPriority wloglevel2dl(DWORD level)
457{
458 switch (level)
459 {
460 case WLOG_TRACE:
461 return SDL_LOG_PRIORITY_VERBOSE;
462 case WLOG_DEBUG:
463 return SDL_LOG_PRIORITY_DEBUG;
464 case WLOG_INFO:
465 return SDL_LOG_PRIORITY_INFO;
466 case WLOG_WARN:
467 return SDL_LOG_PRIORITY_WARN;
468 case WLOG_ERROR:
469 return SDL_LOG_PRIORITY_ERROR;
470 case WLOG_FATAL:
471 return SDL_LOG_PRIORITY_CRITICAL;
472 case WLOG_OFF:
473 default:
474 return SDL_LOG_PRIORITY_VERBOSE;
475 }
476}
477
478[[nodiscard]] static DWORD sdlpriority2wlog(SDL_LogPriority priority)
479{
480 DWORD level = WLOG_OFF;
481 switch (priority)
482 {
483 case SDL_LOG_PRIORITY_VERBOSE:
484 level = WLOG_TRACE;
485 break;
486 case SDL_LOG_PRIORITY_DEBUG:
487 level = WLOG_DEBUG;
488 break;
489 case SDL_LOG_PRIORITY_INFO:
490 level = WLOG_INFO;
491 break;
492 case SDL_LOG_PRIORITY_WARN:
493 level = WLOG_WARN;
494 break;
495 case SDL_LOG_PRIORITY_ERROR:
496 level = WLOG_ERROR;
497 break;
498 case SDL_LOG_PRIORITY_CRITICAL:
499 level = WLOG_FATAL;
500 break;
501 default:
502 break;
503 }
504
505 return level;
506}
507
508static void SDLCALL winpr_LogOutputFunction(void* userdata, int category, SDL_LogPriority priority,
509 const char* message)
510{
511 auto sdl = static_cast<SdlContext*>(userdata);
512 WINPR_ASSERT(sdl);
513
514 const DWORD level = sdlpriority2wlog(priority);
515 auto log = sdl->getWLog();
516 if (!WLog_IsLevelActive(log, level))
517 return;
518
519 WLog_PrintTextMessage(log, level, __LINE__, __FILE__, __func__, "[%s] %s",
520 category2str(category), message);
521}
522
523static void sdl_quit()
524{
525 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
526 SDL_Event ev = {};
527 ev.type = SDL_EVENT_QUIT;
528 if (!SDL_PushEvent(&ev))
529 {
530 SDL_LogError(cat, "An error occurred: %s", SDL_GetError());
531 }
532}
533
534static void SDLCALL rdp_file_cb(void* userdata, const char* const* filelist,
535 WINPR_ATTR_UNUSED int filter)
536{
537 auto rdp = static_cast<std::string*>(userdata);
538
539 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
540 if (!filelist)
541 {
542 SDL_LogError(cat, "An error occurred: %s", SDL_GetError());
543 sdl_quit();
544 return;
545 }
546 else if (!*filelist)
547 {
548 SDL_LogError(cat, "The user did not select any file.");
549 SDL_LogError(cat, "Most likely, the dialog was canceled.");
550 sdl_quit();
551 return;
552 }
553
554 while (*filelist)
555 {
556 SDL_LogError(cat, "Full path to selected file: '%s'", *filelist);
557 *rdp = *filelist;
558 filelist++;
559 }
560
561 sdl_quit();
562}
563
564[[nodiscard]] static std::string getRdpFile()
565{
566 const auto flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS;
567 SDL_DialogFileFilter filters[] = { { "RDP files", "rdp;rdpw" } };
568 std::string rdp;
569
570 bool running = true;
571 if (!SDL_Init(flags))
572 return {};
573
574 auto props = SDL_CreateProperties();
575 SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING,
576 "SDL Freerdp - Open a RDP file");
577 SDL_SetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false);
578 SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, filters);
579 SDL_SetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, ARRAYSIZE(filters));
580 SDL_ShowFileDialogWithProperties(SDL_FILEDIALOG_OPENFILE, rdp_file_cb, &rdp, props);
581 SDL_DestroyProperties(props);
582
583 do
584 {
585 SDL_Event event = {};
586 std::ignore = SDL_PollEvent(&event);
587
588 switch (event.type)
589 {
590 case SDL_EVENT_QUIT:
591 running = false;
592 break;
593 default:
594 break;
595 }
596 } while (running);
597 SDL_Quit();
598 return rdp;
599}
600
601int main(int argc, char* argv[])
602{
603#if defined(_WIN32)
604 sdl::win32::release_transient_console();
605#endif
606
607 int rc = -1;
608 RDP_CLIENT_ENTRY_POINTS clientEntryPoints = {};
609
610 /* Allocate the RDP context first, we need to pass it to SDL */
611 RdpClientEntry(&clientEntryPoints);
612 std::unique_ptr<sdl_rdp_context, void (*)(sdl_rdp_context*)> sdl_rdp(
613 reinterpret_cast<sdl_rdp_context*>(freerdp_client_context_new(&clientEntryPoints)),
614 context_free);
615
616 if (!sdl_rdp)
617 return -1;
618 auto sdl = sdl_rdp->sdl;
619
620 auto settings = sdl->context()->settings;
621 WINPR_ASSERT(settings);
622
623 std::string rdp_file;
624 std::vector<char*> args;
625 args.reserve(WINPR_ASSERTING_INT_CAST(size_t, argc));
626 for (auto x = 0; x < argc; x++)
627 args.push_back(argv[x]);
628
629 if (argc == 1)
630 {
631 rdp_file = getRdpFile();
632 if (!rdp_file.empty())
633 {
634 args.push_back(rdp_file.data());
635 }
636 }
637
638 auto status = freerdp_client_settings_parse_command_line_ex(
639 settings, WINPR_ASSERTING_INT_CAST(int, args.size()), args.data(), FALSE, sdl->args(),
640 sdl->argsCount(), &SdlContext::argumentHandler, sdl);
641 sdl_rdp->sdl->setMetadata();
642 if (status)
643 {
644 rc = freerdp_client_settings_command_line_status_print_ex(settings, status, argc, argv,
645 sdl->args());
646 if (freerdp_settings_get_bool(settings, FreeRDP_ListMonitors))
647 {
648 if (!sdl_list_monitors(sdl))
649 return -1;
650 }
651 else
652 {
653 switch (status)
654 {
655 case COMMAND_LINE_STATUS_PRINT:
656 case COMMAND_LINE_STATUS_PRINT_VERSION:
657 case COMMAND_LINE_STATUS_PRINT_BUILDCONFIG:
658 break;
659 case COMMAND_LINE_STATUS_PRINT_HELP:
660 SdlPref::print_config_file_help(3);
661 break;
662 default:
663 break;
664 }
665 }
666 return rc;
667 }
668
669 if (!SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"))
670 return -1;
671 if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"))
672 return -1;
673 if (!SDL_SetHint(SDL_HINT_PEN_MOUSE_EVENTS, "0"))
674 return -1;
675 if (!SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"))
676 return -1;
677 if (!SDL_SetHint(SDL_HINT_PEN_TOUCH_EVENTS, "1"))
678 return -1;
679 if (!SDL_SetHint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, "1"))
680 return -1;
681 if (!SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, "RDP session running"))
682 return -1;
683#if SDL_VERSION_ATLEAST(3, 4, 0)
684 if (!SDL_SetHint(SDL_HINT_MOUSE_DPI_SCALE_CURSORS, "1"))
685 return -1;
686#endif
687
688 /* Basic SDL initialization */
689 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
690 return -1;
691
692 /* Redirect SDL log messages to wLog */
693 SDL_SetLogOutputFunction(winpr_LogOutputFunction, sdl);
694 auto level = WLog_GetLogLevel(sdl->getWLog());
695 SDL_SetLogPriorities(wloglevel2dl(level));
696
697 auto backend = SDL_GetCurrentVideoDriver();
698 WLog_Print(sdl->getWLog(), WLOG_DEBUG, "client is using backend '%s'", backend);
699 sdl_dialogs_init();
700
701 /* SDL cleanup code if the client exits */
702 ScopeGuard guard(
703 [&]()
704 {
705 sdl->cleanup();
706 freerdp_del_signal_cleanup_handler(sdl->context(), sdl_term_handler);
707 sdl_dialogs_uninit();
708 SDL_Quit();
709 });
710 if (!sdl->detectDisplays())
711 return -1;
712
713 /* Initialize RDP */
714 auto context = sdl->context();
715 WINPR_ASSERT(context);
716
717 if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CREATE_REQUEST, FALSE))
718 return -1;
719
720 if (freerdp_client_start(context) != 0)
721 return -1;
722
723 rc = sdl_run(sdl);
724
725 if (freerdp_client_stop(context) != 0)
726 return -1;
727
728 if (sdl->exitCode() != 0)
729 rc = sdl->exitCode();
730
731 return rc;
732}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.