Package: freerdp3-sdl
Version: 3.32.2+dfsg-1
Severity: important
Tags: patch upstream
Dear Maintainer,
On sway, sdl-freerdp3 cannot start a fullscreen (/f) or /multimon session.
It aborts in pre-connect, before any network traffic:
$ sdl-freerdp3 /cert:ignore /f /v:127.0.0.1:9 /u:x /p:x
[INFO][com.freerdp.client.SDL] - [preConnect]: Update size to 64x64
[ERROR][com.freerdp.settings] -
[freerdp_settings_client_monitors_check_primary_and_origin]: Monitor
configuration virtual desktop width must be 200 <= 64 <= 32766
[ERROR][com.freerdp.settings] -
[freerdp_settings_client_monitors_check_primary_and_origin]: Monitor
configuration virtual desktop height must be 200 <= 64 <= 32766
[ERROR][com.freerdp.settings] - [log_monitor]: [0] [primary] {0x0-64x64} [3]
{64x64, orientation: ORIENTATION_LANDSCAPE [0], desktopScale: 100, deviceScale:
100}
[ERROR][com.freerdp.core] - [freerdp_connect_begin]:
ERRCONNECT_PRE_CONNECT_FAILED [0x00020001]
A configuration error prevented a connection to be established.
(Any target works, because the failure comes before connecting. The output is
a single 1920x1080 sway output, and
XDG_SESSION_DESKTOP=XDG_CURRENT_DESKTOP=sway.)
Windowed sessions (/size:WxH) work. SDL_VIDEODRIVER=x11 (XWayland) also fails
in the same way, reporting 75x64.
Analysis
--------
SdlContext::addOrUpdateDisplay() calls SdlWindow::query(id), which creates
a 64x64 probe window in createDummy(), calls SDL_SetWindowFullscreen(true),
and reads its size back. SdlWindow::rect() contains a workaround for exactly
this wlroots behaviour (the probe keeps its 64x64 creation size, so it falls
back to SDL_GetDisplayBounds()), but the workaround is gated by
const auto fs = (flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN;
if (tryFallback(fs))
and on sway the probe window never reports SDL_WINDOW_FULLSCREEN. Because of
that, tryFallback() returns false in the default auto-detect mode and with
FREERDP_WLROOTS_HACK=1. Only FREERDP_WLROOTS_HACK=force, which overrides
isFullscreen, makes it work:
default -> Update size to 64x64 -> PRE_CONNECT_FAILED
FREERDP_WLROOTS_HACK=1 -> Update size to 64x64 -> PRE_CONNECT_FAILED
FREERDP_WLROOTS_HACK=force -> Update size to 1920x1080, connects
Upstream has the same root cause in
https://github.com/FreeRDP/FreeRDP/issues/13275 (reported for /multimon on
Hyprland, 3.30.0). That issue was closed with "use FREERDP_WLROOTS_HACK=force".
The code is unchanged in current upstream master.
Fix
---
The attached patch passes an "isProbe" flag from the two probe paths
(SdlWindow::query(SDL_DisplayID) and SdlWindow::rect(SDL_DisplayID)) into
SdlWindow::rect(SDL_Window*), which then treats the probe as fullscreen, since
that is what createDummy() requested. Real session windows
(SdlWindow::rect() const) still use the actual window flags, so windowed
sessions are unaffected. The fallback is still limited to detected wlroots
sessions under the Wayland driver, and FREERDP_WLROOTS_HACK=0 still turns it
off.
I built 3.32.2+dfsg-1 with the patch and tested it on sway 1.12 /
libsdl3-0 3.4.18:
/f -> Update size to 1920x1080, reaches connect
/f, FREERDP_WLROOTS_HACK=0 -> 64x64 / PRE_CONNECT_FAILED (opt-out honoured)
/size:1280x720 -> reaches connect (unchanged)
/multimon -> reaches connect
Workaround until fixed: export FREERDP_WLROOTS_HACK=force, or use /size:WxH
instead of /f.
-- System Information:
Debian Release: forky/sid
Architecture: amd64 (x86_64)
Kernel: Linux 7.2.12+deb14-amd64 (SMP w/32 CPU threads; PREEMPT)
Locale: LANG=C
Compositor: sway 1.12-1 (libwlroots-0.20 0.20.2-1)
Versions of packages freerdp3-sdl depends on:
ii libfreerdp-client3-3 3.32.2+dfsg-1
ii libfreerdp3-3 3.32.2+dfsg-1
ii libsdl3-0 3.4.18+ds-1
ii libwinpr3-3 3.32.2+dfsg-1
-- Patch (debian/patches/sdl-wlroots-probe-fullscreen.patch):
Description: sdl: let the wlroots fallback trigger for the display probe window
SdlWindow::query()/rect() probe each display with a 64x64 window created by
createDummy(), which requests fullscreen. On wlroots compositors (sway,
Hyprland, river) the probe never reports SDL_WINDOW_FULLSCREEN, so
tryFallback() returns false in its default (auto-detect) and "1" modes and the
64x64 creation size is taken as the monitor size. FreeRDP then rejects the
configuration ("virtual desktop width must be 200 <= 64 <= 32766") and /f and
/multimon sessions abort with ERRCONNECT_PRE_CONNECT_FAILED. Only
FREERDP_WLROOTS_HACK=force worked.
.
Treat the probe window as fullscreen, since that is what it asked for. Real
session windows still use the actual window flags, and FREERDP_WLROOTS_HACK=0
still turns the fallback off.
Author: Thomas Glanzmann <[email protected]>
Bug: https://github.com/FreeRDP/FreeRDP/issues/13275
Forwarded: no
Last-Update: 2026-11-02
--- a/client/SDL/SDL3/sdl_window.cpp 2026-10-12 13:45:30.000000000 +0200
+++ b/client/SDL/SDL3/sdl_window.cpp 2026-11-02 13:35:48.739790742 +0100
@@ -394,12 +394,13 @@
return SDL_FillSurfaceRect(surface, &rect, color);
}
-rdpMonitor SdlWindow::query(SDL_Window* window, SDL_DisplayID id, bool
forceAsPrimary)
+rdpMonitor SdlWindow::query(SDL_Window* window, SDL_DisplayID id, bool
forceAsPrimary,
+ bool isProbe)
{
if (!window)
return {};
- const auto& r = rect(window, forceAsPrimary);
+ const auto& r = rect(window, forceAsPrimary, isProbe);
const float factor = SDL_GetWindowDisplayScale(window);
const float dpi = std::roundf(factor * 100.0f);
@@ -443,7 +444,7 @@
return monitor;
}
-SDL_Rect SdlWindow::rect(SDL_Window* window, bool forceAsPrimary)
+SDL_Rect SdlWindow::rect(SDL_Window* window, bool forceAsPrimary, bool isProbe)
{
SDL_Rect rect = {};
if (!window)
@@ -460,7 +461,10 @@
const auto flags = SDL_GetWindowFlags(window);
const auto mask = SDL_WINDOW_FULLSCREEN;
- const auto fs = (flags & mask) == mask;
+ /* The probe window from createDummy() always requests fullscreen, but
wlroots
+ * compositors do not report it as fullscreen (it keeps its 64x64
creation size).
+ * Treat it as fullscreen so the wlroots fallback below can trigger. */
+ const auto fs = isProbe || ((flags & mask) == mask);
if (tryFallback(fs))
{
/* On wlroots compositors (Sway, river, etc.), windows that are
hidden/unmapped
@@ -806,7 +810,7 @@
while (SDL_PollEvent(&event))
;
- return query(window.get(), id, forceAsPrimary);
+ return query(window.get(), id, forceAsPrimary, true);
}
SDL_Rect SdlWindow::rect(SDL_DisplayID id, bool forceAsPrimary)
@@ -825,7 +829,7 @@
while (SDL_PollEvent(&event))
;
- return rect(window.get(), forceAsPrimary);
+ return rect(window.get(), forceAsPrimary, true);
}
bool SdlWindow::tryFallback(bool isFullscreen)
--- a/client/SDL/SDL3/sdl_window.hpp 2026-10-12 13:45:30.000000000 +0200
+++ b/client/SDL/SDL3/sdl_window.hpp 2026-11-02 13:35:48.739514263 +0100
@@ -101,8 +101,9 @@
[[nodiscard]] static bool fill(SDL_Window* window, Uint8 r = 0x00,
Uint8 g = 0x00,
Uint8 b = 0x00, Uint8 a = 0xff);
[[nodiscard]] static rdpMonitor query(SDL_Window* window, SDL_DisplayID
id,
- bool forceAsPrimary = false);
- [[nodiscard]] static SDL_Rect rect(SDL_Window* window, bool
forceAsPrimary = false);
+ bool forceAsPrimary = false, bool
isProbe = false);
+ [[nodiscard]] static SDL_Rect rect(SDL_Window* window, bool
forceAsPrimary = false,
+ bool isProbe = false);
[[nodiscard]] static SDL_Rect rect(SDL_DisplayID id, bool
forceAsPrimary = false);
[[nodiscard]] static bool tryFallback(bool isFullscreen);