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