Snippet content copied to clipboard.
Are you sure to delete this snippet? No, don't delete

krfb pointer scale workaround (AI-assisted, unreviewed) - KDE bugs 466276 / 524406

  1. Disclosure: written with AI assistance (Claude) to fix one machine (Fedora 45, Plasma 6.8.2, krfb 26.08.3, one monitor 2560x1440 at 125%). Tested on that setup only. Not a recommended fix; posted in case it helps. Context: KDE bugs 466276 (comment 13) and 524406.
  2. It may need small manual changes on other krfb versions (e.g. 26.04.x).
  3. ​
  4. --- a/events/xdp/xdpevents.cpp
  5. +++ b/events/xdp/xdpevents.cpp
  6. @@ -15,6 +15,10 @@
  7. #include <QApplication>
  8. #include <QGlobalStatic>
  9. +#include <QGuiApplication>
  10. +#include <QScreen>
  11. +#include <QSettings>
  12. +#include <QStandardPaths>
  13. class EventData
  14. {
  15. @@ -45,6 +49,40 @@
  16. QStringLiteral("/org/freedesktop/portal/desktop"), QDBusConnection::sessionBus()));
  17. }
  18. +// The VNC client reports pointer positions in framebuffer (physical) pixels, but the
  19. +// RemoteDesktop portal expects the compositor's logical (scaled) coordinates.
  20. +// Divide by the display scale. An optional override can be set in ~/.config/krfbrc:
  21. +// [General]
  22. +// pointerScale=1.25
  23. +// Without it, the scale is derived from the captured frame width and the logical screen width.
  24. +static qreal configuredPointerScale()
  25. +{
  26. + static const qreal value = []() -> qreal {
  27. + QSettings cfg(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QStringLiteral("/krfbrc"), QSettings::IniFormat);
  28. + bool ok = false;
  29. + const qreal v = cfg.value(QStringLiteral("pointerScale")).toDouble(&ok);
  30. + return (ok && v > 0) ? v : 0;
  31. + }();
  32. + return value;
  33. +}
  34. +
  35. +static qreal pointerScale(int frameBufferWidth)
  36. +{
  37. + const qreal configured = configuredPointerScale();
  38. + if (configured > 0) {
  39. + return configured;
  40. + }
  41. + // QScreen::devicePixelRatio() is rounded up to a whole number on Wayland (2 for a 1.25x
  42. + // output), so derive the scale from the captured frame width and the logical screen width.
  43. + if (const QScreen *screen = QGuiApplication::primaryScreen()) {
  44. + const int logicalWidth = screen->geometry().width();
  45. + if (logicalWidth > 0 && frameBufferWidth > 0) {
  46. + return qreal(frameBufferWidth) / logicalWidth;
  47. + }
  48. + }
  49. + return 1.0;
  50. +}
  51. +
  52. void XdpEventHandler::handleKeyboard(bool down, rfbKeySym keySym)
  53. {
  54. const QDBusObjectPath sessionHandle = frameBuffer()->customProperty(QStringLiteral("session_handle")).value<QDBusObjectPath>();
  55. @@ -61,7 +99,8 @@
  56. }
  57. if (x != data->x || y != data->y) {
  58. - data->dbusXdpRemoteDesktopService->NotifyPointerMotionAbsolute(sessionHandle, QVariantMap(), streamNodeId, x, y);
  59. + const qreal scale = pointerScale(frameBuffer()->width());
  60. + data->dbusXdpRemoteDesktopService->NotifyPointerMotionAbsolute(sessionHandle, QVariantMap(), streamNodeId, x / scale, y / scale);
  61. data->x = x;
  62. data->y = y;
  63. }

Edit this Snippet