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 if (sdl->shallAbort(true))
187 continue;
188
189 if (sdl->getDialog().handleEvent(windowEvent))
190 continue;
191
192 if (!sdl->handleEvent(windowEvent))
193 throw ErrorMsg{ -1, windowEvent.type, "sdl->handleEvent" };
194
195 switch (windowEvent.type)
196 {
197 case SDL_EVENT_QUIT:
198 std::ignore = freerdp_abort_connect_context(sdl->context());
199 break;
200 case SDL_EVENT_USER_CERT_DIALOG:
201 {
203 auto title = static_cast<const char*>(windowEvent.user.data1);
204 auto msg = static_cast<const char*>(windowEvent.user.data2);
205 if (!sdl_cert_dialog_show(title, msg))
206 throw ErrorMsg{ -1, windowEvent.type, "sdl_cert_dialog_show" };
207 }
208 break;
209 case SDL_EVENT_USER_SHOW_DIALOG:
210 {
212 auto title = static_cast<const char*>(windowEvent.user.data1);
213 auto msg = static_cast<const char*>(windowEvent.user.data2);
214 if (!sdl_message_dialog_show(title, msg, windowEvent.user.code))
215 throw ErrorMsg{ -1, windowEvent.type, "sdl_message_dialog_show" };
216 }
217 break;
218 case SDL_EVENT_USER_SCARD_DIALOG:
219 {
221 auto title = static_cast<const char*>(windowEvent.user.data1);
222 auto msg = static_cast<const char**>(windowEvent.user.data2);
223 if (!sdl_scard_dialog_show(title, windowEvent.user.code, msg))
224 throw ErrorMsg{ -1, windowEvent.type, "sdl_scard_dialog_show" };
225 }
226 break;
227 case SDL_EVENT_USER_AUTH_DIALOG:
228 {
230 if (!sdl_auth_dialog_show(
231 reinterpret_cast<const SDL_UserAuthArg*>(windowEvent.padding)))
232 throw ErrorMsg{ -1, windowEvent.type, "sdl_auth_dialog_show" };
233 }
234 break;
235 case SDL_EVENT_USER_UPDATE:
236 {
237 std::vector<SDL_Rect> rectangles;
238 do
239 {
240 rectangles = sdl->pop();
241 if (!sdl->drawToWindows(rectangles))
242 throw ErrorMsg{ -1, windowEvent.type, "sdl->drawToWindows" };
243 } while (!rectangles.empty());
244 }
245 break;
246 case SDL_EVENT_USER_CREATE_WINDOWS:
247 {
248 auto ctx = static_cast<SdlContext*>(windowEvent.user.data1);
249 if (!ctx->createWindows())
250 throw ErrorMsg{ -1, windowEvent.type, "sdl->createWindows" };
251 }
252 break;
253 case SDL_EVENT_USER_WINDOW_RESIZEABLE:
254 {
255 auto window = static_cast<SdlWindow*>(windowEvent.user.data1);
256 const bool use = windowEvent.user.code != 0;
257 if (window)
258 window->resizeable(use);
259 }
260 break;
261 case SDL_EVENT_USER_WINDOW_FULLSCREEN:
262 {
263 auto window = static_cast<SdlWindow*>(windowEvent.user.data1);
264 const bool enter = windowEvent.user.code != 0;
265 const bool forceOriginalDisplay = windowEvent.user.data2 != nullptr;
266 if (window)
267 window->fullscreen(enter, forceOriginalDisplay);
268 }
269 break;
270 case SDL_EVENT_USER_WINDOW_MINIMIZE:
271 if (!sdl->minimizeAllWindows())
272 throw ErrorMsg{ -1, windowEvent.type, "sdl->minimizeAllWindows" };
273 break;
274 case SDL_EVENT_USER_POINTER_NULL:
275 if (!sdl->setCursor(SdlContext::CURSOR_NULL))
276 throw ErrorMsg{ -1, windowEvent.type, "sdl->setCursor" };
277 break;
278 case SDL_EVENT_USER_POINTER_DEFAULT:
279 if (!sdl->setCursor(SdlContext::CURSOR_DEFAULT))
280 throw ErrorMsg{ -1, windowEvent.type, "sdl->setCursor" };
281 break;
282 case SDL_EVENT_USER_POINTER_POSITION:
283 {
284 const auto x =
285 static_cast<INT32>(reinterpret_cast<uintptr_t>(windowEvent.user.data1));
286 const auto y =
287 static_cast<INT32>(reinterpret_cast<uintptr_t>(windowEvent.user.data2));
288 if (!sdl->moveMouseTo(
289 { static_cast<float>(x) * 1.0f, static_cast<float>(y) * 1.0f }))
290 throw ErrorMsg{ -1, windowEvent.type, "sdl->moveMouseTo" };
291 }
292 break;
293 case SDL_EVENT_USER_POINTER_SET:
294 if (!sdl->setCursor(static_cast<rdpPointer*>(windowEvent.user.data1)))
295 throw ErrorMsg{ -1, windowEvent.type, "sdl->setCursor" };
296 break;
297 case SDL_EVENT_USER_QUIT:
298 default:
299 break;
300 }
301 }
302 }
303 rc = 1;
304 }
305 catch (ErrorMsg& msg)
306 {
307 WLog_Print(sdl->getWLog(), WLOG_ERROR, "[exception] %s", msg.what());
308 rc = msg.rc();
309 }
310
311 return rc;
312}
313
314/* Optional global initializer.
315 * Here we just register a signal handler to print out stack traces
316 * if available. */
317[[nodiscard]] static BOOL sdl_client_global_init()
318{
319#if defined(_WIN32)
320 WSADATA wsaData = {};
321 const DWORD wVersionRequested = MAKEWORD(1, 1);
322 const int rc = WSAStartup(wVersionRequested, &wsaData);
323 if (rc != 0)
324 {
325 WLog_ERR(SDL_TAG, "WSAStartup failed with %s [%d]", gai_strerrorA(rc), rc);
326 return FALSE;
327 }
328#endif
329
330 return (freerdp_handle_signals() == 0);
331}
332
333/* Optional global tear down */
334static void sdl_client_global_uninit()
335{
336#if defined(_WIN32)
337 WSACleanup();
338#endif
339}
340
341[[nodiscard]] static BOOL sdl_client_new(freerdp* instance, rdpContext* context)
342{
343 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
344
345 if (!instance || !context)
346 return FALSE;
347
348 sdl->sdl = new SdlContext(context);
349 return sdl->sdl != nullptr;
350}
351
352static void sdl_client_free([[maybe_unused]] freerdp* instance, rdpContext* context)
353{
354 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
355
356 if (!context)
357 return;
358
359 delete sdl->sdl;
360}
361
362[[nodiscard]] static int sdl_client_start(rdpContext* context)
363{
364 auto sdl = get_context(context);
365 WINPR_ASSERT(sdl);
366 return sdl->start();
367}
368
369[[nodiscard]] static int sdl_client_stop(rdpContext* context)
370{
371 auto sdl = get_context(context);
372 WINPR_ASSERT(sdl);
373 return sdl->join();
374}
375
376static void RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
377{
378 WINPR_ASSERT(pEntryPoints);
379
380 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
381 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
382 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
383 pEntryPoints->GlobalInit = sdl_client_global_init;
384 pEntryPoints->GlobalUninit = sdl_client_global_uninit;
385 pEntryPoints->ContextSize = sizeof(sdl_rdp_context);
386 pEntryPoints->ClientNew = sdl_client_new;
387 pEntryPoints->ClientFree = sdl_client_free;
388 pEntryPoints->ClientStart = sdl_client_start;
389 pEntryPoints->ClientStop = sdl_client_stop;
390}
391
392static void context_free(sdl_rdp_context* sdl)
393{
394 if (sdl)
395 freerdp_client_context_free(&sdl->common.context);
396}
397
398[[nodiscard]] static const char* category2str(int category)
399{
400 switch (category)
401 {
402 case SDL_LOG_CATEGORY_APPLICATION:
403 return "SDL_LOG_CATEGORY_APPLICATION";
404 case SDL_LOG_CATEGORY_ERROR:
405 return "SDL_LOG_CATEGORY_ERROR";
406 case SDL_LOG_CATEGORY_ASSERT:
407 return "SDL_LOG_CATEGORY_ASSERT";
408 case SDL_LOG_CATEGORY_SYSTEM:
409 return "SDL_LOG_CATEGORY_SYSTEM";
410 case SDL_LOG_CATEGORY_AUDIO:
411 return "SDL_LOG_CATEGORY_AUDIO";
412 case SDL_LOG_CATEGORY_VIDEO:
413 return "SDL_LOG_CATEGORY_VIDEO";
414 case SDL_LOG_CATEGORY_RENDER:
415 return "SDL_LOG_CATEGORY_RENDER";
416 case SDL_LOG_CATEGORY_INPUT:
417 return "SDL_LOG_CATEGORY_INPUT";
418 case SDL_LOG_CATEGORY_TEST:
419 return "SDL_LOG_CATEGORY_TEST";
420 case SDL_LOG_CATEGORY_GPU:
421 return "SDL_LOG_CATEGORY_GPU";
422 case SDL_LOG_CATEGORY_RESERVED2:
423 return "SDL_LOG_CATEGORY_RESERVED2";
424 case SDL_LOG_CATEGORY_RESERVED3:
425 return "SDL_LOG_CATEGORY_RESERVED3";
426 case SDL_LOG_CATEGORY_RESERVED4:
427 return "SDL_LOG_CATEGORY_RESERVED4";
428 case SDL_LOG_CATEGORY_RESERVED5:
429 return "SDL_LOG_CATEGORY_RESERVED5";
430 case SDL_LOG_CATEGORY_RESERVED6:
431 return "SDL_LOG_CATEGORY_RESERVED6";
432 case SDL_LOG_CATEGORY_RESERVED7:
433 return "SDL_LOG_CATEGORY_RESERVED7";
434 case SDL_LOG_CATEGORY_RESERVED8:
435 return "SDL_LOG_CATEGORY_RESERVED8";
436 case SDL_LOG_CATEGORY_RESERVED9:
437 return "SDL_LOG_CATEGORY_RESERVED9";
438 case SDL_LOG_CATEGORY_RESERVED10:
439 return "SDL_LOG_CATEGORY_RESERVED10";
440 case SDL_LOG_CATEGORY_CUSTOM:
441 default:
442 return "SDL_LOG_CATEGORY_CUSTOM";
443 }
444}
445
446[[nodiscard]] static SDL_LogPriority wloglevel2dl(DWORD level)
447{
448 switch (level)
449 {
450 case WLOG_TRACE:
451 return SDL_LOG_PRIORITY_VERBOSE;
452 case WLOG_DEBUG:
453 return SDL_LOG_PRIORITY_DEBUG;
454 case WLOG_INFO:
455 return SDL_LOG_PRIORITY_INFO;
456 case WLOG_WARN:
457 return SDL_LOG_PRIORITY_WARN;
458 case WLOG_ERROR:
459 return SDL_LOG_PRIORITY_ERROR;
460 case WLOG_FATAL:
461 return SDL_LOG_PRIORITY_CRITICAL;
462 case WLOG_OFF:
463 default:
464 return SDL_LOG_PRIORITY_VERBOSE;
465 }
466}
467
468[[nodiscard]] static DWORD sdlpriority2wlog(SDL_LogPriority priority)
469{
470 DWORD level = WLOG_OFF;
471 switch (priority)
472 {
473 case SDL_LOG_PRIORITY_VERBOSE:
474 level = WLOG_TRACE;
475 break;
476 case SDL_LOG_PRIORITY_DEBUG:
477 level = WLOG_DEBUG;
478 break;
479 case SDL_LOG_PRIORITY_INFO:
480 level = WLOG_INFO;
481 break;
482 case SDL_LOG_PRIORITY_WARN:
483 level = WLOG_WARN;
484 break;
485 case SDL_LOG_PRIORITY_ERROR:
486 level = WLOG_ERROR;
487 break;
488 case SDL_LOG_PRIORITY_CRITICAL:
489 level = WLOG_FATAL;
490 break;
491 default:
492 break;
493 }
494
495 return level;
496}
497
498static void SDLCALL winpr_LogOutputFunction(void* userdata, int category, SDL_LogPriority priority,
499 const char* message)
500{
501 auto sdl = static_cast<SdlContext*>(userdata);
502 WINPR_ASSERT(sdl);
503
504 const DWORD level = sdlpriority2wlog(priority);
505 auto log = sdl->getWLog();
506 if (!WLog_IsLevelActive(log, level))
507 return;
508
509 WLog_PrintTextMessage(log, level, __LINE__, __FILE__, __func__, "[%s] %s",
510 category2str(category), message);
511}
512
513static void sdl_quit()
514{
515 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
516 SDL_Event ev = {};
517 ev.type = SDL_EVENT_QUIT;
518 if (!SDL_PushEvent(&ev))
519 {
520 SDL_LogError(cat, "An error occurred: %s", SDL_GetError());
521 }
522}
523
524static void SDLCALL rdp_file_cb(void* userdata, const char* const* filelist,
525 WINPR_ATTR_UNUSED int filter)
526{
527 auto rdp = static_cast<std::string*>(userdata);
528
529 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
530 if (!filelist)
531 {
532 SDL_LogError(cat, "An error occurred: %s", SDL_GetError());
533 sdl_quit();
534 return;
535 }
536 else if (!*filelist)
537 {
538 SDL_LogError(cat, "The user did not select any file.");
539 SDL_LogError(cat, "Most likely, the dialog was canceled.");
540 sdl_quit();
541 return;
542 }
543
544 while (*filelist)
545 {
546 SDL_LogError(cat, "Full path to selected file: '%s'", *filelist);
547 *rdp = *filelist;
548 filelist++;
549 }
550
551 sdl_quit();
552}
553
554[[nodiscard]] static std::string getRdpFile()
555{
556 const auto flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS;
557 SDL_DialogFileFilter filters[] = { { "RDP files", "rdp;rdpw" } };
558 std::string rdp;
559
560 bool running = true;
561 if (!SDL_Init(flags))
562 return {};
563
564 auto props = SDL_CreateProperties();
565 SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING,
566 "SDL Freerdp - Open a RDP file");
567 SDL_SetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false);
568 SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, filters);
569 SDL_SetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, ARRAYSIZE(filters));
570 SDL_ShowFileDialogWithProperties(SDL_FILEDIALOG_OPENFILE, rdp_file_cb, &rdp, props);
571 SDL_DestroyProperties(props);
572
573 do
574 {
575 SDL_Event event = {};
576 std::ignore = SDL_PollEvent(&event);
577
578 switch (event.type)
579 {
580 case SDL_EVENT_QUIT:
581 running = false;
582 break;
583 default:
584 break;
585 }
586 } while (running);
587 SDL_Quit();
588 return rdp;
589}
590
591int main(int argc, char* argv[])
592{
593#if defined(_WIN32)
594 sdl::win32::release_transient_console();
595#endif
596
597 int rc = -1;
598 RDP_CLIENT_ENTRY_POINTS clientEntryPoints = {};
599
600 /* Allocate the RDP context first, we need to pass it to SDL */
601 RdpClientEntry(&clientEntryPoints);
602 std::unique_ptr<sdl_rdp_context, void (*)(sdl_rdp_context*)> sdl_rdp(
603 reinterpret_cast<sdl_rdp_context*>(freerdp_client_context_new(&clientEntryPoints)),
604 context_free);
605
606 if (!sdl_rdp)
607 return -1;
608 auto sdl = sdl_rdp->sdl;
609
610 auto settings = sdl->context()->settings;
611 WINPR_ASSERT(settings);
612
613 std::string rdp_file;
614 std::vector<char*> args;
615 args.reserve(WINPR_ASSERTING_INT_CAST(size_t, argc));
616 for (auto x = 0; x < argc; x++)
617 args.push_back(argv[x]);
618
619 if (argc == 1)
620 {
621 rdp_file = getRdpFile();
622 if (!rdp_file.empty())
623 {
624 args.push_back(rdp_file.data());
625 }
626 }
627
628 auto status = freerdp_client_settings_parse_command_line(
629 settings, WINPR_ASSERTING_INT_CAST(int, args.size()), args.data(), FALSE);
630 sdl_rdp->sdl->setMetadata();
631 if (status)
632 {
633 rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
634 if (freerdp_settings_get_bool(settings, FreeRDP_ListMonitors))
635 {
636 if (!sdl_list_monitors(sdl))
637 return -1;
638 }
639 else
640 {
641 switch (status)
642 {
643 case COMMAND_LINE_STATUS_PRINT:
644 case COMMAND_LINE_STATUS_PRINT_VERSION:
645 case COMMAND_LINE_STATUS_PRINT_BUILDCONFIG:
646 break;
647 case COMMAND_LINE_STATUS_PRINT_HELP:
648 SdlPref::print_config_file_help(3);
649 break;
650 default:
651 break;
652 }
653 }
654 return rc;
655 }
656
657 /* Basic SDL initialization */
658 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
659 return -1;
660
661 SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0");
662 SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
663 SDL_SetHint(SDL_HINT_PEN_MOUSE_EVENTS, "0");
664 SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
665 SDL_SetHint(SDL_HINT_PEN_TOUCH_EVENTS, "1");
666 SDL_SetHint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, "1");
667#if SDL_VERSION_ATLEAST(3, 4, 0)
668 SDL_SetHint(SDL_HINT_MOUSE_DPI_SCALE_CURSORS, "1");
669#endif
670
671 /* Redirect SDL log messages to wLog */
672 SDL_SetLogOutputFunction(winpr_LogOutputFunction, sdl);
673 auto level = WLog_GetLogLevel(sdl->getWLog());
674 SDL_SetLogPriorities(wloglevel2dl(level));
675
676 auto backend = SDL_GetCurrentVideoDriver();
677 WLog_Print(sdl->getWLog(), WLOG_DEBUG, "client is using backend '%s'", backend);
678 sdl_dialogs_init();
679
680 /* SDL cleanup code if the client exits */
681 ScopeGuard guard(
682 [&]()
683 {
684 sdl->cleanup();
685 freerdp_del_signal_cleanup_handler(sdl->context(), sdl_term_handler);
686 sdl_dialogs_uninit();
687 SDL_Quit();
688 });
689 if (!sdl->detectDisplays())
690 return -1;
691
692 /* Initialize RDP */
693 auto context = sdl->context();
694 WINPR_ASSERT(context);
695
696 if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CREATE_REQUEST, FALSE))
697 return -1;
698
699 if (freerdp_client_start(context) != 0)
700 return -1;
701
702 rc = sdl_run(sdl);
703
704 if (freerdp_client_stop(context) != 0)
705 return -1;
706
707 if (sdl->exitCode() != 0)
708 rc = sdl->exitCode();
709
710 return rc;
711}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.