Bug 443357

Summary: Real cursor position is slightly offset from displayed position on Wayland in virtual machine
Product: [Plasma] kwin Reporter: Adam Williamson <adamw>
Component: platform-drmAssignee: KWin default assignee <kwin-bugs-null>
Status: RESOLVED DUPLICATE    
Severity: normal CC: butirsky, kde, nate, rdieter, tagwerk19
Priority: NOR    
Version: git-stable-Plasma/5.22   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed In:
Sentry Crash Report:

Description Adam Williamson 2021-10-05 16:46:05 UTC
When running KDE on Wayland in a VM, the cursor location is slightly offset from where it is drawn. Usually the 'real' location is slightly above and to the left of the 'apparent' location. This is most obvious when drag-selecting text in e.g. a text editor or console.

As explained in https://bugzilla.redhat.com/show_bug.cgi?id=2009304#c12 , this is caused by using atomic mode setting: "The atomic API does not allow specifying the cursor hotspot causing issues with virtual-machines which draw the cursor them-selves. The solution is to not use the atomic API (at least for the cursor) when running on say QXL graphics or a couple of other virtual GPUs."

mutter has a denylist of virt-y GPUs to not use atomic mode setting on:

https://gitlab.gnome.org/GNOME/mutter/-/blob/af0460d0cedd5a66b2110ab2a99e67c647e7b6fb/src/backends/native/meta-kms-impl-device-atomic.c#L1149-1152

kwin should probably have the same. I think I found the place where atomic mode setting is enabled - https://github.com/KDE/kwin/blob/6a68caef7be72a3ae004c71bb844a0716966ca98/src/plugins/platforms/drm/drm_gpu.cpp#L90 and the function it calls - and it doesn't appear to have any such denylist.


STEPS TO REPRODUCE
1. Run KDE on Wayland in a virtual machine (e.g. a Fedora 34 KDE image in a qemu VM using qxl or virtio graphics)
2. Run a text editor or console and get some text displayed
3. Position the cursor at the beginning of a line and try to select the text

OBSERVED RESULT
You will likely select from the line above, or not select at all as the cursor is 'really' outside the window.

EXPECTED RESULT
Accurate selection due to cursor being where it appears to be.

SOFTWARE/OS VERSIONS
Linux/KDE Plasma: Any KDE/Linux combination in a VM on Wayland.

ADDITIONAL INFORMATION
Comment 1 Adam Williamson 2021-10-05 18:07:10 UTC
I'm trying to do a PR for this, but me writing C++ is like the old gag about dancing bears, so it may take a minute. :D
Comment 2 Adam Williamson 2021-10-05 20:51:52 UTC
Hum, well. Turning off atomic mode setting doesn't actually solve the problem, for some reason, on Plasma. It does on GNOME, but not Plasma. I thought my patch wasn't working so I tested with KWIN_DRM_NO_AMS=1 in /etc/environment and confirmed via `qdbus org.kde.KWin / KWin supportInformation` output that AMS is disabled:

Atomic Mode Setting on GPU 0: false

but the cursor offset is still present when running Plasma on Wayland. When running Plasma on X11, there is no offset.
Comment 3 Adam Williamson 2021-10-06 15:53:21 UTC
jadahl - who dealt with the similar problems on the mutter (GNOME) side - passes this along:

"one possibility is that kde changed to do what mutter did for a while, which is to emulate atomic mode setting with legacy mode setting. what mutter did was to offset things manually, always setting a 0,0 hotspot. when the virtual machine viewer bugs showed up, I had to re-introduce the hotspot to the atomic mode setting layer even if it couldn't be used"
Comment 4 Adam Williamson 2021-10-06 16:12:23 UTC
Aha. Looks like this was fixed on the development branch:

https://github.com/KDE/kwin/commit/998bbf4eba724a9b94a5bae62182456b85b11383#diff-034885748897f413c645e3efd125d7c0db9a8454d580f6093e40c885d974c818

unfortunately, that was after a big change that moved a ton of code around:

https://github.com/KDE/kwin/commit/586efe94d4f3b2c9759a26f27661edcaa54dce0b#diff-034885748897f413c645e3efd125d7c0db9a8454d580f6093e40c885d974c818

