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