DPI configuration on X11 has recently seen several changes (and reverts) in Qt 5. There are competing proposals for how it should work.
For Qt 6 (and maybe Qt 5.15.x) we'd like to find a way to make DPI configuration work for as many X11 users as possible, and then document what the Qt approach is.
- The XCB platform plugin should provide a DPI value for each screen
- Qt applications should behave like other applications on X11, and not require special settings.
- Configuration on single-screen systems using the screen settings dialogs on KDE and GNOME must (continue to) work
- Using logical DPI is preferred (over physical DPI), since this allows user adjustments for UI size preferences and viewing distance.
X11 provides several APIs/properties for getting screen DPI and physical size:
- Global logical DPI value.
- API: xcb_xrm_resource_get_string(…,”Xft.dpi" … )
- Changing the screen scale settings in GNOME will adjust this value.
- Global pixel size and physical size (for the virtual desktop)
- API: xcb_screen_t
- Does not provide dpi or physical size information
The reported physical sizes are sometimes inaccurate (examples):
0 mm * 0 mm, (no value)
16 mm * 9 mm, (the aspect ratio)
X mm * Y mm (back-calculated values which gives a DPI of 96)
Qt platform plugins implement the following functions to provide the per-screen logical DPI to QtGui
- QScreen::logicalBaseDpi() [96 for X11]
(For Qt <5.14, the platform plugin could report a scale factor as well. QtGui now computes the scale factor from the DPI values when AA_HighDpiScaling is enabled).
The platform plugin can compute a DPI value based on pixel size and pysical size, in cases where (per-screen) logical DPI is not available. The result is physical DPI, which might not be exactly what the user wants.
Current Qt behavior (5.15) - priority
- Use global Xft.dpi value, if available
- Calculate global DPI using the reported physical size given by the xcb_screen_t struct.
Suggested Changes (updated)
1 Make QXcbScreen::logicalDpi() return logical DPI only (by reading Xft.dpi); fall back to 96 if no logical DPI value is available. Don’t calculate logical DPI based on physical size.
This is effectively already happening in many cases, since X servers fake the virtual desktop physical size such that the calculated DPI ends up at 96.
We should restrict this further to avoid surprises for the cases where the X server returns the actual physical size. As a first step, restrict the DPI to 96+: https://codereview.qt-project.org/c/qt/qtbase/+/303600
2 Document an option for making high-dpi scaling use physical DPI. We have the QT_USE_PHYSICAL_DPI environment variable already (needs a bugfix: https://codereview.qt-project.org/c/qt/qtbase/+/302865)
QXcbScreen::physicalSize() already returns per-screen values from randr; no changes are needed here.
The xrandr scaling option for configuring mixed-dpi displays
This uses xrandr to scale the low-dpi outputs, which it possible to set a global DPI. This makes all applications happy.
- Run ./xrandr , make a note of the output names and pixel sizes:
Virtual1 connected primary 2880x1800+0+0
Virtual2 connected 1920x1200+2880+450
- Scale each low-dpi output; using a factor corresponding to the difference in display DPI
xrandr --output Virtual2 --scale 2x2
- Set a global Xft.dpi value using e.g. KDE or GNOME screen settings