Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

High DPI scaling on Mac OS and glfw #5081

Open
jokteur opened this issue Mar 6, 2022 · 6 comments
Open

High DPI scaling on Mac OS and glfw #5081

jokteur opened this issue Mar 6, 2022 · 6 comments

Comments

@jokteur
Copy link

jokteur commented Mar 6, 2022

Version/Branch of Dear ImGui:

Version: 1.87, 1.88 WIP
Branch: docking & viewport

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp
Compiler: clang
Operating System: Mac OS Monterey, Mac Mini M1

My Issue/Question:

Hello,

I know the questions about high DPI scaling have been asked a lot around here, however I do not think that I found a solution in the github issues.

My goal is to have an application that correctly DPI scaling. So I am following the instructions shown here. In the main loop, I checking if the scaling of the main window changed and I rebuild the fonts accordingly:

    [...] // In main.cpp of example_glfw_opengl3
    float prev_scale = 0.f;
    while (!glfwWindowShouldClose(window))
    {
        [...] // Event processing

        float xscale, yscale;
        glfwGetWindowContentScale(window, &xscale, &yscale);
        if (xscale != prev_scale) {
            prev_scale = xscale;
            io.Fonts->Clear();

            io.Fonts->AddFontFromFileTTF("Roboto-Regular.ttf", xscale * 16.0f);

            io.Fonts->Build();
            ImGui_ImplOpenGL3_DestroyFontsTexture();
            ImGui_ImplOpenGL3_CreateFontsTexture();

            // ImGui::GetStyle().ScaleAllSizes(xscale);
        }

        // Start the Dear ImGui frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplGlfw_NewFrame();

       [...] // Rest of the code of main.cpp of example_glfw_opengl3
    }

So, this solution works fine on windows, and my fonts are not blurry and are correctly scaled.

However, on Mac OS the window of glfw is by default already scaled, so if I scale the fonts, then I get this:
Capture d’écran 2022-03-06 à 16 54 05

One quick solution would be to not scale the font at all, but then I obtain text that is at the correct scale, but blurry:
Capture d’écran 2022-03-06 à 17 20 51

My first initial guess was to look at the glfw window hints, like GLFW_SCALE_TO_MONITOR and GLFW_COCOA_RETINA_FRAMEBUFFER. The first hint does not seem to change anything, and the second hint does not help either, because it is GLFW_TRUE by default.

With GLFW_COCOA_RETINA_FRAMEBUFFER set to GLFW_TRUE, the framebuffer size of the main window

int display_w, display_h;
glfwGetFramebufferSize(window, &display_w, &display_h);

returns the actual number of pixels the window takes, not virtual pixels. This means on my 4K screen, if I fullscreen the main window, display_w will have 3860 pixels.

Searching further, I found that #3757 exposes a similar problem to mine, but with the SDL. The author of the issue provided a temporary fix. I tried to implement the fix in imgui_impl_glfw.cpp like this:

void ImGui_ImplGlfw_NewFrame()
{
    ImGuiIO& io = ImGui::GetIO();
    [...]
    if (bd->WantUpdateMonitors)
        ImGui_ImplGlfw_UpdateMonitors();

    // Fix
#if defined(__APPLE__)
    // On Apple, The window size is reported in Low DPI, even when running in high DPI mode
    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
    if (!platform_io.Monitors.empty() && platform_io.Monitors[0].DpiScale > 1.0f && display_h != h)
    {
        io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
        io.DisplaySize = ImVec2((float)display_w, (float)display_h);
    }
#endif
    [...] // rest of the function
}

This does, along with the scaled font, produces the result I want:
Capture d’écran 2022-03-06 à 17 27 27

However, the mouse interaction does not work, because ImGui thinks that the mouse coordinates are in virtual pixels (I use glfw terms), but the UI is rendered in true pixels. I looked at

