[Spice-devel] [PATCH spice-gtk 02/25] display: learn to restrict display to an area

Marc-André Lureau marcandre.lureau at gmail.com
Thu Jul 12 15:28:59 PDT 2012


Each spice widget can now restrict the area of the primary
surface they show and interact with by setting the private
area member.

A nice clean-up would be to seperate an area object that
would deal with clipping, input translation and color
conversion, but the resulting code would be similar anyway
---
 gtk/spice-widget-cairo.c |   37 +++++++----
 gtk/spice-widget-priv.h  |    2 +
 gtk/spice-widget-x11.c   |   22 +++----
 gtk/spice-widget.c       |  160 +++++++++++++++++++++++++++-------------------
 4 files changed, 134 insertions(+), 87 deletions(-)

diff --git a/gtk/spice-widget-cairo.c b/gtk/spice-widget-cairo.c
index 2f1ef75..1144308 100644
--- a/gtk/spice-widget-cairo.c
+++ b/gtk/spice-widget-cairo.c
@@ -37,16 +37,22 @@ int spicex_image_create(SpiceDisplay *display)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
 
+    g_return_val_if_fail(d->ximage == NULL, 1);
+
     if (d->format == SPICE_SURFACE_FMT_16_555 ||
         d->format == SPICE_SURFACE_FMT_16_565) {
         d->convert = TRUE;
-        d->data = g_malloc0(d->height * d->stride); /* pixels are 32 bits */
+        d->data = g_malloc0(d->area.width * d->area.height * 4);
+
+        d->ximage = cairo_image_surface_create_for_data
+            (d->data, CAIRO_FORMAT_RGB24, d->area.width, d->area.height, d->area.width * 4);
+
     } else {
         d->convert = FALSE;
-    }
 
-    d->ximage = cairo_image_surface_create_for_data
-        (d->data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
+        d->ximage = cairo_image_surface_create_for_data
+            (d->data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
+    }
 
     return 0;
 }
@@ -64,13 +70,13 @@ void spicex_image_destroy(SpiceDisplay *display)
         g_free(d->data);
         d->data = NULL;
     }
+    d->convert = FALSE;
 }
 
 G_GNUC_INTERNAL
 void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-    int fbw = d->width, fbh = d->height;
     int ww, wh;
 
     gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh);
@@ -86,8 +92,8 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
            behaviour of drawing the rectangle from right to left
            to cut out the whole */
         if (d->ximage)
-            cairo_rectangle(cr, d->mx + fbw, d->my,
-                            -1 * fbw, fbh);
+            cairo_rectangle(cr, d->mx + d->area.width, d->my,
+                            -d->area.width, d->area.height);
         cairo_fill(cr);
     }
 
@@ -97,9 +103,16 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
             double sx, sy;
             spice_display_get_scaling(display, &sx, &sy);
             cairo_scale(cr, sx, sy);
+            if (!d->convert)
+                cairo_translate(cr, -d->area.x, -d->area.y);
             cairo_set_source_surface(cr, d->ximage, 0, 0);
         } else {
-            cairo_set_source_surface(cr, d->ximage, d->mx, d->my);
+            cairo_translate(cr, d->mx, d->my);
+            cairo_rectangle(cr, 0, 0, d->area.width, d->area.height);
+            cairo_clip(cr);
+            if (!d->convert)
+                cairo_translate(cr, -d->area.x, -d->area.y);
+            cairo_set_source_surface(cr, d->ximage, 0, 0);
         }
         cairo_paint(cr);
 
@@ -108,8 +121,8 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
             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);
+                                            d->mouse_guest_x - d->mouse_hotspot.x,
+                                            d->mouse_guest_y - d->mouse_hotspot.y);
                 cairo_paint(cr);
             }
         }
