[client,sdl] implement a static SdlWindow::query function

The display settings can not be proplerly queried by SDL without a
window. Create a temporary, invisible window for a requested monitor and
query the required details from that window.
This commit is contained in:
akallabeth
2026-01-29 19:51:04 +01:00
committed by Armin Novak
parent 99a44990ba
commit efa15e1dc2
3 changed files with 140 additions and 94 deletions

View File

@@ -21,6 +21,7 @@
#include <freerdp/config.h>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@@ -181,10 +182,12 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl)
[[nodiscard]] static BOOL sdl_apply_monitor_properties(rdpMonitor& monitor, SDL_DisplayID id,
bool isPrimary)
{
auto mode = SDL_GetCurrentDisplayMode(id);
if (!mode)
return FALSE;
float dpi = SDL_GetDisplayContentScale(id);
float hdpi = dpi;
float vdpi = dpi;
const float dpi = roundf(mode->pixel_density * 100.0f);
const float factor = mode->pixel_density;
SDL_Rect rect = {};
if (!SDL_GetDisplayBounds(id, &rect))
@@ -195,58 +198,20 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl)
bool highDpi = dpi > 100;
if (highDpi)
{
// HighDPI is problematic with SDL: We can only get native resolution by creating a
// window. Work around this by checking the supported resolutions (and keep maximum)
// Also scale the DPI
const SDL_Rect scaleRect = rect;
int count = 0;
auto modes = SDL_GetFullscreenDisplayModes(id, &count);
for (int i = 0; i < count; i++)
{
auto mode = modes[i];
if (!mode)
break;
if (mode->w > rect.w)
{
rect.w = mode->w;
rect.h = mode->h;
}
else if (mode->w == rect.w)
{
if (mode->h > rect.h)
{
rect.w = mode->w;
rect.h = mode->h;
}
}
}
SDL_free(static_cast<void*>(modes));
const float dw = 1.0f * static_cast<float>(rect.w) / static_cast<float>(scaleRect.w);
const float dh = 1.0f * static_cast<float>(rect.h) / static_cast<float>(scaleRect.h);
hdpi /= dw;
vdpi /= dh;
}
const SDL_DisplayOrientation orientation = SDL_GetCurrentDisplayOrientation(id);
const UINT32 rdp_orientation = sdl::utils::orientaion_to_rdp(orientation);
/* windows uses 96 dpi as 'default' and the scale factors are in percent. */
const auto factor = dpi / 96.0f * 100.0f;
monitor.orig_screen = id;
monitor.x = rect.x;
monitor.y = rect.y;
monitor.width = rect.w;
monitor.height = rect.h;
monitor.width = roundf(rect.w * factor);
monitor.height = roundf(rect.h * factor);
monitor.is_primary = isPrimary;
monitor.attributes.desktopScaleFactor = static_cast<UINT32>(factor);
monitor.attributes.desktopScaleFactor = static_cast<UINT32>(dpi);
monitor.attributes.deviceScaleFactor = 100;
monitor.attributes.orientation = rdp_orientation;
monitor.attributes.physicalWidth = scale(WINPR_ASSERTING_INT_CAST(uint32_t, rect.w), hdpi);
monitor.attributes.physicalHeight = scale(WINPR_ASSERTING_INT_CAST(uint32_t, rect.h), vdpi);
monitor.attributes.physicalWidth = WINPR_ASSERTING_INT_CAST(uint32_t, rect.w);
monitor.attributes.physicalHeight = WINPR_ASSERTING_INT_CAST(uint32_t, rect.h);
return TRUE;
}

View File

