[Spice-devel] [PATCH spice-gtk 11/11] Draw mouse with cairo in server mode

Marc-André Lureau marcandre.lureau at gmail.com
Thu Dec 8 06:12:58 PST 2011


---
 gtk/spice-widget-cairo.c |   14 ++++-
 gtk/spice-widget-priv.h  |    3 +
 gtk/spice-widget.c       |  127 +++++++++++++++++++++++++++-------------------
 3 files changed, 88 insertions(+), 56 deletions(-)

diff --git a/gtk/spice-widget-cairo.c b/gtk/spice-widget-cairo.c
index f78b670..3fc2a22 100644
--- a/gtk/spice-widget-cairo.c
+++ b/gtk/spice-widget-cairo.c
@@ -95,15 +95,23 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
     if (d->ximage) {
         if (d->allow_scaling) {
             double sx, sy;
-            /* Scale to fill window */
-            sx = (double)ww / (double)fbw;
-            sy = (double)wh / (double)fbh;
+            spice_display_get_scaling(display, &sx, &sy);
             cairo_scale(cr, sx, sy);
             cairo_set_source_surface(cr, d->ximage, 0, 0);
         } else {
             cairo_set_source_surface(cr, d->ximage, d->mx, d->my);
         }
         cairo_paint(cr);
+
+        if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER) {
+            GdkPixbuf *image = d->mouse_pixbuf;
+            if (image != NULL) {
+                gdk_cairo_set_source_pixbuf(cr, image,
+                                            d->mx + d->mouse_guest_x - d->mouse_hotspot.x,
+                                            d->my + d->mouse_guest_y - d->mouse_hotspot.y);
+                cairo_paint(cr);
+            }
+        }
     }
 }
 
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index bd5eec6..e0c6c4c 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -87,6 +87,8 @@ struct _SpiceDisplayPrivate {
     int                     mouse_grab_active;
     bool                    mouse_have_pointer;
     GdkCursor               *mouse_cursor;
+    GdkPixbuf               *mouse_pixbuf;
+    GdkPoint                mouse_hotspot;
     GdkCursor               *show_cursor;
     int                     mouse_last_x;
     int                     mouse_last_y;
@@ -116,6 +118,7 @@ void     spicex_draw_event                   (SpiceDisplay *display, cairo_t *cr
 void     spicex_expose_event                 (SpiceDisplay *display, GdkEventExpose *ev);
 #endif
 gboolean spicex_is_scaled                    (SpiceDisplay *display);
+void     spice_display_get_scaling           (SpiceDisplay *display, double *sx, double *sy);
 
 G_END_DECLS
 
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 08aa5a9..528f316 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -116,6 +116,7 @@ static void disconnect_display(SpiceDisplay *display);
 static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data);
 static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data);
 static void sync_keyboard_lock_modifiers(SpiceDisplay *display);
+static void cursor_invalidate(SpiceDisplay *display);
 
 /* ---------------------------------------------------------------- */
 
@@ -507,6 +508,7 @@ static GdkGrabStatus do_pointer_grab(SpiceDisplay *display)
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
     GdkWindow *window = GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(display)));
     GdkGrabStatus status;
+    GdkCursor *blank = get_blank_cursor();
 
     if (!gtk_widget_get_realized(GTK_WIDGET(display)))
         return GDK_GRAB_BROKEN;
@@ -527,7 +529,8 @@ static GdkGrabStatus do_pointer_grab(SpiceDisplay *display)
                      GDK_BUTTON_RELEASE_MASK |
                      GDK_BUTTON_MOTION_MASK |
                      GDK_SCROLL_MASK,
-                     NULL, d->mouse_cursor,
+                     NULL,
+                     blank,
                      GDK_CURRENT_TIME);
     if (status != GDK_GRAB_SUCCESS) {
         d->mouse_grab_active = false;
@@ -539,6 +542,7 @@ static GdkGrabStatus do_pointer_grab(SpiceDisplay *display)
 
     gtk_grab_add(GTK_WIDGET(display));
 
+    g_object_unref(blank);
     return status;
 }
 
