Skip to content

Commit

Permalink
Platform: initial HiDPI support in GlfwApplication.
Browse files Browse the repository at this point in the history
Not basing this off GLFW 3.3 as it's far from being released yet, just a
copy of what's done for SDL2 already.
  • Loading branch information
mosra committed Aug 30, 2018
1 parent df08500 commit dba35ba
Show file tree
Hide file tree
Showing 7 changed files with 353 additions and 24 deletions.
2 changes: 1 addition & 1 deletion doc/changelog.dox
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ See also:
@subsubsection changelog-latest-new-platform Platform libraries

- Initial HiDPI support for Linux and Emscripten in
@ref Platform::Sdl2Application
@ref Platform::Sdl2Application and @ref Platform::GlfwApplication
- Implemented missing resize event support in @ref Platform::Sdl2Application
on @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten"
- Ability to modify CSS classes that control layout of
Expand Down
11 changes: 11 additions & 0 deletions modules/FindMagnum.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,17 @@ foreach(_component ${Magnum_FIND_COMPONENTS})
find_package(GLFW)
set_property(TARGET Magnum::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES GLFW::GLFW)
# Use the Foundation framework on Apple to query the DPI awareness
if(CORRADE_TARGET_APPLE)
find_library(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY Foundation)
mark_as_advanced(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY)
set_property(TARGET Magnum::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ${_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY})
# Needed for opt-in DPI queries
elseif(CORRADE_TARGET_UNIX)
set_property(TARGET Magnum::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS})
endif()

# With GLVND (since CMake 3.11) we need to explicitly link to
# GLX/EGL because libOpenGL doesn't provide it. For EGL we have
Expand Down
23 changes: 23 additions & 0 deletions src/Magnum/Platform/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ if(WITH_GLFWAPPLICATION)
if(TARGET_GL)
list(APPEND MagnumGlfwApplication_SRCS ${MagnumSomeContext_OBJECTS})
endif()
if(CORRADE_TARGET_APPLE)
list(APPEND MagnumGlfwApplication_SRCS Implementation/dpiScaling.mm)
endif()

add_library(MagnumGlfwApplication STATIC
${MagnumGlfwApplication_SRCS}
Expand All @@ -142,6 +145,26 @@ if(WITH_GLFWAPPLICATION)
${MagnumSomeContext_LIBRARY})
endif()

# Use the Foundation framework on Apple to query the DPI awareness
if(CORRADE_TARGET_APPLE)
find_library(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY Foundation)
mark_as_advanced(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY)
find_path(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_INCLUDE_DIR NAMES NSBundle.h)
mark_as_advanced(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_INCLUDE_DIR)
target_link_libraries(MagnumGlfwApplication PUBLIC ${_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY})
target_include_directories(MagnumGlfwApplication PRIVATE ${_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_INCLUDE_DIR})

# If there is X11, ask it for DPI
elseif(CORRADE_TARGET_UNIX)
find_package(X11)
if(X11_FOUND)
# Not linking to X11, we dlopen() instead
target_include_directories(MagnumGlfwApplication PRIVATE ${X11_X11_INCLUDE_PATH})
target_link_libraries(MagnumGlfwApplication PUBLIC ${CMAKE_DL_LIBS})
target_compile_definitions(MagnumGlfwApplication PRIVATE "_MAGNUM_PLATFORM_USE_X11")
endif()
endif()

