From fe452faadcd86addf50bf4ba30537ac8e91cd721 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 22 Apr 2025 12:05:09 +0200 Subject: [PATCH] Fix UI scale for X11. Fix page up and page down keys not working. Fix mouse wheel not working --- .../src/window/sdl2/sdl2_display_backend.cpp | 64 +++++++++++++++++- .../src/window/sdl2/sdl2_display_backend.h | 5 ++ .../src/window/sdl2/sdl2_display_window.cpp | 66 +++++-------------- .../src/window/sdl2/sdl2_display_window.h | 5 +- 4 files changed, 85 insertions(+), 55 deletions(-) diff --git a/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.cpp b/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.cpp index a5b40f743..d67699eda 100644 --- a/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.cpp +++ b/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.cpp @@ -1,10 +1,65 @@ #include "sdl2_display_backend.h" #include "sdl2_display_window.h" +#include +#include +#ifndef WIN32 +#include +#endif + +namespace X11DPI +{ + typedef struct _XDisplay Display; + typedef Display *(*XOpenDisplayPtr)(const char*); + typedef char *(*XGetDefaultPtr)(Display*, const char*, const char*); + typedef int (*XCloseDisplayPtr)(Display*); +} + +SDL2DisplayBackend::SDL2DisplayBackend() +{ + int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + if (result != 0) + throw std::runtime_error(std::string("Unable to initialize SDL:") + SDL_GetError()); + + SDL2DisplayWindow::PaintEventNumber = SDL_RegisterEvents(1); + + // SDL2 doesn't have proper native hidpi support for Linux. Cheat a bit here by asking X11 ourselves. +#if !defined(WIN32) && !defined(__APPLE__) + const char* driver = SDL_GetCurrentVideoDriver(); + if (driver && strcmp(driver, "x11") == 0) + { + void* module = dlopen("libX11.so", RTLD_NOW | RTLD_LOCAL); + if (module) + { + using namespace X11DPI; + auto XOpenDisplay = (XOpenDisplayPtr)dlsym(module, "XOpenDisplay"); + auto XGetDefault = (XGetDefaultPtr)dlsym(module, "XGetDefault"); + auto XCloseDisplay = (XCloseDisplayPtr)dlsym(module, "XCloseDisplay"); + if (XOpenDisplay && XGetDefault && XCloseDisplay) + { + auto display = XOpenDisplay(nullptr); + if (display) + { + char* value = XGetDefault(display, "Xft", "dpi"); + if (value) + { + int dpi = std::atoi(value); + if (dpi != 0) + { + UIScale = dpi / 96.0; + } + } + XCloseDisplay(display); + } + } + } + } +#endif +} std::unique_ptr SDL2DisplayBackend::Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) { - return std::make_unique(windowHost, popupWindow, static_cast(owner), renderAPI); + return std::make_unique(windowHost, popupWindow, static_cast(owner), renderAPI, UIScale); } void SDL2DisplayBackend::ProcessEvents() @@ -24,7 +79,12 @@ void SDL2DisplayBackend::ExitLoop() Size SDL2DisplayBackend::GetScreenSize() { - return SDL2DisplayWindow::GetScreenSize(); + SDL_Rect rect = {}; + int result = SDL_GetDisplayBounds(0, &rect); + if (result != 0) + throw std::runtime_error(std::string("Unable to get screen size:") + SDL_GetError()); + + return Size(rect.w / UIScale, rect.h / UIScale); } void* SDL2DisplayBackend::StartTimer(int timeoutMilliseconds, std::function onTimer) diff --git a/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.h b/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.h index e80b8352e..53b17b0eb 100644 --- a/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.h +++ b/libraries/ZWidget/src/window/sdl2/sdl2_display_backend.h @@ -5,6 +5,8 @@ class SDL2DisplayBackend : public DisplayBackend { public: + SDL2DisplayBackend(); + std::unique_ptr Create(DisplayWindowHost* windowHost, bool popupWindow, DisplayWindow* owner, RenderAPI renderAPI) override; void ProcessEvents() override; void RunLoop() override; @@ -16,4 +18,7 @@ public: Size GetScreenSize() override; bool IsSDL2() override { return true; } + +private: + double UIScale = 1.0; }; diff --git a/libraries/ZWidget/src/window/sdl2/sdl2_display_window.cpp b/libraries/ZWidget/src/window/sdl2/sdl2_display_window.cpp index b5d2ffad5..de4784399 100644 --- a/libraries/ZWidget/src/window/sdl2/sdl2_display_window.cpp +++ b/libraries/ZWidget/src/window/sdl2/sdl2_display_window.cpp @@ -7,28 +7,8 @@ Uint32 SDL2DisplayWindow::PaintEventNumber = 0xffffffff; bool SDL2DisplayWindow::ExitRunLoop; std::unordered_map SDL2DisplayWindow::WindowList; -class InitSDL +SDL2DisplayWindow::SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, SDL2DisplayWindow* owner, RenderAPI renderAPI, double uiscale) : WindowHost(windowHost), UIScale(uiscale) { -public: - InitSDL() - { - int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); - if (result != 0) - throw std::runtime_error(std::string("Unable to initialize SDL:") + SDL_GetError()); - - SDL2DisplayWindow::PaintEventNumber = SDL_RegisterEvents(1); - } -}; - -static void CheckInitSDL() -{ - static InitSDL initsdl; -} - -SDL2DisplayWindow::SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, SDL2DisplayWindow* owner, RenderAPI renderAPI) : WindowHost(windowHost) -{ - CheckInitSDL(); - unsigned int flags = SDL_WINDOW_HIDDEN /*| SDL_WINDOW_ALLOW_HIGHDPI*/; if (renderAPI == RenderAPI::Vulkan) flags |= SDL_WINDOW_VULKAN; @@ -121,8 +101,8 @@ void SDL2DisplayWindow::SetClientFrame(const Rect& box) int w = (int)std::round(box.width * uiscale); int h = (int)std::round(box.height * uiscale); - SDL_SetWindowPosition(Handle.window, x, y); SDL_SetWindowSize(Handle.window, w, h); + SDL_SetWindowPosition(Handle.window, x, y); } void SDL2DisplayWindow::Show() @@ -288,7 +268,8 @@ int SDL2DisplayWindow::GetPixelHeight() const double SDL2DisplayWindow::GetDpiScale() const { // SDL2 doesn't really support this properly. SDL_GetDisplayDPI returns the wrong information according to the docs. - return 1.0; + // We currently cheat by asking X11 directly. See the SDL2DisplayBackend constructor. + return UIScale; } void SDL2DisplayWindow::PresentBitmap(int width, int height, const uint32_t* pixels) @@ -360,8 +341,6 @@ void SDL2DisplayWindow::SetClipboardText(const std::string& text) void SDL2DisplayWindow::ProcessEvents() { - CheckInitSDL(); - SDL_Event event; while (SDL_PollEvent(&event) != 0) { @@ -371,8 +350,6 @@ void SDL2DisplayWindow::ProcessEvents() void SDL2DisplayWindow::RunLoop() { - CheckInitSDL(); - ExitRunLoop = false; while (!ExitRunLoop) { @@ -385,28 +362,11 @@ void SDL2DisplayWindow::RunLoop() void SDL2DisplayWindow::ExitLoop() { - CheckInitSDL(); - ExitRunLoop = true; } -Size SDL2DisplayWindow::GetScreenSize() -{ - CheckInitSDL(); - - SDL_Rect rect = {}; - int result = SDL_GetDisplayBounds(0, &rect); - if (result != 0) - throw std::runtime_error(std::string("Unable to get screen size:") + SDL_GetError()); - - double uiscale = 1.0; // SDL2 doesn't really support this properly. SDL_GetDisplayDPI returns the wrong information according to the docs. - return Size(rect.w / uiscale, rect.h / uiscale); -} - void* SDL2DisplayWindow::StartTimer(int timeoutMilliseconds, std::function onTimer) { - CheckInitSDL(); - // To do: implement timers return nullptr; @@ -414,8 +374,6 @@ void* SDL2DisplayWindow::StartTimer(int timeoutMilliseconds, std::function 0) ? InputKey::MouseWheelUp : (event.y < 0) ? InputKey::MouseWheelDown : InputKey::None; - if (key != InputKey::None) - { - WindowHost->OnWindowMouseWheel(GetMousePos(event), key); - } + int x = 0, y = 0; + SDL_GetMouseState(&x, &y); + double uiscale = GetDpiScale(); + Point mousepos(x / uiscale, y / uiscale); + + if (event.y > 0) + WindowHost->OnWindowMouseWheel(mousepos, InputKey::MouseWheelUp); + else if (event.y < 0) + WindowHost->OnWindowMouseWheel(mousepos, InputKey::MouseWheelDown); } void SDL2DisplayWindow::OnMouseMotion(const SDL_MouseMotionEvent& event) @@ -560,7 +522,9 @@ InputKey SDL2DisplayWindow::ScancodeToInputKey(SDL_Scancode keycode) case SDL_SCANCODE_ESCAPE: return InputKey::Escape; case SDL_SCANCODE_SPACE: return InputKey::Space; case SDL_SCANCODE_END: return InputKey::End; + case SDL_SCANCODE_PAGEDOWN: return InputKey::PageDown; case SDL_SCANCODE_HOME: return InputKey::Home; + case SDL_SCANCODE_PAGEUP: return InputKey::PageUp; case SDL_SCANCODE_LEFT: return InputKey::Left; case SDL_SCANCODE_UP: return InputKey::Up; case SDL_SCANCODE_RIGHT: return InputKey::Right; diff --git a/libraries/ZWidget/src/window/sdl2/sdl2_display_window.h b/libraries/ZWidget/src/window/sdl2/sdl2_display_window.h index f891c6720..b8c7e6a30 100644 --- a/libraries/ZWidget/src/window/sdl2/sdl2_display_window.h +++ b/libraries/ZWidget/src/window/sdl2/sdl2_display_window.h @@ -9,7 +9,7 @@ class SDL2DisplayWindow : public DisplayWindow { public: - SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, SDL2DisplayWindow* owner, RenderAPI renderAPI); + SDL2DisplayWindow(DisplayWindowHost* windowHost, bool popupWindow, SDL2DisplayWindow* owner, RenderAPI renderAPI, double uiscale); ~SDL2DisplayWindow(); void SetWindowTitle(const std::string& text) override; @@ -83,7 +83,6 @@ public: static void ProcessEvents(); static void RunLoop(); static void ExitLoop(); - static Size GetScreenSize(); static void* StartTimer(int timeoutMilliseconds, std::function onTimer); static void StopTimer(void* timerID); @@ -95,6 +94,8 @@ public: int BackBufferWidth = 0; int BackBufferHeight = 0; + double UIScale = 1.0; + bool CursorLocked = false; bool isFullscreen = false;