Wayland and fractional screen scaling reported as integer by wayland-info and (Q)Screen
-
Hello all,
I'm running an up-to-date Arch system (Qt 6.8.1) with Plasma 6.2.4 with Wayland and everything works just great. I have my screen scale factor set to 2 and that also is not causing any problems. However, I received a bug report for an Qt/QML application I'm developing that was related to fractional scaling, and digging a little deeper I noticed the following issue:
When the scale factor in the Plasma display settings is set to anything 101% to 200%, then
wayland-info
simply reports an integer scale factor of2
and I get that very same value from Qt'sQScreen::devicePixelRatio()
(and similarly QML'sScreen.devicePixelRatio
). This means that when the scale factor is set to, for instance, 150%, the wrong value is reported by bothwayland-info
and(Q)Screen
. Note that this does in no way affect the display of the application, everything works just fine there. However, in my application I need to react to the scale factor inside of my application and thus this is causing some issues there.I found that GwenView is able to get the right value , and since it is based on Qt I dug into its source code a little to find out how they do it. Turns out, they use
QGraphicsScene::devicePixelRatio()
which I don't have with QML, however, that method does indeed return the correct scaling factor of 1.5. Thus, Qt knows about the right value but it seems to not report the right one through(Q)Screen
for some reason.I recall there being quite a bit of talk around wayland and (fractional) scaling of screens in the past, however, it seemed to pretty much work as expected by now. Except for this I guess. Did anybody run into this before? Any suggestions as to how to solve this? Is this a Qt bug (not sure about that since
wayland-info
also reports the wrong factor)?Thanks,
aran
-
When using a single screen only I am now parsing the output of
wayland-info -i output
as a workaround:QProcess proc; proc.start("wayland-info", {"-i", "output"}); proc.waitForFinished(1000); const QString out = proc.readAll(); const int ret = proc.exitCode(); if(ret == 0) { int logicalW = 0, logicalH = 0; int physicalW = 0, physicalH = 0; const QStringList parts = out.split("\n"); for(const QString &line : parts) { if(line.contains("logical_width:") && line.contains("logical_height:")) { logicalW = line.split("logical_width: ")[1].split(",")[0].toInt(); logicalH = line.split("logical_height: ")[1].split("\n")[0].toInt(); } if(!line.contains("logical_width:") && !line.contains("logical_height:") && line.contains("width:") && line.contains("height:")) { physicalW = line.split("width: ")[1].split(" ")[0].toInt(); physicalH = line.split("height: ")[1].split(" ")[0].toInt(); } } if(physicalW > 0 && physicalH > 0 && logicalW > 0 && logicalH > 0) { const double fac1 = static_cast<double>(physicalW)/static_cast<double>(logicalW); const double fac2 = static_cast<double>(physicalH)/static_cast<double>(logicalH); if(fabs(fac1-fac2) < 1e-6 && fac1 > 0.5 && fac1 < 5) return fac1; } }
This seems to work quite well based on the limited testing I was able to do, but of course I would prefer a proper solution if anybody has one.