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_RAIL_MOVE:
298 {
299 const auto id = static_cast<uint32_t>(
300 reinterpret_cast<uintptr_t>(windowEvent.user.data1));
301 const auto moveType = static_cast<uint16_t>(windowEvent.user.code);
302 sdl->getRailChannelContext().handleLocalMoveRequested(id, moveType);
303 }
304 break;
305 case SDL_EVENT_USER_QUIT:
306 default:
307 break;
308 }
309 }
310 }
311 rc = 1;
312 }
313 catch (ErrorMsg& msg)
314 {
315 WLog_Print(sdl->getWLog(), WLOG_ERROR, "[exception] %s", msg.what());
316 rc = msg.rc();
317 }
318
319 return rc;
320}
321
322/* Optional global initializer.
323 * Here we just register a signal handler to print out stack traces
324 * if available. */
325[[nodiscard]] static BOOL sdl_client_global_init()
326{
327#if defined(_WIN32)
328 WSADATA wsaData = {};
329 const DWORD wVersionRequested = MAKEWORD(1, 1);
330 const int rc = WSAStartup(wVersionRequested, &wsaData);
331 if (rc != 0)
332 {
333 WLog_ERR(SDL_TAG, "WSAStartup failed with %s [%d]", gai_strerrorA(rc), rc);
334 return FALSE;
335 }
336#endif
337
338 return (freerdp_handle_signals() == 0);
339}
340
341/* Optional global tear down */
342static void sdl_client_global_uninit()
343{
344#if defined(_WIN32)
345 WSACleanup();
346#endif
347}
348
349[[nodiscard]] static BOOL sdl_client_new(freerdp* instance, rdpContext* context)
350{
351 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
352
353 if (!instance || !context)
354 return FALSE;
355
356 sdl->sdl = new SdlContext(context);
357 return sdl->sdl != nullptr;
358}
359
360static void sdl_client_free([[maybe_unused]] freerdp* instance, rdpContext* context)
361{
362 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
363
364 if (!context)
365 return;
366
367 delete sdl->sdl;
368}
369
370[[nodiscard]] static int sdl_client_start(rdpContext* context)
371{
372 auto sdl = get_context(context);
373 WINPR_ASSERT(sdl);
374 return sdl->start();
375}
376
377[[nodiscard]] static int sdl_client_stop(rdpContext* context)
378{
379 auto sdl = get_context(context);
380 WINPR_ASSERT(sdl);
381 return sdl->join();
382}
383
384static void RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
385{
386 WINPR_ASSERT(pEntryPoints);
387
388 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
389 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
390 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
391 pEntryPoints->GlobalInit = sdl_client_global_init;
392 pEntryPoints->GlobalUninit = sdl_client_global_uninit;
393 pEntryPoints->ContextSize = sizeof(sdl_rdp_context);
394 pEntryPoints->ClientNew = sdl_client_new;
395 pEntryPoints->ClientFree = sdl_client_free;
396 pEntryPoints->ClientStart = sdl_client_start;
397 pEntryPoints->ClientStop = sdl_client_stop;
398}
399
400static void context_free(sdl_rdp_context* sdl)
401{
402 if (sdl)
403 freerdp_client_context_free(&sdl->common.context);
404}
405
406[[nodiscard]] static const char* category2str(int category)
407{
408 switch (category)
409 {
410 case SDL_LOG_CATEGORY_APPLICATION:
411 return "SDL_LOG_CATEGORY_APPLICATION";
412 case SDL_LOG_CATEGORY_ERROR:
413 return "SDL_LOG_CATEGORY_ERROR";
414 case SDL_LOG_CATEGORY_ASSERT:
415 return "SDL_LOG_CATEGORY_ASSERT";
416 case SDL_LOG_CATEGORY_SYSTEM:
417 return "SDL_LOG_CATEGORY_SYSTEM";
418 case SDL_LOG_CATEGORY_AUDIO:
419 return "SDL_LOG_CATEGORY_AUDIO";
420 case SDL_LOG_CATEGORY_VIDEO:
421 return "SDL_LOG_CATEGORY_VIDEO";
422 case SDL_LOG_CATEGORY_RENDER:
423 return "SDL_LOG_CATEGORY_RENDER";
424 case SDL_LOG_CATEGORY_INPUT:
425 return "SDL_LOG_CATEGORY_INPUT";
426 case SDL_LOG_CATEGORY_TEST:
427 return "SDL_LOG_CATEGORY_TEST";
428 case SDL_LOG_CATEGORY_GPU:
429 return "SDL_LOG_CATEGORY_GPU";
430 case SDL_LOG_CATEGORY_RESERVED2:
431 return "SDL_LOG_CATEGORY_RESERVED2";
432 case SDL_LOG_CATEGORY_RESERVED3:
433 return "SDL_LOG_CATEGORY_RESERVED3";
434 case SDL_LOG_CATEGORY_RESERVED4:
435 return "SDL_LOG_CATEGORY_RESERVED4";
436 case SDL_LOG_CATEGORY_RESERVED5:
437 return "SDL_LOG_CATEGORY_RESERVED5";
438 case SDL_LOG_CATEGORY_RESERVED6:
439 return "SDL_LOG_CATEGORY_RESERVED6";
440 case SDL_LOG_CATEGORY_RESERVED7:
441 return "SDL_LOG_CATEGORY_RESERVED7";
442 case SDL_LOG_CATEGORY_RESERVED8:
443 return "SDL_LOG_CATEGORY_RESERVED8";
444 case SDL_LOG_CATEGORY_RESERVED9:
445 return "SDL_LOG_CATEGORY_RESERVED9";
446 case SDL_LOG_CATEGORY_RESERVED10:
447 return "SDL_LOG_CATEGORY_RESERVED10";
448 case SDL_LOG_CATEGORY_CUSTOM:
449 default:
450 return "SDL_LOG_CATEGORY_CUSTOM";
451 }
452}
453
454[[nodiscard]] static SDL_LogPriority wloglevel2dl(DWORD level)
455{
456 switch (level)
457 {
458 case WLOG_TRACE:
459 return SDL_LOG_PRIORITY_VERBOSE;
460 case WLOG_DEBUG:
461 return SDL_LOG_PRIORITY_DEBUG;
462 case WLOG_INFO:
463 return SDL_LOG_PRIORITY_INFO;
464 case WLOG_WARN:
465 return SDL_LOG_PRIORITY_WARN;
466 case WLOG_ERROR:
467 return SDL_LOG_PRIORITY_ERROR;
468 case WLOG_FATAL:
469 return SDL_LOG_PRIORITY_CRITICAL;
470 case WLOG_OFF:
471 default:
472 return SDL_LOG_PRIORITY_VERBOSE;
473 }
474}
475
476[[nodiscard]] static DWORD sdlpriority2wlog(SDL_LogPriority priority)
477{
478 DWORD level = WLOG_OFF;
479 switch (priority)
480 {
481 case SDL_LOG_PRIORITY_VERBOSE:
482 level = WLOG_TRACE;
483 break;
484 case SDL_LOG_PRIORITY_DEBUG:
485 level = WLOG_DEBUG;
486 break;
487 case SDL_LOG_PRIORITY_INFO:
488 level = WLOG_INFO;
489 break;
490 case SDL_LOG_PRIORITY_WARN:
491 level = WLOG_WARN;
492 break;
493 case SDL_LOG_PRIORITY_ERROR:
494 level = WLOG_ERROR;
495 break;
496 case SDL_LOG_PRIORITY_CRITICAL:
497 level = WLOG_FATAL;
498 break;
499 default:
500 break;
501 }
502
503 return level;
504}
505
506static void SDLCALL winpr_LogOutputFunction(void* userdata, int category, SDL_LogPriority priority,
507 const char* message)
508{
509 auto sdl = static_cast<SdlContext*>(userdata);
510 WINPR_ASSERT(sdl);
511
512 const DWORD level = sdlpriority2wlog(priority);
513 auto log = sdl->getWLog();
514 if (!WLog_IsLevelActive(log, level))
515 return;
516
517 WLog_PrintTextMessage(log, level, __LINE__, __FILE__, __func__, "[%s] %s",
518 category2str(category), message);
519}
520
521static void sdl_quit()
522{
523 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
524 SDL_Event ev = {};
525 ev.type = SDL_EVENT_QUIT;
526 if (!SDL_PushEvent(&ev))
527 {
528 SDL_LogError(cat, "An error occurred: %s", SDL_GetError());
529 }
530}
531
532static void SDLCALL rdp_file_cb(void* userdata, const char* const* filelist,
533 WINPR_ATTR_UNUSED int filter)
534{
535 auto rdp = static_cast<std::string*>(userdata);
536
537 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
538 if (!filelist)
539 {
540 SDL_LogError(cat, "An error occurred: %s", SDL_GetError());
541 sdl_quit();
542 return;
543 }
544 else if (!*filelist)
545 {
546 SDL_LogError(cat, "The user did not select any file.");
547 SDL_LogError(cat, "Most likely, the dialog was canceled.");
548 sdl_quit();
549 return;
550 }
551
552 while (*filelist)
553 {
554 SDL_LogError(cat, "Full path to selected file: '%s'", *filelist);
555 *rdp = *filelist;
556 filelist++;
557 }
558
559 sdl_quit();
560}
561
562[[nodiscard]] static std::string getRdpFile()
563{
564 const auto flags = SDL_INIT_VIDEO | SDL_INIT_EVENTS;
565 SDL_DialogFileFilter filters[] = { { "RDP files", "rdp;rdpw" } };
566 std::string rdp;
567
568 bool running = true;
569 if (!SDL_Init(flags))
570 return {};
571
572 auto props = SDL_CreateProperties();
573 SDL_SetStringProperty(props, SDL_PROP_FILE_DIALOG_TITLE_STRING,
574 "SDL Freerdp - Open a RDP file");
575 SDL_SetBooleanProperty(props, SDL_PROP_FILE_DIALOG_MANY_BOOLEAN, false);
576 SDL_SetPointerProperty(props, SDL_PROP_FILE_DIALOG_FILTERS_POINTER, filters);
577 SDL_SetNumberProperty(props, SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER, ARRAYSIZE(filters));
578 SDL_ShowFileDialogWithProperties(SDL_FILEDIALOG_OPENFILE, rdp_file_cb, &rdp, props);
579 SDL_DestroyProperties(props);
580
581 do
582 {
583 SDL_Event event = {};
584 std::ignore = SDL_PollEvent(&event);
585
586 switch (event.type)
587 {
588 case SDL_EVENT_QUIT:
589 running = false;
590 break;
591 default:
592 break;
593 }
594 } while (running);
595 SDL_Quit();
596 return rdp;
597}
598
599int main(int argc, char* argv[])
600{
601#if defined(_WIN32)
602 sdl::win32::release_transient_console();
603#endif
604
605 int rc = -1;
606 RDP_CLIENT_ENTRY_POINTS clientEntryPoints = {};
607
608 /* Allocate the RDP context first, we need to pass it to SDL */
609 RdpClientEntry(&clientEntryPoints);
610 std::unique_ptr<sdl_rdp_context, void (*)(sdl_rdp_context*)> sdl_rdp(
611 reinterpret_cast<sdl_rdp_context*>(freerdp_client_context_new(&clientEntryPoints)),
612 context_free);
613
614 if (!sdl_rdp)
615 return -1;
616 auto sdl = sdl_rdp->sdl;
617
618 auto settings = sdl->context()->settings;
619 WINPR_ASSERT(settings);
620
621 std::string rdp_file;
622 std::vector<char*> args;
623 args.reserve(WINPR_ASSERTING_INT_CAST(size_t, argc));
624 for (auto x = 0; x < argc; x++)
625 args.push_back(argv[x]);
626
627 if (argc == 1)
628 {
629 rdp_file = getRdpFile();
630 if (!rdp_file.empty())
631 {
632 args.push_back(rdp_file.data());
633 }
634 }
635
636 auto status = freerdp_client_settings_parse_command_line_ex(
637 settings, WINPR_ASSERTING_INT_CAST(int, args.size()), args.data(), FALSE, sdl->args(),
638 sdl->argsCount(), &SdlContext::argumentHandler, sdl);
639 sdl_rdp->sdl->setMetadata();
640 if (status)
641 {
642 rc = freerdp_client_settings_command_line_status_print_ex(settings, status, argc, argv,
643 sdl->args());
644 if (freerdp_settings_get_bool(settings, FreeRDP_ListMonitors))
645 {
646 if (!sdl_list_monitors(sdl))
647 return -1;
648 }
649 else
650 {
651 switch (status)
652 {
653 case COMMAND_LINE_STATUS_PRINT:
654 case COMMAND_LINE_STATUS_PRINT_VERSION:
655 case COMMAND_LINE_STATUS_PRINT_BUILDCONFIG:
656 break;
657 case COMMAND_LINE_STATUS_PRINT_HELP:
658 SdlPref::print_config_file_help(3);
659 break;
660 default:
661 break;
662 }
663 }
664 return rc;
665 }
666
667 if (!SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"))
668 return -1;
669 if (!SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"))
670 return -1;
671 if (!SDL_SetHint(SDL_HINT_PEN_MOUSE_EVENTS, "0"))
672 return -1;
673 if (!SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"))
674 return -1;
675 if (!SDL_SetHint(SDL_HINT_PEN_TOUCH_EVENTS, "1"))
676 return -1;
677 if (!SDL_SetHint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, "1"))
678 return -1;
679 if (!SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, "RDP session running"))
680 return -1;
681#if SDL_VERSION_ATLEAST(3, 4, 0)
682 if (!SDL_SetHint(SDL_HINT_MOUSE_DPI_SCALE_CURSORS, "1"))
683 return -1;
684#endif
685
686 /* Basic SDL initialization */
687 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS))
688 return -1;
689
690 /* Redirect SDL log messages to wLog */
691 SDL_SetLogOutputFunction(winpr_LogOutputFunction, sdl);
692 auto level = WLog_GetLogLevel(sdl->getWLog());
693 SDL_SetLogPriorities(wloglevel2dl(level));
694
695 auto backend = SDL_GetCurrentVideoDriver();
696 WLog_Print(sdl->getWLog(), WLOG_DEBUG, "client is using backend '%s'", backend);
697 sdl_dialogs_init();
698
699 /* SDL cleanup code if the client exits */
700 ScopeGuard guard(
701 [&]()
702 {
703 sdl->cleanup();
704 freerdp_del_signal_cleanup_handler(sdl->context(), sdl_term_handler);
705 sdl_dialogs_uninit();
706 SDL_Quit();
707 });
708 if (!sdl->detectDisplays())
709 return -1;
710
711 /* Initialize RDP */
712 auto context = sdl->context();
713 WINPR_ASSERT(context);
714
715 if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CREATE_REQUEST, FALSE))
716 return -1;
717
718 if (freerdp_client_start(context) != 0)
719 return -1;
720
721 rc = sdl_run(sdl);
722
723 if (freerdp_client_stop(context) != 0)
724 return -1;
725
726 if (sdl->exitCode() != 0)
727 rc = sdl->exitCode();
728
729 return rc;
730}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.