@@ -149,8 +162,8 @@ void spicex_image_invalidate(SpiceDisplay *display,
         double sx, sy;
 
         /* Scale the exposed region */
-        sx = (double)ww / (double)d->width;
-        sy = (double)wh / (double)d->height;
+        sx = (double)ww / (double)d->area.width;
+        sy = (double)wh / (double)d->area.height;
 
         *x *= sx;
         *y *= sy;
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index 898d86c..a94db68 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -57,6 +57,8 @@ struct _SpiceDisplayPrivate {
     gpointer                data_origin; /* the original display image data */
     gpointer                data; /* converted if necessary to 32 bits */
 
+    GdkRectangle            area;
+    /* window border */
     gint                    ww, wh, mx, my;
 
     bool                    convert;
diff --git a/gtk/spice-widget-x11.c b/gtk/spice-widget-x11.c
index be2b6e7..7fc99ff 100644
--- a/gtk/spice-widget-x11.c
+++ b/gtk/spice-widget-x11.c
@@ -222,30 +222,30 @@ void spicex_expose_event(SpiceDisplay *display, GdkEventExpose *expose)
 
     if (expose->area.x >= d->mx &&
         expose->area.y >= d->my &&
-        expose->area.x + expose->area.width  <= d->mx + d->width &&
-        expose->area.y + expose->area.height <= d->my + d->height) {
+        expose->area.x + expose->area.width  <= d->mx + d->area.width &&
+        expose->area.y + expose->area.height <= d->my + d->area.height) {
         /* area is completely inside the guest screen -- blit it */
         if (d->have_mitshm && d->shminfo) {
             XShmPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                          d->gc, d->ximage,
-                         expose->area.x - d->mx, expose->area.y - d->my,
-                         expose->area.x,         expose->area.y,
+                         d->area.x + expose->area.x - d->mx, d->area.y + expose->area.y - d->my,
+                         expose->area.x, expose->area.y,
                          expose->area.width, expose->area.height,
                          true);
         } else {
             XPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                       d->gc, d->ximage,
-                      expose->area.x - d->mx, expose->area.y - d->my,
-                      expose->area.x,         expose->area.y,
+                      d->area.x + expose->area.x - d->mx, d->area.y + expose->area.y - d->my,
+                      expose->area.x expose->area.y,
                       expose->area.width, expose->area.height);
         }
     } else {
         /* complete window update */
-        if (d->ww > d->width || d->wh > d->height) {
+        if (d->ww > d->area.width || d->wh > d->area.height) {
             int x1 = d->mx;
-            int x2 = d->mx + d->width;
+            int x2 = d->mx + d->area.width;
             int y1 = d->my;
-            int y2 = d->my + d->height;
+            int y2 = d->my + d->area.height;
             XFillRectangle(d->dpy, gdk_x11_drawable_get_xid(window),
                            d->gc, 0, 0, x1, d->wh);
             XFillRectangle(d->dpy, gdk_x11_drawable_get_xid(window),
@@ -258,12 +258,12 @@ void spicex_expose_event(SpiceDisplay *display, GdkEventExpose *expose)
         if (d->have_mitshm && d->shminfo) {
             XShmPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                          d->gc, d->ximage,
-                         0, 0, d->mx, d->my, d->width, d->height,
+                         d->area.x, d->area.y, d->mx, d->my, d->area.width, d->area.height,
                          true);
         } else {
             XPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
                       d->gc, d->ximage,
-                      0, 0, d->mx, d->my, d->width, d->height);
+                      d->area.x, d->area.y, d->mx, d->my, d->area.width, d->area.height);
         }
     }
 }
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 095224c..237d832 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -201,8 +201,8 @@ static void update_size_request(SpiceDisplay *display)
         reqwidth = 640;
         reqheight = 480;
     } else {
-        reqwidth = d->width;
-        reqheight = d->height;
+        reqwidth = d->area.width;
+        reqheight = d->area.height;
     }
 
     gtk_widget_set_size_request(GTK_WIDGET(display), reqwidth, reqheight);
@@ -789,15 +789,16 @@ static void recalc_geometry(GtkWidget *widget)
     d->mx = 0;
     d->my = 0;
     if (!spicex_is_scaled(display)) {
-        if (d->ww > d->width)
-            d->mx = (d->ww - d->width) / 2;
-        if (d->wh > d->height)
-            d->my = (d->wh - d->height) / 2;
+        if (d->ww > d->area.width)
+            d->mx = (d->ww - d->area.width) / 2;
+        if (d->wh > d->area.height)
+            d->my = (d->wh - d->area.height) / 2;
     } else
         zoom = (gdouble)d->zoom_level / 100;
 