@@ -558,8 +562,6 @@ static void update_mouse_pointer(SpiceDisplay *display)
         if (!d->mouse_grab_active) {
             gdk_window_set_cursor(window, NULL);
         } else {
-            // FIXME: should it be transparent instead?
-            gdk_window_set_cursor(window, d->mouse_cursor);
             try_mouse_grab(display);
         }
         break;
@@ -600,24 +602,22 @@ static void mouse_check_edges(GtkWidget *widget, GdkEventMotion *motion)
     int x = (int)motion->x_root;
     int y = (int)motion->y_root;
 
-    if (d->mouse_guest_x != -1 && d->mouse_guest_y != -1) {
-        SPICE_DEBUG("skip mouse_check_edges");
-        return;
-    }
-
     /* from gtk-vnc:
      * In relative mode check to see if client pointer hit one of the
-     * screen edges, and if so move it back by 200 pixels. This is
+     * screen edges, and if so move it back by 100 pixels. This is
      * important because the pointer in the server doesn't correspond
      * 1-for-1, and so may still be only half way across the
      * screen. Without this warp, the server pointer would thus appear
      * to hit an invisible wall */
-    if (motion->x <= 0) x += 200;
-    if (motion->y <= 0) y += 200;
-    if (motion->x >= (gdk_screen_get_width(screen) - 1)) x -= 200;
-    if (motion->y >= (gdk_screen_get_height(screen) - 1)) y -= 200;
+    if (x <= 0) x += 100;
+    if (y <= 0) y += 100;
+    if (x >= (gdk_screen_get_width(screen) - 1)) x -= 100;
+    if (y >= (gdk_screen_get_height(screen) - 1)) y -= 100;
 
     if (x != (int)motion->x_root || y != (int)motion->y_root) {
+        /* FIXME: we try our best to ignore that next pointer move event.. */
+        gdk_display_sync(gdk_screen_get_display(screen));
+
         gdk_display_warp_pointer(gtk_widget_get_display(widget),
                                  screen, x, y);
         d->mouse_last_x = -1;
@@ -1132,7 +1132,8 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button)
     if (d->disable_inputs)
         return true;
 