install(FILES ${MagnumGlfwApplication_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Platform)
install(TARGETS MagnumGlfwApplication
RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}
Expand Down
127 changes: 120 additions & 7 deletions src/Magnum/Platform/GlfwApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <Corrade/Utility/Unicode.h>

#include "Magnum/Platform/ScreenedApplication.hpp"
#include "Magnum/Platform/Implementation/dpiScaling.hpp"

#ifdef MAGNUM_TARGET_GL
#include "Magnum/GL/Version.h"
Expand Down Expand Up @@ -59,10 +60,14 @@ GlfwApplication::GlfwApplication(const Arguments& arguments, const Configuration

GlfwApplication::GlfwApplication(const Arguments& arguments, NoCreateT):
_flags{Flag::Redraw}
{
Utility::Arguments args{Implementation::windowScalingArguments()};
#ifdef MAGNUM_TARGET_GL
, _context{new GLContext{NoCreate, arguments.argc, arguments.argv}}
_context.reset(new GLContext{NoCreate, args, arguments.argc, arguments.argv});
#else
args.parse(arguments.argc, arguments.argv);
#endif
{

/* Init GLFW */
glfwSetErrorCallback([](int, const char* const description) {
Error{} << description;
Expand All @@ -73,9 +78,24 @@ GlfwApplication::GlfwApplication(const Arguments& arguments, NoCreateT):
std::exit(8);
}

#ifndef MAGNUM_TARGET_GL
static_cast<void>(arguments);
/* Save command-line arguments */
if(args.value("log") == "verbose") _verboseLog = true;
const std::string dpiScaling = args.value("dpi-scaling");
if(dpiScaling == "default")
_commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Default;
#ifdef CORRADE_TARGET_APPLE
else if(dpiScaling == "framebuffer")
_commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Framebuffer;
#else
else if(dpiScaling == "virtual")
_commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Virtual;
else if(dpiScaling == "physical")
_commandLineDpiScalingPolicy = Implementation::GlfwDpiScalingPolicy::Physical;
#endif
else if(dpiScaling.find_first_of(" \t\n") != std::string::npos)
_commandLineDpiScaling = args.value<Vector2>("dpi-scaling");
else
_commandLineDpiScaling = Vector2{args.value<Float>("dpi-scaling")};
}

void GlfwApplication::create() {
Expand All @@ -92,6 +112,82 @@ void GlfwApplication::create(const Configuration& configuration, const GLConfigu
}
#endif

Vector2 GlfwApplication::dpiScaling(const Configuration& configuration) const {
std::ostream* verbose = _verboseLog ? Debug::output() : nullptr;

/* Print a helpful warning in case some extra steps are needed for HiDPI
support */
#ifdef CORRADE_TARGET_APPLE
if(!Implementation::isAppleBundleHiDpiEnabled())
Warning{} << "Platform::GlfwApplication: warning: the executable is not a HiDPI-enabled app bundle";
#elif defined(CORRADE_TARGET_WINDOWS)
/** @todo */
#endif

/* Use values from the configuration only if not overriden on command line.
In any case explicit scaling has a precedence before the policy. */
Implementation::GlfwDpiScalingPolicy dpiScalingPolicy{};
if(!_commandLineDpiScaling.isZero()) {
Debug{verbose} << "Platform::GlfwApplication: user-defined DPI scaling" << _commandLineDpiScaling.x();
return _commandLineDpiScaling;
} else if(UnsignedByte(_commandLineDpiScalingPolicy)) {
dpiScalingPolicy = _commandLineDpiScalingPolicy;
} else if(!configuration.dpiScaling().isZero()) {
Debug{verbose} << "Platform::GlfwApplication: app-defined DPI scaling" << _commandLineDpiScaling.x();
return configuration.dpiScaling();
} else {
dpiScalingPolicy = configuration.dpiScalingPolicy();
}

/* There's no choice on Apple, it's all controlled by the plist file. So
unless someone specified custom scaling via config or command-line
above, return the default. */
#ifdef CORRADE_TARGET_APPLE
return Vector2{1.0f};

/* Otherwise there's a choice between virtual and physical DPI scaling */
#else
/* Try to get virtual DPI scaling first, if supported and requested */
if(dpiScalingPolicy == Implementation::GlfwDpiScalingPolicy::Virtual) {
/* Use Xft.dpi on X11 */
#ifdef _MAGNUM_PLATFORM_USE_X11
const Vector2 dpiScaling{Implementation::x11DpiScaling()};
if(!dpiScaling.isZero()) {
Debug{verbose} << "Platform::GlfwApplication: virtual DPI scaling" << dpiScaling.x();
return dpiScaling;
}

/* Otherwise ¯\_(ツ)_/¯ */
#else
Debug{verbose} << "Platform::GlfwApplication: sorry, virtual DPI scaling not implemented on this platform yet";
return Vector2{1.0f};
#endif
}

/* At this point, either the virtual DPI query failed or a physical DPI
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
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: physical DPI scaling" << dpiScaling;
return dpiScaling;

/* Not implemented otherwise */
#else
Debug{verbose} << "Platform::GlfwApplication: sorry, physical DPI scaling not implemented on this platform yet";
return Vector2{1.0f};
#endif
#endif
}

bool GlfwApplication::tryCreate(const Configuration& configuration) {
#ifdef MAGNUM_TARGET_GL
#ifdef GLFW_NO_API
Expand All @@ -104,6 +200,10 @@ bool GlfwApplication::tryCreate(const Configuration& configuration) {

CORRADE_ASSERT(!_window, "Platform::GlfwApplication::tryCreate(): window already created", false);

/* Scale window based on DPI */
_dpiScaling = dpiScaling(configuration);
const Vector2i scaledWindowSize = configuration.size()*_dpiScaling;

/* Window flags */
GLFWmonitor* monitor = nullptr; /* Needed for setting fullscreen */
if (configuration.windowFlags() >= Configuration::WindowFlag::Fullscreen) {
Expand All @@ -126,7 +226,7 @@ bool GlfwApplication::tryCreate(const Configuration& configuration) {
#endif

/* Create the window */
_window = glfwCreateWindow(configuration.size().x(), configuration.size().y(), configuration.title().c_str(), monitor, nullptr);
_window = glfwCreateWindow(scaledWindowSize.x(), scaledWindowSize.y(), configuration.title().c_str(), monitor, nullptr);
if(!_window) {
Error() << "Platform::GlfwApplication::tryCreate(): cannot create window";
glfwTerminate();
Expand Down Expand Up @@ -194,6 +294,10 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf

CORRADE_ASSERT(!_window && _context->version() == GL::Version::None, "Platform::GlfwApplication::tryCreate(): window with OpenGL context already created", false);

/* Scale window based on DPI */
_dpiScaling = dpiScaling(configuration);
const Vector2i scaledWindowSize = configuration.size()*_dpiScaling;

/* Window flags */
GLFWmonitor* monitor = nullptr; /* Needed for setting fullscreen */
if (configuration.windowFlags() >= Configuration::WindowFlag::Fullscreen) {
Expand Down Expand Up @@ -271,7 +375,7 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf
blinking in case we have to destroy it again right away. If the creation
succeeds, make the context current so we can query GL_VENDOR below. */
glfwWindowHint(GLFW_VISIBLE, false);
if((_window = glfwCreateWindow(configuration.size().x(), configuration.size().y(), configuration.title().c_str(), monitor, nullptr)))
if((_window = glfwCreateWindow(scaledWindowSize.x(), scaledWindowSize.y(), configuration.title().c_str(), monitor, nullptr)))
glfwMakeContextCurrent(_window);

#ifndef MAGNUM_TARGET_GLES
Expand Down Expand Up @@ -312,7 +416,7 @@ bool GlfwApplication::tryCreate(const Configuration& configuration, const GLConf
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_ANY_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, false);

_window = glfwCreateWindow(configuration.size().x(), configuration.size().y(), configuration.title().c_str(), monitor, nullptr);
_window = glfwCreateWindow(scaledWindowSize.x(), scaledWindowSize.y(), configuration.title().c_str(), monitor, nullptr);
}
#endif

Expand Down Expand Up @@ -433,6 +537,14 @@ Vector2i GlfwApplication::windowSize() const {
return size;
}

Vector2i GlfwApplication::framebufferSize() const {
CORRADE_ASSERT(_window, "Platform::GlfwApplication::framebufferSize(): no window opened", {});

Vector2i size;
glfwGetFramebufferSize(_window, &size.x(), &size.y());
return size;
}

void GlfwApplication::setSwapInterval(const Int interval) {
glfwSwapInterval(interval);
}
Expand Down Expand Up @@ -518,6 +630,7 @@ GlfwApplication::Configuration::Configuration():
_title{"Magnum GLFW Application"},
_size{800, 600},
_windowFlags{WindowFlag::Focused},
_dpiScalingPolicy{DpiScalingPolicy::Default},
_cursorMode{CursorMode::Normal}
#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL)
, _sampleCount{0}, _version{GL::Version::None}
Expand Down
Loading

0 comments on commit dba35ba

Please sign in to comment.