[Spice-devel] [PATCH spice-gtk 11/11] Draw mouse with cairo in server mode
Hans de Goede
hdegoede at redhat.com
Thu Dec 8 07:38:24 PST 2011
ACK.
On 12/08/2011 03:12 PM, Marc-André Lureau wrote:
> ---
> 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;
More information about the Spice-devel
mailing list