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