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