void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
{
    ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
    if (bd->PrevUserCallbackCursorPos != NULL && window == bd->Window)
        bd->PrevUserCallbackCursorPos(window, x, y);

    ImGuiIO& io = ImGui::GetIO();
    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
    {
        int window_x, window_y;
        glfwGetWindowPos(window, &window_x, &window_y);
        x += window_x;
        y += window_y;
    }
    io.AddMousePosEvent((float)x, (float)y);
    bd->LastValidMousePos = ImVec2((float)x, (float)y);
}

but I failed to provide a valid fix, because glfw provides virtual pixels positions (for both the callback and glfwGetWindowPos). This means if I have a 3680x2160px screen and a scaling of 2, with a mouse position of 400x200 the callback will return 200x100.

Multiplying the mouse position by the scale like in #3757 does not work, and this is because glfw always provides virtual pixel coordinates.

Do you know how I could resolve this ? I found a way to have the mouse coordinates match the window like this (with the viewport flags):

int window_x, window_y;
glfwGetWindowPos(window, &window_x, &window_y);
x += window_x;
y += window_y;

float xscale, yscale;
glfwGetWindowContentScale(window, &xscale, &yscale);
x *= xscale;
y *= yscale;

x -= window_x;
y -= window_y;

but this is obviously wrong because even if I can interact correctly with the widgets inside the main window, it means that I can also interact with widget when the mouse is outside the window...

Thank you in advance

@DickyQi
Copy link

DickyQi commented Apr 2, 2022

My solution is change draw_data->FramebufferScale at function SetupViewportDrawData
original code is: draw_data->FramebufferScale = io.DisplayFramebufferScale;
modified code as: draw_data->FramebufferScale = ImVec2(floor(viewport->DpiScale), floor(viewport->DpiScale));
This is only workround, and I test sdl/glfw at Mac and Windows.

@wolfpld
Copy link
Contributor

wolfpld commented Apr 20, 2022

@rokups
Copy link
Contributor

rokups commented May 10, 2022

#5301 also a relevant issue with a workaround.

@TheBrokenRail
Copy link

I experienced this issue with GLFW and Wayland. I worked-around it with this patch: https://gist.github.com/TheBrokenRail/9ed21b810a4f33a5b1bb062024573128

It:

  1. Sets DisplaySize to glfwGetFramebufferSize instead of glfwGetWindowSize
  2. Leaves DisplayFramebufferScale at the default of [1.0f, 1.0f]
  3. Converts mouse-coordinates from window-pixels to framebuffer-pixels (see ImGui_ImplGlfw_ScaleMousePos)
  4. Updates the GLFW+OpenGL2 example to scale the font size by glfwGetWindowContentScale
    1. In a proper fix, this should be checked every frame in-case the DPI changes. My patch only does it on startup.
    2. It should also call ImGui::GetStyle().ScaleAllSizes

This works on Wayland, where just like macOS, "screen coordinates can be scaled relative to pixel coordinates" (to quote the GLFW documentation). It also works on X11, where just like Windows, "screen coordinates and pixels always map 1:1" (also quoting GLFW).

Screenshot from 2024-11-19 23-50-20

@wolfpld
Copy link
Contributor

wolfpld commented Nov 20, 2024

Sets DisplaySize to glfwGetFramebufferSize instead of glfwGetWindowSize
Converts mouse-coordinates from window-pixels to framebuffer-pixels (see ImGui_ImplGlfw_ScaleMousePos)

That sounds about right, and is more or less what I did in a (non-imgui) work project to get hidpi on macos.

@ocornut
Copy link
Owner

ocornut commented Nov 20, 2024

I am hoping to tackle this on Q1 2025, but yes that's basically it above.
For "(3) Convert Mouse Coordinates" i am hoping to add general input transform support, perhaps in the backend perhaps in core imgui. There are other valid reasons to do that transform (e.g. try to keep all imgui coordinates positive).

The more tricky aspect is to understand how coordinates and transform would work in multi-viewport multi-monitor mode. If you have access to a Mac setup where one monitor is hi-dpi and the other isn't, it would be the ideal test-bed and I'd be happy if you can report on this. Not having investigated this is the primary blocker for me.

EDIT The transform would also generally apply when interacting with the windowing system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants