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