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