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

Implement DisplayServer.screen_get_scale() on Windows, Linux and Android #2661

Open
Calinou opened this issue Apr 27, 2021 · 8 comments
Open

Comments

@Calinou
Copy link
Member

Calinou commented Apr 27, 2021

Describe the project you are working on

The Godot editor 🙂

Describe the problem or limitation you are having in your project

The editor's automatic scaling factor isn't very accurate since it can't rely on the OS-defined scaling factor. Instead, it has to do guesswork based on the screen resolution and reported DPI.
There is a screen_get_scale() method in DisplayServer that can be used for this purpose, but it's currently only implemented for macOS, iOS, and HTML5.

See godotengine/godot#48226.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Implement DisplayServer.screen_get_scale(int p_screen) on Windows, Linux and Android. This method returns a floating-point value corresponding to the screen's scaling factor (1.0 being 100% scale).

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

  • On Windows, this will require using the Windows API. Note that, like on macOS, different screens can have different scaling factors. On Windows 10, scaling factors can be set anywhere from 100% to 350% with increments of 25%.
  • On Linux, this will likely require tapping into the desktop environment somehow. I don't know if the scaling factor configured in GNOME or KDE's settings is reported to X11. KDE allows fractional scaling even on X11, so this should be taken into account ideally. Help is welcome in that area 🙂
  • On Android, this will require using Java code to get the scale factor. The named scaling factors such as xxhdpi correspond to floating-point values – you can see a list here.

If this enhancement will not be used often, can it be worked around with a few lines of script?

No, as this is native OS-specific code and is required to ensure the editor scales correctly on all platforms out of the box.

Is there a reason why this should be core and not an add-on in the asset library?

This is core functionality for the editor.

Keywords for easier searching: hiDPI

@lyuma
Copy link

lyuma commented Apr 27, 2021

Implementing this screen scale proposal would drastically improve editor usability on my system.

Here is a comparison screenshot of default settings on the 4.x master branch to a webpage with my ideal default text size:
image

@alvinhochun
Copy link

Implementing this for Windows should be as simple as this:

diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index ca2b68371c..57ba0ef0b2 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -445,6 +445,14 @@ int DisplayServerWindows::screen_get_dpi(int p_screen) const {
 	return data.dpi;
 }
 
+float DisplayServerWindows::screen_get_scale(int p_screen) const {
+	_THREAD_SAFE_METHOD_
+
+	EnumDpiData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, 96 };
+	EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);
+	return data.dpi / 96.f;
+}
+
 bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const {
 #ifndef _MSC_VER
 #warning touchscreen not working

... but let me think this over.

@Calinou
Copy link
Member Author

Calinou commented Jan 4, 2022

Implementing this for Windows should be as simple as this:

Does this return the actual scale factor, or just the screen DPI divided by 96? The user can customize the scale factor independently of the monitor DPI. For instance, I have a 14" 4K laptop which defaults to 300% scaling, but I reduced it to 200% to get the same real estate as if it was a 1080p display.

@alvinhochun
Copy link

Does this return the actual scale factor, or just the screen DPI divided by 96?

When it comes to Windows, the monitor scale factor sets the logical screen DPI. The base DPI at 100% is 96, for the rest it's just multiplied (125% -> 120, 150% -> 144, 200% -> 192, etc.). The callback _MonitorEnumProcDpi using QueryDpiForMonitor calls GetDpiForMonitor with MDT_DEFAULT which is MDT_EFFECTIVE_DPI, which returns exactly the logical DPI we need to calculate the screen scaling. (If the expectation of screen_get_dpi is to return the physical screen DPI as opposed to the logical DPI, then its implementation is wrong.)

For GetDpiForMonitor to actually give the logical DPI of non-primary monitors, the process needs to be per-monitor DPI aware (can be v1 or v2), otherwise it always returns the "system DPI", aka the logical DPI of the primary monitor.

(Trivia: Even when using the advanced "custom scaling" setting, or when setting the scaling on a version before Windows 8.1, the DPI is calculated and stored as an integer. This has the caveat that some scale factors cannot be stored exactly. For example, if you set the scaling to 110%, the DPI is rounded to 106 and effectively you have a scaling of 1.1041666... times.)

@TechnicalSoup
Copy link

TechnicalSoup commented Feb 8, 2022

Taking a look over the Windows API docs the GetScaleFactorForMonitor call as described here might give us what we're looking for.
As mentioned, dragging a window between monitors with a different scaling setting could make things interesting, but this call appears to give us the scaling factor from the Windows settings rather than the DPI capabilities of the monitor.

I'll do a bit of sandbox testing in the coming days to see if this actually returns something useful.

@alvinhochun
Copy link

This post says that GetScaleFactorForMonitor would return the wrong value.

@lostminds
Copy link

As mentioned, dragging a window between monitors with a different scaling setting could make things interesting, but this call appears to give us the scaling factor from the Windows settings rather than the DPI capabilities of the monitor.

There already seems to be some sort of detection of this implemented for the Window.dpi_changed() signal, even if that only seems to be implemented for macOS currently as well. I very much agree a DisplayServer.screen_get_scale() would be nice on all platforms. And consequently then perhaps a Window.screen_scale_changed() could be implemented to help deal with these situations.

@MaasTL
Copy link

MaasTL commented Jan 10, 2024

I need this feature for my Godot project.
I am running it on my Desktop and on my Tablet computer.
It would be very helpful to autodetect the systems scaling factor on both,
so I don't have to scale the project manualy, depending on the computer I'm on.

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