diff --git a/doc/changelog.dox b/doc/changelog.dox
index c7a9308339..0103d1d1f2 100644
--- a/doc/changelog.dox
+++ b/doc/changelog.dox
@@ -79,6 +79,10 @@ See also:
@subsubsection changelog-latest-new-platform Platform libraries
+- @ref Platform::Sdl2Application and @ref Platform::GlfwApplication are now
+ DPI-aware on Windows as well. See their documentation,
+ @ref platforms-windows-hidpi and [mosra/magnum#243](https://github.com/mosra/magnum/issues/243)
+ for more information.
- Added @ref Platform::Sdl2Application::glContext() to access the underlying
`SDL_GLContext` (see [mosra/magnum#325](https://github.com/mosra/magnum/pull/325))
diff --git a/doc/platforms-windows.dox b/doc/platforms-windows.dox
index a295b79d1c..7afcaa4496 100644
--- a/doc/platforms-windows.dox
+++ b/doc/platforms-windows.dox
@@ -34,9 +34,74 @@ namespace Magnum {
@section platforms-windows-hidpi HiDPI support
-Windows supports two approaches to advertising HiDPI support --- either via the
-manifest file or through the [SetProcessDpiAwareness()](https://docs.microsoft.com/en-us/windows/desktop/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness)
-API. See the API documentation for more information.
+Windows supports two approaches to advertising HiDPI support. The recommended
+way is via a so-called manifest file added to an executable, but it's also
+possible to it programatically through the `SetProcessDpiAwareness()` family of
+APIs. Note there's three different levels of DPI awareness setup for Windows
+Vista and newer, Windows 8.1 and newer and Windows 10, and for best support may
+want to support all three.
+
+When using MSVC, the manifest file can be added directly via CMake. Advertising application-wide per-monitor support can look like in the following snippet,
+together with fallbacks for older systems:
+
+@code{.xml}
+
+
+
+
+ true/pm
+
+
+ permonitorv2,permonitor
+
+
+
+
+@endcode
+
+Then, the manifest file can be supplied directly in the sources list for
+@cmake add_executable() @ce, via a variable, or you can add it conditionally
+later using @cmake target_sources() @ce. For example:
+
+@code{.cmake}
+add_executable(my-application MyApplication.cpp)
+if(CORRADE_TARGET_WINDOWS)
+ target_sources(my-application PRIVATE WindowsHiDPI.manifest)
+endif()
+@endcode
+
+Some toolkits (such as GLFW in @ref Platform-GlfwApplication-dpi "Platform::GlfwApplication")
+are advertising HiDPI support implicitly programatically. In that case the
+manifest file doesn't need to be supplied, but there may be some disadvantages
+compared to supplying the manifest. See the
+[MSDN documentation about DPI awareness](https://msdn.microsoft.com/en-us/library/windows/desktop/mt846517(v=vs.85).aspx)
+for more information.
+
+@m_class{m-block m-info}
+
+@par Supplying manifests with MinGW
+ With MinGw the operation is slightly more involved, as you need to pass it
+ through a `*.rc` file. A downside is that MinGW is not able to merge
+ information from multiple manifests like the MSVC toolchain can.
+@par
+ @code{.txt}
+ 1 RT_MANIFEST "WindowsHiDPI.manifest"
+ @endcode
+@par
+ Then you add the `*.rc` file using @cb{.cmake} target_sources @ce like
+ above. Here's a CMake snippet that will work for both:
+@par
+ @code{.cmake}
+ add_executable(my-application MyApplication.cpp)
+ if(CORRADE_TARGET_WINDOWS)
+ if(MSVC)
+ target_sources(my-application PRIVATE WindowsHiDPI.manifest)
+ elif(MINGW)
+ target_sources(my-application PRIVATE WindowsHiDPI.rc)
+ endif()
+ endif()
+ @endcode
@section platforms-windows-rt Windows RT
diff --git a/src/Magnum/Platform/GlfwApplication.cpp b/src/Magnum/Platform/GlfwApplication.cpp
index ec75ea040f..05d3824d23 100644
--- a/src/Magnum/Platform/GlfwApplication.cpp
+++ b/src/Magnum/Platform/GlfwApplication.cpp
@@ -149,6 +149,7 @@ Vector2 GlfwApplication::dpiScaling(const Configuration& configuration) const {
/* Otherwise there's a choice between virtual and physical DPI scaling */
#else
/* Try to get virtual DPI scaling first, if supported and requested */
+ /** @todo Revisit this for GLFW 3.3 -- https://github.com/glfw/glfw/issues/677 */
if(dpiScalingPolicy == Implementation::GlfwDpiScalingPolicy::Virtual) {
/* Use Xft.dpi on X11 */
#ifdef _MAGNUM_PLATFORM_USE_X11
@@ -158,6 +159,28 @@ Vector2 GlfwApplication::dpiScaling(const Configuration& configuration) const {
return dpiScaling;
}
+ /* Check for DPI awareness on non-RT Windows and then ask for DPI. GLFW
+ is advertising the application to be DPI-aware on its own even
+ without supplying an explicit manifest --
+ https://github.com/glfw/glfw/blob/089ea9af227fdffdf872348923e1c12682e63029/src/win32_init.c#L564-L569
+ If, for some reason, the app is still not DPI-aware, tell that to
+ the user explicitly and don't even attempt to query the value if the
+ app is not DPI aware. If it's desired to get the DPI value
+ unconditionally, the user should use physical DPI scaling instead. */
+ #elif defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)
+ if(!Implementation::isWindowsAppDpiAware()) {
+ Warning{verbose} << "Platform::GlfwApplication: your application is not set as DPI-aware, DPI scaling won't be used";
+ return Vector2{1.0f};
+ }
+ GLFWmonitor* const monitor = glfwGetPrimaryMonitor();
+ const GLFWvidmode* const mode = glfwGetVideoMode(monitor);
+ Vector2i monitorSize;
+ glfwGetMonitorPhysicalSize(monitor, &monitorSize.x(), &monitorSize.y());
+ auto dpi = Vector2{Vector2i{mode->width, mode->height}*25.4f/Vector2{monitorSize}};
+ const Vector2 dpiScaling{dpi/96.0f};
+ Debug{verbose} << "Platform::GlfwApplication: virtual DPI scaling" << dpiScaling;
+ return dpiScaling;
+
/* Otherwise ¯\_(ツ)_/¯ */
#else
Debug{verbose} << "Platform::GlfwApplication: sorry, virtual DPI scaling not implemented on this platform yet, falling back to physical DPI scaling";
@@ -168,9 +191,11 @@ Vector2 GlfwApplication::dpiScaling(const Configuration& configuration) const {
scaling is requested */
CORRADE_INTERNAL_ASSERT(dpiScalingPolicy == Implementation::GlfwDpiScalingPolicy::Virtual || dpiScalingPolicy == Implementation::GlfwDpiScalingPolicy::Physical);
- /* Take display DPI. Enable only on Linux for now, I need to test this
- properly on Windows first. */
- #ifdef CORRADE_TARGET_UNIX
+ /* Take display DPI elsewhere. Enable only on Linux (where it gets the
+ usually very-off value from X11) and on non-RT Windows (where it takes
+ the UI scale value like with virtual DPI scaling, but without checking
+ for DPI awareness first). */
+ #if defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT))
GLFWmonitor* const monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* const mode = glfwGetVideoMode(monitor);
Vector2i monitorSize;
diff --git a/src/Magnum/Platform/GlfwApplication.h b/src/Magnum/Platform/GlfwApplication.h
index cc46930f0a..c2fb60f496 100644
--- a/src/Magnum/Platform/GlfwApplication.h
+++ b/src/Magnum/Platform/GlfwApplication.h
@@ -130,9 +130,11 @@ If no other application header is included, this class is also aliased to
@section Platform-GlfwApplication-dpi DPI awareness
DPI awareness behavior is consistent with @ref Sdl2Application except that iOS
-or Emscripten specifics don't apply here. See
-@ref Platform-Sdl2Application-dpi "its DPI awareness documentation" for more
-information.
+or Emscripten specifics don't apply here. In addition, on Windows, GLFW is
+implicitly advertising DPI awareness, so the manifest file described in
+@ref platforms-windows-hidpi doesn't necessarily need to be supplied. See
+@ref Platform-Sdl2Application-dpi "Sdl2Application DPI awareness documentation"
+for more information.
*/
class GlfwApplication {
public:
diff --git a/src/Magnum/Platform/Implementation/DpiScaling.cpp b/src/Magnum/Platform/Implementation/DpiScaling.cpp
index 91f8d56612..3f971b70d7 100644
--- a/src/Magnum/Platform/Implementation/DpiScaling.cpp
+++ b/src/Magnum/Platform/Implementation/DpiScaling.cpp
@@ -45,6 +45,18 @@
#include
#endif
+#if defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)
+#define WIN32_LEAN_AND_MEAN 1
+#define VC_EXTRALEAN
+#include
+#ifdef __has_include
+#if __has_include()
+#include
+#endif
+#endif
+#include
+#endif
+
namespace Magnum { namespace Platform { namespace Implementation {
Utility::Arguments windowScalingArguments() {
@@ -129,6 +141,37 @@ Float emscriptenDpiScaling() {
}
#endif
+#if defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)
+bool isWindowsAppDpiAware() {
+ /** @todo use GetWindowDpiAwarenessContext() (since Windows 10)? I think
+ it's not needed for a simple boolean return value. */
+
+ #ifdef DPI_ENUMS_DECLARED
+ /* The GetProcessDpiAwareness() function is available only since Windows
+ 8.1, so load it manually to avoid a link-time error when building for
+ Windows 7. Also, the shellscalingapi.h include might not be available
+ on older MinGW, so it's guarded by __has_include(). Here, if the
+ DPI_ENUMS_DECLARED define is present, the header exists and has what we
+ need. */
+ HMODULE const shcore = GetModuleHandleA("Shcore.dll");
+ if(shcore) {
+ auto* const getProcessDpiAwareness = reinterpret_cast(GetProcAddress(shcore, "GetProcessDpiAwareness"));
+ PROCESS_DPI_AWARENESS result{};
+ return getProcessDpiAwareness && getProcessDpiAwareness(nullptr, &result) == S_OK && result != PROCESS_DPI_UNAWARE;
+ }
+ #endif
+
+ /* IsProcessDPIAware() is available since Windows Vista. At this point we
+ can require it (XP support? haha no), so assert that everything works
+ correctly. */
+ HMODULE const user32 = GetModuleHandleA("User32.dll");
+ CORRADE_INTERNAL_ASSERT(user32);
+ auto const isProcessDPIAware = reinterpret_cast(GetProcAddress(user32, "IsProcessDPIAware"));
+ CORRADE_INTERNAL_ASSERT(isProcessDPIAware);
+ return isProcessDPIAware();
+}
+#endif
+
}}}
#endif
diff --git a/src/Magnum/Platform/Implementation/DpiScaling.h b/src/Magnum/Platform/Implementation/DpiScaling.h
index 57ef511b7e..8bdfdd1340 100644
--- a/src/Magnum/Platform/Implementation/DpiScaling.h
+++ b/src/Magnum/Platform/Implementation/DpiScaling.h
@@ -48,6 +48,10 @@ Float emscriptenDpiScaling();
bool isAppleBundleHiDpiEnabled();
#endif
+#if defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)
+bool isWindowsAppDpiAware();
+#endif
+
}}}
#endif
diff --git a/src/Magnum/Platform/Sdl2Application.cpp b/src/Magnum/Platform/Sdl2Application.cpp
index 97f817a51d..ff62f70ef1 100644
--- a/src/Magnum/Platform/Sdl2Application.cpp
+++ b/src/Magnum/Platform/Sdl2Application.cpp
@@ -199,6 +199,26 @@ Vector2 Sdl2Application::dpiScaling(const Configuration& configuration) const {
return dpiScaling;
}
+ /* Check for DPI awareness on (non-RT) Windows and then ask for DPI.
+ SDL_GetDisplayDPI() is querying GetDpiForMonitor() --
+ https://github.com/spurious/SDL-mirror/blob/17af4584cb28cdb3c2feba17e7d989a806007d9f/src/video/windows/SDL_windowsmodes.c#L266
+ and GetDpiForMonitor() returns 96 if the application is DPI unaware.
+ So we instead check for DPI awareness first (and tell the user if
+ not), and only if the app is, then we use SDL_GetDisplayDPI(). If
+ it's for some reason desired to get the DPI value unconditionally,
+ the user should use physical DPI scaling instead. */
+ #elif defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT)
+ if(!Implementation::isWindowsAppDpiAware()) {
+ Warning{verbose} << "Platform::Sdl2Application: your application is not set as DPI-aware, DPI scaling won't be used";
+ return Vector2{1.0f};
+ }
+ Vector2 dpi;
+ if(SDL_GetDisplayDPI(0, nullptr, &dpi.x(), &dpi.y()) == 0) {
+ const Vector2 dpiScaling{dpi/96.0f};
+ Debug{verbose} << "Platform::Sdl2Application: virtual DPI scaling" << dpiScaling;
+ return dpiScaling;
+ }
+
/* Otherwise ¯\_(ツ)_/¯ */
#else
Debug{verbose} << "Platform::Sdl2Application: sorry, virtual DPI scaling not implemented on this platform yet, falling back to physical DPI scaling";
@@ -220,9 +240,11 @@ Vector2 Sdl2Application::dpiScaling(const Configuration& configuration) const {
Debug{verbose} << "Platform::Sdl2Application: physical DPI scaling" << dpiScaling.x();
return dpiScaling;
- /* Take display DPI elsewhere. Enable only on Linux for now, I need to
- test this properly on Windows first. Also only since SDL 2.0.4. */
- #elif defined(CORRADE_TARGET_UNIX) && SDL_VERSION_ATLEAST(2, 0, 4)
+ /* Take display DPI elsewhere. Enable only on Linux (where it gets the
+ usually very-off value from X11) and on non-RT Windows (where it takes
+ the UI scale value like with virtual DPI scaling, but without checking
+ for DPI awareness first). Also only since SDL 2.0.4. */
+ #elif (defined(CORRADE_TARGET_UNIX) || (defined(CORRADE_TARGET_WINDOWS) && !defined(CORRADE_TARGET_WINDOWS_RT))) && SDL_VERSION_ATLEAST(2, 0, 4)
Vector2 dpi;
if(SDL_GetDisplayDPI(0, nullptr, &dpi.x(), &dpi.y()) == 0) {
const Vector2 dpiScaling{dpi/96.0f};
diff --git a/src/Magnum/Platform/Sdl2Application.h b/src/Magnum/Platform/Sdl2Application.h
index 28993d3def..d2e7ef39ff 100644
--- a/src/Magnum/Platform/Sdl2Application.h
+++ b/src/Magnum/Platform/Sdl2Application.h
@@ -339,8 +339,11 @@ variable).
system. For example if a 800x600 window is requested and DPI scaling is set
to 200%, the resulting window will have 1600x1200 pixels. The backing
framebuffer will have the same size. This is supported on Linux and
- Windows. Equivalent to passing @ref Configuration::DpiScalingPolicy::Virtual
- to @ref Configuration::setSize() or `virtual` on command line.
+ Windows; on Windows the application is first checked for DPI awareness
+ as described in @ref platforms-windows-hidpi and if the application is not
+ DPI-aware, 1:1 scaling is used. Equivalent to passing
+ @ref Configuration::DpiScalingPolicy::Virtual to
+ @ref Configuration::setSize() or `virtual` on command line.
- Physical DPI scaling. Takes the requested window size as a physical size
that a window would have on platform's default DPI and scales it to have
the same physical size on given display physical DPI. So, for example on a
@@ -349,9 +352,10 @@ variable).
DPI display. On platforms that don't have a concept of a window (such
as mobile platforms or @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten"), it
causes the framebuffer to match display pixels 1:1 without any scaling.
- This is supported on Linux, Windows, all mobile platforms except iOS and
- Emscripten. Equivalent to passing
- @ref Configuration::DpiScalingPolicy::Physical to
+ This is supported on Linux and all mobile platforms (except iOS) and
+ Emscripten. On Windows this is equivalent to virtual DPI scaling but
+ without doing an explicit check for DPI awareness first. Equivalent to
+ passing @ref Configuration::DpiScalingPolicy::Physical to
@ref Configuration::setSize() or `physical` via command line / environment.
Besides the above, it's possible to supply a custom DPI scaling value to
diff --git a/src/Magnum/Platform/Test/CMakeLists.txt b/src/Magnum/Platform/Test/CMakeLists.txt
index 2bd6eb0c57..c17eb2314c 100644
--- a/src/Magnum/Platform/Test/CMakeLists.txt
+++ b/src/Magnum/Platform/Test/CMakeLists.txt
@@ -34,6 +34,7 @@ endif()
if(WITH_GLFWAPPLICATION)
add_executable(PlatformGlfwApplicationTest GlfwApplicationTest.cpp)
+ # HiDPi.manifest not needed, as GLFW sets that on its own
target_link_libraries(PlatformGlfwApplicationTest PRIVATE MagnumGlfwApplication)
set_target_properties(PlatformGlfwApplicationTest PROPERTIES FOLDER "Magnum/Platform/Test")
endif()
@@ -52,6 +53,13 @@ endif()
if(WITH_SDL2APPLICATION)
add_executable(PlatformSdl2ApplicationTest Sdl2ApplicationTest.cpp)
+ if(CORRADE_TARGET_WINDOWS AND NOT CORRADE_TARGET_WINDOWS_RT)
+ if(MSVC)
+ target_sources(PlatformSdl2ApplicationTest PRIVATE WindowsHiDPI.manifest)
+ elseif(MINGW)
+ target_sources(PlatformSdl2ApplicationTest PRIVATE WindowsHiDPI.rc)
+ endif()
+ endif()
target_link_libraries(PlatformSdl2ApplicationTest PRIVATE MagnumSdl2Application)
set_target_properties(PlatformSdl2ApplicationTest PROPERTIES FOLDER "Magnum/Platform/Test")
if(CORRADE_TARGET_EMSCRIPTEN)
diff --git a/src/Magnum/Platform/Test/WindowsHiDPI.manifest b/src/Magnum/Platform/Test/WindowsHiDPI.manifest
new file mode 100644
index 0000000000..54a0888847
--- /dev/null
+++ b/src/Magnum/Platform/Test/WindowsHiDPI.manifest
@@ -0,0 +1,14 @@
+
+
+
+
+ true/pm
+
+
+ permonitorv2,permonitor
+
+
+
+
+
diff --git a/src/Magnum/Platform/Test/WindowsHiDPI.rc b/src/Magnum/Platform/Test/WindowsHiDPI.rc
new file mode 100644
index 0000000000..cf21a63ec6
--- /dev/null
+++ b/src/Magnum/Platform/Test/WindowsHiDPI.rc
@@ -0,0 +1 @@
+1 RT_MANIFEST "WindowsHiDPI.manifest"