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