-    if (!spicex_is_scaled(display)) {
+    if (!spicex_is_scaled(display)
+        && d->mouse_mode == SPICE_MOUSE_MODE_CLIENT) {
         gint x, y;
 
         /* rule out clicks in outside region */
@@ -1474,31 +1475,34 @@ static void cursor_set(SpiceCursorChannel *channel,
     SpiceDisplay *display = data;
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
 
+    cursor_invalidate(display);
+
     if (d->mouse_cursor) {
         gdk_cursor_unref(d->mouse_cursor);
         d->mouse_cursor = NULL;
     }
 
-    if (rgba != NULL) {
-        GdkPixbuf *pixbuf;
-        GdkDisplay *gtkdpy;
-
-        gtkdpy = gtk_widget_get_display(GTK_WIDGET(display));
+    if (d->mouse_pixbuf) {
+        g_object_unref(d->mouse_pixbuf);
+        d->mouse_pixbuf = NULL;
+    }
 
-        pixbuf = gdk_pixbuf_new_from_data(rgba,
-                                          GDK_COLORSPACE_RGB,
-                                          TRUE, 8,
-                                          width,
-                                          height,
-                                          width * 4,
-                                          NULL, NULL);
+    if (rgba != NULL) {
+        d->mouse_pixbuf = gdk_pixbuf_new_from_data(g_memdup(rgba, width * height * 4),
+                                                   GDK_COLORSPACE_RGB,
+                                                   TRUE, 8,
+                                                   width,
+                                                   height,
+                                                   width * 4,
+                                                   (GdkPixbufDestroyNotify)g_free, NULL);
+        d->mouse_hotspot.x = hot_x;
+        d->mouse_hotspot.y = hot_y;
 
         /* gdk_cursor_new_from_pixbuf() will copy pixbuf data on
            x11/win32/macos. No worries if rgba pointer is freed
            after. */
-        d->mouse_cursor = gdk_cursor_new_from_pixbuf(gtkdpy, pixbuf,
-                                                     hot_x, hot_y);
-        g_object_unref(pixbuf);
+        d->mouse_cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(GTK_WIDGET(display)),
+                                                     d->mouse_pixbuf, hot_x, hot_y);
     }
 
     update_mouse_pointer(display);
@@ -1513,42 +1517,59 @@ static void cursor_hide(SpiceCursorChannel *channel, gpointer data)
         return;
 
     d->show_cursor = d->mouse_cursor;
-    d->mouse_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
+    d->mouse_cursor = get_blank_cursor();
     update_mouse_pointer(display);
 }
 
+G_GNUC_INTERNAL
+void spice_display_get_scaling(SpiceDisplay *display, double *sx, double *sy)
+{
+    SpiceDisplayPrivate *d = display->priv;
+    int fbw = d->width, fbh = d->height;
+    int ww, wh;
+
+    if (!spicex_is_scaled(display)) {
+        *sx = 1.0;
+        *sy = 1.0;
+        return;
+    }
+
+    gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh);
+
+    *sx = (double)ww / (double)fbw;
+    *sy = (double)wh / (double)fbh;
+}
+
+static void cursor_invalidate(SpiceDisplay *display)
+{
+    SpiceDisplayPrivate *d = display->priv;
+    double sx, sy;
+
+    if (d->mouse_pixbuf == NULL)
+        return;
+
+    spice_display_get_scaling(display, &sx, &sy);
+
+    gtk_widget_queue_draw_area(GTK_WIDGET(display),
+                               (d->mouse_guest_x + d->mx - d->mouse_hotspot.x) * sx,
+                               (d->mouse_guest_y + d->my - d->mouse_hotspot.y) * sy,
+                               gdk_pixbuf_get_width(d->mouse_pixbuf) * sx,
+                               gdk_pixbuf_get_height(d->mouse_pixbuf) * sy);
+}
+
 static void cursor_move(SpiceCursorChannel *channel, gint x, gint y, gpointer data)
 {
     SpiceDisplay *display = data;
-    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-    GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(display));
-    int wx, wy;
+    SpiceDisplayPrivate *d = display->priv;
 
-    SPICE_DEBUG("%s: +%d+%d", __FUNCTION__, x, y);
+    cursor_invalidate(display);
 
-    d->mouse_last_x = x;
-    d->mouse_last_y = y;
     d->mouse_guest_x = x;
     d->mouse_guest_y = y;
 
-    if (spicex_is_scaled(display) && gtk_widget_get_realized(GTK_WIDGET(display))) {
-        gint ww, wh;
-
-        gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh);
-        x = x * ((double)ww / (double)(d->width));
-        y = y * ((double)wh / (double)(d->height));
-    } else {
-        /* black borders offset */
-        x += d->mx;
-        y += d->my;
-    }
-
-    if (d->mouse_grab_active && gtk_widget_get_realized(GTK_WIDGET(display))) {
-        gdk_window_get_origin(GDK_WINDOW(gtk_widget_get_window(GTK_WIDGET(display))), &wx, &wy);
-        gdk_display_warp_pointer(gtk_widget_get_display(GTK_WIDGET(display)), screen, x + wx, y + wy);
-    }
+    cursor_invalidate(display);
 
-    /* FIXME: apparently we have to restore cursor when "cursor_move" ?? */
+    /* apparently we have to restore cursor when "cursor_move" */
     if (d->show_cursor != NULL) {
         gdk_cursor_unref(d->mouse_cursor);
         d->mouse_cursor = d->show_cursor;
-- 
1.7.7.3



More information about the Spice-devel mailing list