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#include <cerrno>
27#include <cstdio>
28#include <cstring>
29
30#include <freerdp/constants.h>
31#include <freerdp/freerdp.h>
32#include <freerdp/gdi/gdi.h>
33#include <freerdp/streamdump.h>
34#include <freerdp/utils/signal.h>
35
36#include <freerdp/channels/channels.h>
37#include <freerdp/client/channels.h>
38#include <freerdp/client/cliprdr.h>
39#include <freerdp/client/cmdline.h>
40#include <freerdp/client/file.h>
41
42#include <freerdp/log.h>
43#include <winpr/assert.h>
44#include <winpr/config.h>
45#include <winpr/crt.h>
46#include <winpr/synch.h>
47
48#include <SDL3/SDL.h>
49#if !defined(__MINGW32__)
50#include <SDL3/SDL_main.h>
51#endif
52#include <SDL3/SDL_hints.h>
53#include <SDL3/SDL_oldnames.h>
54#include <SDL3/SDL_video.h>
55
56#include <sdl_config.hpp>
57
58#include "dialogs/sdl_connection_dialog_hider.hpp"
59#include "dialogs/sdl_dialogs.hpp"
60#include "scoped_guard.hpp"
61#include "sdl_channels.hpp"
62#include "sdl_disp.hpp"
63#include "sdl_freerdp.hpp"
64#include "sdl_kbd.hpp"
65#include "sdl_monitor.hpp"
66#include "sdl_pointer.hpp"
67#include "sdl_prefs.hpp"
68#include "sdl_touch.hpp"
69#include "sdl_utils.hpp"
70
71#if defined(WITH_WEBVIEW)
72#include <aad/sdl_webview.hpp>
73#endif
74
75#define SDL_TAG CLIENT_TAG("SDL")
76
77enum SDL_EXIT_CODE
78{
79 /* section 0-15: protocol-independent codes */
80 SDL_EXIT_SUCCESS = 0,
81 SDL_EXIT_DISCONNECT = 1,
82 SDL_EXIT_LOGOFF = 2,
83 SDL_EXIT_IDLE_TIMEOUT = 3,
84 SDL_EXIT_LOGON_TIMEOUT = 4,
85 SDL_EXIT_CONN_REPLACED = 5,
86 SDL_EXIT_OUT_OF_MEMORY = 6,
87 SDL_EXIT_CONN_DENIED = 7,
88 SDL_EXIT_CONN_DENIED_FIPS = 8,
89 SDL_EXIT_USER_PRIVILEGES = 9,
90 SDL_EXIT_FRESH_CREDENTIALS_REQUIRED = 10,
91 SDL_EXIT_DISCONNECT_BY_USER = 11,
92
93 /* section 16-31: license error set */
94 SDL_EXIT_LICENSE_INTERNAL = 16,
95 SDL_EXIT_LICENSE_NO_LICENSE_SERVER = 17,
96 SDL_EXIT_LICENSE_NO_LICENSE = 18,
97 SDL_EXIT_LICENSE_BAD_CLIENT_MSG = 19,
98 SDL_EXIT_LICENSE_HWID_DOESNT_MATCH = 20,
99 SDL_EXIT_LICENSE_BAD_CLIENT = 21,
100 SDL_EXIT_LICENSE_CANT_FINISH_PROTOCOL = 22,
101 SDL_EXIT_LICENSE_CLIENT_ENDED_PROTOCOL = 23,
102 SDL_EXIT_LICENSE_BAD_CLIENT_ENCRYPTION = 24,
103 SDL_EXIT_LICENSE_CANT_UPGRADE = 25,
104 SDL_EXIT_LICENSE_NO_REMOTE_CONNECTIONS = 26,
105
106 /* section 32-127: RDP protocol error set */
107 SDL_EXIT_RDP = 32,
108
109 /* section 128-254: xfreerdp specific exit codes */
110 SDL_EXIT_PARSE_ARGUMENTS = 128,
111 SDL_EXIT_MEMORY = 129,
112 SDL_EXIT_PROTOCOL = 130,
113 SDL_EXIT_CONN_FAILED = 131,
114 SDL_EXIT_AUTH_FAILURE = 132,
115 SDL_EXIT_NEGO_FAILURE = 133,
116 SDL_EXIT_LOGON_FAILURE = 134,
117 SDL_EXIT_ACCOUNT_LOCKED_OUT = 135,
118 SDL_EXIT_PRE_CONNECT_FAILED = 136,
119 SDL_EXIT_CONNECT_UNDEFINED = 137,
120 SDL_EXIT_POST_CONNECT_FAILED = 138,
121 SDL_EXIT_DNS_ERROR = 139,
122 SDL_EXIT_DNS_NAME_NOT_FOUND = 140,
123 SDL_EXIT_CONNECT_FAILED = 141,
124 SDL_EXIT_MCS_CONNECT_INITIAL_ERROR = 142,
125 SDL_EXIT_TLS_CONNECT_FAILED = 143,
126 SDL_EXIT_INSUFFICIENT_PRIVILEGES = 144,
127 SDL_EXIT_CONNECT_CANCELLED = 145,
128
129 SDL_EXIT_CONNECT_TRANSPORT_FAILED = 147,
130 SDL_EXIT_CONNECT_PASSWORD_EXPIRED = 148,
131 SDL_EXIT_CONNECT_PASSWORD_MUST_CHANGE = 149,
132 SDL_EXIT_CONNECT_KDC_UNREACHABLE = 150,
133 SDL_EXIT_CONNECT_ACCOUNT_DISABLED = 151,
134 SDL_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED = 152,
135 SDL_EXIT_CONNECT_CLIENT_REVOKED = 153,
136 SDL_EXIT_CONNECT_WRONG_PASSWORD = 154,
137 SDL_EXIT_CONNECT_ACCESS_DENIED = 155,
138 SDL_EXIT_CONNECT_ACCOUNT_RESTRICTION = 156,
139 SDL_EXIT_CONNECT_ACCOUNT_EXPIRED = 157,
140 SDL_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED = 158,
141 SDL_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS = 159,
142
143 SDL_EXIT_UNKNOWN = 255,
144};
145
146struct sdl_exit_code_map_t
147{
148 DWORD error;
149 int code;
150 const char* code_tag;
151};
152
153#define ENTRY(x, y) { x, y, #y }
154static const struct sdl_exit_code_map_t sdl_exit_code_map[] = {
155 ENTRY(FREERDP_ERROR_SUCCESS, SDL_EXIT_SUCCESS), ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_DISCONNECT),
156 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LOGOFF), ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_IDLE_TIMEOUT),
157 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LOGON_TIMEOUT),
158 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_CONN_REPLACED),
159 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_OUT_OF_MEMORY),
160 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_CONN_DENIED),
161 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_CONN_DENIED_FIPS),
162 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_USER_PRIVILEGES),
163 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_FRESH_CREDENTIALS_REQUIRED),
164 ENTRY(ERRINFO_LOGOFF_BY_USER, SDL_EXIT_DISCONNECT_BY_USER),
165 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_UNKNOWN),
166
167 /* section 16-31: license error set */
168 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_INTERNAL),
169 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_NO_LICENSE_SERVER),
170 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_NO_LICENSE),
171 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_BAD_CLIENT_MSG),
172 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_HWID_DOESNT_MATCH),
173 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_BAD_CLIENT),
174 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_CANT_FINISH_PROTOCOL),
175 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_CLIENT_ENDED_PROTOCOL),
176 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_BAD_CLIENT_ENCRYPTION),
177 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_CANT_UPGRADE),
178 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_NO_REMOTE_CONNECTIONS),
179 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_LICENSE_CANT_UPGRADE),
180
181 /* section 32-127: RDP protocol error set */
182 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_RDP),
183
184 /* section 128-254: xfreerdp specific exit codes */
185 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_PARSE_ARGUMENTS), ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_MEMORY),
186 ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_PROTOCOL), ENTRY(FREERDP_ERROR_NONE, SDL_EXIT_CONN_FAILED),
187
188 ENTRY(FREERDP_ERROR_AUTHENTICATION_FAILED, SDL_EXIT_AUTH_FAILURE),
189 ENTRY(FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED, SDL_EXIT_NEGO_FAILURE),
190 ENTRY(FREERDP_ERROR_CONNECT_LOGON_FAILURE, SDL_EXIT_LOGON_FAILURE),
191 ENTRY(FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT, SDL_EXIT_ACCOUNT_LOCKED_OUT),
192 ENTRY(FREERDP_ERROR_PRE_CONNECT_FAILED, SDL_EXIT_PRE_CONNECT_FAILED),
193 ENTRY(FREERDP_ERROR_CONNECT_UNDEFINED, SDL_EXIT_CONNECT_UNDEFINED),
194 ENTRY(FREERDP_ERROR_POST_CONNECT_FAILED, SDL_EXIT_POST_CONNECT_FAILED),
195 ENTRY(FREERDP_ERROR_DNS_ERROR, SDL_EXIT_DNS_ERROR),
196 ENTRY(FREERDP_ERROR_DNS_NAME_NOT_FOUND, SDL_EXIT_DNS_NAME_NOT_FOUND),
197 ENTRY(FREERDP_ERROR_CONNECT_FAILED, SDL_EXIT_CONNECT_FAILED),
198 ENTRY(FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR, SDL_EXIT_MCS_CONNECT_INITIAL_ERROR),
199 ENTRY(FREERDP_ERROR_TLS_CONNECT_FAILED, SDL_EXIT_TLS_CONNECT_FAILED),
200 ENTRY(FREERDP_ERROR_INSUFFICIENT_PRIVILEGES, SDL_EXIT_INSUFFICIENT_PRIVILEGES),
201 ENTRY(FREERDP_ERROR_CONNECT_CANCELLED, SDL_EXIT_CONNECT_CANCELLED),
202 ENTRY(FREERDP_ERROR_CONNECT_TRANSPORT_FAILED, SDL_EXIT_CONNECT_TRANSPORT_FAILED),
203 ENTRY(FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED, SDL_EXIT_CONNECT_PASSWORD_EXPIRED),
204 ENTRY(FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE, SDL_EXIT_CONNECT_PASSWORD_MUST_CHANGE),
205 ENTRY(FREERDP_ERROR_CONNECT_KDC_UNREACHABLE, SDL_EXIT_CONNECT_KDC_UNREACHABLE),
206 ENTRY(FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED, SDL_EXIT_CONNECT_ACCOUNT_DISABLED),
207 ENTRY(FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED,
208 SDL_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED),
209 ENTRY(FREERDP_ERROR_CONNECT_CLIENT_REVOKED, SDL_EXIT_CONNECT_CLIENT_REVOKED),
210 ENTRY(FREERDP_ERROR_CONNECT_WRONG_PASSWORD, SDL_EXIT_CONNECT_WRONG_PASSWORD),
211 ENTRY(FREERDP_ERROR_CONNECT_ACCESS_DENIED, SDL_EXIT_CONNECT_ACCESS_DENIED),
212 ENTRY(FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION, SDL_EXIT_CONNECT_ACCOUNT_RESTRICTION),
213 ENTRY(FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED, SDL_EXIT_CONNECT_ACCOUNT_EXPIRED),
214 ENTRY(FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED, SDL_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED),
215 ENTRY(FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS,
216 SDL_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS)
217};
218
219static const struct sdl_exit_code_map_t* sdl_map_entry_by_code(int exit_code)
220{
221 for (const auto& x : sdl_exit_code_map)
222 {
223 const struct sdl_exit_code_map_t* cur = &x;
224 if (cur->code == exit_code)
225 return cur;
226 }
227 return nullptr;
228}
229
230static const struct sdl_exit_code_map_t* sdl_map_entry_by_error(UINT32 error)
231{
232 for (const auto& x : sdl_exit_code_map)
233 {
234 const struct sdl_exit_code_map_t* cur = &x;
235 if (cur->error == error)
236 return cur;
237 }
238 return nullptr;
239}
240
241static int sdl_map_error_to_exit_code(DWORD error)
242{
243 const struct sdl_exit_code_map_t* entry = sdl_map_entry_by_error(error);
244 if (entry)
245 return entry->code;
246
247 return SDL_EXIT_CONN_FAILED;
248}
249
250static const char* sdl_map_error_to_code_tag(UINT32 error)
251{
252 const struct sdl_exit_code_map_t* entry = sdl_map_entry_by_error(error);
253 if (entry)
254 return entry->code_tag;
255 return nullptr;
256}
257
258static const char* sdl_map_to_code_tag(int code)
259{
260 const struct sdl_exit_code_map_t* entry = sdl_map_entry_by_code(code);
261 if (entry)
262 return entry->code_tag;
263 return nullptr;
264}
265
266static int error_info_to_error(freerdp* instance, DWORD* pcode, char** msg, size_t* len)
267{
268 const DWORD code = freerdp_error_info(instance);
269 const char* name = freerdp_get_error_info_name(code);
270 const char* str = freerdp_get_error_info_string(code);
271 const int exit_code = sdl_map_error_to_exit_code(code);
272
273 winpr_asprintf(msg, len, "Terminate with %s due to ERROR_INFO %s [0x%08" PRIx32 "]: %s",
274 sdl_map_error_to_code_tag(code), name, code, str);
275 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", *msg);
276 if (pcode)
277 *pcode = code;
278 return exit_code;
279}
280
281/* This function is called whenever a new frame starts.
282 * It can be used to reset invalidated areas. */
283static BOOL sdl_begin_paint(rdpContext* context)
284{
285 auto gdi = context->gdi;
286 WINPR_ASSERT(gdi);
287 WINPR_ASSERT(gdi->primary);
288 WINPR_ASSERT(gdi->primary->hdc);
289 WINPR_ASSERT(gdi->primary->hdc->hwnd);
290 WINPR_ASSERT(gdi->primary->hdc->hwnd->invalid);
291 gdi->primary->hdc->hwnd->invalid->null = TRUE;
292 gdi->primary->hdc->hwnd->ninvalid = 0;
293
294 return TRUE;
295}
296
297static bool sdl_draw_to_window_rect([[maybe_unused]] SdlContext* sdl, SdlWindow& window,
298 SDL_Surface* surface, SDL_Point offset, const SDL_Rect& srcRect)
299{
300 WINPR_ASSERT(surface);
301 SDL_Rect dstRect = { offset.x + srcRect.x, offset.y + srcRect.y, srcRect.w, srcRect.h };
302 return window.blit(surface, srcRect, dstRect);
303}
304
305static bool sdl_draw_to_window_rect(SdlContext* sdl, SdlWindow& window, SDL_Surface* surface,
306 SDL_Point offset, const std::vector<SDL_Rect>& rects = {})
307{
308 if (rects.empty())
309 {
310 return sdl_draw_to_window_rect(sdl, window, surface, offset,
311 { 0, 0, surface->w, surface->h });
312 }
313 for (auto& srcRect : rects)
314 {
315 if (!sdl_draw_to_window_rect(sdl, window, surface, offset, srcRect))
316 return false;
317 }
318 return true;
319}
320
321static BOOL sdl_draw_to_window(SdlContext* sdl, SdlWindow& window,
322 const std::vector<SDL_Rect>& rects = {})
323{
324 WINPR_ASSERT(sdl);
325
326 if (!sdl->isConnected())
327 return TRUE;
328
329 auto context = sdl->context();
330 auto gdi = context->gdi;
331 WINPR_ASSERT(gdi);
332
333 auto size = window.rect();
334
335 if (freerdp_settings_get_bool(context->settings, FreeRDP_SmartSizing))
336 {
337 window.setOffsetX(0);
338 window.setOffsetY(0);
339 if (gdi->width < size.w)
340 {
341 window.setOffsetX((size.w - gdi->width) / 2);
342 }
343 if (gdi->height < size.h)
344 {
345 window.setOffsetY((size.h - gdi->height) / 2);
346 }
347 }
348 auto surface = sdl->primary.get();
349 if (!sdl_draw_to_window_rect(sdl, window, surface, { window.offsetX(), window.offsetY() },
350 rects))
351 return FALSE;
352 window.updateSurface();
353 return TRUE;
354}
355
356static BOOL sdl_draw_to_window(SdlContext* sdl, std::map<Uint32, SdlWindow>& windows,
357 const std::vector<SDL_Rect>& rects = {})
358{
359 for (auto& window : windows)
360 {
361 if (!sdl_draw_to_window(sdl, window.second, rects))
362 return FALSE;
363 }
364
365 return TRUE;
366}
367
368/* This function is called when the library completed composing a new
369 * frame. Read out the changed areas and blit them to your output device.
370 * The image buffer will have the format specified by gdi_init
371 */
372static BOOL sdl_end_paint(rdpContext* context)
373{
374 auto sdl = get_context(context);
375 WINPR_ASSERT(sdl);
376
377 auto gdi = context->gdi;
378 WINPR_ASSERT(gdi);
379 WINPR_ASSERT(gdi->primary);
380 WINPR_ASSERT(gdi->primary->hdc);
381 WINPR_ASSERT(gdi->primary->hdc->hwnd);
382 WINPR_ASSERT(gdi->primary->hdc->hwnd->invalid);
383 if (gdi->suppressOutput || gdi->primary->hdc->hwnd->invalid->null)
384 return TRUE;
385
386 const INT32 ninvalid = gdi->primary->hdc->hwnd->ninvalid;
387 const GDI_RGN* cinvalid = gdi->primary->hdc->hwnd->cinvalid;
388
389 if (ninvalid < 1)
390 return TRUE;
391
392 std::vector<SDL_Rect> rects;
393 for (INT32 x = 0; x < ninvalid; x++)
394 {
395 auto& rgn = cinvalid[x];
396 rects.push_back({ rgn.x, rgn.y, rgn.w, rgn.h });
397 }
398
399 sdl->push(std::move(rects));
400 return sdl_push_user_event(SDL_EVENT_USER_UPDATE);
401}
402
403static void sdl_destroy_primary(SdlContext* sdl)
404{
405 if (!sdl)
406 return;
407 sdl->primary.reset();
408}
409
410/* Create a SDL surface from the GDI buffer */
411static BOOL sdl_create_primary(SdlContext* sdl)
412{
413 rdpGdi* gdi = nullptr;
414
415 WINPR_ASSERT(sdl);
416
417 gdi = sdl->context()->gdi;
418 WINPR_ASSERT(gdi);
419
420 sdl_destroy_primary(sdl);
421 sdl->primary =
422 SDLSurfacePtr(SDL_CreateSurfaceFrom(static_cast<int>(gdi->width),
423 static_cast<int>(gdi->height), sdl->sdl_pixel_format,
424 gdi->primary_buffer, static_cast<int>(gdi->stride)),
425 SDL_DestroySurface);
426 if (!sdl->primary)
427 return FALSE;
428
429 SDL_SetSurfaceBlendMode(sdl->primary.get(), SDL_BLENDMODE_NONE);
430 SDL_Rect surfaceRect = { 0, 0, gdi->width, gdi->height };
431 SDL_FillSurfaceRect(sdl->primary.get(), &surfaceRect,
432 SDL_MapSurfaceRGBA(sdl->primary.get(), 0, 0, 0, 0xff));
433
434 return TRUE;
435}
436
437static BOOL sdl_desktop_resize(rdpContext* context)
438{
439 rdpGdi* gdi = nullptr;
440 rdpSettings* settings = nullptr;
441 auto sdl = get_context(context);
442
443 WINPR_ASSERT(sdl);
444 WINPR_ASSERT(context);
445
446 settings = context->settings;
447 WINPR_ASSERT(settings);
448
449 std::lock_guard<CriticalSection> lock(sdl->critical);
450 gdi = context->gdi;
451 if (!gdi_resize(gdi, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
452 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)))
453 return FALSE;
454 return sdl_create_primary(sdl);
455}
456
457/* This function is called to output a System BEEP */
458static BOOL sdl_play_sound(rdpContext* context, const PLAY_SOUND_UPDATE* play_sound)
459{
460 /* TODO: Implement */
461 WINPR_UNUSED(context);
462 WINPR_UNUSED(play_sound);
463 return TRUE;
464}
465
466static BOOL sdl_wait_for_init(SdlContext* sdl)
467{
468 WINPR_ASSERT(sdl);
469 sdl->initialize.set();
470
471 HANDLE handles[] = { sdl->initialized.handle(), freerdp_abort_event(sdl->context()) };
472
473 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
474 switch (rc)
475 {
476 case WAIT_OBJECT_0:
477 return TRUE;
478 default:
479 return FALSE;
480 }
481}
482
483/* Called before a connection is established.
484 * Set all configuration options to support and load channels here. */
485static BOOL sdl_pre_connect(freerdp* instance)
486{
487 WINPR_ASSERT(instance);
488 WINPR_ASSERT(instance->context);
489
490 auto sdl = get_context(instance->context);
491
492 auto settings = instance->context->settings;
493 WINPR_ASSERT(settings);
494
495 if (!freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, TRUE))
496 return FALSE;
497
498 /* Optional OS identifier sent to server */
499 if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX))
500 return FALSE;
501 if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_NATIVE_SDL))
502 return FALSE;
503 /* OrderSupport is initialized at this point.
504 * Only override it if you plan to implement custom order
505 * callbacks or deactivate certain features. */
506 /* Register the channel listeners.
507 * They are required to set up / tear down channels if they are loaded. */
508 PubSub_SubscribeChannelConnected(instance->context->pubSub, sdl_OnChannelConnectedEventHandler);
509 PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
510 sdl_OnChannelDisconnectedEventHandler);
511
512 if (!freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
513 {
514 UINT32 maxWidth = 0;
515 UINT32 maxHeight = 0;
516
517 if (!sdl_wait_for_init(sdl))
518 return FALSE;
519
520 if (!sdl_detect_monitors(sdl, &maxWidth, &maxHeight))
521 return FALSE;
522
523 if ((maxWidth != 0) && (maxHeight != 0) &&
524 !freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
525 {
526 WLog_Print(sdl->log, WLOG_INFO, "Update size to %ux%u", maxWidth, maxHeight);
527 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, maxWidth))
528 return FALSE;
529 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, maxHeight))
530 return FALSE;
531 }
532 }
533 else
534 {
535 /* Check +auth-only has a username and password. */
536 if (!freerdp_settings_get_string(settings, FreeRDP_Password))
537 {
538 WLog_Print(sdl->log, WLOG_INFO, "auth-only, but no password set. Please provide one.");
539 return FALSE;
540 }
541
542 if (!freerdp_settings_set_bool(settings, FreeRDP_DeactivateClientDecoding, TRUE))
543 return FALSE;
544
545 WLog_Print(sdl->log, WLOG_INFO, "Authentication only. Don't connect SDL.");
546 }
547
548 if (!sdl->input.initialize())
549 return FALSE;
550
551 /* TODO: Any code your client requires */
552 return TRUE;
553}
554
555static const char* sdl_window_get_title(rdpSettings* settings)
556{
557 const char* windowTitle = nullptr;
558 UINT32 port = 0;
559 BOOL addPort = 0;
560 const char* name = nullptr;
561 const char* prefix = "FreeRDP:";
562
563 if (!settings)
564 return nullptr;
565
566 windowTitle = freerdp_settings_get_string(settings, FreeRDP_WindowTitle);
567 if (windowTitle)
568 return windowTitle;
569
570 name = freerdp_settings_get_server_name(settings);
571 port = freerdp_settings_get_uint32(settings, FreeRDP_ServerPort);
572
573 addPort = (port != 3389);
574
575 char buffer[MAX_PATH + 64] = {};
576
577 if (!addPort)
578 (void)sprintf_s(buffer, sizeof(buffer), "%s %s", prefix, name);
579 else
580 (void)sprintf_s(buffer, sizeof(buffer), "%s %s:%" PRIu32, prefix, name, port);
581
582 if (!freerdp_settings_set_string(settings, FreeRDP_WindowTitle, buffer))
583 return nullptr;
584 return freerdp_settings_get_string(settings, FreeRDP_WindowTitle);
585}
586
587static void sdl_term_handler([[maybe_unused]] int signum, [[maybe_unused]] const char* signame,
588 [[maybe_unused]] void* context)
589{
590 sdl_push_quit();
591}
592
593static void sdl_cleanup_sdl(SdlContext* sdl)
594{
595 if (!sdl)
596 return;
597
598 std::lock_guard<CriticalSection> lock(sdl->critical);
599 sdl->windows.clear();
600 sdl->dialog.destroy();
601
602 sdl_destroy_primary(sdl);
603
604 freerdp_del_signal_cleanup_handler(sdl->context(), sdl_term_handler);
605 sdl_dialogs_uninit();
606 SDL_Quit();
607}
608
609static BOOL sdl_create_windows(SdlContext* sdl)
610{
611 WINPR_ASSERT(sdl);
612
613 auto settings = sdl->context()->settings;
614 auto title = sdl_window_get_title(settings);
615
616 ScopeGuard guard1([&]() { sdl->windows_created.set(); });
617
618 UINT32 windowCount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
619
620 Sint32 originX = 0;
621 Sint32 originY = 0;
622 for (UINT32 x = 0; x < windowCount; x++)
623 {
624 auto id = sdl->monitorId(x);
625 if (id < 0)
626 return FALSE;
627
628 auto monitor = static_cast<rdpMonitor*>(
629 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
630
631 originX = std::min<Sint32>(monitor->x, originX);
632 originY = std::min<Sint32>(monitor->y, originY);
633 }
634
635 for (UINT32 x = 0; x < windowCount; x++)
636 {
637 auto id = sdl->monitorId(x);
638 if (id < 0)
639 return FALSE;
640
641 auto monitor = static_cast<rdpMonitor*>(
642 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
643
644 auto w = WINPR_ASSERTING_INT_CAST(Uint32, monitor->width);
645 auto h = WINPR_ASSERTING_INT_CAST(Uint32, monitor->height);
646 if (!(freerdp_settings_get_bool(settings, FreeRDP_UseMultimon) ||
647 freerdp_settings_get_bool(settings, FreeRDP_Fullscreen)))
648 {
649 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
650 h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
651 }
652
653 Uint32 flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
654 auto startupX = SDL_WINDOWPOS_CENTERED_DISPLAY(id);
655 auto startupY = SDL_WINDOWPOS_CENTERED_DISPLAY(id);
656
657 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen) &&
658 !freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
659 {
660 flags |= SDL_WINDOW_FULLSCREEN;
661 }
662
663 if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
664 {
665 flags |= SDL_WINDOW_BORDERLESS;
666 }
667
668 if (!freerdp_settings_get_bool(settings, FreeRDP_Decorations))
669 flags |= SDL_WINDOW_BORDERLESS;
670
671 char buffer[MAX_PATH + 64] = {};
672 (void)sprintf_s(buffer, sizeof(buffer), "%s:%" PRIu32, title, x);
673 SdlWindow window{ buffer,
674 static_cast<int>(startupX),
675 static_cast<int>(startupY),
676 static_cast<int>(w),
677 static_cast<int>(h),
678 flags };
679 if (!window.window())
680 return FALSE;
681
682 if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
683 {
684 window.setOffsetX(originX - monitor->x);
685 window.setOffsetY(originY - monitor->y);
686 }
687
688 sdl->windows.insert({ window.id(), std::move(window) });
689 }
690
691 return TRUE;
692}
693
694static BOOL sdl_wait_create_windows(SdlContext* sdl)
695{
696 {
697 std::unique_lock<CriticalSection> lock(sdl->critical);
698 sdl->windows_created.clear();
699 if (!sdl_push_user_event(SDL_EVENT_USER_CREATE_WINDOWS, sdl))
700 return FALSE;
701 }
702
703 HANDLE handles[] = { sdl->windows_created.handle(), freerdp_abort_event(sdl->context()) };
704
705 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
706 switch (rc)
707 {
708 case WAIT_OBJECT_0:
709 return TRUE;
710 default:
711 return FALSE;
712 }
713}
714
715static bool shall_abort(SdlContext* sdl)
716{
717 std::lock_guard<CriticalSection> lock(sdl->critical);
718 if (freerdp_shall_disconnect_context(sdl->context()))
719 {
720 if (sdl->rdp_thread_running)
721 return false;
722 return !sdl->dialog.isRunning();
723 }
724 return false;
725}
726
727static int sdl_run(SdlContext* sdl)
728{
729 int rc = -1;
730 WINPR_ASSERT(sdl);
731
732 HANDLE handles[] = { sdl->initialize.handle(), freerdp_abort_event(sdl->context()) };
733 const DWORD status = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
734 switch (status)
735 {
736 case WAIT_OBJECT_0:
737 break;
738 default:
739 return 0;
740 }
741
742 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
743 auto backend = SDL_GetCurrentVideoDriver();
744 WLog_Print(sdl->log, WLOG_DEBUG, "client is using backend '%s'", backend);
745 sdl_dialogs_init();
746
747 SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0");
748 SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
749
750 freerdp_add_signal_cleanup_handler(sdl->context(), sdl_term_handler);
751 sdl->dialog.create(sdl->context());
752 sdl->dialog.setTitle("Connecting to '%s'",
753 freerdp_settings_get_server_name(sdl->context()->settings));
754 sdl->dialog.showInfo("The connection is being established\n\nPlease wait...");
755 if (!freerdp_settings_get_bool(sdl->context()->settings, FreeRDP_UseCommonStdioCallbacks))
756 {
757 sdl->dialog.show(true);
758 }
759
760 sdl->initialized.set();
761
762 while (!shall_abort(sdl))
763 {
764 SDL_Event windowEvent = {};
765 while (!shall_abort(sdl) && SDL_WaitEventTimeout(nullptr, 1000))
766 {
767 /* Only poll standard SDL events and SDL_EVENT_USERS meant to create
768 * dialogs. do not process the dialog return value events here.
769 */
770 const int prc = SDL_PeepEvents(&windowEvent, 1, SDL_GETEVENT, SDL_EVENT_FIRST,
771 SDL_EVENT_USER_RETRY_DIALOG);
772 if (prc < 0)
773 {
774 if (sdl_log_error(prc, sdl->log, "SDL_PeepEvents"))
775 continue;
776 }
777
778#if defined(WITH_DEBUG_SDL_EVENTS)
779 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "got event %s [0x%08" PRIx32 "]",
780 sdl_event_type_str(windowEvent.type), windowEvent.type);
781#endif
782 {
783 std::lock_guard<CriticalSection> lock(sdl->critical);
784 /* The session might have been disconnected while we were waiting for a
785 * new SDL event. In that case ignore the SDL event and terminate. */
786 if (freerdp_shall_disconnect_context(sdl->context()))
787 continue;
788 }
789
790 if (sdl->dialog.handleEvent(windowEvent))
791 {
792 continue;
793 }
794
795 auto point2pix = [](Uint32 win_id, float& x, float& y)
796 {
797 auto win = SDL_GetWindowFromID(win_id);
798 if (win)
799 {
800 auto scale = SDL_GetWindowDisplayScale(win);
801 assert(scale);
802 x *= scale;
803 y *= scale;
804 }
805 };
806
807 switch (windowEvent.type)
808 {
809 case SDL_EVENT_QUIT:
810 freerdp_abort_connect_context(sdl->context());
811 break;
812 case SDL_EVENT_KEY_DOWN:
813 case SDL_EVENT_KEY_UP:
814 {
815 const SDL_KeyboardEvent* ev = &windowEvent.key;
816 sdl->input.keyboard_handle_event(ev);
817 }
818 break;
819 case SDL_EVENT_KEYMAP_CHANGED:
820 {
821 }
822 break; // TODO: Switch keyboard layout
823 case SDL_EVENT_MOUSE_MOTION:
824 {
825 SDL_MouseMotionEvent& ev = windowEvent.motion;
826 point2pix(ev.windowID, ev.x, ev.y);
827 point2pix(ev.windowID, ev.xrel, ev.yrel);
828 sdl_handle_mouse_motion(sdl, &ev);
829 }
830 break;
831 case SDL_EVENT_MOUSE_BUTTON_DOWN:
832 case SDL_EVENT_MOUSE_BUTTON_UP:
833 {
834 SDL_MouseButtonEvent& ev = windowEvent.button;
835 point2pix(ev.windowID, ev.x, ev.y);
836 sdl_handle_mouse_button(sdl, &ev);
837 }
838 break;
839 case SDL_EVENT_MOUSE_WHEEL:
840 {
841 const SDL_MouseWheelEvent* ev = &windowEvent.wheel;
842 sdl_handle_mouse_wheel(sdl, ev);
843 }
844 break;
845 case SDL_EVENT_FINGER_DOWN:
846 {
847 const SDL_TouchFingerEvent* ev = &windowEvent.tfinger;
848 sdl_handle_touch_down(sdl, ev);
849 }
850 break;
851 case SDL_EVENT_FINGER_UP:
852 {
853 const SDL_TouchFingerEvent* ev = &windowEvent.tfinger;
854 sdl_handle_touch_up(sdl, ev);
855 }
856 break;
857 case SDL_EVENT_FINGER_MOTION:
858 {
859 const SDL_TouchFingerEvent* ev = &windowEvent.tfinger;
860 sdl_handle_touch_motion(sdl, ev);
861 }
862 break;
863
864 case SDL_EVENT_RENDER_TARGETS_RESET:
865 (void)sdl->redraw();
866 break;
867 case SDL_EVENT_RENDER_DEVICE_RESET:
868 (void)sdl->redraw();
869 break;
870 case SDL_EVENT_WILL_ENTER_FOREGROUND:
871 (void)sdl->redraw();
872 break;
873 case SDL_EVENT_USER_CERT_DIALOG:
874 {
875 SDLConnectionDialogHider hider(sdl);
876 auto title = static_cast<const char*>(windowEvent.user.data1);
877 auto msg = static_cast<const char*>(windowEvent.user.data2);
878 sdl_cert_dialog_show(title, msg);
879 }
880 break;
881 case SDL_EVENT_USER_SHOW_DIALOG:
882 {
883 SDLConnectionDialogHider hider(sdl);
884 auto title = static_cast<const char*>(windowEvent.user.data1);
885 auto msg = static_cast<const char*>(windowEvent.user.data2);
886 sdl_message_dialog_show(title, msg, windowEvent.user.code);
887 }
888 break;
889 case SDL_EVENT_USER_SCARD_DIALOG:
890 {
891 SDLConnectionDialogHider hider(sdl);
892 auto title = static_cast<const char*>(windowEvent.user.data1);
893 auto msg = static_cast<const char**>(windowEvent.user.data2);
894 sdl_scard_dialog_show(title, windowEvent.user.code, msg);
895 }
896 break;
897 case SDL_EVENT_USER_AUTH_DIALOG:
898 {
899 SDLConnectionDialogHider hider(sdl);
900 sdl_auth_dialog_show(
901 reinterpret_cast<const SDL_UserAuthArg*>(windowEvent.padding));
902 }
903 break;
904 case SDL_EVENT_USER_UPDATE:
905 {
906 std::vector<SDL_Rect> rectangles;
907 do
908 {
909 rectangles = sdl->pop();
910 sdl_draw_to_window(sdl, sdl->windows, rectangles);
911 } while (!rectangles.empty());
912 }
913 break;
914 case SDL_EVENT_USER_CREATE_WINDOWS:
915 {
916 auto ctx = static_cast<SdlContext*>(windowEvent.user.data1);
917 sdl_create_windows(ctx);
918 }
919 break;
920 case SDL_EVENT_USER_WINDOW_RESIZEABLE:
921 {
922 auto window = static_cast<SdlWindow*>(windowEvent.user.data1);
923 const bool use = windowEvent.user.code != 0;
924 if (window)
925 window->resizeable(use);
926 }
927 break;
928 case SDL_EVENT_USER_WINDOW_FULLSCREEN:
929 {
930 auto window = static_cast<SdlWindow*>(windowEvent.user.data1);
931 const bool enter = windowEvent.user.code != 0;
932 if (window)
933 window->fullscreen(enter);
934 }
935 break;
936 case SDL_EVENT_USER_WINDOW_MINIMIZE:
937 for (auto& window : sdl->windows)
938 {
939 window.second.minimize();
940 }
941 break;
942 case SDL_EVENT_USER_POINTER_NULL:
943 SDL_HideCursor();
944 sdl->setCursor(nullptr);
945 sdl->setHasCursor(false);
946 break;
947 case SDL_EVENT_USER_POINTER_DEFAULT:
948 {
949 SDL_Cursor* def = SDL_GetDefaultCursor();
950 SDL_SetCursor(def);
951 SDL_ShowCursor();
952 sdl->setCursor(nullptr);
953 sdl->setHasCursor(true);
954 }
955 break;
956 case SDL_EVENT_USER_POINTER_POSITION:
957 {
958 const auto x =
959 static_cast<INT32>(reinterpret_cast<uintptr_t>(windowEvent.user.data1));
960 const auto y =
961 static_cast<INT32>(reinterpret_cast<uintptr_t>(windowEvent.user.data2));
962
963 SDL_Window* window = SDL_GetMouseFocus();
964 if (window)
965 {
966 const Uint32 id = SDL_GetWindowID(window);
967
968 INT32 sx = x;
969 INT32 sy = y;
970 if (sdl_scale_coordinates(sdl, id, &sx, &sy, FALSE, FALSE))
971 SDL_WarpMouseInWindow(window, static_cast<float>(sx),
972 static_cast<float>(sy));
973 }
974 }
975 break;
976 case SDL_EVENT_USER_POINTER_SET:
977 sdl->setCursor(static_cast<rdpPointer*>(windowEvent.user.data1));
978 sdl_Pointer_Set_Process(sdl);
979 break;
980 case SDL_EVENT_CLIPBOARD_UPDATE:
981 sdl->clip.handle_update(windowEvent.clipboard);
982 break;
983 case SDL_EVENT_USER_QUIT:
984 default:
985 if ((windowEvent.type >= SDL_EVENT_DISPLAY_FIRST) &&
986 (windowEvent.type <= SDL_EVENT_DISPLAY_LAST))
987 {
988 const SDL_DisplayEvent* ev = &windowEvent.display;
989 (void)sdl->disp.handle_display_event(ev);
990 }
991 else if ((windowEvent.type >= SDL_EVENT_WINDOW_FIRST) &&
992 (windowEvent.type <= SDL_EVENT_WINDOW_LAST))
993 {
994 const SDL_WindowEvent* ev = &windowEvent.window;
995 auto window = sdl->windows.find(ev->windowID);
996 if (window != sdl->windows.end())
997 {
998 (void)sdl->disp.handle_window_event(ev);
999
1000 switch (ev->type)
1001 {
1002 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1003 if (sdl->isConnected())
1004 {
1005 sdl_Pointer_Set_Process(sdl);
1007 sdl->context()->settings,
1008 FreeRDP_DynamicResolutionUpdate))
1009 {
1010 break;
1011 }
1012 auto win = window->second.window();
1013 int w_pix{};
1014 int h_pix{};
1015 [[maybe_unused]] auto rcpix =
1016 SDL_GetWindowSizeInPixels(win, &w_pix, &h_pix);
1017 assert(rcpix);
1018 auto scale = SDL_GetWindowDisplayScale(win);
1019 if (scale <= SDL_FLT_EPSILON)
1020 {
1021 auto err = SDL_GetError();
1022 SDL_LogWarn(
1023 SDL_LOG_CATEGORY_APPLICATION,
1024 "SDL_GetWindowDisplayScale() failed with %s", err);
1025 }
1026 else
1027 {
1028 assert(SDL_isnanf(scale) == 0);
1029 assert(SDL_isinff(scale) == 0);
1030 assert(scale > SDL_FLT_EPSILON);
1031 auto w_gdi = sdl->context()->gdi->width;
1032 auto h_gdi = sdl->context()->gdi->height;
1033 auto pix2point = [=](int pix)
1034 {
1035 return static_cast<int>(static_cast<float>(pix) /
1036 scale);
1037 };
1038 if (w_pix != w_gdi || h_pix != h_gdi)
1039 {
1040 const auto ssws = SDL_SetWindowSize(
1041 win, pix2point(w_gdi), pix2point(h_gdi));
1042 if (!ssws)
1043 {
1044 auto err = SDL_GetError();
1045 SDL_LogWarn(
1046 SDL_LOG_CATEGORY_APPLICATION,
1047 "SDL_SetWindowSize() failed with %s", err);
1048 }
1049 }
1050 }
1051 }
1052 break;
1053 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1054 window->second.fill();
1055 sdl_draw_to_window(sdl, window->second);
1056 sdl_Pointer_Set_Process(sdl);
1057 break;
1058 case SDL_EVENT_WINDOW_MOVED:
1059 {
1060 auto r = window->second.rect();
1061 auto id = window->second.id();
1062 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%u: %dx%d-%dx%d",
1063 id, r.x, r.y, r.w, r.h);
1064 }
1065 break;
1066 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1067 {
1068 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1069 "Window closed, terminating RDP session...");
1070 freerdp_abort_connect_context(sdl->context());
1071 }
1072 break;
1073 default:
1074 break;
1075 }
1076 }
1077 }
1078 break;
1079 }
1080 }
1081 }
1082
1083 rc = 1;
1084
1085 sdl_cleanup_sdl(sdl);
1086 return rc;
1087}
1088
1089/* Called after a RDP connection was successfully established.
1090 * Settings might have changed during negotiation of client / server feature
1091 * support.
1092 *
1093 * Set up local framebuffers and paing callbacks.
1094 * If required, register pointer callbacks to change the local mouse cursor
1095 * when hovering over the RDP window
1096 */
1097static BOOL sdl_post_connect(freerdp* instance)
1098{
1099 WINPR_ASSERT(instance);
1100
1101 auto context = instance->context;
1102 WINPR_ASSERT(context);
1103
1104 auto sdl = get_context(context);
1105
1106 // Retry was successful, discard dialog
1107 sdl->dialog.show(false);
1108
1109 if (freerdp_settings_get_bool(context->settings, FreeRDP_AuthenticationOnly))
1110 {
1111 /* Check +auth-only has a username and password. */
1112 if (!freerdp_settings_get_string(context->settings, FreeRDP_Password))
1113 {
1114 WLog_Print(sdl->log, WLOG_INFO, "auth-only, but no password set. Please provide one.");
1115 return FALSE;
1116 }
1117
1118 WLog_Print(sdl->log, WLOG_INFO, "Authentication only. Don't connect to X.");
1119 return TRUE;
1120 }
1121
1122 if (!sdl_wait_create_windows(sdl))
1123 return FALSE;
1124
1125 sdl->sdl_pixel_format = SDL_PIXELFORMAT_BGRA32;
1126 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
1127 return FALSE;
1128
1129 if (!sdl_create_primary(sdl))
1130 return FALSE;
1131
1132 if (!sdl_register_pointer(instance->context->graphics))
1133 return FALSE;
1134
1135 WINPR_ASSERT(context->update);
1136
1137 context->update->BeginPaint = sdl_begin_paint;
1138 context->update->EndPaint = sdl_end_paint;
1139 context->update->PlaySound = sdl_play_sound;
1140 context->update->DesktopResize = sdl_desktop_resize;
1141 context->update->SetKeyboardIndicators = sdlInput::keyboard_set_indicators;
1142 context->update->SetKeyboardImeStatus = sdlInput::keyboard_set_ime_status;
1143
1144 if (!sdl->update_resizeable(false))
1145 return FALSE;
1146 if (!sdl->update_fullscreen(freerdp_settings_get_bool(context->settings, FreeRDP_Fullscreen) ||
1147 freerdp_settings_get_bool(context->settings, FreeRDP_UseMultimon)))
1148 return FALSE;
1149 sdl->setConnected(true);
1150 return TRUE;
1151}
1152
1153/* This function is called whether a session ends by failure or success.
1154 * Clean up everything allocated by pre_connect and post_connect.
1155 */
1156static void sdl_post_disconnect(freerdp* instance)
1157{
1158 if (!instance)
1159 return;
1160
1161 if (!instance->context)
1162 return;
1163
1164 auto sdl = get_context(instance->context);
1165 sdl->setConnected(false);
1166
1167 gdi_free(instance);
1168}
1169
1170static void sdl_post_final_disconnect(freerdp* instance)
1171{
1172 if (!instance)
1173 return;
1174
1175 if (!instance->context)
1176 return;
1177
1178 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
1179 sdl_OnChannelConnectedEventHandler);
1180 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
1181 sdl_OnChannelDisconnectedEventHandler);
1182}
1183
1184static void sdl_client_cleanup(SdlContext* sdl, int exit_code, const std::string& error_msg)
1185{
1186 WINPR_ASSERT(sdl);
1187
1188 rdpContext* context = sdl->context();
1189 WINPR_ASSERT(context);
1190 rdpSettings* settings = context->settings;
1191 WINPR_ASSERT(settings);
1192
1193 sdl->rdp_thread_running = false;
1194 bool showError = false;
1195 if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
1196 WLog_Print(sdl->log, WLOG_INFO, "Authentication only, exit status %s [%" PRId32 "]",
1197 sdl_map_to_code_tag(exit_code), exit_code);
1198 else
1199 {
1200 switch (exit_code)
1201 {
1202 case SDL_EXIT_SUCCESS:
1203 case SDL_EXIT_DISCONNECT:
1204 case SDL_EXIT_LOGOFF:
1205 case SDL_EXIT_DISCONNECT_BY_USER:
1206 case SDL_EXIT_CONNECT_CANCELLED:
1207 break;
1208 default:
1209 {
1210 sdl->dialog.showError(error_msg);
1211 }
1212 break;
1213 }
1214 }
1215
1216 if (!showError)
1217 sdl->dialog.show(false);
1218
1219 sdl->exit_code = exit_code;
1220 sdl_push_user_event(SDL_EVENT_USER_QUIT);
1221 SDL_CleanupTLS();
1222}
1223
1224static int sdl_client_thread_connect(SdlContext* sdl, std::string& error_msg)
1225{
1226 WINPR_ASSERT(sdl);
1227
1228 auto instance = sdl->context()->instance;
1229 WINPR_ASSERT(instance);
1230
1231 sdl->rdp_thread_running = true;
1232 BOOL rc = freerdp_connect(instance);
1233
1234 rdpContext* context = sdl->context();
1235 rdpSettings* settings = context->settings;
1236 WINPR_ASSERT(settings);
1237
1238 int exit_code = SDL_EXIT_SUCCESS;
1239 if (!rc)
1240 {
1241 UINT32 error = freerdp_get_last_error(context);
1242 exit_code = sdl_map_error_to_exit_code(error);
1243 }
1244
1245 if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
1246 {
1247 DWORD code = freerdp_get_last_error(context);
1248 freerdp_abort_connect_context(context);
1249 WLog_Print(sdl->log, WLOG_ERROR, "Authentication only, %s [0x%08" PRIx32 "] %s",
1250 freerdp_get_last_error_name(code), code, freerdp_get_last_error_string(code));
1251 return exit_code;
1252 }
1253
1254 if (!rc)
1255 {
1256 DWORD code = freerdp_error_info(instance);
1257 if (exit_code == SDL_EXIT_SUCCESS)
1258 {
1259 char* msg = nullptr;
1260 size_t len = 0;
1261 exit_code = error_info_to_error(instance, &code, &msg, &len);
1262 if (msg)
1263 error_msg = msg;
1264 free(msg);
1265 }
1266
1267 auto last = freerdp_get_last_error(context);
1268 if (error_msg.empty())
1269 {
1270 char* msg = nullptr;
1271 size_t len = 0;
1272 winpr_asprintf(&msg, &len, "%s [0x%08" PRIx32 "]\n%s",
1273 freerdp_get_last_error_name(last), last,
1274 freerdp_get_last_error_string(last));
1275 if (msg)
1276 error_msg = msg;
1277 free(msg);
1278 }
1279
1280 if (exit_code == SDL_EXIT_SUCCESS)
1281 {
1282 if (last == FREERDP_ERROR_AUTHENTICATION_FAILED)
1283 exit_code = SDL_EXIT_AUTH_FAILURE;
1284 else if (code == ERRINFO_SUCCESS)
1285 exit_code = SDL_EXIT_CONN_FAILED;
1286 }
1287
1288 sdl->dialog.show(false);
1289 }
1290
1291 return exit_code;
1292}
1293
1294static int sdl_client_thread_run(SdlContext* sdl, std::string& error_msg)
1295{
1296 WINPR_ASSERT(sdl);
1297
1298 auto context = sdl->context();
1299 WINPR_ASSERT(context);
1300
1301 auto instance = context->instance;
1302 WINPR_ASSERT(instance);
1303
1304 int exit_code = SDL_EXIT_SUCCESS;
1305 while (!freerdp_shall_disconnect_context(context))
1306 {
1307 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
1308 /*
1309 * win8 and server 2k12 seem to have some timing issue/race condition
1310 * when a initial sync request is send to sync the keyboard indicators
1311 * sending the sync event twice fixed this problem
1312 */
1313 if (freerdp_focus_required(instance))
1314 {
1315 auto ctx = get_context(context);
1316 WINPR_ASSERT(ctx);
1317 if (!ctx->input.keyboard_focus_in())
1318 break;
1319 if (!ctx->input.keyboard_focus_in())
1320 break;
1321 }
1322
1323 const DWORD nCount = freerdp_get_event_handles(context, handles, ARRAYSIZE(handles));
1324
1325 if (nCount == 0)
1326 {
1327 WLog_Print(sdl->log, WLOG_ERROR, "freerdp_get_event_handles failed");
1328 break;
1329 }
1330
1331 const DWORD status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
1332
1333 if (status == WAIT_FAILED)
1334 break;
1335
1336 if (!freerdp_check_event_handles(context))
1337 {
1338 if (client_auto_reconnect(instance))
1339 {
1340 // Retry was successful, discard dialog
1341 sdl->dialog.show(false);
1342 continue;
1343 }
1344 else
1345 {
1346 /*
1347 * Indicate an unsuccessful connection attempt if reconnect
1348 * did not succeed and no other error was specified.
1349 */
1350 if (freerdp_error_info(instance) == 0)
1351 exit_code = SDL_EXIT_CONN_FAILED;
1352 }
1353
1354 if (freerdp_get_last_error(context) == FREERDP_ERROR_SUCCESS)
1355 WLog_Print(sdl->log, WLOG_ERROR, "WaitForMultipleObjects failed with %" PRIu32 "",
1356 status);
1357 if (freerdp_get_last_error(context) == FREERDP_ERROR_SUCCESS)
1358 WLog_Print(sdl->log, WLOG_ERROR, "Failed to check FreeRDP event handles");
1359 break;
1360 }
1361 }
1362
1363 if (exit_code == SDL_EXIT_SUCCESS)
1364 {
1365 DWORD code = 0;
1366 {
1367 char* emsg = nullptr;
1368 size_t elen = 0;
1369 exit_code = error_info_to_error(instance, &code, &emsg, &elen);
1370 if (emsg)
1371 error_msg = emsg;
1372 free(emsg);
1373 }
1374
1375 if ((code == ERRINFO_LOGOFF_BY_USER) &&
1376 (freerdp_get_disconnect_ultimatum(context) == Disconnect_Ultimatum_user_requested))
1377 {
1378 const char* msg = "Error info says user did not initiate but disconnect ultimatum says "
1379 "they did; treat this as a user logoff";
1380
1381 char* emsg = nullptr;
1382 size_t elen = 0;
1383 winpr_asprintf(&emsg, &elen, "%s", msg);
1384 if (emsg)
1385 error_msg = emsg;
1386 free(emsg);
1387
1388 /* This situation might be limited to Windows XP. */
1389 WLog_Print(sdl->log, WLOG_INFO, "%s", msg);
1390 exit_code = SDL_EXIT_LOGOFF;
1391 }
1392 }
1393
1394 freerdp_disconnect(instance);
1395
1396 return exit_code;
1397}
1398
1399/* RDP main loop.
1400 * Connects RDP, loops while running and handles event and dispatch, cleans up
1401 * after the connection ends. */
1402static DWORD WINAPI sdl_client_thread_proc(SdlContext* sdl)
1403{
1404 WINPR_ASSERT(sdl);
1405
1406 std::string error_msg;
1407 int exit_code = sdl_client_thread_connect(sdl, error_msg);
1408 if (exit_code == SDL_EXIT_SUCCESS)
1409 exit_code = sdl_client_thread_run(sdl, error_msg);
1410 sdl_client_cleanup(sdl, exit_code, error_msg);
1411
1412 return static_cast<DWORD>(exit_code);
1413}
1414
1415/* Optional global initializer.
1416 * Here we just register a signal handler to print out stack traces
1417 * if available. */
1418static BOOL sdl_client_global_init()
1419{
1420#if defined(_WIN32)
1421 WSADATA wsaData = {};
1422 const DWORD wVersionRequested = MAKEWORD(1, 1);
1423 const int rc = WSAStartup(wVersionRequested, &wsaData);
1424 if (rc != 0)
1425 {
1426 WLog_ERR(SDL_TAG, "WSAStartup failed with %s [%d]", gai_strerrorA(rc), rc);
1427 return FALSE;
1428 }
1429#endif
1430
1431 return (freerdp_handle_signals() == 0);
1432}
1433
1434/* Optional global tear down */
1435static void sdl_client_global_uninit()
1436{
1437#if defined(_WIN32)
1438 WSACleanup();
1439#endif
1440}
1441
1442static BOOL sdl_client_new(freerdp* instance, rdpContext* context)
1443{
1444 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
1445
1446 if (!instance || !context)
1447 return FALSE;
1448
1449 sdl->sdl = new SdlContext(context);
1450 if (!sdl->sdl)
1451 return FALSE;
1452
1453 instance->PreConnect = sdl_pre_connect;
1454 instance->PostConnect = sdl_post_connect;
1455 instance->PostDisconnect = sdl_post_disconnect;
1456 instance->PostFinalDisconnect = sdl_post_final_disconnect;
1457 instance->AuthenticateEx = sdl_authenticate_ex;
1458 instance->VerifyCertificateEx = sdl_verify_certificate_ex;
1459 instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
1460 instance->LogonErrorInfo = sdl_logon_error_info;
1461 instance->PresentGatewayMessage = sdl_present_gateway_message;
1462 instance->ChooseSmartcard = sdl_choose_smartcard;
1463 instance->RetryDialog = sdl_retry_dialog;
1464
1465#ifdef WITH_WEBVIEW
1466 instance->GetAccessToken = sdl_webview_get_access_token;
1467#else
1468 instance->GetAccessToken = client_cli_get_access_token;
1469#endif
1470 /* TODO: Client display set up */
1471
1472 return TRUE;
1473}
1474
1475static void sdl_client_free([[maybe_unused]] freerdp* instance, rdpContext* context)
1476{
1477 auto sdl = reinterpret_cast<sdl_rdp_context*>(context);
1478
1479 if (!context)
1480 return;
1481
1482 delete sdl->sdl;
1483}
1484
1485static int sdl_client_start(rdpContext* context)
1486{
1487 auto sdl = get_context(context);
1488 WINPR_ASSERT(sdl);
1489
1490 sdl->thread = std::thread(sdl_client_thread_proc, sdl);
1491 return 0;
1492}
1493
1494static int sdl_client_stop(rdpContext* context)
1495{
1496 auto sdl = get_context(context);
1497 WINPR_ASSERT(sdl);
1498
1499 /* We do not want to use freerdp_abort_connect_context here.
1500 * It would change the exit code and we do not want that. */
1501 HANDLE event = freerdp_abort_event(context);
1502 if (!SetEvent(event))
1503 return -1;
1504
1505 sdl->thread.join();
1506 return 0;
1507}
1508
1509static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1510{
1511 WINPR_ASSERT(pEntryPoints);
1512
1513 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
1514 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
1515 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1516 pEntryPoints->GlobalInit = sdl_client_global_init;
1517 pEntryPoints->GlobalUninit = sdl_client_global_uninit;
1518 pEntryPoints->ContextSize = sizeof(sdl_rdp_context);
1519 pEntryPoints->ClientNew = sdl_client_new;
1520 pEntryPoints->ClientFree = sdl_client_free;
1521 pEntryPoints->ClientStart = sdl_client_start;
1522 pEntryPoints->ClientStop = sdl_client_stop;
1523 return 0;
1524}
1525
1526static void context_free(sdl_rdp_context* sdl)
1527{
1528 if (sdl)
1529 freerdp_client_context_free(&sdl->common.context);
1530}
1531
1532static const char* category2str(int category)
1533{
1534 switch (category)
1535 {
1536 case SDL_LOG_CATEGORY_APPLICATION:
1537 return "SDL_LOG_CATEGORY_APPLICATION";
1538 case SDL_LOG_CATEGORY_ERROR:
1539 return "SDL_LOG_CATEGORY_ERROR";
1540 case SDL_LOG_CATEGORY_ASSERT:
1541 return "SDL_LOG_CATEGORY_ASSERT";
1542 case SDL_LOG_CATEGORY_SYSTEM:
1543 return "SDL_LOG_CATEGORY_SYSTEM";
1544 case SDL_LOG_CATEGORY_AUDIO:
1545 return "SDL_LOG_CATEGORY_AUDIO";
1546 case SDL_LOG_CATEGORY_VIDEO:
1547 return "SDL_LOG_CATEGORY_VIDEO";
1548 case SDL_LOG_CATEGORY_RENDER:
1549 return "SDL_LOG_CATEGORY_RENDER";
1550 case SDL_LOG_CATEGORY_INPUT:
1551 return "SDL_LOG_CATEGORY_INPUT";
1552 case SDL_LOG_CATEGORY_TEST:
1553 return "SDL_LOG_CATEGORY_TEST";
1554 case SDL_LOG_CATEGORY_GPU:
1555 return "SDL_LOG_CATEGORY_GPU";
1556 case SDL_LOG_CATEGORY_RESERVED2:
1557 return "SDL_LOG_CATEGORY_RESERVED2";
1558 case SDL_LOG_CATEGORY_RESERVED3:
1559 return "SDL_LOG_CATEGORY_RESERVED3";
1560 case SDL_LOG_CATEGORY_RESERVED4:
1561 return "SDL_LOG_CATEGORY_RESERVED4";
1562 case SDL_LOG_CATEGORY_RESERVED5:
1563 return "SDL_LOG_CATEGORY_RESERVED5";
1564 case SDL_LOG_CATEGORY_RESERVED6:
1565 return "SDL_LOG_CATEGORY_RESERVED6";
1566 case SDL_LOG_CATEGORY_RESERVED7:
1567 return "SDL_LOG_CATEGORY_RESERVED7";
1568 case SDL_LOG_CATEGORY_RESERVED8:
1569 return "SDL_LOG_CATEGORY_RESERVED8";
1570 case SDL_LOG_CATEGORY_RESERVED9:
1571 return "SDL_LOG_CATEGORY_RESERVED9";
1572 case SDL_LOG_CATEGORY_RESERVED10:
1573 return "SDL_LOG_CATEGORY_RESERVED10";
1574 case SDL_LOG_CATEGORY_CUSTOM:
1575 default:
1576 return "SDL_LOG_CATEGORY_CUSTOM";
1577 }
1578}
1579
1580static SDL_LogPriority wloglevel2dl(DWORD level)
1581{
1582 switch (level)
1583 {
1584 case WLOG_TRACE:
1585 return SDL_LOG_PRIORITY_VERBOSE;
1586 case WLOG_DEBUG:
1587 return SDL_LOG_PRIORITY_DEBUG;
1588 case WLOG_INFO:
1589 return SDL_LOG_PRIORITY_INFO;
1590 case WLOG_WARN:
1591 return SDL_LOG_PRIORITY_WARN;
1592 case WLOG_ERROR:
1593 return SDL_LOG_PRIORITY_ERROR;
1594 case WLOG_FATAL:
1595 return SDL_LOG_PRIORITY_CRITICAL;
1596 case WLOG_OFF:
1597 default:
1598 return SDL_LOG_PRIORITY_VERBOSE;
1599 }
1600}
1601
1602static DWORD sdlpriority2wlog(SDL_LogPriority priority)
1603{
1604 DWORD level = WLOG_OFF;
1605 switch (priority)
1606 {
1607 case SDL_LOG_PRIORITY_VERBOSE:
1608 level = WLOG_TRACE;
1609 break;
1610 case SDL_LOG_PRIORITY_DEBUG:
1611 level = WLOG_DEBUG;
1612 break;
1613 case SDL_LOG_PRIORITY_INFO:
1614 level = WLOG_INFO;
1615 break;
1616 case SDL_LOG_PRIORITY_WARN:
1617 level = WLOG_WARN;
1618 break;
1619 case SDL_LOG_PRIORITY_ERROR:
1620 level = WLOG_ERROR;
1621 break;
1622 case SDL_LOG_PRIORITY_CRITICAL:
1623 level = WLOG_FATAL;
1624 break;
1625 default:
1626 break;
1627 }
1628
1629 return level;
1630}
1631
1632static void SDLCALL winpr_LogOutputFunction(void* userdata, int category, SDL_LogPriority priority,
1633 const char* message)
1634{
1635 auto sdl = static_cast<SdlContext*>(userdata);
1636 WINPR_ASSERT(sdl);
1637
1638 const DWORD level = sdlpriority2wlog(priority);
1639 auto log = sdl->log;
1640 if (!WLog_IsLevelActive(log, level))
1641 return;
1642
1643 WLog_PrintTextMessage(log, level, __LINE__, __FILE__, __func__, "[%s] %s",
1644 category2str(category), message);
1645}
1646
1647int main(int argc, char* argv[])
1648{
1649 int rc = -1;
1650 int status = 0;
1651 RDP_CLIENT_ENTRY_POINTS clientEntryPoints = {};
1652
1653 RdpClientEntry(&clientEntryPoints);
1654 std::unique_ptr<sdl_rdp_context, void (*)(sdl_rdp_context*)> sdl_rdp(
1655 reinterpret_cast<sdl_rdp_context*>(freerdp_client_context_new(&clientEntryPoints)),
1656 context_free);
1657
1658 if (!sdl_rdp)
1659 return -1;
1660 auto sdl = sdl_rdp->sdl;
1661
1662 auto settings = sdl->context()->settings;
1663 WINPR_ASSERT(settings);
1664
1665 status = freerdp_client_settings_parse_command_line(settings, argc, argv, FALSE);
1666 if (status)
1667 {
1668 rc = freerdp_client_settings_command_line_status_print(settings, status, argc, argv);
1669 if (freerdp_settings_get_bool(settings, FreeRDP_ListMonitors))
1670 sdl_list_monitors(sdl);
1671 else
1672 {
1673 switch (status)
1674 {
1675 case COMMAND_LINE_STATUS_PRINT:
1676 case COMMAND_LINE_STATUS_PRINT_VERSION:
1677 case COMMAND_LINE_STATUS_PRINT_BUILDCONFIG:
1678 break;
1679 case COMMAND_LINE_STATUS_PRINT_HELP:
1680 default:
1681 SdlPref::print_config_file_help(3);
1682 break;
1683 }
1684 }
1685 return rc;
1686 }
1687
1688 SDL_SetLogOutputFunction(winpr_LogOutputFunction, sdl);
1689 auto level = WLog_GetLogLevel(sdl->log);
1690 SDL_SetLogPriorities(wloglevel2dl(level));
1691
1692 auto context = sdl->context();
1693 WINPR_ASSERT(context);
1694
1695 if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CREATE_REQUEST, FALSE))
1696 return -1;
1697
1698 if (freerdp_client_start(context) != 0)
1699 return -1;
1700
1701 rc = sdl_run(sdl);
1702
1703 if (freerdp_client_stop(context) != 0)
1704 return -1;
1705
1706 if (sdl->exit_code != 0)
1707 rc = sdl->exit_code;
1708
1709 return rc;
1710}
1711
1712bool SdlContext::update_fullscreen(bool enter)
1713{
1714 for (const auto& window : windows)
1715 {
1716 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_FULLSCREEN, &window.second, enter))
1717 return false;
1718 }
1719 fullscreen = enter;
1720 return true;
1721}
1722
1723bool SdlContext::update_minimize()
1724{
1725 return sdl_push_user_event(SDL_EVENT_USER_WINDOW_MINIMIZE);
1726}
1727
1728bool SdlContext::update_resizeable(bool enable)
1729{
1730 const auto settings = context()->settings;
1731 const bool dyn = freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate);
1732 const bool smart = freerdp_settings_get_bool(settings, FreeRDP_SmartSizing);
1733 bool use = (dyn && enable) || smart;
1734
1735 for (const auto& window : windows)
1736 {
1737 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_RESIZEABLE, &window.second, use))
1738 return false;
1739 }
1740 resizeable = use;
1741
1742 return true;
1743}
1744
1745SdlContext::SdlContext(rdpContext* context)
1746 : _context(context), log(WLog_Get(SDL_TAG)), disp(this), input(this), clip(this),
1747 primary(nullptr, SDL_DestroySurface), rdp_thread_running(false), dialog(log)
1748{
1749 WINPR_ASSERT(context);
1750
1751 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, SDL_CLIENT_NAME);
1752 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, SDL_CLIENT_VERSION);
1753 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, SDL_CLIENT_UUID);
1754 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, SDL_CLIENT_VENDOR);
1755 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, SDL_CLIENT_COPYRIGHT);
1756 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, SDL_CLIENT_URL);
1757 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, SDL_CLIENT_TYPE);
1758}
1759
1760void SdlContext::setHasCursor(bool val)
1761{
1762 this->_cursor_visible = val;
1763}
1764
1765bool SdlContext::hasCursor() const
1766{
1767 return _cursor_visible;
1768}
1769
1770bool SdlContext::redraw(bool suppress) const
1771{
1772 if (!_connected)
1773 return true;
1774
1775 auto gdi = context()->gdi;
1776 WINPR_ASSERT(gdi);
1777 return gdi_send_suppress_output(gdi, suppress);
1778}
1779
1780void SdlContext::setConnected(bool val)
1781{
1782 _connected = val;
1783}
1784
1785bool SdlContext::isConnected() const
1786{
1787 return _connected;
1788}
1789
1790rdpContext* SdlContext::context() const
1791{
1792 WINPR_ASSERT(_context);
1793 return _context;
1794}
1795
1796rdpClientContext* SdlContext::common() const
1797{
1798 return reinterpret_cast<rdpClientContext*>(context());
1799}
1800
1801void SdlContext::setCursor(rdpPointer* cursor)
1802{
1803 _cursor = cursor;
1804}
1805
1806rdpPointer* SdlContext::cursor() const
1807{
1808 return _cursor;
1809}
1810
1811void SdlContext::setMonitorIds(const std::vector<SDL_DisplayID>& ids)
1812{
1813 _monitorIds.clear();
1814 for (auto id : ids)
1815 {
1816 _monitorIds.push_back(id);
1817 }
1818}
1819
1820const std::vector<SDL_DisplayID>& SdlContext::monitorIds() const
1821{
1822 return _monitorIds;
1823}
1824
1825int64_t SdlContext::monitorId(uint32_t index) const
1826{
1827 if (index >= _monitorIds.size())
1828 {
1829 return -1;
1830 }
1831 return _monitorIds[index];
1832}
1833
1834void SdlContext::push(std::vector<SDL_Rect>&& rects)
1835{
1836 std::unique_lock lock(_queue_mux);
1837 _queue.emplace(std::move(rects));
1838}
1839
1840std::vector<SDL_Rect> SdlContext::pop()
1841{
1842 std::unique_lock lock(_queue_mux);
1843 if (_queue.empty())
1844 {
1845 return {};
1846 }
1847 auto val = std::move(_queue.front());
1848 _queue.pop();
1849 return val;
1850}
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_set_string(rdpSettings *settings, FreeRDP_Settings_Keys_String id, const char *param)
Sets a string settings value. The param is copied.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_server_name(const rdpSettings *settings)
A helper function to return the correct server name.
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.