FreeRDP
Loading...
Searching...
No Matches
sdl_context.cpp
1
20#include <algorithm>
21
22#include "sdl_context.hpp"
23#include "sdl_config.hpp"
24#include "sdl_channels.hpp"
25#include "sdl_monitor.hpp"
26#include "sdl_pointer.hpp"
27#include "sdl_touch.hpp"
28
29#include <sdl_common_utils.hpp>
30#include <scoped_guard.hpp>
31
32#include "dialogs/sdl_dialogs.hpp"
33
34#if defined(WITH_WEBVIEW)
35#include <aad/sdl_webview.hpp>
36#endif
37
38SdlContext::SdlContext(rdpContext* context)
39 : _context(context), _log(WLog_Get(CLIENT_TAG("SDL"))), _rdpThreadRunning(false),
40 _primary(nullptr, SDL_DestroySurface), _disp(this), _input(this), _clip(this), _dialog(_log)
41{
42 WINPR_ASSERT(context);
43 setMetadata();
44
45 auto instance = _context->instance;
46 WINPR_ASSERT(instance);
47
48 instance->PreConnect = preConnect;
49 instance->PostConnect = postConnect;
50 instance->PostDisconnect = postDisconnect;
51 instance->PostFinalDisconnect = postFinalDisconnect;
52 instance->AuthenticateEx = sdl_authenticate_ex;
53 instance->VerifyCertificateEx = sdl_verify_certificate_ex;
54 instance->VerifyChangedCertificateEx = sdl_verify_changed_certificate_ex;
55 instance->LogonErrorInfo = sdl_logon_error_info;
56 instance->PresentGatewayMessage = sdl_present_gateway_message;
57 instance->ChooseSmartcard = sdl_choose_smartcard;
58 instance->RetryDialog = sdl_retry_dialog;
59
60#ifdef WITH_WEBVIEW
61 instance->GetAccessToken = sdl_webview_get_access_token;
62#else
63 instance->GetAccessToken = client_cli_get_access_token;
64#endif
65 /* TODO: Client display set up */
66}
67
68void SdlContext::setHasCursor(bool val)
69{
70 this->_cursor_visible = val;
71}
72
73bool SdlContext::hasCursor() const
74{
75 return _cursor_visible;
76}
77
78void SdlContext::setMetadata()
79{
80 auto wmclass = freerdp_settings_get_string(_context->settings, FreeRDP_WmClass);
81 if (!wmclass || (strlen(wmclass) == 0))
82 wmclass = SDL_CLIENT_UUID;
83
84 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, wmclass);
85 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, SDL_CLIENT_NAME);
86 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, SDL_CLIENT_VERSION);
87 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, SDL_CLIENT_VENDOR);
88 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, SDL_CLIENT_COPYRIGHT);
89 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, SDL_CLIENT_URL);
90 SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, SDL_CLIENT_TYPE);
91}
92
93int SdlContext::start()
94{
95 _thread = std::thread(rdpThreadRun, this);
96 return 0;
97}
98
99int SdlContext::join()
100{
101 /* We do not want to use freerdp_abort_connect_context here.
102 * It would change the exit code and we do not want that. */
103 HANDLE event = freerdp_abort_event(context());
104 if (!SetEvent(event))
105 return -1;
106
107 _thread.join();
108 return 0;
109}
110
111void SdlContext::cleanup()
112{
113 std::unique_lock lock(_critical);
114 _windows.clear();
115 _dialog.destroy();
116 _primary.reset();
117}
118
119bool SdlContext::shallAbort(bool ignoreDialogs)
120{
121 std::unique_lock lock(_critical);
122 if (freerdp_shall_disconnect_context(context()))
123 {
124 if (ignoreDialogs)
125 return true;
126 if (_rdpThreadRunning)
127 return false;
128 return !getDialog().isRunning();
129 }
130 return false;
131}
132
133/* Called before a connection is established.
134 * Set all configuration options to support and load channels here. */
135BOOL SdlContext::preConnect(freerdp* instance)
136{
137 WINPR_ASSERT(instance);
138 WINPR_ASSERT(instance->context);
139
140 auto sdl = get_context(instance->context);
141
142 auto settings = instance->context->settings;
143 WINPR_ASSERT(settings);
144
145 if (!freerdp_settings_set_bool(settings, FreeRDP_CertificateCallbackPreferPEM, TRUE))
146 return FALSE;
147
148 /* Optional OS identifier sent to server */
149 if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_UNIX))
150 return FALSE;
151 if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_NATIVE_SDL))
152 return FALSE;
153 /* OrderSupport is initialized at this point.
154 * Only override it if you plan to implement custom order
155 * callbacks or deactivate certain features. */
156 /* Register the channel listeners.
157 * They are required to set up / tear down channels if they are loaded. */
158 PubSub_SubscribeChannelConnected(instance->context->pubSub, sdl_OnChannelConnectedEventHandler);
159 PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
160 sdl_OnChannelDisconnectedEventHandler);
161
162 if (!freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
163 {
164 UINT32 maxWidth = 0;
165 UINT32 maxHeight = 0;
166
167 if (!sdl_detect_monitors(sdl, &maxWidth, &maxHeight))
168 return FALSE;
169
170 if ((maxWidth != 0) && (maxHeight != 0) &&
171 !freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
172 {
173 WLog_Print(sdl->getWLog(), WLOG_INFO, "Update size to %ux%u", maxWidth, maxHeight);
174 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, maxWidth))
175 return FALSE;
176 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, maxHeight))
177 return FALSE;
178 }
179
185 const uint32_t sw = freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingWidth);
186 const uint32_t sh = freerdp_settings_get_uint32(settings, FreeRDP_SmartSizingHeight);
187 const BOOL sm = freerdp_settings_get_bool(settings, FreeRDP_SmartSizing);
188 if (sm && (sw > 0) && (sh > 0))
189 {
190 const BOOL mm = freerdp_settings_get_bool(settings, FreeRDP_UseMultimon);
191 if (mm)
192 WLog_Print(sdl->getWLog(), WLOG_WARN,
193 "/smart-sizing and /multimon are currently not supported, ignoring "
194 "/smart-sizing!");
195 else
196 {
197 sdl->_windowWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
198 sdl->_windowHeigth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
199
200 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, sw))
201 return FALSE;
202 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, sh))
203 return FALSE;
204 }
205 }
206 }
207 else
208 {
209 /* Check +auth-only has a username and password. */
210 if (!freerdp_settings_get_string(settings, FreeRDP_Password))
211 {
212 WLog_Print(sdl->getWLog(), WLOG_INFO,
213 "auth-only, but no password set. Please provide one.");
214 return FALSE;
215 }
216
217 if (!freerdp_settings_set_bool(settings, FreeRDP_DeactivateClientDecoding, TRUE))
218 return FALSE;
219
220 WLog_Print(sdl->getWLog(), WLOG_INFO, "Authentication only. Don't connect SDL.");
221 }
222
223 if (!sdl->getInputChannelContext().initialize())
224 return FALSE;
225
226 /* TODO: Any code your client requires */
227 return TRUE;
228}
229
230/* Called after a RDP connection was successfully established.
231 * Settings might have changed during negotiation of client / server feature
232 * support.
233 *
234 * Set up local framebuffers and paing callbacks.
235 * If required, register pointer callbacks to change the local mouse cursor
236 * when hovering over the RDP window
237 */
238BOOL SdlContext::postConnect(freerdp* instance)
239{
240 WINPR_ASSERT(instance);
241
242 auto context = instance->context;
243 WINPR_ASSERT(context);
244
245 auto sdl = get_context(context);
246
247 if (freerdp_settings_get_bool(context->settings, FreeRDP_UseMultimon))
248 {
249 const auto driver = SDL_GetCurrentVideoDriver();
250 bool buggy = false;
251 if (driver)
252 {
253 if (strcmp(driver, "wayland") == 0)
254 buggy = true;
255 else if (strcmp(driver, "x11") == 0)
256 {
257 auto env = SDL_GetEnvironment();
258 auto xdg = SDL_GetEnvironmentVariable(env, "XDG_SESSION_TYPE");
259 auto qpa = SDL_GetEnvironmentVariable(env, "QT_QPA_PLATFORM");
260 if (xdg && (strcmp(xdg, "wayland") == 0))
261 buggy = true;
262 else if (qpa && (strcmp(qpa, "wayland") == 0))
263 buggy = true;
264 }
265 }
266
267 if (buggy)
268 {
269 const auto name = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING);
270
271 WLog_Print(sdl->getWLog(), WLOG_WARN,
272 "%s is affected by wayland bug "
273 "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/179",
274 name);
275 WLog_Print(
276 sdl->getWLog(), WLOG_WARN,
277 "you will not be able to properly use all monitors for FreeRDP unless this is "
278 "resolved and the SDL library you are using supports this.");
279 WLog_Print(sdl->getWLog(), WLOG_WARN,
280 "For the time being run %s from an X11 session or only use single monitor "
281 "fullscreen /f",
282 name);
283 }
284 }
285
286 // Retry was successful, discard dialog
287 sdl->getDialog().show(false);
288
289 if (freerdp_settings_get_bool(context->settings, FreeRDP_AuthenticationOnly))
290 {
291 /* Check +auth-only has a username and password. */
292 if (!freerdp_settings_get_string(context->settings, FreeRDP_Password))
293 {
294 WLog_Print(sdl->getWLog(), WLOG_INFO,
295 "auth-only, but no password set. Please provide one.");
296 return FALSE;
297 }
298
299 WLog_Print(sdl->getWLog(), WLOG_INFO, "Authentication only. Don't connect to X.");
300 return TRUE;
301 }
302
303 if (!sdl->waitForWindowsCreated())
304 return FALSE;
305
306 sdl->_sdlPixelFormat = SDL_PIXELFORMAT_BGRA32;
307 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
308 return FALSE;
309
310 if (!sdl->createPrimary())
311 return FALSE;
312
313 if (!sdl_register_pointer(instance->context->graphics))
314 return FALSE;
315
316 WINPR_ASSERT(context->update);
317
318 context->update->BeginPaint = beginPaint;
319 context->update->EndPaint = endPaint;
320 context->update->PlaySound = playSound;
321 context->update->DesktopResize = desktopResize;
322 context->update->SetKeyboardIndicators = sdlInput::keyboard_set_indicators;
323 context->update->SetKeyboardImeStatus = sdlInput::keyboard_set_ime_status;
324
325 if (!sdl->setResizeable(false))
326 return FALSE;
327 if (!sdl->setFullscreen(freerdp_settings_get_bool(context->settings, FreeRDP_Fullscreen) ||
328 freerdp_settings_get_bool(context->settings, FreeRDP_UseMultimon),
329 true))
330 return FALSE;
331 sdl->setConnected(true);
332 return TRUE;
333}
334
335/* This function is called whether a session ends by failure or success.
336 * Clean up everything allocated by pre_connect and post_connect.
337 */
338void SdlContext::postDisconnect(freerdp* instance)
339{
340 if (!instance)
341 return;
342
343 if (!instance->context)
344 return;
345
346 auto sdl = get_context(instance->context);
347 sdl->setConnected(false);
348
349 gdi_free(instance);
350}
351
352void SdlContext::postFinalDisconnect(freerdp* instance)
353{
354 if (!instance)
355 return;
356
357 if (!instance->context)
358 return;
359
360 PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
361 sdl_OnChannelConnectedEventHandler);
362 PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
363 sdl_OnChannelDisconnectedEventHandler);
364}
365
366/* Create a SDL surface from the GDI buffer */
367bool SdlContext::createPrimary()
368{
369 auto gdi = context()->gdi;
370 WINPR_ASSERT(gdi);
371
372 _primary = SDLSurfacePtr(
373 SDL_CreateSurfaceFrom(static_cast<int>(gdi->width), static_cast<int>(gdi->height),
374 pixelFormat(), gdi->primary_buffer, static_cast<int>(gdi->stride)),
375 SDL_DestroySurface);
376 if (!_primary)
377 return false;
378
379 SDL_SetSurfaceBlendMode(_primary.get(), SDL_BLENDMODE_NONE);
380 SDL_Rect surfaceRect = { 0, 0, gdi->width, gdi->height };
381 SDL_FillSurfaceRect(_primary.get(), &surfaceRect,
382 SDL_MapSurfaceRGBA(_primary.get(), 0, 0, 0, 0xff));
383
384 return true;
385}
386
387bool SdlContext::createWindows()
388{
389 auto settings = context()->settings;
390 const auto& title = windowTitle();
391
392 ScopeGuard guard1([&]() { _windowsCreatedEvent.set(); });
393
394 UINT32 windowCount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
395
396 Sint32 originX = 0;
397 Sint32 originY = 0;
398 for (UINT32 x = 0; x < windowCount; x++)
399 {
400 auto id = monitorId(x);
401 if (id < 0)
402 return false;
403
404 auto monitor = static_cast<rdpMonitor*>(
405 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
406
407 originX = std::min<Sint32>(monitor->x, originX);
408 originY = std::min<Sint32>(monitor->y, originY);
409 }
410
411 for (UINT32 x = 0; x < windowCount; x++)
412 {
413 auto id = monitorId(x);
414 if (id < 0)
415 return false;
416
417 auto monitor = static_cast<rdpMonitor*>(
418 freerdp_settings_get_pointer_array_writable(settings, FreeRDP_MonitorDefArray, x));
419
420 Uint32 w = WINPR_ASSERTING_INT_CAST(Uint32, monitor->width);
421 Uint32 h = WINPR_ASSERTING_INT_CAST(Uint32, monitor->height);
422 if (!(freerdp_settings_get_bool(settings, FreeRDP_UseMultimon) ||
423 freerdp_settings_get_bool(settings, FreeRDP_Fullscreen)))
424 {
425 if (_windowWidth > 0)
426 w = _windowWidth;
427 else
428 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
429
430 if (_windowHeigth > 0)
431 h = _windowHeigth;
432 else
433 h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
434 }
435
436 Uint32 flags = SDL_WINDOW_HIGH_PIXEL_DENSITY;
437
438 if (freerdp_settings_get_bool(settings, FreeRDP_Fullscreen) &&
439 !freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
440 {
441 flags |= SDL_WINDOW_FULLSCREEN;
442 }
443
444 if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
445 {
446 flags |= SDL_WINDOW_BORDERLESS;
447 }
448
449 if (!freerdp_settings_get_bool(settings, FreeRDP_Decorations))
450 flags |= SDL_WINDOW_BORDERLESS;
451
452 auto did = WINPR_ASSERTING_INT_CAST(SDL_DisplayID, id);
453 auto window = SdlWindow::create(did, title, flags, w, h);
454
455 if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
456 {
457 window.setOffsetX(originX - monitor->x);
458 window.setOffsetY(originY - monitor->y);
459 }
460
461 _windows.insert({ window.id(), std::move(window) });
462 }
463
464 return true;
465}
466
467bool SdlContext::updateWindowList()
468{
469 std::vector<rdpMonitor> list;
470 list.reserve(_windows.size());
471 for (const auto& win : _windows)
472 list.push_back(win.second.monitor(_windows.size() == 1));
473
474 return freerdp_settings_set_monitor_def_array_sorted(context()->settings, list.data(),
475 list.size());
476}
477
478bool SdlContext::updateWindow(SDL_WindowID id)
479{
480 if (freerdp_settings_get_bool(_context->settings, FreeRDP_Fullscreen) ||
481 freerdp_settings_get_bool(_context->settings, FreeRDP_UseMultimon))
482 return true;
483
484 auto& w = _windows.at(id);
485 auto m = w.monitor(true);
486 auto r = w.rect();
487 m.width = r.w;
488 m.height = r.h;
489 m.attributes.physicalWidth = static_cast<UINT32>(r.w);
490 m.attributes.physicalHeight = static_cast<UINT32>(r.h);
491 w.setMonitor(m);
492 return true;
493}
494
495std::string SdlContext::windowTitle() const
496{
497 const char* prefix = "FreeRDP:";
498
499 const auto windowTitle = freerdp_settings_get_string(context()->settings, FreeRDP_WindowTitle);
500 if (windowTitle)
501 return windowTitle;
502
503 const auto name = freerdp_settings_get_server_name(context()->settings);
504 const auto port = freerdp_settings_get_uint32(context()->settings, FreeRDP_ServerPort);
505 const auto addPort = (port != 3389);
506
507 std::stringstream ss;
508 ss << prefix << " " << name;
509
510 if (addPort)
511 ss << ":" << port;
512
513 return ss.str();
514}
515
516bool SdlContext::waitForWindowsCreated()
517{
518 {
519 std::unique_lock<CriticalSection> lock(_critical);
520 _windowsCreatedEvent.clear();
521 if (!sdl_push_user_event(SDL_EVENT_USER_CREATE_WINDOWS, this))
522 return false;
523 }
524
525 HANDLE handles[] = { _windowsCreatedEvent.handle(), freerdp_abort_event(context()) };
526
527 const DWORD rc = WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, INFINITE);
528 switch (rc)
529 {
530 case WAIT_OBJECT_0:
531 return true;
532 default:
533 return false;
534 }
535}
536
537/* This function is called when the library completed composing a new
538 * frame. Read out the changed areas and blit them to your output device.
539 * The image buffer will have the format specified by gdi_init
540 */
541BOOL SdlContext::endPaint(rdpContext* context)
542{
543 auto sdl = get_context(context);
544 WINPR_ASSERT(sdl);
545
546 auto gdi = context->gdi;
547 WINPR_ASSERT(gdi);
548 WINPR_ASSERT(gdi->primary);
549
550 HGDI_DC hdc = gdi->primary->hdc;
551 WINPR_ASSERT(hdc);
552 if (!hdc->hwnd)
553 return TRUE;
554
555 HGDI_WND hwnd = hdc->hwnd;
556 WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
557
558 if (hwnd->invalid->null)
559 return TRUE;
560
561 WINPR_ASSERT(hwnd->invalid);
562 if (gdi->suppressOutput || hwnd->invalid->null)
563 return TRUE;
564
565 const INT32 ninvalid = hwnd->ninvalid;
566 const GDI_RGN* cinvalid = hwnd->cinvalid;
567
568 if (ninvalid < 1)
569 return TRUE;
570
571 std::vector<SDL_Rect> rects;
572 for (INT32 x = 0; x < ninvalid; x++)
573 {
574 auto& rgn = cinvalid[x];
575 rects.push_back({ rgn.x, rgn.y, rgn.w, rgn.h });
576 }
577
578 sdl->push(std::move(rects));
579 return sdl_push_user_event(SDL_EVENT_USER_UPDATE);
580}
581
582void SdlContext::sdl_client_cleanup(int exit_code, const std::string& error_msg)
583{
584 rdpSettings* settings = context()->settings;
585 WINPR_ASSERT(settings);
586
587 _rdpThreadRunning = false;
588 bool showError = false;
589 if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
590 WLog_Print(getWLog(), WLOG_INFO, "Authentication only, exit status %s [%" PRId32 "]",
591 sdl::error::exitCodeToTag(exit_code), exit_code);
592 else
593 {
594 switch (exit_code)
595 {
596 case sdl::error::SUCCESS:
597 case sdl::error::DISCONNECT:
598 case sdl::error::LOGOFF:
599 case sdl::error::DISCONNECT_BY_USER:
600 case sdl::error::CONNECT_CANCELLED:
601 break;
602 default:
603 {
604 getDialog().showError(error_msg);
605 }
606 break;
607 }
608 }
609
610 if (!showError)
611 getDialog().show(false);
612
613 _exitCode = exit_code;
614 std::ignore = sdl_push_user_event(SDL_EVENT_USER_QUIT);
615 SDL_CleanupTLS();
616}
617
618int SdlContext::sdl_client_thread_connect(std::string& error_msg)
619{
620 auto instance = context()->instance;
621 WINPR_ASSERT(instance);
622
623 _rdpThreadRunning = true;
624 BOOL rc = freerdp_connect(instance);
625
626 rdpSettings* settings = context()->settings;
627 WINPR_ASSERT(settings);
628
629 int exit_code = sdl::error::SUCCESS;
630 if (!rc)
631 {
632 UINT32 error = freerdp_get_last_error(context());
633 exit_code = sdl::error::errorToExitCode(error);
634 }
635
636 if (freerdp_settings_get_bool(settings, FreeRDP_AuthenticationOnly))
637 {
638 DWORD code = freerdp_get_last_error(context());
639 freerdp_abort_connect_context(context());
640 WLog_Print(getWLog(), WLOG_ERROR, "Authentication only, %s [0x%08" PRIx32 "] %s",
641 freerdp_get_last_error_name(code), code, freerdp_get_last_error_string(code));
642 return exit_code;
643 }
644
645 if (!rc)
646 {
647 DWORD code = freerdp_error_info(instance);
648 if (exit_code == sdl::error::SUCCESS)
649 {
650 char* msg = nullptr;
651 size_t len = 0;
652 exit_code = error_info_to_error(&code, &msg, &len);
653 if (msg)
654 error_msg = msg;
655 free(msg);
656 }
657
658 auto last = freerdp_get_last_error(context());
659 if (error_msg.empty())
660 {
661 char* msg = nullptr;
662 size_t len = 0;
663 winpr_asprintf(&msg, &len, "%s [0x%08" PRIx32 "]\n%s",
664 freerdp_get_last_error_name(last), last,
665 freerdp_get_last_error_string(last));
666 if (msg)
667 error_msg = msg;
668 free(msg);
669 }
670
671 if (exit_code == sdl::error::SUCCESS)
672 {
673 if (last == FREERDP_ERROR_AUTHENTICATION_FAILED)
674 exit_code = sdl::error::AUTH_FAILURE;
675 else if (code == ERRINFO_SUCCESS)
676 exit_code = sdl::error::CONN_FAILED;
677 }
678
679 getDialog().show(false);
680 }
681
682 return exit_code;
683}
684
685int SdlContext::sdl_client_thread_run(std::string& error_msg)
686{
687 auto instance = context()->instance;
688 WINPR_ASSERT(instance);
689
690 int exit_code = sdl::error::SUCCESS;
691 while (!freerdp_shall_disconnect_context(context()))
692 {
693 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = {};
694 /*
695 * win8 and server 2k12 seem to have some timing issue/race condition
696 * when a initial sync request is send to sync the keyboard indicators
697 * sending the sync event twice fixed this problem
698 */
699 if (freerdp_focus_required(instance))
700 {
701 auto ctx = get_context(context());
702 WINPR_ASSERT(ctx);
703
704 auto& input = ctx->getInputChannelContext();
705 if (!input.keyboard_focus_in())
706 break;
707 if (!input.keyboard_focus_in())
708 break;
709 }
710
711 const DWORD nCount = freerdp_get_event_handles(context(), handles, ARRAYSIZE(handles));
712
713 if (nCount == 0)
714 {
715 WLog_Print(getWLog(), WLOG_ERROR, "freerdp_get_event_handles failed");
716 break;
717 }
718
719 const DWORD status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
720
721 if (status == WAIT_FAILED)
722 {
723 WLog_Print(getWLog(), WLOG_ERROR, "WaitForMultipleObjects WAIT_FAILED");
724 break;
725 }
726
727 if (!freerdp_check_event_handles(context()))
728 {
729 if (client_auto_reconnect(instance))
730 {
731 // Retry was successful, discard dialog
732 getDialog().show(false);
733 continue;
734 }
735 else
736 {
737 /*
738 * Indicate an unsuccessful connection attempt if reconnect
739 * did not succeed and no other error was specified.
740 */
741 if (freerdp_error_info(instance) == 0)
742 exit_code = sdl::error::CONN_FAILED;
743 }
744
745 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
746 WLog_Print(getWLog(), WLOG_ERROR, "WaitForMultipleObjects failed with %" PRIu32 "",
747 status);
748 if (freerdp_get_last_error(context()) == FREERDP_ERROR_SUCCESS)
749 WLog_Print(getWLog(), WLOG_ERROR, "Failed to check FreeRDP event handles");
750 break;
751 }
752 }
753
754 if (exit_code == sdl::error::SUCCESS)
755 {
756 DWORD code = 0;
757 {
758 char* emsg = nullptr;
759 size_t elen = 0;
760 exit_code = error_info_to_error(&code, &emsg, &elen);
761 if (emsg)
762 error_msg = emsg;
763 free(emsg);
764 }
765
766 if ((code == ERRINFO_LOGOFF_BY_USER) &&
767 (freerdp_get_disconnect_ultimatum(context()) == Disconnect_Ultimatum_user_requested))
768 {
769 const char* msg = "Error info says user did not initiate but disconnect ultimatum says "
770 "they did; treat this as a user logoff";
771
772 char* emsg = nullptr;
773 size_t elen = 0;
774 winpr_asprintf(&emsg, &elen, "%s", msg);
775 if (emsg)
776 error_msg = emsg;
777 free(emsg);
778
779 /* This situation might be limited to Windows XP. */
780 WLog_Print(getWLog(), WLOG_INFO, "%s", msg);
781 exit_code = sdl::error::LOGOFF;
782 }
783 }
784
785 freerdp_disconnect(instance);
786
787 return exit_code;
788}
789
790/* RDP main loop.
791 * Connects RDP, loops while running and handles event and dispatch, cleans up
792 * after the connection ends. */
793DWORD SdlContext::rdpThreadRun(SdlContext* sdl)
794{
795 WINPR_ASSERT(sdl);
796
797 std::string error_msg;
798 int exit_code = sdl->sdl_client_thread_connect(error_msg);
799 if (exit_code == sdl::error::SUCCESS)
800 exit_code = sdl->sdl_client_thread_run(error_msg);
801 sdl->sdl_client_cleanup(exit_code, error_msg);
802
803 return static_cast<DWORD>(exit_code);
804}
805
806int SdlContext::error_info_to_error(DWORD* pcode, char** msg, size_t* len) const
807{
808 const DWORD code = freerdp_error_info(context()->instance);
809 const char* name = freerdp_get_error_info_name(code);
810 const char* str = freerdp_get_error_info_string(code);
811 const int exit_code = sdl::error::errorToExitCode(code);
812
813 winpr_asprintf(msg, len, "Terminate with %s due to ERROR_INFO %s [0x%08" PRIx32 "]: %s",
814 sdl::error::errorToExitCodeTag(code), name, code, str);
815 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", *msg);
816 if (pcode)
817 *pcode = code;
818 return exit_code;
819}
820
821void SdlContext::applyMonitorOffset(SDL_WindowID window, float& x, float& y) const
822{
823 if (!freerdp_settings_get_bool(context()->settings, FreeRDP_UseMultimon))
824 return;
825
826 auto w = getWindowForId(window);
827 x -= static_cast<float>(w->offsetX());
828 y -= static_cast<float>(w->offsetY());
829}
830
831static bool alignX(const SDL_Rect& a, const SDL_Rect& b)
832{
833 if (a.x + a.w == b.x)
834 return true;
835 if (b.x + b.w == a.x)
836 return true;
837 return false;
838}
839
840static bool alignY(const SDL_Rect& a, const SDL_Rect& b)
841{
842 if (a.y + a.h == b.y)
843 return true;
844 if (b.y + b.h == a.y)
845 return true;
846 return false;
847}
848
849std::vector<SDL_DisplayID>
850SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID id,
851 const std::vector<SDL_DisplayID>& ignore)
852{
853 auto first = _offsets.at(id);
854 std::vector<SDL_DisplayID> neighbours;
855
856 for (auto& entry : _offsets)
857 {
858 if (entry.first == id)
859 continue;
860 if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end())
861 continue;
862
863 bool neighbor = false;
864 if (alignX(entry.second.first, first.first))
865 {
866 if (entry.second.first.x < first.first.x)
867 entry.second.second.x = first.second.x - entry.second.second.w;
868 else
869 entry.second.second.x = first.second.x + first.second.w;
870 neighbor = true;
871 }
872 if (alignY(entry.second.first, first.first))
873 {
874 if (entry.second.first.y < first.first.y)
875 entry.second.second.y = first.second.y - entry.second.second.h;
876 else
877 entry.second.second.y = first.second.y + first.second.h;
878 neighbor = true;
879 }
880
881 if (neighbor)
882 neighbours.push_back(entry.first);
883 }
884 return neighbours;
885}
886
887void SdlContext::updateMonitorDataFromOffsets()
888{
889 for (auto& entry : _displays)
890 {
891 auto offsets = _offsets.at(entry.first);
892 entry.second.x = offsets.second.x;
893 entry.second.y = offsets.second.y;
894 }
895
896 for (auto& entry : _windows)
897 {
898 const auto& monitor = _displays.at(entry.first);
899 entry.second.setMonitor(monitor);
900 }
901}
902
903bool SdlContext::drawToWindow(SdlWindow& window, const std::vector<SDL_Rect>& rects)
904{
905 if (!isConnected())
906 return true;
907
908 auto gdi = context()->gdi;
909 WINPR_ASSERT(gdi);
910
911 auto size = window.rect();
912
913 std::unique_lock lock(_critical);
914 auto surface = _primary.get();
915 if (freerdp_settings_get_bool(context()->settings, FreeRDP_SmartSizing))
916 {
917 window.setOffsetX(0);
918 window.setOffsetY(0);
919 if (gdi->width < size.w)
920 {
921 window.setOffsetX((size.w - gdi->width) / 2);
922 }
923 if (gdi->height < size.h)
924 {
925 window.setOffsetY((size.h - gdi->height) / 2);
926 }
927
928 _localScale = { static_cast<float>(size.w) / static_cast<float>(gdi->width),
929 static_cast<float>(size.h) / static_cast<float>(gdi->height) };
930 if (!window.drawScaledRects(surface, _localScale, rects))
931 return false;
932 }
933 else
934 {
935 SDL_Point offset{ 0, 0 };
936 if (freerdp_settings_get_bool(context()->settings, FreeRDP_UseMultimon))
937 offset = { window.offsetX(), window.offsetY() };
938 if (!window.drawRects(surface, offset, rects))
939 return false;
940 }
941
942 window.updateSurface();
943 return true;
944}
945
946bool SdlContext::minimizeAllWindows()
947{
948 for (auto& w : _windows)
949 w.second.minimize();
950 return true;
951}
952
953int SdlContext::exitCode() const
954{
955 return _exitCode;
956}
957
958SDL_PixelFormat SdlContext::pixelFormat() const
959{
960 return _sdlPixelFormat;
961}
962
963bool SdlContext::addDisplayWindow(SDL_DisplayID id)
964{
965 const auto flags =
966 SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS;
967 auto title = sdl::utils::windowTitle(context()->settings);
968 auto w = SdlWindow::create(id, title, flags);
969 _windows.emplace(w.id(), std::move(w));
970 return true;
971}
972
973bool SdlContext::removeDisplayWindow(SDL_DisplayID id)
974{
975 for (auto& w : _windows)
976 {
977 if (w.second.displayIndex() == id)
978 _windows.erase(w.first);
979 }
980 return true;
981}
982
983bool SdlContext::detectDisplays()
984{
985 int count = 0;
986 auto display = SDL_GetDisplays(&count);
987 if (!display)
988 return false;
989 for (int x = 0; x < count; x++)
990 {
991 const auto id = display[x];
992 addOrUpdateDisplay(id);
993 }
994 SDL_free(display);
995 return true;
996}
997
998rdpMonitor SdlContext::getDisplay(SDL_DisplayID id) const
999{
1000 return _displays.at(id);
1001}
1002
1003std::vector<SDL_DisplayID> SdlContext::getDisplayIds() const
1004{
1005 std::vector<SDL_DisplayID> keys;
1006 keys.reserve(_displays.size());
1007 for (const auto& entry : _displays)
1008 {
1009 keys.push_back(entry.first);
1010 }
1011 return keys;
1012}
1013
1014const SdlWindow* SdlContext::getWindowForId(SDL_WindowID id) const
1015{
1016 auto it = _windows.find(id);
1017 if (it == _windows.end())
1018 return nullptr;
1019 return &it->second;
1020}
1021
1022SdlWindow* SdlContext::getWindowForId(SDL_WindowID id)
1023{
1024 auto it = _windows.find(id);
1025 if (it == _windows.end())
1026 return nullptr;
1027 return &it->second;
1028}
1029
1030SdlWindow* SdlContext::getFirstWindow()
1031{
1032 if (_windows.empty())
1033 return nullptr;
1034 return &_windows.begin()->second;
1035}
1036
1037sdlDispContext& SdlContext::getDisplayChannelContext()
1038{
1039 return _disp;
1040}
1041
1042sdlInput& SdlContext::getInputChannelContext()
1043{
1044 return _input;
1045}
1046
1047sdlClip& SdlContext::getClipboardChannelContext()
1048{
1049 return _clip;
1050}
1051
1052SdlConnectionDialogWrapper& SdlContext::getDialog()
1053{
1054 return _dialog;
1055}
1056
1057wLog* SdlContext::getWLog()
1058{
1059 return _log;
1060}
1061
1062bool SdlContext::moveMouseTo(const SDL_FPoint& pos)
1063{
1064 auto window = SDL_GetMouseFocus();
1065 if (!window)
1066 return true;
1067
1068 const auto id = SDL_GetWindowID(window);
1069 const auto spos = pixelToScreen(id, pos);
1070 SDL_WarpMouseInWindow(window, spos.x, spos.y);
1071 return true;
1072}
1073
1074bool SdlContext::handleEvent(const SDL_MouseMotionEvent& ev)
1075{
1076 SDL_Event copy{};
1077 copy.motion = ev;
1078 if (!eventToPixelCoordinates(ev.windowID, copy))
1079 return false;
1080 removeLocalScaling(copy.motion.x, copy.motion.y);
1081 removeLocalScaling(copy.motion.xrel, copy.motion.yrel);
1082 applyMonitorOffset(copy.motion.windowID, copy.motion.x, copy.motion.y);
1083
1084 return SdlTouch::handleEvent(this, copy.motion);
1085}
1086
1087bool SdlContext::handleEvent(const SDL_MouseWheelEvent& ev)
1088{
1089 SDL_Event copy{};
1090 copy.wheel = ev;
1091 if (!eventToPixelCoordinates(ev.windowID, copy))
1092 return false;
1093 removeLocalScaling(copy.wheel.mouse_x, copy.wheel.mouse_y);
1094 return SdlTouch::handleEvent(this, copy.wheel);
1095}
1096
1097bool SdlContext::handleEvent(const SDL_WindowEvent& ev)
1098{
1099 if (!getDisplayChannelContext().handleEvent(ev))
1100 return false;
1101
1102 auto window = getWindowForId(ev.windowID);
1103 if (!window)
1104 return true;
1105
1106 {
1107 const auto& r = window->rect();
1108 const auto& b = window->bounds();
1109 const auto& scale = window->scale();
1110 const auto& orientation = window->orientation();
1111 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1112 "%s: [%u] %dx%d-%dx%d {%dx%d-%dx%d}{scale=%f,orientation=%s}",
1113 sdl::utils::toString(ev.type).c_str(), ev.windowID, r.x, r.y, r.w, r.h, b.x,
1114 b.y, b.w, b.h, static_cast<double>(scale),
1115 sdl::utils::toString(orientation).c_str());
1116 }
1117
1118 switch (ev.type)
1119 {
1120 case SDL_EVENT_WINDOW_MOUSE_ENTER:
1121 return restoreCursor();
1122 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
1123 if (isConnected())
1124 {
1125 if (!window->fill())
1126 return false;
1127 if (!drawToWindow(*window))
1128 return false;
1129 if (!restoreCursor())
1130 return false;
1131 }
1132 break;
1133 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
1134 if (!window->fill())
1135 return false;
1136 if (!drawToWindow(*window))
1137 return false;
1138 if (!restoreCursor())
1139 return false;
1140 break;
1141 case SDL_EVENT_WINDOW_MOVED:
1142 {
1143 auto r = window->rect();
1144 auto id = window->id();
1145 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%u: %dx%d-%dx%d", id, r.x, r.y, r.w, r.h);
1146 }
1147 break;
1148 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
1149 {
1150 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Window closed, terminating RDP session...");
1151 freerdp_abort_connect_context(context());
1152 }
1153 break;
1154 default:
1155 break;
1156 }
1157 return true;
1158}
1159
1160bool SdlContext::handleEvent(const SDL_DisplayEvent& ev)
1161{
1162 if (!getDisplayChannelContext().handleEvent(ev))
1163 return false;
1164
1165 switch (ev.type)
1166 {
1167 case SDL_EVENT_DISPLAY_REMOVED: // Can't show details for this one...
1168 break;
1169 default:
1170 {
1171 SDL_Rect r = {};
1172 if (!SDL_GetDisplayBounds(ev.displayID, &r))
1173 return false;
1174 const auto name = SDL_GetDisplayName(ev.displayID);
1175 if (!name)
1176 return false;
1177 const auto orientation = SDL_GetCurrentDisplayOrientation(ev.displayID);
1178 const auto scale = SDL_GetDisplayContentScale(ev.displayID);
1179 const auto mode = SDL_GetCurrentDisplayMode(ev.displayID);
1180 if (!mode)
1181 return false;
1182
1183 SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
1184 "%s: [%u, %s] %dx%d-%dx%d {orientation=%s, scale=%f}%s",
1185 sdl::utils::toString(ev.type).c_str(), ev.displayID, name, r.x, r.y, r.w,
1186 r.h, sdl::utils::toString(orientation).c_str(), static_cast<double>(scale),
1187 sdl::utils::toString(mode).c_str());
1188 }
1189 break;
1190 }
1191 return true;
1192}
1193
1194bool SdlContext::handleEvent(const SDL_MouseButtonEvent& ev)
1195{
1196 SDL_Event copy = {};
1197 copy.button = ev;
1198 if (!eventToPixelCoordinates(ev.windowID, copy))
1199 return false;
1200 removeLocalScaling(copy.button.x, copy.button.y);
1201 applyMonitorOffset(copy.button.windowID, copy.button.x, copy.button.y);
1202 return SdlTouch::handleEvent(this, copy.button);
1203}
1204
1205bool SdlContext::handleEvent(const SDL_TouchFingerEvent& ev)
1206{
1207 SDL_Event copy{};
1208 copy.tfinger = ev;
1209 if (!eventToPixelCoordinates(ev.windowID, copy))
1210 return false;
1211 removeLocalScaling(copy.tfinger.dx, copy.tfinger.dy);
1212 removeLocalScaling(copy.tfinger.x, copy.tfinger.y);
1213 applyMonitorOffset(copy.tfinger.windowID, copy.tfinger.x, copy.tfinger.y);
1214 return SdlTouch::handleEvent(this, copy.tfinger);
1215}
1216
1217void SdlContext::addOrUpdateDisplay(SDL_DisplayID id)
1218{
1219 auto monitor = SdlWindow::query(id, false);
1220 _displays.emplace(id, monitor);
1221
1222 /* Update actual display rectangles:
1223 *
1224 * 1. Get logical display bounds
1225 * 2. Use already known pixel width and height
1226 * 3. Iterate over each display and update the x and y offsets by adding all monitor
1227 * widths/heights from the primary
1228 */
1229 _offsets.clear();
1230 for (auto& entry : _displays)
1231 {
1232 SDL_Rect bounds{};
1233 std::ignore = SDL_GetDisplayBounds(entry.first, &bounds);
1234
1235 SDL_Rect pixel{};
1236 pixel.w = entry.second.width;
1237 pixel.h = entry.second.height;
1238 _offsets.emplace(entry.first, std::pair{ bounds, pixel });
1239 }
1240
1241 /* 1. Find primary and update all neighbors
1242 * 2. For each neighbor update all neighbors
1243 * 3. repeat until all displays updated.
1244 */
1245 const auto primary = SDL_GetPrimaryDisplay();
1246 std::vector<SDL_DisplayID> handled;
1247 handled.push_back(primary);
1248
1249 auto neighbors = updateDisplayOffsetsForNeighbours(primary);
1250 while (!neighbors.empty())
1251 {
1252 auto neighbor = *neighbors.begin();
1253 neighbors.pop_back();
1254
1255 if (std::find(handled.begin(), handled.end(), neighbor) != handled.end())
1256 continue;
1257 handled.push_back(neighbor);
1258
1259 auto next = updateDisplayOffsetsForNeighbours(neighbor, handled);
1260 neighbors.insert(neighbors.end(), next.begin(), next.end());
1261 }
1262 updateMonitorDataFromOffsets();
1263}
1264
1265void SdlContext::deleteDisplay(SDL_DisplayID id)
1266{
1267 _displays.erase(id);
1268}
1269
1270bool SdlContext::eventToPixelCoordinates(SDL_WindowID id, SDL_Event& ev)
1271{
1272 auto w = getWindowForId(id);
1273 if (!w)
1274 return false;
1275
1276 /* Ignore errors here, sometimes SDL has no renderer */
1277 auto renderer = SDL_GetRenderer(w->window());
1278 if (!renderer)
1279 return true;
1280 return SDL_ConvertEventToRenderCoordinates(renderer, &ev);
1281}
1282
1283SDL_FPoint SdlContext::applyLocalScaling(const SDL_FPoint& val) const
1284{
1285 if (!freerdp_settings_get_bool(context()->settings, FreeRDP_SmartSizing))
1286 return val;
1287
1288 auto rval = val;
1289 rval.x *= _localScale.x;
1290 rval.y *= _localScale.y;
1291 return rval;
1292}
1293
1294void SdlContext::removeLocalScaling(float& x, float& y) const
1295{
1296 if (!freerdp_settings_get_bool(context()->settings, FreeRDP_SmartSizing))
1297 return;
1298 x /= _localScale.x;
1299 y /= _localScale.y;
1300}
1301
1302SDL_FPoint SdlContext::screenToPixel(SDL_WindowID id, const SDL_FPoint& pos)
1303{
1304 auto w = getWindowForId(id);
1305 if (!w)
1306 return {};
1307
1308 /* Ignore errors here, sometimes SDL has no renderer */
1309 auto renderer = SDL_GetRenderer(w->window());
1310 if (!renderer)
1311 return pos;
1312
1313 SDL_FPoint rpos{};
1314 if (!SDL_RenderCoordinatesFromWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1315 return {};
1316 removeLocalScaling(rpos.x, rpos.y);
1317 return rpos;
1318}
1319
1320SDL_FPoint SdlContext::pixelToScreen(SDL_WindowID id, const SDL_FPoint& pos)
1321{
1322 auto w = getWindowForId(id);
1323 if (!w)
1324 return {};
1325
1326 /* Ignore errors here, sometimes SDL has no renderer */
1327 auto renderer = SDL_GetRenderer(w->window());
1328 if (!renderer)
1329 return pos;
1330
1331 SDL_FPoint rpos{};
1332 if (!SDL_RenderCoordinatesToWindow(renderer, pos.x, pos.y, &rpos.x, &rpos.y))
1333 return {};
1334 return applyLocalScaling(rpos);
1335}
1336
1337SDL_FRect SdlContext::pixelToScreen(SDL_WindowID id, const SDL_FRect& pos)
1338{
1339 const auto fpos = pixelToScreen(id, SDL_FPoint{ pos.x, pos.y });
1340 const auto size = pixelToScreen(id, SDL_FPoint{ pos.w, pos.h });
1341 return { fpos.x, fpos.y, size.x, size.y };
1342}
1343
1344bool SdlContext::handleEvent(const SDL_Event& ev)
1345{
1346 if ((ev.type >= SDL_EVENT_DISPLAY_FIRST) && (ev.type <= SDL_EVENT_DISPLAY_LAST))
1347 {
1348 const auto& dev = ev.display;
1349 return handleEvent(dev);
1350 }
1351 if ((ev.type >= SDL_EVENT_WINDOW_FIRST) && (ev.type <= SDL_EVENT_WINDOW_LAST))
1352 {
1353 const auto& wev = ev.window;
1354 return handleEvent(wev);
1355 }
1356 switch (ev.type)
1357 {
1358 case SDL_EVENT_FINGER_DOWN:
1359 case SDL_EVENT_FINGER_UP:
1360 case SDL_EVENT_FINGER_MOTION:
1361 {
1362 const auto& cev = ev.tfinger;
1363 return handleEvent(cev);
1364 }
1365 case SDL_EVENT_MOUSE_MOTION:
1366 {
1367 const auto& cev = ev.motion;
1368 return handleEvent(cev);
1369 }
1370 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1371 case SDL_EVENT_MOUSE_BUTTON_UP:
1372 {
1373 const auto& cev = ev.button;
1374 return handleEvent(cev);
1375 }
1376 case SDL_EVENT_MOUSE_WHEEL:
1377 {
1378 const auto& cev = ev.wheel;
1379 return handleEvent(cev);
1380 }
1381 case SDL_EVENT_CLIPBOARD_UPDATE:
1382 {
1383 const auto& cev = ev.clipboard;
1384 return getClipboardChannelContext().handleEvent(cev);
1385 }
1386 case SDL_EVENT_KEY_DOWN:
1387 case SDL_EVENT_KEY_UP:
1388 {
1389 const auto& cev = ev.key;
1390 return getInputChannelContext().handleEvent(cev);
1391 }
1392 case SDL_EVENT_RENDER_TARGETS_RESET:
1393 case SDL_EVENT_RENDER_DEVICE_RESET:
1394 case SDL_EVENT_WILL_ENTER_FOREGROUND:
1395 return redraw();
1396 default:
1397 return true;
1398 }
1399}
1400
1401bool SdlContext::drawToWindows(const std::vector<SDL_Rect>& rects)
1402{
1403 for (auto& window : _windows)
1404 {
1405 if (!drawToWindow(window.second, rects))
1406 return FALSE;
1407 }
1408
1409 return TRUE;
1410}
1411
1412BOOL SdlContext::desktopResize(rdpContext* context)
1413{
1414 rdpGdi* gdi = nullptr;
1415 rdpSettings* settings = nullptr;
1416 auto sdl = get_context(context);
1417
1418 WINPR_ASSERT(sdl);
1419 WINPR_ASSERT(context);
1420
1421 settings = context->settings;
1422 WINPR_ASSERT(settings);
1423
1424 std::unique_lock lock(sdl->_critical);
1425 gdi = context->gdi;
1426 if (!gdi_resize(gdi, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
1427 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)))
1428 return FALSE;
1429 return sdl->createPrimary();
1430}
1431
1432/* This function is called to output a System BEEP */
1433BOOL SdlContext::playSound(rdpContext* context, const PLAY_SOUND_UPDATE* play_sound)
1434{
1435 /* TODO: Implement */
1436 WINPR_UNUSED(context);
1437 WINPR_UNUSED(play_sound);
1438 return TRUE;
1439}
1440
1441/* This function is called whenever a new frame starts.
1442 * It can be used to reset invalidated areas. */
1443BOOL SdlContext::beginPaint(rdpContext* context)
1444{
1445 auto gdi = context->gdi;
1446 WINPR_ASSERT(gdi);
1447 WINPR_ASSERT(gdi->primary);
1448
1449 HGDI_DC hdc = gdi->primary->hdc;
1450 WINPR_ASSERT(hdc);
1451 if (!hdc->hwnd)
1452 return TRUE;
1453
1454 HGDI_WND hwnd = hdc->hwnd;
1455 WINPR_ASSERT(hwnd->invalid);
1456 hwnd->invalid->null = TRUE;
1457 hwnd->ninvalid = 0;
1458
1459 return TRUE;
1460}
1461
1462bool SdlContext::redraw(bool suppress) const
1463{
1464 if (!_connected)
1465 return true;
1466
1467 auto gdi = context()->gdi;
1468 WINPR_ASSERT(gdi);
1469 return gdi_send_suppress_output(gdi, suppress);
1470}
1471
1472void SdlContext::setConnected(bool val)
1473{
1474 _connected = val;
1475}
1476
1477bool SdlContext::isConnected() const
1478{
1479 return _connected;
1480}
1481
1482rdpContext* SdlContext::context() const
1483{
1484 WINPR_ASSERT(_context);
1485 return _context;
1486}
1487
1488rdpClientContext* SdlContext::common() const
1489{
1490 return reinterpret_cast<rdpClientContext*>(context());
1491}
1492
1493bool SdlContext::setCursor(CursorType type)
1494{
1495 _cursorType = type;
1496 return restoreCursor();
1497}
1498
1499bool SdlContext::setCursor(rdpPointer* cursor)
1500{
1501 _cursor = cursor;
1502 return setCursor(CURSOR_IMAGE);
1503}
1504
1505rdpPointer* SdlContext::cursor() const
1506{
1507 return _cursor;
1508}
1509
1510bool SdlContext::restoreCursor()
1511{
1512 WLog_Print(getWLog(), WLOG_DEBUG, "restore cursor: %d", _cursorType);
1513 switch (_cursorType)
1514 {
1515 case CURSOR_NULL:
1516 if (!SDL_HideCursor())
1517 {
1518 WLog_Print(getWLog(), WLOG_ERROR, "SDL_HideCursor failed");
1519 return false;
1520 }
1521
1522 setHasCursor(false);
1523 return true;
1524
1525 case CURSOR_DEFAULT:
1526 {
1527 auto def = SDL_GetDefaultCursor();
1528 if (!SDL_SetCursor(def))
1529 {
1530 WLog_Print(getWLog(), WLOG_ERROR, "SDL_SetCursor(default=%p) failed",
1531 static_cast<void*>(def));
1532 return false;
1533 }
1534 if (!SDL_ShowCursor())
1535 {
1536 WLog_Print(getWLog(), WLOG_ERROR, "SDL_ShowCursor failed");
1537 return false;
1538 }
1539 setHasCursor(true);
1540 return true;
1541 }
1542 case CURSOR_IMAGE:
1543 setHasCursor(true);
1544 return sdl_Pointer_Set_Process(this);
1545 default:
1546 WLog_Print(getWLog(), WLOG_ERROR, "Unknown cursorType %s",
1547 sdl::utils::toString(_cursorType).c_str());
1548 return false;
1549 }
1550}
1551
1552void SdlContext::setMonitorIds(const std::vector<SDL_DisplayID>& ids)
1553{
1554 _monitorIds.clear();
1555 for (auto id : ids)
1556 {
1557 _monitorIds.push_back(id);
1558 }
1559}
1560
1561const std::vector<SDL_DisplayID>& SdlContext::monitorIds() const
1562{
1563 return _monitorIds;
1564}
1565
1566int64_t SdlContext::monitorId(uint32_t index) const
1567{
1568 if (index >= _monitorIds.size())
1569 {
1570 return -1;
1571 }
1572 return _monitorIds.at(index);
1573}
1574
1575void SdlContext::push(std::vector<SDL_Rect>&& rects)
1576{
1577 std::unique_lock lock(_queue_mux);
1578 _queue.emplace(std::move(rects));
1579}
1580
1581std::vector<SDL_Rect> SdlContext::pop()
1582{
1583 std::unique_lock lock(_queue_mux);
1584 if (_queue.empty())
1585 {
1586 return {};
1587 }
1588 auto val = std::move(_queue.front());
1589 _queue.pop();
1590 return val;
1591}
1592
1593bool SdlContext::setFullscreen(bool enter, bool forceOriginalDisplay)
1594{
1595 for (const auto& window : _windows)
1596 {
1597 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_FULLSCREEN, &window.second, enter,
1598 forceOriginalDisplay))
1599 return false;
1600 }
1601 _fullscreen = enter;
1602 return true;
1603}
1604
1605bool SdlContext::setMinimized()
1606{
1607 return sdl_push_user_event(SDL_EVENT_USER_WINDOW_MINIMIZE);
1608}
1609
1610bool SdlContext::grabMouse() const
1611{
1612 return _grabMouse;
1613}
1614
1615bool SdlContext::toggleGrabMouse()
1616{
1617 return setGrabMouse(!grabMouse());
1618}
1619
1620bool SdlContext::setGrabMouse(bool enter)
1621{
1622 _grabMouse = enter;
1623 return true;
1624}
1625
1626bool SdlContext::grabKeyboard() const
1627{
1628 return _grabKeyboard;
1629}
1630
1631bool SdlContext::toggleGrabKeyboard()
1632{
1633 return setGrabKeyboard(!grabKeyboard());
1634}
1635
1636bool SdlContext::setGrabKeyboard(bool enter)
1637{
1638 _grabKeyboard = enter;
1639 return true;
1640}
1641
1642bool SdlContext::setResizeable(bool enable)
1643{
1644 const auto settings = context()->settings;
1645 const bool dyn = freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate);
1646 const bool smart = freerdp_settings_get_bool(settings, FreeRDP_SmartSizing);
1647 bool use = (dyn && enable) || smart;
1648
1649 for (const auto& window : _windows)
1650 {
1651 if (!sdl_push_user_event(SDL_EVENT_USER_WINDOW_RESIZEABLE, &window.second, use))
1652 return false;
1653 }
1654 _resizeable = use;
1655
1656 return true;
1657}
1658
1659bool SdlContext::resizeable() const
1660{
1661 return _resizeable;
1662}
1663
1664bool SdlContext::toggleResizeable()
1665{
1666 return setResizeable(!resizeable());
1667}
1668
1669bool SdlContext::fullscreen() const
1670{
1671 return _fullscreen;
1672}
1673
1674bool SdlContext::toggleFullscreen()
1675{
1676 return setFullscreen(!fullscreen());
1677}
SdlContext(rdpContext *context)
object that handles clipboard context for the SDL3 client
Definition sdl_clip.hpp:76
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_server_name(const rdpSettings *settings)
A helper function to return the correct server name.
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_monitor_def_array_sorted(rdpSettings *settings, const rdpMonitor *monitors, size_t count)
Sort monitor array according to:
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.