[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