diff --git a/client/SDL/SDL3/sdl_context.cpp b/client/SDL/SDL3/sdl_context.cpp index 3f2328c46..17c98fe4e 100644 --- a/client/SDL/SDL3/sdl_context.cpp +++ b/client/SDL/SDL3/sdl_context.cpp @@ -16,6 +16,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#include + #include "sdl_context.hpp" #include "sdl_config.hpp" #include "sdl_channels.hpp" @@ -808,6 +811,78 @@ void SdlContext::applyMonitorOffset(SDL_WindowID window, float& x, float& y) con y -= static_cast(w->offsetY()); } +static bool alignX(const SDL_Rect& a, const SDL_Rect& b) +{ + if (a.x + a.w == b.x) + return true; + if (b.x + b.w == a.x) + return true; + return false; +} + +static bool alignY(const SDL_Rect& a, const SDL_Rect& b) +{ + if (a.y + a.h == b.y) + return true; + if (b.y + b.h == a.y) + return true; + return false; +} + +std::vector +SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID id, + const std::vector& ignore) +{ + auto first = _offsets.at(id); + std::vector neighbours; + + for (auto& entry : _offsets) + { + if (entry.first == id) + continue; + if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end()) + continue; + + bool neighbor = false; + if (alignX(entry.second.first, first.first)) + { + if (entry.second.first.x < first.first.x) + entry.second.second.x = first.second.x - entry.second.second.w; + else + entry.second.second.x = first.second.x + first.second.w; + neighbor = true; + } + if (alignY(entry.second.first, first.first)) + { + if (entry.second.first.y < first.first.y) + entry.second.second.y = first.second.y - entry.second.second.h; + else + entry.second.second.y = first.second.y + first.second.h; + neighbor = true; + } + + if (neighbor) + neighbours.push_back(entry.first); + } + return neighbours; +} + +void SdlContext::updateMonitorDataFromOffsets() +{ + for (auto& entry : _displays) + { + auto offsets = _offsets.at(entry.first); + entry.second.x = offsets.second.x; + entry.second.y = offsets.second.y; + } + + for (auto& entry : _windows) + { + const auto& monitor = _displays.at(entry.first); + entry.second.setMonitor(monitor); + } +} + bool SdlContext::drawToWindow(SdlWindow& window, const std::vector& rects) { if (!isConnected()) @@ -878,7 +953,7 @@ bool SdlContext::addDisplayWindow(SDL_DisplayID id) return true; } -bool SdlContext::removeDisplay(SDL_DisplayID id) +bool SdlContext::removeDisplayWindow(SDL_DisplayID id) { for (auto& w : _windows) { @@ -888,6 +963,37 @@ bool SdlContext::removeDisplay(SDL_DisplayID id) return true; } +bool SdlContext::detectDisplays() +{ + int count = 0; + auto display = SDL_GetDisplays(&count); + if (!display) + return false; + for (int x = 0; x < count; x++) + { + const auto id = display[x]; + addOrUpdateDisplay(id); + } + + return true; +} + +rdpMonitor SdlContext::getDisplay(SDL_DisplayID id) const +{ + return _displays.at(id); +} + +std::vector SdlContext::getDisplayIds() const +{ + std::vector keys; + keys.reserve(_displays.size()); + for (const auto& entry : _displays) + { + keys.push_back(entry.first); + } + return keys; +} + const SdlWindow* SdlContext::getWindowForId(SDL_WindowID id) const { auto it = _windows.find(id); @@ -1091,6 +1197,59 @@ bool SdlContext::handleEvent(const SDL_TouchFingerEvent& ev) return SdlTouch::handleEvent(this, copy.tfinger); } +void SdlContext::addOrUpdateDisplay(SDL_DisplayID id) +{ + auto monitor = SdlWindow::query(id, false); + _displays.emplace(id, monitor); + + /* Update actual display rectangles: + * + * 1. Get logical display bounds + * 2. Use already known pixel width and height + * 3. Iterate over each display and update the x and y offsets by adding all monitor + * widths/heights from the primary + */ + _offsets.clear(); + for (auto& entry : _displays) + { + SDL_Rect bounds{}; + std::ignore = SDL_GetDisplayBounds(entry.first, &bounds); + + SDL_Rect pixel{}; + pixel.w = entry.second.width; + pixel.h = entry.second.height; + _offsets.emplace(entry.first, std::pair{ bounds, pixel }); + } + + /* 1. Find primary and update all neighbors + * 2. For each neighbor update all neighbors + * 3. repeat until all displays updated. + */ + const auto primary = SDL_GetPrimaryDisplay(); + std::vector handled; + handled.push_back(primary); + + auto neighbors = updateDisplayOffsetsForNeighbours(primary); + while (!neighbors.empty()) + { + auto neighbor = *neighbors.begin(); + neighbors.pop_back(); + + if (std::find(handled.begin(), handled.end(), neighbor) != handled.end()) + continue; + handled.push_back(neighbor); + + auto next = updateDisplayOffsetsForNeighbours(neighbor, handled); + neighbors.insert(neighbors.end(), next.begin(), next.end()); + } + updateMonitorDataFromOffsets(); +} + +void SdlContext::deleteDisplay(SDL_DisplayID id) +{ + _displays.erase(id); +} + bool SdlContext::eventToPixelCoordinates(SDL_WindowID id, SDL_Event& ev) { auto w = getWindowForId(id); diff --git a/client/SDL/SDL3/sdl_context.hpp b/client/SDL/SDL3/sdl_context.hpp index 2110c4e30..6ea8cc774 100644 --- a/client/SDL/SDL3/sdl_context.hpp +++ b/client/SDL/SDL3/sdl_context.hpp @@ -120,7 +120,10 @@ class SdlContext [[nodiscard]] SdlWindow* getFirstWindow(); [[nodiscard]] bool addDisplayWindow(SDL_DisplayID id); - [[nodiscard]] bool removeDisplay(SDL_DisplayID id); + [[nodiscard]] bool removeDisplayWindow(SDL_DisplayID id); + [[nodiscard]] bool detectDisplays(); + [[nodiscard]] rdpMonitor getDisplay(SDL_DisplayID id) const; + [[nodiscard]] std::vector getDisplayIds() const; [[nodiscard]] sdlDispContext& getDisplayChannelContext(); [[nodiscard]] sdlInput& getInputChannelContext(); @@ -162,6 +165,9 @@ class SdlContext [[nodiscard]] bool handleEvent(const SDL_MouseWheelEvent& ev); [[nodiscard]] bool handleEvent(const SDL_TouchFingerEvent& ev); + void addOrUpdateDisplay(SDL_DisplayID id); + void deleteDisplay(SDL_DisplayID id); + [[nodiscard]] bool createPrimary(); [[nodiscard]] std::string windowTitle() const; [[nodiscard]] bool waitForWindowsCreated(); @@ -174,6 +180,11 @@ class SdlContext void applyMonitorOffset(SDL_WindowID window, float& x, float& y) const; + [[nodiscard]] std::vector + updateDisplayOffsetsForNeighbours(SDL_DisplayID id, + const std::vector& ignore = {}); + void updateMonitorDataFromOffsets(); + rdpContext* _context = nullptr; wLog* _log = nullptr; @@ -206,7 +217,9 @@ class SdlContext SdlConnectionDialogWrapper _dialog; - std::map _windows; + std::map _displays; + std::map _windows; + std::map> _offsets; uint32_t _windowWidth = 0; uint32_t _windowHeigth = 0; diff --git a/client/SDL/SDL3/sdl_disp.cpp b/client/SDL/SDL3/sdl_disp.cpp index 4478f070d..02ff07a45 100644 --- a/client/SDL/SDL3/sdl_disp.cpp +++ b/client/SDL/SDL3/sdl_disp.cpp @@ -308,7 +308,7 @@ bool sdlDispContext::updateMonitors(SDL_EventType type, SDL_DisplayID displayID) return false; break; case SDL_EVENT_DISPLAY_REMOVED: - if (!_sdl->removeDisplay(displayID)) + if (!_sdl->removeDisplayWindow(displayID)) return false; break; default: diff --git a/client/SDL/SDL3/sdl_freerdp.cpp b/client/SDL/SDL3/sdl_freerdp.cpp index ef761324a..5bab3abd7 100644 --- a/client/SDL/SDL3/sdl_freerdp.cpp +++ b/client/SDL/SDL3/sdl_freerdp.cpp @@ -605,6 +605,13 @@ int main(int argc, char* argv[]) if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) return -1; + SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); + SDL_SetHint(SDL_HINT_PEN_MOUSE_EVENTS, "0"); + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + SDL_SetHint(SDL_HINT_PEN_TOUCH_EVENTS, "1"); + SDL_SetHint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, "1"); + /* Redirect SDL log messages to wLog */ SDL_SetLogOutputFunction(winpr_LogOutputFunction, sdl); auto level = WLog_GetLogLevel(sdl->getWLog()); @@ -614,13 +621,6 @@ int main(int argc, char* argv[]) WLog_Print(sdl->getWLog(), WLOG_DEBUG, "client is using backend '%s'", backend); sdl_dialogs_init(); - SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); - SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); - SDL_SetHint(SDL_HINT_PEN_MOUSE_EVENTS, "0"); - SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); - SDL_SetHint(SDL_HINT_PEN_TOUCH_EVENTS, "1"); - SDL_SetHint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, "1"); - /* SDL cleanup code if the client exits */ ScopeGuard guard( [&]() @@ -630,6 +630,8 @@ int main(int argc, char* argv[]) sdl_dialogs_uninit(); SDL_Quit(); }); + if (!sdl->detectDisplays()) + return -1; /* Initialize RDP */ auto context = sdl->context(); diff --git a/client/SDL/SDL3/sdl_monitor.cpp b/client/SDL/SDL3/sdl_monitor.cpp index 9b8b78031..cd890768e 100644 --- a/client/SDL/SDL3/sdl_monitor.cpp +++ b/client/SDL/SDL3/sdl_monitor.cpp @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -171,85 +172,6 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl) return TRUE; } -[[nodiscard]] static Uint32 scale(Uint32 val, float scale) -{ - const auto dval = static_cast(val); - const auto sval = dval / scale; - return static_cast(sval); -} - -[[nodiscard]] static BOOL sdl_apply_monitor_properties(rdpMonitor& monitor, SDL_DisplayID id, - bool isPrimary) -{ - - float dpi = SDL_GetDisplayContentScale(id); - float hdpi = dpi; - float vdpi = dpi; - SDL_Rect rect = {}; - - if (!SDL_GetDisplayBounds(id, &rect)) - return FALSE; - - WINPR_ASSERT(rect.w > 0); - WINPR_ASSERT(rect.h > 0); - - 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(modes)); - - const float dw = 1.0f * static_cast(rect.w) / static_cast(scaleRect.w); - const float dh = 1.0f * static_cast(rect.h) / static_cast(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.is_primary = isPrimary; - monitor.attributes.desktopScaleFactor = static_cast(factor); - 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); - return TRUE; -} - [[nodiscard]] static BOOL sdl_apply_display_properties(SdlContext* sdl) { WINPR_ASSERT(sdl); @@ -266,9 +188,10 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl) if (sdl->monitorIds().empty()) return FALSE; const auto id = sdl->monitorIds().front(); - rdpMonitor monitor = {}; - if (!sdl_apply_monitor_properties(monitor, id, TRUE)) - return FALSE; + auto monitor = sdl->getDisplay(id); + monitor.is_primary = true; + monitor.x = 0; + monitor.y = 0; monitors.emplace_back(monitor); return freerdp_settings_set_monitor_def_array_sorted(settings, monitors.data(), monitors.size()); @@ -277,10 +200,7 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl) } for (const auto& id : sdl->monitorIds()) { - rdpMonitor monitor = {}; - const auto primary = SDL_GetPrimaryDisplay(); - if (!sdl_apply_monitor_properties(monitor, id, id == primary)) - return FALSE; + const auto monitor = sdl->getDisplay(id); monitors.emplace_back(monitor); } return freerdp_settings_set_monitor_def_array_sorted(settings, monitors.data(), @@ -315,7 +235,6 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl) sdl->setMonitorIds({ id }); } - // TODO: Fill monitor struct if (!sdl_apply_display_properties(sdl)) return FALSE; return sdl_apply_max_size(sdl, pMaxWidth, pMaxHeight); @@ -332,17 +251,7 @@ BOOL sdl_detect_monitors(SdlContext* sdl, UINT32* pMaxWidth, UINT32* pMaxHeight) rdpSettings* settings = sdl->context()->settings; WINPR_ASSERT(settings); - std::vector ids; - { - int numDisplays = 0; - auto sids = SDL_GetDisplays(&numDisplays); - if (sids && (numDisplays > 0)) - ids = std::vector(sids, sids + numDisplays); - SDL_free(sids); - if (numDisplays < 0) - return FALSE; - } - + const auto& ids = sdl->getDisplayIds(); auto nr = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds); if (nr == 0) { diff --git a/client/SDL/SDL3/sdl_window.cpp b/client/SDL/SDL3/sdl_window.cpp index 0bed3e3fe..913af1a10 100644 --- a/client/SDL/SDL3/sdl_window.cpp +++ b/client/SDL/SDL3/sdl_window.cpp @@ -19,10 +19,13 @@ */ #include #include +#include #include "sdl_window.hpp" #include "sdl_utils.hpp" +#include + SdlWindow::SdlWindow(SDL_DisplayID id, const std::string& title, const SDL_Rect& rect, [[maybe_unused]] Uint32 flags) : _displayID(id) @@ -53,6 +56,8 @@ SdlWindow::SdlWindow(SDL_DisplayID id, const std::string& title, const SDL_Rect& std::ignore = resize({ w, h }); SDL_SetHint(SDL_HINT_APP_NAME, ""); std::ignore = SDL_SyncWindow(_window); + + _monitor = query(_window, id, true); } SdlWindow::SdlWindow(SdlWindow&& other) noexcept @@ -83,13 +88,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 +96,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 +131,18 @@ Sint32 SdlWindow::offsetY() const rdpMonitor SdlWindow::monitor(bool isPrimary) const { - rdpMonitor mon{}; - - const auto factor = scale(); - const auto dsf = static_cast(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) + auto m = _monitor; + if (isPrimary) { - mon.x = rect.x; - mon.y = rect.y; + m.x = 0; + m.y = 0; } + return m; +} - 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; +void SdlWindow::setMonitor(rdpMonitor monitor) +{ + _monitor = monitor; } float SdlWindow::scale() const @@ -291,14 +268,106 @@ 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, forceAsPrimary); + const float factor = SDL_GetWindowDisplayScale(window); + const float dpi = std::roundf(factor * 100.0f); + + WINPR_ASSERT(r.w > 0); + WINPR_ASSERT(r.h > 0); + + 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 = r.x; + monitor.y = r.y; + monitor.width = r.w; + monitor.height = r.h; + monitor.is_primary = forceAsPrimary || (id == primary); + monitor.attributes.desktopScaleFactor = static_cast(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, bool forceAsPrimary) +{ + SDL_Rect rect = {}; + if (!window) + return {}; + + if (!forceAsPrimary) + { + if (!SDL_GetWindowPosition(window, &rect.x, &rect.y)) + return {}; + } + + if (!SDL_GetWindowSizeInPixels(window, &rect.w, &rect.h)) + return {}; + + return rect; +} + +SdlWindow::HighDPIMode SdlWindow::isHighDPIWindowsMode(SDL_Window* window) +{ + if (!window) + return MODE_INVALID; + + const auto id = SDL_GetDisplayForWindow(window); + if (id == 0) + return MODE_INVALID; + + const auto cs = SDL_GetDisplayContentScale(id); + const auto ds = SDL_GetWindowDisplayScale(window); + const auto pd = SDL_GetWindowPixelDensity(window); + + /* mac os x style, but no HighDPI display */ + if ((cs == 1.0f) && (ds == 1.0f) && (pd == 1.0f)) + return MODE_NONE; + + /* mac os x style HighDPI */ + if ((cs == 1.0f) && (ds > 1.0f) && (pd > 1.0f)) + return MODE_MACOS; + + /* rest is windows style */ + return MODE_WINDOWS; } bool SdlWindow::blit(SDL_Surface* surface, const SDL_Rect& srcRect, SDL_Rect& dstRect) @@ -347,3 +416,67 @@ 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 window(createDummy(id), SDL_DestroyWindow); + if (!window) + return {}; + + std::unique_ptr 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); +} + +SDL_Rect SdlWindow::rect(SDL_DisplayID id, bool forceAsPrimary) +{ + std::unique_ptr window(createDummy(id), SDL_DestroyWindow); + if (!window) + return {}; + + std::unique_ptr renderer( + SDL_CreateRenderer(window.get(), nullptr), SDL_DestroyRenderer); + + if (!SDL_SyncWindow(window.get())) + return {}; + + SDL_Event event{}; + while (SDL_PollEvent(&event)) + ; + + return rect(window.get(), forceAsPrimary); +} diff --git a/client/SDL/SDL3/sdl_window.hpp b/client/SDL/SDL3/sdl_window.hpp index 303e2a9b3..0b4a4ec44 100644 --- a/client/SDL/SDL3/sdl_window.hpp +++ b/client/SDL/SDL3/sdl_window.hpp @@ -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; @@ -52,6 +53,7 @@ class SdlWindow [[nodiscard]] Sint32 offsetY() const; [[nodiscard]] rdpMonitor monitor(bool isPrimary) const; + void setMonitor(rdpMonitor monitor); [[nodiscard]] float scale() const; [[nodiscard]] SDL_DisplayOrientation orientation() const; @@ -82,9 +84,27 @@ 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, bool forceAsPrimary = false); + [[nodiscard]] static SDL_Rect rect(SDL_DisplayID id, bool forceAsPrimary = false); + + enum HighDPIMode + { + MODE_INVALID, + MODE_NONE, + MODE_WINDOWS, + MODE_MACOS + }; + + [[nodiscard]] static enum HighDPIMode isHighDPIWindowsMode(SDL_Window* window); + private: SDL_Window* _window = nullptr; SDL_DisplayID _displayID = 0; Sint32 _offset_x = 0; Sint32 _offset_y = 0; + rdpMonitor _monitor{}; };