I'll have a go at backporting it to the 5.22 stable branch today, as that's what we need downstream for Fedora 35.
Comment 5 Adam Williamson 2021-10-06 17:05:20 UTC
So, it looks like the DrmPipeline addition changed all the cursor setting code considerably, such that it's beyond me how the bug can be fixed on the 5.22 branch. CCing Andrey Butirsky, who wrote the fix on the development branch - Andrey, would you possibly have time to see if this can be fixed on the 5.22 code? Thanks!
Comment 6 Adam Williamson 2021-10-06 18:14:13 UTC
Oh, hmm, think I may have managed it myself after all. Does anyone see anything wrong with this?

diff --git a/src/plugins/platforms/drm/drm_output.cpp b/src/plugins/platforms/drm/drm_output.cpp
index 917bb857d..7ee387219 100644
--- a/src/plugins/platforms/drm/drm_output.cpp
+++ b/src/plugins/platforms/drm/drm_output.cpp
@@ -102,10 +102,15 @@ bool DrmOutput::hideCursor()
     return drmModeSetCursor(m_gpu->fd(), m_crtc->id(), 0, 0, 0) == 0;
 }
 
-bool DrmOutput::showCursor(DrmDumbBuffer *c)
+bool DrmOutput::showCursor(DrmDumbBuffer *c, const QPoint &hotspot)
 {
     const QSize &s = c->size();
-    if (drmModeSetCursor(m_gpu->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0) {
+    int ret = drmModeSetCursor2(m_gpu->fd(), m_crtc->id(), c->handle(), s.width(), s.height(), hotspot.x(), hotspot.y());
+    if (ret == ENOTSUP) {
+        // for NVIDIA case that does not support drmModeSetCursor2
+        ret = drmModeSetCursor(m_gpu->fd(), m_crtc->id(), c->handle(), s.width(), s.height());
+    }
+    if (ret == 0) {
         if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive
             && isCursorVisible()) {
             m_renderLoop->scheduleRepaint();
@@ -122,7 +127,8 @@ bool DrmOutput::showCursor()
         return false;
     }
 
-    const bool ret = showCursor(m_cursor[m_cursorIndex].data());
+    const Cursor * const cursor = Cursors::self()->currentCursor();
+    const bool ret = showCursor(m_cursor[m_cursorIndex].data(), logicalToNativeMatrix(cursor->rect(), scale(), transform()).map(cursor->hotspot()));
     if (!ret) {
         qCDebug(KWIN_DRM) << "DrmOutput::showCursor(DrmDumbBuffer) failed";
         return ret;
diff --git a/src/plugins/platforms/drm/drm_output.h b/src/plugins/platforms/drm/drm_output.h
index 1f89f9064..af46c88a0 100644
--- a/src/plugins/platforms/drm/drm_output.h
+++ b/src/plugins/platforms/drm/drm_output.h
@@ -45,7 +45,7 @@ public:
     ///queues deleting the output after a page flip has completed.
     void teardown();
     void releaseGbm();
-    bool showCursor(DrmDumbBuffer *buffer);
+    bool showCursor(DrmDumbBuffer *buffer, const QPoint &hotspot);
     bool showCursor();
     bool hideCursor();
     bool updateCursor();
-- 
2.32.0
Comment 7 David Edmundson 2021-10-06 20:17:36 UTC
>Aha. Looks like this was fixed on the development branch:

Indeed.

That patch looks ok.
Comment 8 Andrey 2021-10-06 21:39:10 UTC
(In reply to Adam Williamson from comment #5)
> CCing Andrey Butirsky, who wrote the fix on the development
> branch - Andrey, would you possibly have time to see if this can be fixed on
> the 5.22 code? Thanks!

If it's still needed - your patch above looks OK
Comment 9 Adam Williamson 2021-10-06 21:40:25 UTC
Thanks for the check, both of you. I've applied that to our package downstream. Sounds like no more upstream 5.22 releases are planned, so I won't bother submitting it for that branch.
Comment 10 Andrey 2021-10-06 21:48:16 UTC

*** This bug has been marked as a duplicate of bug 427060 ***