Greetings!
We're currently implementing RandR 1.5 in Qt, and in that work popped up the
age old question of DPI, so I thought I'd check with the horse's mouth, so to
speak :)
I've read the very informative post by Alex on why EDID is not trustworthy [1],
and other related materials, but I may have misunderstood things, so please
correct me if I'm wrong!
Now, the question from Qt's side is what we should report from QScreen's
physicalDpi() and logicalDpi() on X [2]. The former being the actual DPI of the
glass/panel, and the latter a "virtual DPI", affected by OS decisions such as
multiplying the physical DPI with 1.3 to account for viewing distance, rounding
it to common fixed values, or based on user preference.
For reference, macOS has a fixed logical DPI of 72, whereas Windows allows the
user to set the logical DPI in various (possibly fractional) increments of 96
DPI.
(Both OSes allow us to also get the physical DPI (however inaccurate it is from
EDID), which we report as physicalDpi(), e.g. for users who want to draw
graphics they can put a ruler up to their screen and compare against.)
Now, in the past this logical DPI was mostly relevant for how to draw text, but
nowadays this is also how platforms like Windows deals with high-DPI panels.
Setting a scale of 200% in the Windows display preferences results in Windows
reporting 192DPI to us, which we in turn report to the user as a device
independent coordinate system with a 96DPI and 2.0 device pixel ratio,
resulting in scaled text, and UI.
Now, for X, there's at least four different things to consider, as far as I can
tell:
1) The resolution and size of the X Screen
2) The resolution and size of the individual outputs
3) The resolution and size of the RandR 1.5 monitors
4) The Xft.DPI setting.
(For all the things exposed through RandR (1-3), as far as I can tell they are
all stored as resolution and size (in mm), so all DPI-numbers going in or out
of X are effectively converted to a width and height in mm to represent that
DPI with the current resolution taken into account.)
The last one is the easy one, it's clearly a logical DPI, and we reflect that
in Qt if set. Unfortunately it's a global DPI.
Now, I'm guessing that #1, as set by Xorg -dpi, xorg.conf DisplaySize, or
xrandr --dpi, originally was meant as a physical DPI override, for cases where
the detection and heuristics in X would fail? But nowadays, especially with a
single X Screen representing multiple physical displays, with potentially
different physical DPIs, it feels like it's effectively a logical DPI setting
on an X level, with the same limitation as Xft.DPI in that it's a global
setting. What is your take on this?
If it's the former — a physical DPI override (however little that makes sense
when reflecting multiple displays) — we don't want to reflect it per QScreen,
as that would not be specific enough in a multi monitor setup. Nor do we want
to reflect it for a QScreen's logicalDpi, if it's strictly defined as a
physical property, not to be used for adjusting logical DPI.
But if it's in practice the latter — a logical DPI override — then we should
reflect it through a QScreen's logicalDpi, if Xft.DPI hasn't been set to
override it.
Now, for #2, as far as I can tell there isn't any option in xrandr to override
this, nor does tweaking DisplaySize in xorg.conf affect it (even for multiple
Monitor sections), so I'm guessing it's strictly a physical size picked up from
EDID? If that's not the case, and it's possible to override it for the user,
then the same questions as for #1 apply: Does that make it a logical DPI?
Finally, for #3, this is where it gets interesting. From reading the RandR spec
[3] about the new Monitors introduced in 1.5, this seems like a defined logical
DPI:
"This new object separates the physical configuration of the hardware
from the logical subsets of the screen that applications should
consider as single viewable areas."
It's possible to combine two outputs into one monitor, to split a single output
into multiple monitors, or even to override the auto-generated monitor for a an
output. And all these allow you to pass a width and height, effectively setting
the DPI. E.g.
xrandr --setmonitor DUMMY0-DPIOVERRIDE 1600/200x1200/200+0+0 DUMMY0
This seems like the definition of logical DPI, where the desktop environment
can give the user a nice control panel on how to adjust these things, either
directly by adding/removing/moving monitors, or by setting a DPI or scale (200%
e.g.) on an individual monitor, and then reflect that as RandR updates.
Based on all of this, it seems Qt should do the following:
1. If Xft.DPI has been set, respect that as a global override, and reflect
that as the logical DPI for all QScreens
2. If not, reflect the resolution and size of individual RandR 1.5 monitors
as logical DPI per QScreen
3. If 1.5 is not available, reflect the resolution and size of the X Screen
as a global logical DPI for all QScreens
4. Reflect the resolution and size of the individual outputs as physical DPI,
or read EDID ourselves
As far as I can tell this should cover DEs like Ubuntu 20.04 that sets a global
192 Xft.DPI to represent 200% scaling (and fractional scales in between 100%
and 200%), as well as DEs that (in the future) allow per-monitor DPI/scale
control via the 1.5 monitors.
Thanks for reading this far! Let me know if I missed something, and what your
thoughts are :)
Cheers,
Tor Arne
[1] https://lists.fedoraproject.org/pipermail/devel/2011-October/157671.html
[2] https://doc.qt.io/qt-5/qscreen.html#logicalDotsPerInch-prop
[3]
https://gitlab.freedesktop.org/xorg/proto/xorgproto/-/raw/master/randrproto.txt
_______________________________________________
[email protected]: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: https://lists.x.org/mailman/listinfo/xorg-devel