Skip to content

Commit

Permalink
Hack in basic HighDPI support
Browse files Browse the repository at this point in the history
  • Loading branch information
adamkewley committed Nov 29, 2024
1 parent 4f1b29e commit 2c8c1f4
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 51 deletions.
49 changes: 42 additions & 7 deletions src/oscar/Platform/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,11 @@ class osc::App::Impl final {
return rv;
}

Vec2 main_window_dimensions() const
{
return main_window_pixel_dimensions() / main_window_device_pixel_ratio();
}

Vec2 main_window_pixel_dimensions() const
{
int w = 0;
Expand All @@ -659,11 +664,24 @@ class osc::App::Impl final {
return Vec2{static_cast<float>(w), static_cast<float>(h)};
}

float main_window_display_scale() const
float main_window_device_pixel_ratio() const
{
return SDL_GetWindowDisplayScale(main_window_.get());
}

float os_to_main_window_device_independent_ratio() const
{
// i.e. scale the event by multiplying it by the pixel density (yielding a
// pixel-based event value) and then dividing it by the suggested window
// display scale (yielding a device-independent pixel value).
return SDL_GetWindowPixelDensity(main_window_.get()) / SDL_GetWindowDisplayScale(main_window_.get());
}

float main_window_device_independent_to_os_ratio() const
{
return 1.0f / os_to_main_window_device_independent_ratio();
}

bool is_main_window_minimized() const
{
return (SDL_GetWindowFlags(main_window_.get()) & SDL_WINDOW_MINIMIZED) != 0u;
Expand Down Expand Up @@ -691,11 +709,13 @@ class osc::App::Impl final {

void set_unicode_input_rect(const Rect& rect)
{
const float device_independent_to_sdl3_ratio = main_window_device_independent_to_os_ratio();

const SDL_Rect r{
.x = static_cast<int>(rect.p1.x),
.y = static_cast<int>(rect.p1.y),
.w = static_cast<int>(dimensions_of(rect).x),
.h = static_cast<int>(dimensions_of(rect).y),
.x = static_cast<int>(device_independent_to_sdl3_ratio * rect.p1.x),
.y = static_cast<int>(device_independent_to_sdl3_ratio * rect.p1.y),
.w = static_cast<int>(device_independent_to_sdl3_ratio * dimensions_of(rect).x),
.h = static_cast<int>(device_independent_to_sdl3_ratio * dimensions_of(rect).y),
};
SDL_SetTextInputArea(main_window_.get(), &r, 0);
}
Expand Down Expand Up @@ -1203,14 +1223,29 @@ std::vector<Monitor> osc::App::monitors() const
return impl_->monitors();
}

Vec2 osc::App::main_window_dimensions() const
{
return impl_->main_window_dimensions();
}

Vec2 osc::App::main_window_pixel_dimensions() const
{
return impl_->main_window_pixel_dimensions();
}

float osc::App::main_window_display_scale() const
float osc::App::main_window_device_pixel_ratio() const
{
return impl_->main_window_device_pixel_ratio();
}

float osc::App::os_to_main_window_device_independent_ratio() const
{
return impl_->os_to_main_window_device_independent_ratio();
}

float osc::App::main_window_device_independent_to_os_ratio() const
{
return impl_->main_window_display_scale();
return impl_->main_window_device_independent_to_os_ratio();
}

bool osc::App::is_main_window_minimized() const
Expand Down
42 changes: 26 additions & 16 deletions src/oscar/Platform/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,26 +197,36 @@ namespace osc
// this `App` is connected to.
std::vector<Monitor> monitors() const;

// returns the main window's addressble pixel dimensions
//
// note: you might want to combine this with `main_window_content_scale`, because the
// application may be running in HighDPI mode. In that case, content should be
// scaled up accordingly so that everything doesn't become tiny.
//
// see: https://github.com/libsdl-org/SDL/blob/main/docs/README-highdpi.md
// Returns the dimensions of the main application window in device-independent pixels.
Vec2 main_window_dimensions() const;

// Returns the dimensions of the main application window in physical pixels.
Vec2 main_window_pixel_dimensions() const;

// returns the suggested amplification factor when drawing "native" (e.g. UI, text -
// stuff that should occupy a similar percentage of the screen) elements to the
// window's pixel buffer (as returned by `main_window_pixel_dimensions`).
// Returns the ratio of the resolution in physical pixels to the resolution of
// device-independent pixels.
//
// I.e. if this is == 1.0f, it usually means "not high DPI, probably 96 DPI monitor"
// whereas higher values usually mean "high DPI display" or "retina display" and, in
// those cases, some parts of the UI will need to be scaled up to higher pixel
// imensions (via this factor).
// E.g. a high DPI monitor might return `2.0f`, which means "two physical pixels
// along X and Y map to one device-independent pixel".
//
// see: https://github.com/libsdl-org/SDL/blob/main/docs/README-highdpi.md
float main_window_display_scale() const;
// Related (other libraries):
//
// - https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
// - https://doc.qt.io/qt-6/highdpi.html
// - https://doc.qt.io/qt-6/qwindow.html#devicePixelRatio
// - https://github.com/libsdl-org/SDL/blob/main/docs/README-highdpi.md
float main_window_device_pixel_ratio() const;

// Returns the ratio between the underlying operating system's coordinate system (i.e. SDL3 API,
// events) to the main window's device independent pixels.
//
// Note: this is mostly for internal use: the `osc` APIs should uniformly use either device
// independent pixels (mostly) or physical pixels (e.g. low-level rendering APIs).
float os_to_main_window_device_independent_ratio() const;

// Returns the ratio between the main window's device-independent pixels and the underlying
// operating system's coordinate system.
float main_window_device_independent_to_os_ratio() const;

// returns `true` if the main application window is minimized
bool is_main_window_minimized() const;
Expand Down
6 changes: 6 additions & 0 deletions src/oscar/Platform/Event.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "Event.h"

#include <oscar/Platform/App.h>
#include <oscar/Platform/Log.h>
#include <oscar/Utils/Assertions.h>
#include <oscar/Utils/Conversion.h>
Expand Down Expand Up @@ -250,8 +251,13 @@ osc::MouseEvent::MouseEvent(const SDL_Event& e) :
break;
}
case SDL_EVENT_MOUSE_MOTION: {
// scales from SDL3 (events) to device-independent pixels
const float sdl3_to_device_independent_ratio = App::get().os_to_main_window_device_independent_ratio();

relative_delta_ = {static_cast<float>(e.motion.xrel), static_cast<float>(e.motion.yrel)};
relative_delta_ *= sdl3_to_device_independent_ratio;
position_in_window_ = {static_cast<float>(e.motion.x), static_cast<float>(e.motion.y)};
position_in_window_ *= sdl3_to_device_independent_ratio;
input_source_ = e.motion.which == SDL_TOUCH_MOUSEID ? MouseInputSource::TouchScreen : MouseInputSource::Mouse;
break;
}
Expand Down
7 changes: 7 additions & 0 deletions src/oscar/Platform/Event.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,15 @@ namespace osc

MouseInputSource input_source() const { return input_source_; }
MouseButton button() const { return button_; }

// Returns the relative delta of the mouse motion (i.e. how much the mouse moved
// since the previous `MouseEvent`) in device-independent pixels.
Vec2 relative_delta() const { return relative_delta_; }

// Returns the position of the mouse cursor in a top-left coordinate system in
// virtual device-independent pixels.
Vec2 position_in_window() const { return position_in_window_; }

private:
Vec2 relative_delta_{};
Vec2 position_in_window_{};
Expand Down
5 changes: 4 additions & 1 deletion src/oscar/Platform/Screenshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ namespace osc
{
class Screenshot final {
public:
Screenshot(Texture2D image, std::vector<ScreenshotAnnotation> annotations) :
Screenshot(
Texture2D image,
std::vector<ScreenshotAnnotation> annotations) :

image_{std::move(image)},
annotations_{std::move(annotations)}
{}
Expand Down
66 changes: 39 additions & 27 deletions src/oscar/UI/ui_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ namespace
void load_imgui_config(const std::filesystem::path& user_data_directory, ResourceLoader& loader)
{
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags = ImGuiConfigFlags_DockingEnable;

// make it so that windows can only ever be moved from the title bar
io.ConfigWindowsMoveFromTitleBarOnly = true;
Expand All @@ -244,34 +244,43 @@ namespace
#ifdef EMSCRIPTEN
static_cast<void>(app);
#else
const float scale = app.main_window_display_scale();

ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
io.FontDefault = nullptr;
const float scale = app.main_window_device_pixel_ratio();

ImFontConfig base_config;
base_config.SizePixels = 15.0f * scale;
base_config.PixelSnapH = true;
base_config.FontDataOwnedByAtlas = true;
add_resource_as_font(app.upd_resource_loader(), base_config, *io.Fonts, "oscar/fonts/Ruda-Bold.ttf");
// ensure imgui-to-renderer scaling is correct
io.DisplayFramebufferScale = {scale, scale};

// add FontAwesome icon support
// setup fonts to use correct pixel scale
{
ImFontConfig config = base_config;
config.MergeMode = true;
config.GlyphMinAdvanceX = floor(1.5f * config.SizePixels);
config.GlyphMaxAdvanceX = floor(1.5f * config.SizePixels);
static constexpr auto c_icon_ranges = std::to_array<ImWchar>({ OSC_ICON_MIN, OSC_ICON_MAX, 0 });
add_resource_as_font(app.upd_resource_loader(), config, *io.Fonts, "oscar/fonts/fa-solid-900.ttf", c_icon_ranges.data());
}
io.Fonts->Clear();
io.FontDefault = nullptr;

ImFontConfig base_config;
base_config.SizePixels = 15.0f;
base_config.RasterizerDensity = scale;
base_config.PixelSnapH = true;
base_config.FontDataOwnedByAtlas = true;
add_resource_as_font(app.upd_resource_loader(), base_config, *io.Fonts, "oscar/fonts/Ruda-Bold.ttf");

// add FontAwesome icon support
{
ImFontConfig config = base_config;
config.MergeMode = true;
config.GlyphMinAdvanceX = floor(1.5f * config.SizePixels);
config.GlyphMaxAdvanceX = floor(1.5f * config.SizePixels);
static constexpr auto c_icon_ranges = std::to_array<ImWchar>({ OSC_ICON_MIN, OSC_ICON_MAX, 0 });
add_resource_as_font(app.upd_resource_loader(), config, *io.Fonts, "oscar/fonts/fa-solid-900.ttf", c_icon_ranges.data());
}

io.Fonts->Build();
ui::graphics_backend::mark_fonts_for_reupload();
io.Fonts->Build();
ui::graphics_backend::mark_fonts_for_reupload();
}

ImGui::GetStyle() = ImGuiStyle{};
ui::apply_dark_theme();
ImGui::GetStyle().ScaleAllSizes(scale);
// ensure style is scaled correctly
{
ImGui::GetStyle() = ImGuiStyle{};
ui::apply_dark_theme();
}
#endif
}
}
Expand Down Expand Up @@ -481,13 +490,15 @@ static void ImGui_ImplSDL2_UpdateMouseData()
#endif

if (is_app_focused) {

// (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos) {
const float scale = App::get().main_window_device_independent_to_os_ratio();
if (SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE and (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) {
SDL_WarpMouseGlobal(io.MousePos.x, io.MousePos.y);
SDL_WarpMouseGlobal(scale * io.MousePos.x, scale * io.MousePos.y);
}
else {
SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y);
SDL_WarpMouseInWindow(bd->Window, scale * io.MousePos.x, scale * io.MousePos.y);
}
}

Expand All @@ -505,7 +516,8 @@ static void ImGui_ImplSDL2_UpdateMouseData()
mouse_x -= static_cast<float>(window_x);
mouse_y -= static_cast<float>(window_y);
}
io.AddMousePosEvent(mouse_x, mouse_y);
const float scale = App::get().os_to_main_window_device_independent_ratio();
io.AddMousePosEvent(scale * mouse_x, scale * mouse_y);
}
}

Expand Down Expand Up @@ -555,7 +567,7 @@ static void ImGui_ImplOscar_NewFrame(App& app)
//
// Performed every frame to accomodate for runtime window resizes
{
auto window_dimensions = app.main_window_pixel_dimensions();
auto window_dimensions = app.main_window_dimensions();
if (app.is_main_window_minimized()) {
window_dimensions = {0.0f, 0.0f};
}
Expand Down

0 comments on commit 2c8c1f4

Please sign in to comment.