-    SPICE_DEBUG("monitors: id %d, guest %dx%d, window %dx%d, zoom %g, offset +%d+%d",
-                d->channel_id, d->width, d->height, d->ww, d->wh, zoom, d->mx, d->my);
+    SPICE_DEBUG("monitors: id %d, guest +%d+%d:%dx%d, window %dx%d, zoom %g, offset +%d+%d",
+                d->channel_id, d->area.x, d->area.y, d->area.width, d->area.height,
+                d->ww, d->wh, zoom, d->mx, d->my);
 
     if (d->resize_guest_enable)
         spice_main_set_display(d->main, d->channel_id,
@@ -820,44 +821,36 @@ static void recalc_geometry(GtkWidget *widget)
 
 #define CONVERT_0555_TO_8888(s) (CONVERT_0555_TO_0888(s) | 0xff000000)
 
-static gboolean do_color_convert(SpiceDisplay *display,
-                                 gint x, gint y, gint w, gint h)
+static gboolean do_color_convert(SpiceDisplay *display, GdkRectangle *r)
 {
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-    int i, j, maxy, maxx, miny, minx;
     guint32 *dest = d->data;
     guint16 *src = d->data_origin;
+    gint x, y;
 
-    if (!d->convert)
-        return true;
-
+    g_return_val_if_fail(r != NULL, false);
     g_return_val_if_fail(d->format == SPICE_SURFACE_FMT_16_555 ||
                          d->format == SPICE_SURFACE_FMT_16_565, false);
 
-    miny = MAX(y, 0);
-    minx = MAX(x, 0);
-    maxy = MIN(y + h, d->height);
-    maxx = MIN(x + w, d->width);
-
-    dest +=  (d->stride / 4) * miny;
-    src += (d->stride / 2) * miny;
+    src += (d->stride / 2) * r->y + r->x;
+    dest += d->area.width * (r->y - d->area.y) + (r->x - d->area.x);
 
     if (d->format == SPICE_SURFACE_FMT_16_555) {
-        for (j = miny; j < maxy; j++) {
-            for (i = minx; i < maxx; i++) {
-                dest[i] = CONVERT_0555_TO_0888(src[i]);
+        for (y = 0; y < r->height; y++) {
+            for (x = 0; x < r->width; x++) {
+                dest[x] = CONVERT_0555_TO_0888(src[x]);
             }
 
-            dest += d->stride / 4;
+            dest += d->area.width;
             src += d->stride / 2;
         }
     } else if (d->format == SPICE_SURFACE_FMT_16_565) {
-        for (j = miny; j < maxy; j++) {
-            for (i = minx; i < maxx; i++) {
-                dest[i] = CONVERT_0565_TO_0888(src[i]);
+        for (y = 0; y < r->height; y++) {
+            for (x = 0; x < r->width; x++) {
+                dest[x] = CONVERT_0565_TO_0888(src[x]);
             }
 
-            dest += d->stride / 4;
+            dest += d->area.width;
             src += d->stride / 2;
         }
     }
@@ -875,10 +868,7 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
 
     if (d->mark == 0 || d->data == NULL)
         return false;
-
-    if (!d->ximage) {
-        spicex_image_create(display);
-    }
+    g_return_val_if_fail(d->ximage != NULL, false);
 
     spicex_draw_event(display, cr);
     update_mouse_pointer(display);
@@ -894,10 +884,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose)
 
     if (d->mark == 0 || d->data == NULL)
         return false;
-
-    if (!d->ximage) {
-        spicex_image_create(display);
-    }
+    g_return_val_if_fail(d->ximage != NULL, false);
 
     spicex_expose_event(display, expose);
     update_mouse_pointer(display);
@@ -1181,10 +1168,10 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
         return true;
 
     gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
-    if (spicex_is_scaled(display) && (d->width != ww || d->height != wh)) {
+    if (spicex_is_scaled(display) && (d->area.width != ww || d->area.height != wh)) {
         double sx, sy;
-        sx = (double)d->width / (double)ww;
-        sy = (double)d->height / (double)wh;
+        sx = (double)d->area.width / (double)ww;
+        sy = (double)d->area.height / (double)wh;
 
         /* Scaling the desktop, so scale the mouse coords by the same.
          * Ratio for the rounding used:
@@ -1208,10 +1195,10 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
 
     switch (d->mouse_mode) {
     case SPICE_MOUSE_MODE_CLIENT:
-        if (motion->x >= 0 && motion->x < d->width &&
-            motion->y >= 0 && motion->y < d->height) {
+        if (motion->x >= 0 && motion->x < d->area.width &&
+            motion->y >= 0 && motion->y < d->area.height) {
             spice_inputs_position(d->inputs,
-                                  motion->x, motion->y,
+                                  motion->x + d->area.x, motion->y + d->area.y,
                                   d->channel_id,
                                   button_mask_gdk_to_spice(motion->state));
         }
@@ -1285,7 +1272,7 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button)
         gtk_widget_get_pointer (widget, &x, &y);
         x -= d->mx;
         y -= d->my;
-        if (!(x >= 0 && x < d->width && y >= 0 && y < d->height))
+        if (!(x >= 0 && x < d->area.width && y >= 0 && y < d->area.height))
             return true;
     }
 
@@ -1577,6 +1564,40 @@ static void update_mouse_mode(SpiceChannel *channel, gpointer data)
     cursor_invalidate(display);
 }
 
+static void update_area(SpiceDisplay *display,
+                        gint x, gint y, gint width, gint height)
+{
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+    GdkRectangle primary = {
+        .x = 0,
+        .y = 0,
+        .width = d->width,
+        .height = d->height
+    };
+    GdkRectangle area = {
+        .x = x,
+        .y = y,
+        .width = width,
+        .height = height
+    };
+
+    SPICE_DEBUG("update area, primary: %dx%d, area: +%d+%d %dx%d", d->width, d->height, area.x, area.y, area.width, area.height);
+
+    if (!gdk_rectangle_intersect(&primary, &area, &area)) {
+        SPICE_DEBUG("The monitor area is not intersecting primary surface");
+        memset(&d->area, '\0', sizeof(d->area));
+        /* FIXME mark false? */
+        return;
+    }
+
+    spicex_image_destroy(display);
+    d->area = area;
+    spicex_image_create(display);
+    update_size_request(display);
+
+    gtk_widget_queue_draw(GTK_WIDGET(display));
+}
+
 static void primary_create(SpiceChannel *channel, gint format,
                            gint width, gint height, gint stride,
                            gint shmid, gpointer imgdata, gpointer data)
@@ -1586,14 +1607,13 @@ static void primary_create(SpiceChannel *channel, gint format,
 
     d->format = format;
     d->stride = stride;
-    d->shmid  = shmid;
+    d->shmid = shmid;
+    d->width = width;
+    d->height = height;
     d->data_origin = d->data = imgdata;
 
-    if (d->width != width || d->height != height) {
-        d->width  = width;
-        d->height = height;
-        update_size_request(display);
-    }
+    /* by default, display whole surface */
+    update_area(display, 0, 0, width, height);
 }
 
 static void primary_destroy(SpiceChannel *channel, gpointer data)
@@ -1602,26 +1622,40 @@ static void primary_destroy(SpiceChannel *channel, gpointer data)
     SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
 
     spicex_image_destroy(display);
-    d->format = 0;
     d->width  = 0;
     d->height = 0;
     d->stride = 0;
     d->shmid  = 0;
-    d->data   = 0;
-    d->data_origin = 0;
+    d->data = NULL;
+    d->data_origin = NULL;
 }
 
 static void invalidate(SpiceChannel *channel,
                        gint x, gint y, gint w, gint h, gpointer data)
 {
     SpiceDisplay *display = data;
+    SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+    GdkRectangle rect = {
+        .x = x,
+        .y = y,
+        .width = w,
+        .height = h
+    };
 
     if (!gtk_widget_get_window(GTK_WIDGET(display)))
         return;
 
-    if (!do_color_convert(display, x, y, w, h))
+    if (!gdk_rectangle_intersect(&rect, &d->area, &rect))
         return;
 
+    if (d->convert)
+        do_color_convert(display, &rect);
+
+    x = rect.x - d->area.x;
+    y = rect.y - d->area.y;
+    w = rect.width;
+    h = rect.height;
+
     spicex_image_invalidate(display, &x, &y, &w, &h);
     gtk_widget_queue_draw_area(GTK_WIDGET(display),
                                x, y, w, h);
@@ -1706,7 +1740,7 @@ 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 fbw = d->area.width, fbh = d->area.height;
     int ww, wh;
 
     if (!spicex_is_scaled(display) || !gtk_widget_get_window(GTK_WIDGET(display))) {
@@ -1732,8 +1766,8 @@ static void cursor_invalidate(SpiceDisplay *display)
     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,
+                               (d->mouse_guest_x + d->mx - d->mouse_hotspot.x - d->area.x) * sx,
+                               (d->mouse_guest_y + d->my - d->mouse_hotspot.y - d->area.y) * sy,
                                gdk_pixbuf_get_width(d->mouse_pixbuf) * sx,
                                gdk_pixbuf_get_height(d->mouse_pixbuf) * sy);
 }
@@ -2000,16 +2034,14 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
 
     g_return_val_if_fail(d != NULL, NULL);
     /* TODO: ensure d->data has been exposed? */
+    g_return_val_if_fail(d->data != NULL, NULL);
 
-    data = g_malloc(d->width * d->height * 3);
+    data = g_malloc(d->area.width * d->area.height * 3);
     src = d->data;
     dest = data;
 
-    if (src == NULL || dest == NULL)
-        return NULL;
-
-    for (y = 0; y < d->height; ++y) {
-        for (x = 0; x < d->width; ++x) {
+    for (y = d->area.y; y < d->area.height; ++y) {
+        for (x = d->area.x; x < d->area.width; ++x) {
           dest[0] = src[x * 4 + 2];
           dest[1] = src[x * 4 + 1];
           dest[2] = src[x * 4 + 0];
@@ -2019,7 +2051,7 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
     }
 
     pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, false,
-                                      8, d->width, d->height, d->width * 3,
+                                      8, d->area.width, d->area.height, d->area.width * 3,
                                       (GdkPixbufDestroyNotify)g_free, NULL);
     return pixbuf;
 }
-- 
1.7.10.4



More information about the Spice-devel mailing list