From 3ea97a242f42c8dde060a87cbc4ff7f47b2ddee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 15 Feb 2020 18:51:03 +0100 Subject: [PATCH] [wip] Platform: respond to DPI change events in Sdl and GlfwApp. TODO: docs TODO: I don't like the extra flag in viewport event, should this be separate? TODO: resizing directly from inside the event handler on GLFW causes a loop where the window switches between the DPIs several times and then gets resized to something extremely wide at the end, how to prevent this? Is that due to window borders being differently thick? TODO: there's a GLFW_SCALE_TO_MONITOR that makes the resizing somehow automagic but it's too automagic for my taste, and should be under app control instead TODO: SDL halts all event processing while the window is dragged, meaning the window only gets resized *after* it jumps to the other display, which is freaking bad TODO: Windows supply a "desired window rectangle" in the WM_DPICHANGED event and it should probably get used, how to hammer it out of GLFW? Interesting is that GLFW actually calls this but it does nothing unless GLFW_SCALE_TO_MONITOR is enabled as well. Huh? TODO: I guess because of the different window border sizes, even under SDL whers is no feedback loop going back and forth between two monitors several times causes the window to not be sized as expected anymore, being a bunch of pixels off (803x607 and such). Not good for my OCD. Solutions? GLFW is doing some calculations in its GLFW_SCALE_TO_MONITOR routine, maybe that could get used? TODO: WM_DPICHANGED can be caught by enabling SDL_SYSWMEVENT that's disabled by default. Should this one be propagated to anyEvent() as well? It generates *a lot* of events, however not propagating it might be limiting for the user :/ --- src/Magnum/Platform/GlfwApplication.cpp | 18 +++++++++++-- src/Magnum/Platform/GlfwApplication.h | 13 +++++++-- src/Magnum/Platform/Sdl2Application.cpp | 27 +++++++++++++++++-- src/Magnum/Platform/Sdl2Application.h | 13 +++++++-- .../Platform/Test/GlfwApplicationTest.cpp | 12 +++++++++ .../Platform/Test/Sdl2ApplicationTest.cpp | 15 +++++++++-- 6 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp index 2f7f19ba5..d5ff2811f 100644 --- a/src/Magnum/Platform/GlfwApplication.cpp +++ b/src/Magnum/Platform/GlfwApplication.cpp @@ -584,13 +584,27 @@ void GlfwApplication::setupCallbacks() { #ifdef MAGNUM_TARGET_GL glfwSetFramebufferSizeCallback(_window, [](GLFWwindow* const window, const int w, const int h) { auto& app = *static_cast(glfwGetWindowUserPointer(window)); - ViewportEvent e{app.windowSize(), {w, h}, app.dpiScaling()}; + ViewportEvent e{ViewportEvent::Type::Resized, app.windowSize(), {w, h}, app.dpiScaling()}; app.viewportEvent(e); }); #else glfwSetWindowSizeCallback(_window, [](GLFWwindow* const window, const int w, const int h) { auto& app = *static_cast(glfwGetWindowUserPointer(window)); - ViewportEvent e{{w, h}, app.dpiScaling()}; + ViewportEvent e{ViewportEvent::Type::Resized, {w, h}, app.dpiScaling()}; + app.viewportEvent(e); + }); + #endif + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 303 + glfwSetWindowContentScaleCallback(_window, [](GLFWwindow* const window, float xScale, float yScale) { + auto& app = *static_cast(glfwGetWindowUserPointer(window)); + ViewportEvent e{ViewportEvent::Type::DpiScalingChanged, + #ifdef MAGNUM_TARGET_GL + app.windowSize(), app.framebufferSize(), + #else + app.windowSize(), + #endif + /* Update the cached DPI scaling value as well */ + app._dpiScaling = {xScale, yScale}}; app.viewportEvent(e); }); #endif diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h index f62853ac6..f7f8410a5 100644 --- a/src/Magnum/Platform/GlfwApplication.h +++ b/src/Magnum/Platform/GlfwApplication.h @@ -1300,6 +1300,11 @@ class GlfwApplication::ExitEvent { */ class GlfwApplication::ViewportEvent { public: + enum class Type: UnsignedByte { + Resized, + DpiScalingChanged + }; + /** @brief Copying is not allowed */ ViewportEvent(const ViewportEvent&) = delete; @@ -1312,6 +1317,9 @@ class GlfwApplication::ViewportEvent { /** @brief Moving is not allowed */ ViewportEvent& operator=(ViewportEvent&&) = delete; + /** @brief Event type */ + Type type() const { return _type; } + /** * @brief Window size * @@ -1353,16 +1361,17 @@ class GlfwApplication::ViewportEvent { private: friend GlfwApplication; - explicit ViewportEvent(const Vector2i& windowSize, + explicit ViewportEvent(Type type, const Vector2i& windowSize, #ifdef MAGNUM_TARGET_GL const Vector2i& framebufferSize, #endif - const Vector2& dpiScaling): _windowSize{windowSize}, + const Vector2& dpiScaling): _type{type}, _windowSize{windowSize}, #ifdef MAGNUM_TARGET_GL _framebufferSize{framebufferSize}, #endif _dpiScaling{dpiScaling} {} + Type _type; const Vector2i _windowSize; #ifdef MAGNUM_TARGET_GL const Vector2i _framebufferSize; diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp index 276c7ee55..1d15d861e 100644 --- a/src/Magnum/Platform/Sdl2Application.cpp +++ b/src/Magnum/Platform/Sdl2Application.cpp @@ -65,6 +65,8 @@ #define WIN32_LEAN_AND_MEAN #endif #include +/* For capturing the WM_DPICHANGED event */ +#include #endif namespace Magnum { namespace Platform { @@ -155,6 +157,11 @@ Sdl2Application::Sdl2Application(const Arguments& arguments, NoCreateT): std::exit(1); } + #ifdef CORRADE_TARGET_WINDOWS + /* Enable SysWM events to get the WM_DPICHANGED event on Windows */ + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + #endif + /* Save command-line arguments */ if(args.value("log") == "verbose") _verboseLog = true; const std::string dpiScaling = args.value("dpi-scaling"); @@ -834,7 +841,8 @@ bool Sdl2Application::mainLoopIteration() { _lastKnownCanvasSize = canvasSizei; const Vector2i size = _dpiScaling*canvasSizei; emscripten_set_canvas_element_size("#canvas", size.x(), size.y()); - ViewportEvent e{ + /** @todo dpi scaling changed? */ + ViewportEvent e{ViewportEvent::Type::Resized, #ifdef MAGNUM_TARGET_GL size, #endif @@ -864,7 +872,7 @@ bool Sdl2Application::mainLoopIteration() { framebuffer size and not window size on macOS, which is weird. Query the values directly instead to be really sure. */ - ViewportEvent e{event, windowSize(), + ViewportEvent e{ViewportEvent::Type::Resized, event, windowSize(), #ifdef MAGNUM_TARGET_GL framebufferSize(), #endif @@ -942,6 +950,21 @@ bool Sdl2Application::mainLoopIteration() { } } break; + #ifdef CORRADE_TARGET_WINDOWS + case SDL_SYSWMEVENT: { + if(event.syswm.msg->msg.win.msg == WM_DPICHANGED) { + DWORD dpi = event.syswm.msg->msg.win.wParam; + ViewportEvent e{ViewportEvent::Type::DpiScalingChanged, event, windowSize(), + #ifdef MAGNUM_TARGET_GL + framebufferSize(), + #endif + /* Update the cached DPI scaling value as well */ + _dpiScaling = Vector2{Vector2i{LOWORD(dpi), HIWORD(dpi)}}/96.0f}; + viewportEvent(e); + } + } break; + #endif + /* Direct everything else to anyEvent(), so users can implement event handling for things not present in the Application APIs */ default: if(!(_flags & Flag::NoAnyEvent)) anyEvent(event); diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h index b2426d513..8d0fdfefa 100644 --- a/src/Magnum/Platform/Sdl2Application.h +++ b/src/Magnum/Platform/Sdl2Application.h @@ -1829,6 +1829,11 @@ class Sdl2Application::ExitEvent { */ class Sdl2Application::ViewportEvent { public: + enum class Type: UnsignedByte { + Resized, + DpiScalingChanged + }; + /** @brief Copying is not allowed */ ViewportEvent(const ViewportEvent&) = delete; @@ -1841,6 +1846,9 @@ class Sdl2Application::ViewportEvent { /** @brief Moving is not allowed */ ViewportEvent& operator=(ViewportEvent&&) = delete; + /** @brief Event type */ + Type type() const { return _type; } + /** * @brief Window size * @@ -1893,7 +1901,7 @@ class Sdl2Application::ViewportEvent { private: friend Sdl2Application; - explicit ViewportEvent( + explicit ViewportEvent(Type type, #ifndef CORRADE_TARGET_EMSCRIPTEN const SDL_Event& event, #endif @@ -1901,7 +1909,7 @@ class Sdl2Application::ViewportEvent { #ifdef MAGNUM_TARGET_GL const Vector2i& framebufferSize, #endif - const Vector2& dpiScaling): + const Vector2& dpiScaling): _type{type}, #ifndef CORRADE_TARGET_EMSCRIPTEN _event(event), #endif @@ -1911,6 +1919,7 @@ class Sdl2Application::ViewportEvent { #endif _dpiScaling{dpiScaling} {} + Type _type; #ifndef CORRADE_TARGET_EMSCRIPTEN const SDL_Event& _event; #endif diff --git a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp index df4a65dd3..6faa792bf 100644 --- a/src/Magnum/Platform/Test/GlfwApplicationTest.cpp +++ b/src/Magnum/Platform/Test/GlfwApplicationTest.cpp @@ -47,6 +47,11 @@ struct GlfwApplicationTest: Platform::Application { << event.framebufferSize() #endif << event.dpiScaling(); + + if(event.type() == ViewportEvent::Type::DpiScalingChanged) { + Debug{} << "DPI scaling changed, resizing to" << _lastUnscaledWindowSize; + setWindowSize(_lastUnscaledWindowSize); + } else _lastUnscaledWindowSize = event.windowSize()/event.dpiScaling(); } void keyPressEvent(KeyEvent& event) override { @@ -96,6 +101,9 @@ struct GlfwApplicationTest: Platform::Application { } void drawEvent() override {} + + private: + Vector2i _lastUnscaledWindowSize; }; GlfwApplicationTest::GlfwApplicationTest(const Arguments& arguments): Platform::Application{arguments, NoCreate} { @@ -116,6 +124,10 @@ GlfwApplicationTest::GlfwApplicationTest(const Arguments& arguments): Platform:: #endif << dpiScaling(); + /* Save desired window size for resizing when going between differently + dense displays */ + _lastUnscaledWindowSize = windowSize()/dpiScaling(); + #if GLFW_VERSION_MAJOR*100 + GLFW_VERSION_MINOR >= 302 Utility::Resource rs{"icons"}; PluginManager::Manager manager; diff --git a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp index a16ad9ddf..9275170ae 100644 --- a/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp +++ b/src/Magnum/Platform/Test/Sdl2ApplicationTest.cpp @@ -66,6 +66,11 @@ struct Sdl2ApplicationTest: Platform::Application { << event.framebufferSize() #endif << event.dpiScaling(); + + if(event.type() == ViewportEvent::Type::DpiScalingChanged) { + Debug{} << "DPI scaling changed, resizing to" << _lastUnscaledWindowSize; + setWindowSize(_lastUnscaledWindowSize); + } else _lastUnscaledWindowSize = event.windowSize()/event.dpiScaling(); } /* For testing event coordinates */ @@ -137,10 +142,12 @@ struct Sdl2ApplicationTest: Platform::Application { if(event.type == SDL_WINDOWEVENT) d << event.window.event; } - #ifdef CORRADE_TARGET_EMSCRIPTEN private: + #ifdef CORRADE_TARGET_EMSCRIPTEN bool _fullscreen = false; - #endif + #else + Vector2i _lastUnscaledWindowSize; + #endif }; Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform::Application{arguments, NoCreate} { @@ -162,6 +169,10 @@ Sdl2ApplicationTest::Sdl2ApplicationTest(const Arguments& arguments): Platform:: #endif << dpiScaling(); + /* Save desired window size for resizing when going between differently + dense displays */ + _lastUnscaledWindowSize = windowSize()/dpiScaling(); + #ifndef CORRADE_TARGET_EMSCRIPTEN #if SDL_MAJOR_VERSION*1000 + SDL_MINOR_VERSION*100 + SDL_PATCHLEVEL >= 2005 Utility::Resource rs{"icons"};