[PATCH] ui/gtk: Fix guest absolute pointer position under Wayland

Andrei Borzenkov posted 1 patch 3 years, 5 months ago
Test checkpatch passed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20201108164329.249482-1-arvidjaar@gmail.com
ui/gtk-egl.c | 12 ++++--------
ui/gtk.c     | 21 ++++++++-------------
2 files changed, 12 insertions(+), 21 deletions(-)
[PATCH] ui/gtk: Fix guest absolute pointer position under Wayland
Posted by Andrei Borzenkov 3 years, 5 months ago
Ubuntu 20.04, GNOME Wayland desktop, QEMU 5.1.0 compiled from sources.

Absolute tablet/vmmouse location in guest window does not match
cursor - coordinates reported to guest are smaller than visible
mouse cursor location.

Adding debug print to ui/gtk.c:gd_motion_event() to print surface
and window szies and mouse coordinates and running guest with 1280x960
size I see the following.

Under X11

fbw = 1280, fbh = 960, ww = 1280, wh = 960, x = 1278.000000, y = 38.000000
fbw = 1280, fbh = 960, ww = 1280, wh = 960, x = 1276.000000, y = 38.000000

Guest surface size is the same as containing window, mouse coordinates
are reported relative to guest surface. It matches X11 window hierarchy:

    0x606f8f (has no name): ()  1300x1040+145+50  +145+50
        1 child:
        0x2e00009 "QEMU (Tumbleweed)": ("qemu" "Qemu-system-x86_64")  1280x985+10+45  +155+95
           2 children:
           0x2e00019 (has no name): ()  1280x960+0+25  +155+120
           0x2e0000a (has no name): ()  1x1+-1+-1  +154+94

So guest surface has matching window with 1280x960 size.

Under Wayland

[430579.942] wl_pointer@11.motion(183842180, 1314.042969, 90.894531)
...
[430680.056] wl_pointer@11.motion(183842288, 1306.355469, 91.171875)
[430697.031] wl_pointer@11.motion(183842300, 1305.722656, 91.171875)

[430735.908] wl_pointer@11.motion(183842347, 1304.832031, 91.171875)
fbw = 1280, fbh = 960, ww = 1332, wh = 1074, x = 1278.832031, y = 6.171875
[430783.750] wl_pointer@11.motion(183842395, 1304.500000, 91.171875)
fbw = 1280, fbh = 960, ww = 1332, wh = 1074, x = 1278.500000, y = 6.171875
[430863.804] wl_pointer@11.motion(183842467, 1303.492188, 91.527344)
fbw = 1280, fbh = 960, ww = 1332, wh = 1074, x = 1277.492188, y = 6.527344

While mouse coordinates are still reported relative to guest surface,
it seems that there is single (GDK) top level window with size 1332x1074
and all other widgets belong to this window. Mouse events are registered
as soon as pointer enters this top level window but are reported to application
only when they enter guest window surface. Because QEMU believes UI window
was resized, it attempts to adjust mouse position before reporting it
to guest.

This does not contradict GTK manual which does not require that every widget
has own window:

Note that all realized widgets have a non-NULL “window” pointer (gtk_widget_get_window()
never returns a NULL window when a widget is realized), but for many of them it’s actually
the GdkWindow of one of its parent widgets.

This patch replaces gdk_window_get_width/height(gtk_widget_get_window()) with
gtk_widget_get_allocated_width/height(). I tested it both under Wayland and X11
and it fixed mouse issue and did not introduce regressions for me.
I tested also UI resizing with fixed guest size and full screen which also
worked.

Signed-off-by: Andrei Borzenkov <arvidjaar@gmail.com>
---
 ui/gtk-egl.c | 12 ++++--------
 ui/gtk.c     | 21 ++++++++-------------
 2 files changed, 12 insertions(+), 21 deletions(-)

diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 99231a3597..ce25db6d8e 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -61,16 +61,14 @@ void gd_egl_init(VirtualConsole *vc)
 
 void gd_egl_draw(VirtualConsole *vc)
 {
-    GdkWindow *window;
     int ww, wh;
 
     if (!vc->gfx.gls) {
         return;
     }
 
-    window = gtk_widget_get_window(vc->gfx.drawing_area);
-    ww = gdk_window_get_width(window);
-    wh = gdk_window_get_height(window);
+    ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+    wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
 
     if (vc->gfx.scanout_mode) {
         gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h);
@@ -255,7 +253,6 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
                           uint32_t x, uint32_t y, uint32_t w, uint32_t h)
 {
     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-    GdkWindow *window;
     int ww, wh;
 
     if (!vc->gfx.scanout_mode) {
@@ -268,9 +265,8 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
     eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
                    vc->gfx.esurface, vc->gfx.ectx);
 
-    window = gtk_widget_get_window(vc->gfx.drawing_area);
-    ww = gdk_window_get_width(window);
-    wh = gdk_window_get_height(window);
+    ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+    wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
     egl_fb_setup_default(&vc->gfx.win_fb, ww, wh);
     if (vc->gfx.cursor_fb.texture) {
         egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb,
diff --git a/ui/gtk.c b/ui/gtk.c
index b11594d817..f1aef1ff2d 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -393,8 +393,8 @@ static void gd_update_full_redraw(VirtualConsole *vc)
 {
     GtkWidget *area = vc->gfx.drawing_area;
     int ww, wh;
-    ww = gdk_window_get_width(gtk_widget_get_window(area));
-    wh = gdk_window_get_height(gtk_widget_get_window(area));
+    ww = gtk_widget_get_allocated_width(area);
+    wh = gtk_widget_get_allocated_height(area);
 #if defined(CONFIG_GTK_GL)
     if (vc->gfx.gls && gtk_use_gl_area) {
         gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
@@ -440,7 +440,6 @@ static void gd_update(DisplayChangeListener *dcl,
                       int x, int y, int w, int h)
 {
     VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
-    GdkWindow *win;
     int x1, x2, y1, y2;
     int mx, my;
     int fbw, fbh;
@@ -467,12 +466,8 @@ static void gd_update(DisplayChangeListener *dcl,
     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
 
-    win = gtk_widget_get_window(vc->gfx.drawing_area);
-    if (!win) {
-        return;
-    }
-    ww = gdk_window_get_width(win);
-    wh = gdk_window_get_height(win);
+    ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+    wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
 
     mx = my = 0;
     if (ww > fbw) {
@@ -803,8 +798,8 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
     fbw = surface_width(vc->gfx.ds);
     fbh = surface_height(vc->gfx.ds);
 
-    ww = gdk_window_get_width(gtk_widget_get_window(widget));
-    wh = gdk_window_get_height(gtk_widget_get_window(widget));
+    ww = gtk_widget_get_allocated_width(widget);
+    wh = gtk_widget_get_allocated_height(widget);
 
     if (s->full_screen) {
         vc->gfx.scale_x = (double)ww / fbw;
@@ -865,8 +860,8 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
     fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
     fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
 
-    ww = gdk_window_get_width(gtk_widget_get_window(vc->gfx.drawing_area));
-    wh = gdk_window_get_height(gtk_widget_get_window(vc->gfx.drawing_area));
+    ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+    wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
 
     mx = my = 0;
     if (ww > fbw) {
-- 
2.25.1