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