FreeRDP
Loading...
Searching...
No Matches
SDL3/sdl_window.cpp
1
20#include <limits>
21#include <sstream>
22#include <cmath>
23
24#include "sdl_window.hpp"
25#include "sdl_utils.hpp"
26
27#include <freerdp/utils/string.h>
28
29SdlWindow::SdlWindow(SDL_DisplayID id, const std::string& title, const SDL_Rect& rect,
30 [[maybe_unused]] Uint32 flags)
31 : _initialW(rect.w), _initialH(rect.h), _displayID(id)
32{
33 auto props = SDL_CreateProperties();
34 SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title.c_str());
35 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, rect.x);
36 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, rect.y);
37 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, rect.w);
38 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, rect.h);
39 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, true);
40
41 if (flags & SDL_WINDOW_HIGH_PIXEL_DENSITY)
42 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true);
43
44 if (flags & SDL_WINDOW_FULLSCREEN)
45 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, true);
46
47 if (flags & SDL_WINDOW_BORDERLESS)
48 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, true);
49
50 _window = SDL_CreateWindowWithProperties(props);
51 SDL_DestroyProperties(props);
52 SDL_SetHint(SDL_HINT_APP_NAME, "");
53 std::ignore = SDL_SyncWindow(_window);
54
55 _renderer = SDL_CreateRenderer(_window, nullptr);
56
57 std::ignore = resizeToScale();
58
59 _monitor = query(_window, id, true);
60}
61
62SdlWindow::SdlWindow(SdlWindow&& other) noexcept
63 : _window(other._window), _renderer(other._renderer), _renderTarget(other._renderTarget),
64 _gdiTexture(other._gdiTexture), _gdiTextureW(other._gdiTextureW),
65 _gdiTextureH(other._gdiTextureH), _initialW(other._initialW), _initialH(other._initialH),
66 _displayID(other._displayID), _offset_x(other._offset_x), _offset_y(other._offset_y),
67 _monitor(other._monitor)
68{
69 other._window = nullptr;
70 other._renderer = nullptr;
71 other._renderTarget = nullptr;
72 other._gdiTexture = nullptr;
73}
74
75SdlWindow::~SdlWindow()
76{
77 if (_gdiTexture)
78 SDL_DestroyTexture(_gdiTexture);
79 if (_renderTarget)
80 SDL_DestroyTexture(_renderTarget);
81 if (_renderer)
82 SDL_DestroyRenderer(_renderer);
83 if (_window)
84 SDL_DestroyWindow(_window);
85}
86
87SDL_WindowID SdlWindow::id() const
88{
89 if (!_window)
90 return 0;
91 return SDL_GetWindowID(_window);
92}
93
94SDL_DisplayID SdlWindow::displayIndex() const
95{
96 if (!_window)
97 return 0;
98 return SDL_GetDisplayForWindow(_window);
99}
100
101SDL_Rect SdlWindow::rect() const
102{
103 return rect(_window);
104}
105
106SDL_Rect SdlWindow::bounds() const
107{
108 SDL_Rect rect = {};
109 if (_window)
110 {
111 if (!SDL_GetWindowPosition(_window, &rect.x, &rect.y))
112 return {};
113 if (!SDL_GetWindowSize(_window, &rect.w, &rect.h))
114 return {};
115 }
116 return rect;
117}
118
119SDL_Window* SdlWindow::window() const
120{
121 return _window;
122}
123
124SDL_Renderer* SdlWindow::renderer() const
125{
126 return _renderer;
127}
128
129Sint32 SdlWindow::offsetX() const
130{
131 return _offset_x;
132}
133
134void SdlWindow::setOffsetX(Sint32 x)
135{
136 _offset_x = x;
137}
138
139void SdlWindow::setOffsetY(Sint32 y)
140{
141 _offset_y = y;
142}
143
144Sint32 SdlWindow::offsetY() const
145{
146 return _offset_y;
147}
148
149rdpMonitor SdlWindow::monitor(bool isPrimary) const
150{
151 auto m = _monitor;
152 if (isPrimary)
153 {
154 m.x = 0;
155 m.y = 0;
156 }
157 return m;
158}
159
160void SdlWindow::setMonitor(rdpMonitor monitor)
161{
162 _monitor = monitor;
163}
164
165float SdlWindow::scale() const
166{
167 return SDL_GetWindowDisplayScale(_window);
168}
169
170SDL_DisplayOrientation SdlWindow::orientation() const
171{
172 const auto did = displayIndex();
173 return SDL_GetCurrentDisplayOrientation(did);
174}
175
176bool SdlWindow::grabKeyboard(bool enable)
177{
178 if (!_window)
179 return false;
180 SDL_SetWindowKeyboardGrab(_window, enable);
181 return true;
182}
183
184bool SdlWindow::grabMouse(bool enable)
185{
186 if (!_window)
187 return false;
188 SDL_SetWindowMouseGrab(_window, enable);
189 return true;
190}
191
192void SdlWindow::setBordered(bool bordered)
193{
194 if (_window)
195 SDL_SetWindowBordered(_window, bordered);
196 std::ignore = SDL_SyncWindow(_window);
197}
198
199void SdlWindow::raise()
200{
201 SDL_RaiseWindow(_window);
202 std::ignore = SDL_SyncWindow(_window);
203}
204
205void SdlWindow::resizeable(bool use)
206{
207 SDL_SetWindowResizable(_window, use);
208 std::ignore = SDL_SyncWindow(_window);
209}
210
211void SdlWindow::fullscreen(bool enter, bool forceOriginalDisplay)
212{
213 if (enter && forceOriginalDisplay && _displayID != 0)
214 {
215 /* Move the window to the desired display. We should not wait
216 * for the window to be moved, because some backends can refuse
217 * the move. The intent of moving the window is enough for SDL
218 * to decide which display will be used for fullscreen. */
219 SDL_Rect rect = {};
220 std::ignore = SDL_GetDisplayBounds(_displayID, &rect);
221 std::ignore = SDL_SetWindowPosition(_window, rect.x, rect.y);
222 }
223 std::ignore = SDL_SetWindowFullscreen(_window, enter);
224 std::ignore = SDL_SyncWindow(_window);
225}
226
227void SdlWindow::minimize()
228{
229 SDL_MinimizeWindow(_window);
230 std::ignore = SDL_SyncWindow(_window);
231}
232
233bool SdlWindow::resizeToScale()
234{
235 if (!_window || _initialW <= 0 || _initialH <= 0)
236 return false;
237 if ((SDL_GetWindowFlags(_window) & SDL_WINDOW_FULLSCREEN) != 0)
238 return true;
239
240 float pd = SDL_GetWindowPixelDensity(_window);
241 if (pd <= 0.0f)
242 pd = 1.0f;
243
244 const int targetW = static_cast<int>(std::ceil(static_cast<float>(_initialW) / pd));
245 const int targetH = static_cast<int>(std::ceil(static_cast<float>(_initialH) / pd));
246
247 int curW = 0;
248 int curH = 0;
249 if (!SDL_GetWindowSize(_window, &curW, &curH))
250 return false;
251
252 if (curW == targetW && curH == targetH)
253 return true;
254
255 return resize({ targetW, targetH });
256}
257
258bool SdlWindow::resize(const SDL_Point& size)
259{
260 return SDL_SetWindowSize(_window, size.x, size.y);
261}
262
263void SdlWindow::ensureRenderTarget()
264{
265 if (!_renderer)
266 return;
267
268 int w = 0;
269 int h = 0;
270 SDL_GetWindowSizeInPixels(_window, &w, &h);
271 if (w <= 0 || h <= 0)
272 return;
273
274 /* Recreate if missing or if window size changed */
275 if (_renderTarget)
276 {
277 float tw = 0;
278 float th = 0;
279 if (!SDL_GetTextureSize(_renderTarget, &tw, &th))
280 return;
281 if (static_cast<int>(tw) == w && static_cast<int>(th) == h)
282 return;
283 SDL_DestroyTexture(_renderTarget);
284 }
285
286 _renderTarget =
287 SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, w, h);
288 if (!_renderTarget)
289 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL_CreateTexture (render target): %s",
290 SDL_GetError());
291}
292
293bool SdlWindow::drawRect(SDL_Surface* surface, SDL_Point offset, const SDL_Rect& srcRect)
294{
295 WINPR_ASSERT(surface);
296 SDL_Rect dstRect = { offset.x + srcRect.x, offset.y + srcRect.y, srcRect.w, srcRect.h };
297 return blit(surface, srcRect, dstRect);
298}
299
300bool SdlWindow::drawRects(SDL_Surface* surface, SDL_Point offset,
301 const std::vector<SDL_Rect>& rects)
302{
303 if (rects.empty())
304 {
305 return drawRect(surface, offset, { 0, 0, surface->w, surface->h });
306 }
307 for (auto& srcRect : rects)
308 {
309 if (!drawRect(surface, offset, srcRect))
310 return false;
311 }
312 return true;
313}
314
315bool SdlWindow::drawScaledRect(SDL_Surface* surface, const SDL_FPoint& scale,
316 const SDL_Rect& srcRect)
317{
318 SDL_Rect dstRect = srcRect;
319 dstRect.x = static_cast<Sint32>(static_cast<float>(dstRect.x) * scale.x);
320 dstRect.w = static_cast<Sint32>(static_cast<float>(dstRect.w) * scale.x);
321 dstRect.y = static_cast<Sint32>(static_cast<float>(dstRect.y) * scale.y);
322 dstRect.h = static_cast<Sint32>(static_cast<float>(dstRect.h) * scale.y);
323 return blit(surface, srcRect, dstRect);
324}
325
326bool SdlWindow::drawScaledRects(SDL_Surface* surface, const SDL_FPoint& scale,
327 const std::vector<SDL_Rect>& rects)
328{
329 if (rects.empty())
330 {
331 return drawScaledRect(surface, scale, { 0, 0, surface->w, surface->h });
332 }
333 for (const auto& srcRect : rects)
334 {
335 if (!drawScaledRect(surface, scale, srcRect))
336 return false;
337 }
338 return true;
339}
340
341bool SdlWindow::fill(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
342{
343 if (_renderer)
344 {
345 ensureRenderTarget();
346 if (!SDL_SetRenderTarget(_renderer, _renderTarget))
347 return false;
348 if (!SDL_SetRenderDrawColor(_renderer, r, g, b, a))
349 return false;
350 return SDL_RenderClear(_renderer);
351 }
352 return fill(_window, r, g, b, a);
353}
354
355bool SdlWindow::fill(SDL_Window* window, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
356{
357 auto surface = SDL_GetWindowSurface(window);
358 if (!surface)
359 return false;
360 SDL_Rect rect = { 0, 0, surface->w, surface->h };
361 auto color = SDL_MapSurfaceRGBA(surface, r, g, b, a);
362
363 return SDL_FillSurfaceRect(surface, &rect, color);
364}
365
366rdpMonitor SdlWindow::query(SDL_Window* window, SDL_DisplayID id, bool forceAsPrimary)
367{
368 if (!window)
369 return {};
370
371 const auto& r = rect(window, forceAsPrimary);
372 const float factor = SDL_GetWindowDisplayScale(window);
373 const float dpi = std::roundf(factor * 100.0f);
374
375 WINPR_ASSERT(r.w > 0);
376 WINPR_ASSERT(r.h > 0);
377
378 const auto primary = SDL_GetPrimaryDisplay();
379 const auto orientation = SDL_GetCurrentDisplayOrientation(id);
380 const auto rdp_orientation = sdl::utils::orientaion_to_rdp(orientation);
381
382 rdpMonitor monitor{};
383 monitor.orig_screen = id;
384 monitor.x = r.x;
385 monitor.y = r.y;
386 monitor.width = r.w;
387 monitor.height = r.h;
388 monitor.is_primary = forceAsPrimary || (id == primary);
389 monitor.attributes.desktopScaleFactor = static_cast<UINT32>(dpi);
390 monitor.attributes.deviceScaleFactor = 100;
391 monitor.attributes.orientation = rdp_orientation;
392 monitor.attributes.physicalWidth = WINPR_ASSERTING_INT_CAST(uint32_t, r.w);
393 monitor.attributes.physicalHeight = WINPR_ASSERTING_INT_CAST(uint32_t, r.h);
394
395 const auto cat = SDL_LOG_CATEGORY_APPLICATION;
396 SDL_LogDebug(cat, "monitor.orig_screen %" PRIu32, monitor.orig_screen);
397 SDL_LogDebug(cat, "monitor.x %" PRId32, monitor.x);
398 SDL_LogDebug(cat, "monitor.y %" PRId32, monitor.y);
399 SDL_LogDebug(cat, "monitor.width %" PRId32, monitor.width);
400 SDL_LogDebug(cat, "monitor.height %" PRId32, monitor.height);
401 SDL_LogDebug(cat, "monitor.is_primary %" PRIu32, monitor.is_primary);
402 SDL_LogDebug(cat, "monitor.attributes.desktopScaleFactor %" PRIu32,
403 monitor.attributes.desktopScaleFactor);
404 SDL_LogDebug(cat, "monitor.attributes.deviceScaleFactor %" PRIu32,
405 monitor.attributes.deviceScaleFactor);
406 SDL_LogDebug(cat, "monitor.attributes.orientation %s",
407 freerdp_desktop_rotation_flags_to_string(monitor.attributes.orientation));
408 SDL_LogDebug(cat, "monitor.attributes.physicalWidth %" PRIu32,
409 monitor.attributes.physicalWidth);
410 SDL_LogDebug(cat, "monitor.attributes.physicalHeight %" PRIu32,
411 monitor.attributes.physicalHeight);
412 return monitor;
413}
414
415SDL_Rect SdlWindow::rect(SDL_Window* window, bool forceAsPrimary)
416{
417 SDL_Rect rect = {};
418 if (!window)
419 return {};
420
421 if (!forceAsPrimary)
422 {
423 if (!SDL_GetWindowPosition(window, &rect.x, &rect.y))
424 return {};
425 }
426
427 if (!SDL_GetWindowSizeInPixels(window, &rect.w, &rect.h))
428 return {};
429
430 const auto flags = SDL_GetWindowFlags(window);
431 const auto mask = SDL_WINDOW_FULLSCREEN;
432 const auto fs = (flags & mask) == mask;
433 if (tryFallback(fs))
434 {
435 /* On wlroots compositors (Sway, river, etc.), windows that are hidden/unmapped
436 * don't get their actual display dimensions. The dummy window returns its creation size
437 * (64x64) instead of the display size. This causes validation errors since we require >=
438 * 200px. Workaround: If we got dimensions that are too small, query the display directly.
439 */
440
441 const auto displayID = SDL_GetDisplayForWindow(window);
442 SDL_Rect displayBounds = {};
443 if (SDL_GetDisplayBounds(displayID, &displayBounds))
444 {
445 if (forceAsPrimary)
446 {
447 rect.x = 0;
448 rect.y = 0;
449 }
450 rect.w = displayBounds.w;
451 rect.h = displayBounds.h;
452
453 const float contentScale = SDL_GetDisplayContentScale(displayID);
454 if (contentScale > 1.0f)
455 {
456 const auto fw = static_cast<float>(rect.w);
457 const auto fh = static_cast<float>(rect.h);
458 rect.w = static_cast<int>(std::roundf(fw * contentScale));
459 rect.h = static_cast<int>(std::roundf(fh * contentScale));
460 }
461 }
462 }
463
464 return rect;
465}
466
467SdlWindow::HighDPIMode SdlWindow::isHighDPIWindowsMode(SDL_Window* window)
468{
469 if (!window)
470 return MODE_INVALID;
471
472 const auto id = SDL_GetDisplayForWindow(window);
473 if (id == 0)
474 return MODE_INVALID;
475
476 const auto cs = SDL_GetDisplayContentScale(id);
477 const auto ds = SDL_GetWindowDisplayScale(window);
478 const auto pd = SDL_GetWindowPixelDensity(window);
479
480 /* mac os x style, but no HighDPI display */
481 if ((cs == 1.0f) && (ds == 1.0f) && (pd == 1.0f))
482 return MODE_NONE;
483
484 /* mac os x style HighDPI */
485 if ((cs == 1.0f) && (ds > 1.0f) && (pd > 1.0f))
486 return MODE_MACOS;
487
488 /* rest is windows style */
489 return MODE_WINDOWS;
490}
491
492bool SdlWindow::blit(SDL_Surface* surface, const SDL_Rect& srcRect, SDL_Rect& dstRect)
493{
494 if (!_renderer || !surface)
495 return false;
496
497 /* Lazily create or recreate the persistent GDI texture */
498 if (!_gdiTexture || _gdiTextureW != surface->w || _gdiTextureH != surface->h)
499 {
500 if (_gdiTexture)
501 SDL_DestroyTexture(_gdiTexture);
502 _gdiTexture = SDL_CreateTexture(_renderer, surface->format, SDL_TEXTUREACCESS_STREAMING,
503 surface->w, surface->h);
504 if (!_gdiTexture)
505 {
506 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL_CreateTexture: %s", SDL_GetError());
507 return false;
508 }
509 _gdiTextureW = surface->w;
510 _gdiTextureH = surface->h;
511 }
512
513 /* Upload only the dirty region */
514 const auto* details = SDL_GetPixelFormatDetails(surface->format);
515 const int bpp = details ? details->bytes_per_pixel : 4;
516 const auto* pixels = static_cast<const uint8_t*>(surface->pixels) +
517 (1ll * srcRect.y * surface->pitch) + (1ll * srcRect.x * bpp);
518 if (!SDL_UpdateTexture(_gdiTexture, &srcRect, pixels, surface->pitch))
519 {
520 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL_UpdateTexture: %s", SDL_GetError());
521 return false;
522 }
523
524 /* Render onto persistent render target to accumulate dirty rects */
525 if (!SDL_SetRenderTarget(_renderer, _renderTarget))
526 return false;
527
528 SDL_FRect fsrc = { static_cast<float>(srcRect.x), static_cast<float>(srcRect.y),
529 static_cast<float>(srcRect.w), static_cast<float>(srcRect.h) };
530 SDL_FRect fdst = { static_cast<float>(dstRect.x), static_cast<float>(dstRect.y),
531 static_cast<float>(dstRect.w), static_cast<float>(dstRect.h) };
532 if (!SDL_RenderTexture(_renderer, _gdiTexture, &fsrc, &fdst))
533 {
534 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "SDL_RenderTexture: %s", SDL_GetError());
535 return false;
536 }
537 return true;
538}
539
540void SdlWindow::updateSurface()
541{
542 if (!_renderer)
543 return;
544
545 ensureRenderTarget();
546
547 /* Copy accumulated render target to screen and present */
548 if (!SDL_SetRenderTarget(_renderer, nullptr))
549 return;
550 if (!SDL_RenderTexture(_renderer, _renderTarget, nullptr, nullptr))
551 return;
552 if (!SDL_RenderPresent(_renderer))
553 return;
554}
555
556SdlWindow SdlWindow::create(SDL_DisplayID id, const std::string& title, Uint32 flags, Uint32 width,
557 Uint32 height)
558{
559 flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY;
560
561 SDL_Rect rect = { static_cast<int>(SDL_WINDOWPOS_CENTERED_DISPLAY(id)),
562 static_cast<int>(SDL_WINDOWPOS_CENTERED_DISPLAY(id)), static_cast<int>(width),
563 static_cast<int>(height) };
564
565 if ((flags & SDL_WINDOW_FULLSCREEN) != 0)
566 {
567 std::ignore = SDL_GetDisplayBounds(id, &rect);
568 }
569
570 SdlWindow window{ id, title, rect, flags };
571
572 if ((flags & SDL_WINDOW_FULLSCREEN) != 0)
573 {
574 window.setOffsetX(rect.x);
575 window.setOffsetY(rect.y);
576 }
577
578 return window;
579}
580
581static SDL_Window* createDummy(SDL_DisplayID id)
582{
583 const auto x = SDL_WINDOWPOS_CENTERED_DISPLAY(id);
584 const auto y = SDL_WINDOWPOS_CENTERED_DISPLAY(id);
585 const int w = 64;
586 const int h = 64;
587
588 auto props = SDL_CreateProperties();
589 std::stringstream ss;
590 ss << "SdlWindow::query(" << id << ")";
591 SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ss.str().c_str());
592 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x);
593 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y);
594 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w);
595 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h);
596
597 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true);
598 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, false);
599 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, true);
600 SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN, false);
601
602 auto window = SDL_CreateWindowWithProperties(props);
603 SDL_DestroyProperties(props);
604
605 /* Workaround: we need to properly position the window on the correct monitor
606 * before going fullscreen. Otherwise we will get the primary monitor details.
607 */
608 if (window)
609 {
610 SDL_Rect rect = {};
611 std::ignore = SDL_GetDisplayBounds(id, &rect);
612 std::ignore = SDL_SetWindowPosition(window, rect.x, rect.y);
613 std::ignore = SDL_SetWindowFullscreen(window, true);
614 }
615 return window;
616}
617
618rdpMonitor SdlWindow::query(SDL_DisplayID id, bool forceAsPrimary)
619{
620 std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window(createDummy(id), SDL_DestroyWindow);
621 if (!window)
622 return {};
623
624 std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> renderer(
625 SDL_CreateRenderer(window.get(), nullptr), SDL_DestroyRenderer);
626
627 if (!SDL_SyncWindow(window.get()))
628 return {};
629
630 SDL_Event event{};
631 while (SDL_PollEvent(&event))
632 ;
633
634 return query(window.get(), id, forceAsPrimary);
635}
636
637SDL_Rect SdlWindow::rect(SDL_DisplayID id, bool forceAsPrimary)
638{
639 std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window(createDummy(id), SDL_DestroyWindow);
640 if (!window)
641 return {};
642
643 std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> renderer(
644 SDL_CreateRenderer(window.get(), nullptr), SDL_DestroyRenderer);
645
646 if (!SDL_SyncWindow(window.get()))
647 return {};
648
649 SDL_Event event{};
650 while (SDL_PollEvent(&event))
651 ;
652
653 return rect(window.get(), forceAsPrimary);
654}
655
656bool SdlWindow::tryFallback(bool isFullscreen)
657{
658 /* If we define a custom env variable to use the wlroots hack
659 * then enable/disable according to this setting only.
660 */
661 const auto wlroots_hack = SDL_getenv("FREERDP_WLROOTS_HACK");
662 if (wlroots_hack != nullptr)
663 {
664 const auto enabled = strcmp(wlroots_hack, "0") != 0;
665 if (strcmp(wlroots_hack, "force") == 0)
666 isFullscreen = true;
667 return enabled && isFullscreen;
668 }
669
670 const auto platform = SDL_GetPlatform();
671 if ((platform == nullptr) || (strcmp(platform, "Linux") != 0))
672 return false;
673
674 const auto driver = SDL_GetCurrentVideoDriver();
675 if ((driver == nullptr) || (strcmp(driver, "wayland") != 0))
676 return false;
677
678 /* Check XDG_SESSION_DESKTOP and XDG_CURRENT_DESKTOP for wlroots-based
679 * compositors. The original check only matched Sway, but other wlroots
680 * compositors (Hyprland, river, etc.) have the same dummy-window sizing
681 * behavior where hidden/unmapped windows return 64x64 instead of the
682 * display size. Use strstr for substring matching since XDG_CURRENT_DESKTOP
683 * can be a colon-separated list (e.g. "sway:wlroots", "Hyprland").
684 */
685 auto isWlrootsCompositor = [](const char* value) -> bool
686 {
687 if (!value)
688 return false;
689 if (strstr(value, "sway") || strstr(value, "Sway") || strstr(value, "Hyprland") ||
690 strstr(value, "hyprland") || strstr(value, "river") || strstr(value, "wlroots"))
691 return true;
692 return false;
693 };
694
695 const auto xdg_session = SDL_getenv("XDG_SESSION_DESKTOP");
696 if (isWlrootsCompositor(xdg_session))
697 return isFullscreen;
698
699 const auto xdg_desktop = SDL_getenv("XDG_CURRENT_DESKTOP");
700 if (isWlrootsCompositor(xdg_desktop))
701 return isFullscreen;
702
703 return false;
704}
SdlWindow(const std::string &title, Sint32 startupX, Sint32 startupY, Sint32 width, Sint32 height, Uint32 flags)