Skip to content

Instantly share code, notes, and snippets.

@eddy-geek
Last active October 16, 2023 10:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eddy-geek/2a682a415abb5d351537ae3fc865dffd to your computer and use it in GitHub Desktop.
Save eddy-geek/2a682a415abb5d351537ae3fc865dffd to your computer and use it in GitHub Desktop.

KDE Bug 466373 – Qt apps don't use wp-fractional-scale-v1 for hidpi fractional scaling on Wayland

SUMMARY Qt client apps using system Qt 5.15.8 still use the old downscaling mechanism to deal with fractional scaling, instead of wp-fractional-scale-v1, leading to visual artefacts.

STEPS TO REPRODUCE

  1. Set "Scale" to fractional eg 135% in Display Configuration
  2. Open a Qt app

OBSERVED RESULT App is upscaled to 2x then downscaled. This produces aliasing artifacts as detailed previously here 1

EXPECTED RESULT Native scaling... like what is done by XWayland in "Apply scaling themselves" mode. See attached picture. On the left is XWayland (Platfom=xcb) and on the right Wayland.

SOFTWARE/OS VERSIONS KDE Neon based on ubuntu 22.04.2 (available in About System) KDE Plasma Version: 5.27.1 KDE Frameworks Version: 5.104.0 Qt Version: 5.15.8 libqt5core5a Version: 5.15.8+p22.04+tunstable+git20230209.0323-0 python3-pyqt5 Version: 5.15.9+dfsg-1+22.04+jammy+unstable+build21

ADDITIONAL INFORMATION

I believe the previous scaling is offered by Qt since 5.6 and is described here 2 and 3. And the new one was announced by Nate here 4.

I think the KWin part landed:

> wayland-info | rg frac
interface: 'wp_fractional_scale_manager_v1',             version:  1, name: 10

but maybe the Qt patches were not added to Neon's patch collection? Is this 5 related? how can I verify if the Qt5 client libs have support?

I attach the small PyQt sample 6 used to show that the scale given to the app is 2x and the screen geometry and physicalDotsPerInch is fake (dpi 115.834 instead of 163), with system Qt 5.15.8.

from datetime import datetime
import os
import sys
if sys.argv[1:] or '' == '5':
# `exec` to avoid messing with the IDE smarts
exec('''
from PyQt5.QtWidgets import \\
QMainWindow, QLabel, QGraphicsRectItem,\\
QGridLayout, QWidget, \\
QGraphicsScene, QGraphicsView, QApplication
from PyQt5.QtGui import QPixmap, QColor
from PyQt5.QtCore import Qt, QT_VERSION, QT_VERSION_STR, PYQT_VERSION_STR
TextSelectableByMouse = Qt.TextInteractionFlag.TextSelectableByMouse
''')
else:
from PyQt6.QtWidgets import \
QMainWindow, QLabel, QGraphicsRectItem,\
QGridLayout, QWidget, \
QGraphicsScene, QGraphicsView, QApplication
from PyQt6.QtGui import QPixmap, QColor
from PyQt6.QtCore import Qt, QT_VERSION, QT_VERSION_STR, PYQT_VERSION_STR
TextSelectableByMouse = Qt.TextInteractionFlag.TextSelectableByMouse
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.buildUI()
self.initUI()
self.signals()
def signals(self):
self.windowHandle().screenChanged.connect(self.update)
for scr in app.screens():
scr.logicalDotsPerInchChanged.connect(self.update)
def buildUI(self):
mainLayout = QGridLayout()
scene = QGraphicsScene()
# https://stackoverflow.com/a/48782280/2017781
pxm = QPixmap(os.path.join(sys.path[0], 'dpitest1px.png'))
pxit = scene.addPixmap(pxm)
pxit.setPos(1, 1)
# the image is 126x23 px, add 2*pen.width
rect = QGraphicsRectItem(0, 0, 128, 25)
rect.setPen(QColor(0, 255, 0))
scene.addItem(rect)
self.view = QGraphicsView(scene)
self.view.setFixedSize(150, 50)
mainLayout.addWidget(self.view)
self.labf = QLabel()
self.updateLabel()
self.labf.setTextInteractionFlags(TextSelectableByMouse)
mainLayout.addWidget(self.labf)
self.setLayout(mainLayout)
def update(self):
self.updateLabel()
def updateLabel(self):
ss = ''
for scr in app.screens():
g = scr.availableGeometry().getRect()
v = scr.availableVirtualGeometry().getRect()
ss += f'''
{scr.name()}: scale = {scr.devicePixelRatio()}
physicalDotsPerInch = {round(scr.physicalDotsPerInch(), 4)}
logicalDotsPerInch = {scr.logicalDotsPerInch()}
Geometry = {g}
VirtualGeometry = {v}'''
h = self.windowHandle()
screenName = h.screen().name() if h else None
self.labf.setText(f'''
On {datetime.now().isoformat()}
Qt {QT_VERSION_STR}
PyQt {PYQT_VERSION_STR}
app.platformName = {app.platformName()}
app.devicePixelRatio = {app.devicePixelRatio()}
screenName = {screenName}
View:
devicePixelRatioF = {self.view.devicePixelRatioF()}
devicePixelRatio = {self.view.devicePixelRatio()}
devicePixelRatioFScale = {self.view.devicePixelRatioFScale()}
logicalDpiX = {self.view.logicalDpiX()}
size = {self.view.size()}
Screens: {ss}
''')
def initUI(self):
self.setWindowTitle(f'Qt {QT_VERSION_STR} {app.platformName()} demo')
# self.adjustSize()
self.center()
self.show()
def center(self):
qr = self.frameGeometry()
cp = app.primaryScreen().availableGeometry().center()
# cp = app.activeWindow().screen.g windowHandle().sc
qr.moveCenter(cp)
self.move(qr.topLeft())
app = QApplication([])
ex = MyApp()
app.exec()
@eddy-geek
Copy link
Author

eddy-geek commented Oct 16, 2023

20231016_120713, Neon_unstable:
20231016_120713_Neon_unstable

With display at 120%, Pixel ratio is 2 and dpi is 96.

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