@@ -19,10 +19,13 @@
*/
#include <limits>
#include <sstream>
#include <cmath>
#include "sdl_window.hpp"
#include "sdl_utils.hpp"
#include <freerdp/utils/string.h>
SdlWindow::SdlWindow(SDL_DisplayID id, const std::string& title, const SDL_Rect& rect,
[[maybe_unused]] Uint32 flags)
: _displayID(id)
@@ -83,13 +86,7 @@ SDL_DisplayID SdlWindow::displayIndex() const
SDL_Rect SdlWindow::rect() const
{
SDL_Rect rect = {};
if (_window)
{
SDL_GetWindowPosition(_window, &rect.x, &rect.y);
SDL_GetWindowSizeInPixels(_window, &rect.w, &rect.h);
}
return rect;
return rect(_window);
}
SDL_Rect SdlWindow::bounds() const
@@ -97,8 +94,10 @@ SDL_Rect SdlWindow::bounds() const
SDL_Rect rect = {};
if (_window)
{
SDL_GetWindowPosition(_window, &rect.x, &rect.y);
SDL_GetWindowSize(_window, &rect.w, &rect.h);
if (!SDL_GetWindowPosition(_window, &rect.x, &rect.y))
return {};
if (!SDL_GetWindowSize(_window, &rect.w, &rect.h))
return {};
}
return rect;
}
@@ -130,42 +129,7 @@ Sint32 SdlWindow::offsetY() const
rdpMonitor SdlWindow::monitor(bool isPrimary) const
{
rdpMonitor mon{};
const auto factor = scale();
const auto dsf = static_cast<UINT32>(100 * factor);
mon.attributes.desktopScaleFactor = dsf;
mon.attributes.deviceScaleFactor = 100;
const auto r = rect();
mon.width = r.w;
mon.height = r.h;
mon.attributes.physicalWidth = WINPR_ASSERTING_INT_CAST(uint32_t, r.w);
mon.attributes.physicalHeight = WINPR_ASSERTING_INT_CAST(uint32_t, r.h);
SDL_Rect rect = {};
auto did = SDL_GetDisplayForWindow(_window);
auto rc = SDL_GetDisplayBounds(did, &rect);
if (rc)
{
mon.x = rect.x;
mon.y = rect.y;
}
const auto orient = orientation();
mon.attributes.orientation = sdl::utils::orientaion_to_rdp(orient);
auto primary = SDL_GetPrimaryDisplay();
mon.is_primary = isPrimary || (SDL_GetWindowID(_window) == primary);
mon.orig_screen = did;
if (mon.is_primary)
{
mon.x = 0;
mon.y = 0;
}
return mon;
return query(_window, displayIndex(), isPrimary);
}
float SdlWindow::scale() const
@@ -291,14 +255,79 @@ bool SdlWindow::drawScaledRects(SDL_Surface* surface, const SDL_FPoint& scale,
bool SdlWindow::fill(Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
auto surface = SDL_GetWindowSurface(_window);
return fill(_window, r, g, b, a);
}
bool SdlWindow::fill(SDL_Window* window, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
auto surface = SDL_GetWindowSurface(window);
if (!surface)
return false;
SDL_Rect rect = { 0, 0, surface->w, surface->h };
auto color = SDL_MapSurfaceRGBA(surface, r, g, b, a);
SDL_FillSurfaceRect(surface, &rect, color);
return true;
return SDL_FillSurfaceRect(surface, &rect, color);
}
rdpMonitor SdlWindow::query(SDL_Window* window, SDL_DisplayID id, bool forceAsPrimary)
{
if (!window)
return {};
const auto& r = rect(window);
const float factor = SDL_GetWindowDisplayScale(window);
const float dpi = std::roundf(factor * 100.0f);
WINPR_ASSERT(r.w > 0);
WINPR_ASSERT(r.h > 0);
bool highDpi = dpi > 100;
const auto primary = SDL_GetPrimaryDisplay();
const auto orientation = SDL_GetCurrentDisplayOrientation(id);
const auto rdp_orientation = sdl::utils::orientaion_to_rdp(orientation);
rdpMonitor monitor{};
monitor.orig_screen = id;
monitor.x = forceAsPrimary ? 0 : r.x;
monitor.y = forceAsPrimary ? 0 : r.y;
monitor.width = r.w;
monitor.height = r.h;
monitor.is_primary = forceAsPrimary || (id == primary);
monitor.attributes.desktopScaleFactor = static_cast<UINT32>(dpi);
monitor.attributes.deviceScaleFactor = 100;
monitor.attributes.orientation = rdp_orientation;
monitor.attributes.physicalWidth = WINPR_ASSERTING_INT_CAST(uint32_t, r.w);
monitor.attributes.physicalHeight = WINPR_ASSERTING_INT_CAST(uint32_t, r.h);
SDL_Log("monitor.orig_screen %" PRIu32, monitor.orig_screen);
SDL_Log("monitor.x %" PRId32, monitor.x);
SDL_Log("monitor.y %" PRId32, monitor.y);
SDL_Log("monitor.width %" PRId32, monitor.width);
SDL_Log("monitor.height %" PRId32, monitor.height);
SDL_Log("monitor.is_primary %" PRIu32, monitor.is_primary);
SDL_Log("monitor.attributes.desktopScaleFactor %" PRIu32,
monitor.attributes.desktopScaleFactor);
SDL_Log("monitor.attributes.deviceScaleFactor %" PRIu32, monitor.attributes.deviceScaleFactor);
SDL_Log("monitor.attributes.orientation %s",
freerdp_desktop_rotation_flags_to_string(monitor.attributes.orientation));
SDL_Log("monitor.attributes.physicalWidth %" PRIu32, monitor.attributes.physicalWidth);
SDL_Log("monitor.attributes.physicalHeight %" PRIu32, monitor.attributes.physicalHeight);
return monitor;
}
SDL_Rect SdlWindow::rect(SDL_Window* window)
{
SDL_Rect rect = {};
if (!window)
return {};
if (!SDL_GetWindowPosition(window, &rect.x, &rect.y))
return {};
if (!SDL_GetWindowSizeInPixels(window, &rect.w, &rect.h))
return {};
return rect;
}
bool SdlWindow::blit(SDL_Surface* surface, const SDL_Rect& srcRect, SDL_Rect& dstRect)
@@ -347,3 +376,48 @@ SdlWindow SdlWindow::create(SDL_DisplayID id, const std::string& title, Uint32 f
return window;
}
static SDL_Window* createDummy(SDL_DisplayID id)
{
const auto x = SDL_WINDOWPOS_CENTERED_DISPLAY(id);
const auto y = SDL_WINDOWPOS_CENTERED_DISPLAY(id);
const int w = 64;
const int h = 64;
auto props = SDL_CreateProperties();
std::stringstream ss;
ss << "SdlWindow::query(" << id << ")";
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, ss.str().c_str());
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN, true);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, true);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, true);
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN, false);
auto window = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
return window;
}
rdpMonitor SdlWindow::query(SDL_DisplayID id, bool forceAsPrimary)
{
std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window(createDummy(id), SDL_DestroyWindow);
if (!window)
return {};
std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> renderer(
SDL_CreateRenderer(window.get(), nullptr), SDL_DestroyRenderer);
if (!SDL_SyncWindow(window.get()))
return {};
SDL_Event event{};
while (SDL_PollEvent(&event))
;
return query(window.get(), id, forceAsPrimary);
}

View File

@@ -31,6 +31,7 @@ class SdlWindow
public:
[[nodiscard]] static SdlWindow create(SDL_DisplayID id, const std::string& title, Uint32 flags,
Uint32 width = 0, Uint32 height = 0);
[[nodiscard]] static rdpMonitor query(SDL_DisplayID id, bool forceAsPrimary = false);
SdlWindow(SdlWindow&& other) noexcept;
SdlWindow(const SdlWindow& other) = delete;
@@ -82,6 +83,12 @@ class SdlWindow
protected:
SdlWindow(SDL_DisplayID id, const std::string& title, const SDL_Rect& rect, Uint32 flags);
[[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);
private:
SDL_Window* _window = nullptr;
SDL_DisplayID _displayID = 0;