[gtk-vnc] Add a cairo based framebuffer implementation



commit 44dd203e80b5310feaa3af6969a83dc54186069c
Author: Daniel P. Berrange <berrange redhat com>
Date:   Fri Aug 20 16:26:32 2010 +0100

    Add a cairo based framebuffer implementation
    
    In GTK3, GdkImage has been removed completely. The vncimageframebuffer
    class cannot thus be used. Replace it with a vnccairoframebuffer
    class instead, but keep the old one around in GTK2 builds for ABI
    compatability.
    
    * src/vnccairoframebuffer.c, src/vnccairoframebuffer.h,
      src/libgtk-vnc_sym.version: Add new framebuffer impl
      based on Cairo.
    * src/Makefile.am: Disable GdkImage based framebuffer on
      GTK3 builds
    * src/vncdisplay.c: Always use Cairo based framebuffer

 src/Makefile.am            |   17 ++++-
 src/libgtk-vnc_sym.version |    4 +
 src/vnccairoframebuffer.c  |  178 ++++++++++++++++++++++++++++++++++++++++++++
 src/vnccairoframebuffer.h  |   83 ++++++++++++++++++++
 src/vncdisplay.c           |   90 ++++++++++-------------
 5 files changed, 319 insertions(+), 53 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 5ea1c51..85649fb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -98,13 +98,20 @@ gtk_vnc_include_HEADERS = \
 			vncimageframebuffer.h
 
 gtk_vnc_SOURCES = \
-			vncimageframebuffer.h vncimageframebuffer.c \
+			vnccairoframebuffer.h vnccairoframebuffer.c \
 			vncdisplay.h vncdisplay.c \
 			vncdisplayenums.h vncdisplayenums.c \
 			vncdisplaykeymap.h vncdisplaykeymap.c \
 			vncgrabsequence.h vncgrabsequence.c \
 			vncmarshal.h vncmarshal.c
 
+if HAVE_GTK_2
+gtk_vnc_SOURCES += \
+			vncimageframebuffer.h vncimageframebuffer.c
+else
+EXTRA_DIST +=		vncimageframebuffer.h vncimageframebuffer.c
+endif
+
 gtk_vnc_LDFLAGS = \
 			$(VERSION_SCRIPT_FLAGS)$(GTK_VNC_VERSION_FILE) \
 			$(NO_UNDEFINED_FLAGS)
@@ -269,11 +276,17 @@ GVNC_INTROSPECTION_SRCS = \
 			$(srcdir)/vncutil.h $(srcdir)/vncutil.c
 
 GTK_VNC_INTROSPECTION_SRCS = \
-			$(srcdir)/vncimageframebuffer.h $(srcdir)/vncimageframebuffer.c \
+			$(srcdir)/vnccairoframebuffer.h $(srcdir)/vnccairoframebuffer.c \
 			$(srcdir)/vncdisplay.h $(srcdir)/vncdisplay.c \
 			$(srcdir)/vncgrabsequence.h $(srcdir)/vncgrabsequence.c \
 			$(builddir)/vncdisplayenums.h $(builddir)/vncdisplayenums.c
 
+if HAVE_GTK_2
+GTK_VNC_INTROSPECTION_SRCS += \
+			$(srcdir)/vncimageframebuffer.h $(srcdir)/vncimageframebuffer.c \
+else
+endif
+
 GVnc-1.0.gir: libgvnc-1.0.la $(G_IR_SCANNER) Makefile.am
 	$(AM_V_GEN)$(G_IR_SCANNER) -v \
                 --namespace GVnc \
diff --git a/src/libgtk-vnc_sym.version b/src/libgtk-vnc_sym.version
index dc219d4..7b641ec 100644
--- a/src/libgtk-vnc_sym.version
+++ b/src/libgtk-vnc_sym.version
@@ -68,6 +68,10 @@
     vnc_image_framebuffer_new;
     vnc_image_framebuffer_get_image;
 
+    vnc_cairo_framebuffer_get_type;
+    vnc_cairo_framebuffer_new;
+    vnc_cairo_framebuffer_get_surface;
+
 # grab key settings support
     vnc_display_set_grab_keys;
     vnc_display_get_grab_keys;
diff --git a/src/vnccairoframebuffer.c b/src/vnccairoframebuffer.c
new file mode 100644
index 0000000..48e2f2a
--- /dev/null
+++ b/src/vnccairoframebuffer.c
@@ -0,0 +1,178 @@
+/*
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006  Anthony Liguori <anthony codemonkey ws>
+ * Copyright (C) 2009-2010 Daniel P. Berrange <dan berrange com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "vnccairoframebuffer.h"
+#include "vncutil.h"
+
+#define VNC_CAIRO_FRAMEBUFFER_GET_PRIVATE(obj)				\
+	(G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_CAIRO_FRAMEBUFFER, VncCairoFramebufferPrivate))
+
+struct _VncCairoFramebufferPrivate {
+	cairo_surface_t *surface;
+};
+
+
+G_DEFINE_TYPE(VncCairoFramebuffer, vnc_cairo_framebuffer, VNC_TYPE_BASE_FRAMEBUFFER);
+
+
+enum {
+	PROP_0,
+	PROP_SURFACE,
+};
+
+
+static void vnc_cairo_framebuffer_get_property(GObject *object,
+					       guint prop_id,
+					       GValue *value,
+					       GParamSpec *pspec)
+{
+	VncCairoFramebuffer *framebuffer = VNC_CAIRO_FRAMEBUFFER(object);
+	VncCairoFramebufferPrivate *priv = framebuffer->priv;
+
+	switch (prop_id) {
+	case PROP_SURFACE:
+		g_value_set_pointer(value, priv->surface);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+	}
+}
+
+static void vnc_cairo_framebuffer_set_property(GObject *object,
+					       guint prop_id,
+					       const GValue *value,
+					       GParamSpec *pspec)
+{
+	VncCairoFramebuffer *framebuffer = VNC_CAIRO_FRAMEBUFFER(object);
+	VncCairoFramebufferPrivate *priv = framebuffer->priv;
+
+	switch (prop_id) {
+	case PROP_SURFACE:
+		priv->surface = g_value_get_pointer(value);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+	}
+}
+
+static void vnc_cairo_framebuffer_finalize (GObject *object)
+{
+	VncCairoFramebuffer *fb = VNC_CAIRO_FRAMEBUFFER(object);
+	VncCairoFramebufferPrivate *priv = fb->priv;
+
+	if (priv->surface)
+		cairo_surface_destroy(priv->surface);
+
+	G_OBJECT_CLASS(vnc_cairo_framebuffer_parent_class)->finalize (object);
+}
+
+static void vnc_cairo_framebuffer_class_init(VncCairoFramebufferClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = vnc_cairo_framebuffer_finalize;
+	object_class->get_property = vnc_cairo_framebuffer_get_property;
+	object_class->set_property = vnc_cairo_framebuffer_set_property;
+
+	g_object_class_install_property(object_class,
+					PROP_SURFACE,
+					g_param_spec_pointer("surface",
+							     "The cairo surface",
+							     "The cairo surface for the framebuffer",
+							     G_PARAM_READABLE |
+							     G_PARAM_WRITABLE |
+							     G_PARAM_CONSTRUCT_ONLY |
+							     G_PARAM_STATIC_NAME |
+							     G_PARAM_STATIC_NICK |
+							     G_PARAM_STATIC_BLURB));
+
+	g_type_class_add_private(klass, sizeof(VncCairoFramebufferPrivate));
+}
+
+
+void vnc_cairo_framebuffer_init(VncCairoFramebuffer *fb)
+{
+	VncCairoFramebufferPrivate *priv;
+
+	priv = fb->priv = VNC_CAIRO_FRAMEBUFFER_GET_PRIVATE(fb);
+
+	memset(priv, 0, sizeof(*priv));
+}
+
+
+VncCairoFramebuffer *vnc_cairo_framebuffer_new(guint16 width, guint16 height,
+					       const VncPixelFormat *remoteFormat)
+{
+	VncPixelFormat localFormat;
+	cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+	guint8 *pixels;
+
+	VNC_DEBUG("Surface %dx%d", width, height);
+
+	localFormat.red_max = 255;
+	localFormat.green_max = 255;
+	localFormat.blue_max = 255;
+	localFormat.red_shift = 16;
+	localFormat.green_shift = 8;
+	localFormat.blue_shift = 0;
+	localFormat.depth = 32;
+	localFormat.bits_per_pixel = 32;
+	/* XXX is cairo native endian ? */
+	localFormat.byte_order = G_LITTLE_ENDIAN;
+
+	pixels = cairo_image_surface_get_data(surface);
+
+	memset(pixels, 0, width * height * 4);
+
+	return VNC_CAIRO_FRAMEBUFFER(g_object_new(VNC_TYPE_CAIRO_FRAMEBUFFER,
+						  "surface", surface,
+						  "buffer", pixels,
+						  "width", width,
+						  "height", height,
+						  "rowstride", cairo_image_surface_get_stride(surface),
+						  "local-format", &localFormat,
+						  "remote-format", remoteFormat,
+						  NULL));
+}
+
+
+cairo_surface_t *vnc_cairo_framebuffer_get_surface(VncCairoFramebuffer *fb)
+{
+	VncCairoFramebufferPrivate *priv = fb->priv;
+
+	return priv->surface;
+}
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff --git a/src/vnccairoframebuffer.h b/src/vnccairoframebuffer.h
new file mode 100644
index 0000000..5d4d675
--- /dev/null
+++ b/src/vnccairoframebuffer.h
@@ -0,0 +1,83 @@
+/*
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006  Anthony Liguori <anthony codemonkey ws>
+ * Copyright (C) 2009-2010 Daniel P. Berrange <dan berrange com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#ifndef VNC_CAIRO_FRAMEBUFFER_H
+#define VNC_CAIRO_FRAMEBUFFER_H
+
+#include <gdk/gdk.h>
+
+#include <vncbaseframebuffer.h>
+#include <vncutil.h>
+
+G_BEGIN_DECLS
+
+#define VNC_TYPE_CAIRO_FRAMEBUFFER            (vnc_cairo_framebuffer_get_type ())
+#define VNC_CAIRO_FRAMEBUFFER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), VNC_TYPE_CAIRO_FRAMEBUFFER, VncCairoFramebuffer))
+#define VNC_CAIRO_FRAMEBUFFER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), VNC_TYPE_CAIRO_FRAMEBUFFER, VncCairoFramebufferClass))
+#define VNC_IS_CAIRO_FRAMEBUFFER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VNC_TYPE_CAIRO_FRAMEBUFFER))
+#define VNC_IS_CAIRO_FRAMEBUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VNC_TYPE_CAIRO_FRAMEBUFFER))
+#define VNC_CAIRO_FRAMEBUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), VNC_TYPE_CAIRO_FRAMEBUFFER, VncCairoFramebufferClass))
+
+
+typedef struct _VncCairoFramebuffer VncCairoFramebuffer;
+typedef struct _VncCairoFramebufferPrivate VncCairoFramebufferPrivate;
+typedef struct _VncCairoFramebufferClass VncCairoFramebufferClass;
+
+struct _VncCairoFramebuffer
+{
+	VncBaseFramebuffer parent;
+
+	VncCairoFramebufferPrivate *priv;
+
+	/* Do not add fields to this struct */
+};
+
+struct _VncCairoFramebufferClass
+{
+	VncBaseFramebufferClass parent_class;
+
+	/*
+	 * If adding fields to this struct, remove corresponding
+	 * amount of padding to avoid changing overall struct size
+	 */
+	gpointer _vnc_reserved[VNC_PADDING];
+};
+
+
+GType vnc_cairo_framebuffer_get_type(void) G_GNUC_CONST;
+
+VncCairoFramebuffer *vnc_cairo_framebuffer_new(guint16 width, guint16 height,
+					       const VncPixelFormat *remoteFormat);
+
+cairo_surface_t *vnc_cairo_framebuffer_get_surface(VncCairoFramebuffer *fb);
+
+
+G_END_DECLS
+
+#endif /* VNC_CAIRO_FRAMEBUFFER_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index 60a25fd..5c7845e 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -24,11 +24,11 @@
 
 #include "vncdisplay.h"
 #include "vncconnection.h"
-#include "vncimageframebuffer.h"
 #include "vncutil.h"
 #include "vncmarshal.h"
 #include "vncdisplaykeymap.h"
 #include "vncdisplayenums.h"
+#include "vnccairoframebuffer.h"
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
@@ -45,13 +45,12 @@
 
 struct _VncDisplayPrivate
 {
-	GdkGC *gc;
 	GdkPixmap *pixmap;
 	GdkCursor *null_cursor;
 	GdkCursor *remote_cursor;
 
 	VncConnection *conn;
-	VncImageFramebuffer *fb;
+	VncCairoFramebuffer *fb;
 
 	VncDisplayDepthColor depth;
 
@@ -258,17 +257,20 @@ GtkWidget *vnc_display_new(void)
 
 static GdkCursor *create_null_cursor(void)
 {
-	GdkBitmap *image;
-	gchar data[4] = {0};
+	cairo_t *cr;
 	GdkColor fg = { 0, 0, 0, 0 };
 	GdkCursor *cursor;
+	GdkPixmap *pixmap;
 
-	image = gdk_bitmap_create_from_data(NULL, data, 1, 1);
+	pixmap = gdk_pixmap_new(NULL, 1, 1, 1);
+	cr = gdk_cairo_create(pixmap);
+	cairo_rectangle(cr, 0, 0, 1, 1);
+	cairo_fill(cr);
+	cairo_destroy(cr);
 
-	cursor = gdk_cursor_new_from_pixmap(GDK_PIXMAP(image),
-					    GDK_PIXMAP(image),
+	cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap,
 					    &fg, &fg, 0, 0);
-	g_object_unref(image);
+	g_object_unref(pixmap);
 
 	return cursor;
 }
@@ -302,10 +304,10 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose)
 
 	cr = gdk_cairo_create(gtk_widget_get_window(GTK_WIDGET(obj)));
 	cairo_rectangle(cr,
-			expose->area.x,
-			expose->area.y,
-			expose->area.width + 1,
-			expose->area.height + 1);
+			expose->area.x-1,
+			expose->area.y-1,
+			expose->area.width + 2,
+			expose->area.height + 2);
 	cairo_clip(cr);
 
 	/* If we don't have a pixmap, or we're not scaling, then
@@ -820,17 +822,22 @@ static void on_framebuffer_update(VncConnection *conn G_GNUC_UNUSED,
 	VncDisplay *obj = VNC_DISPLAY(widget);
 	VncDisplayPrivate *priv = obj->priv;
 	int ww, wh;
-	GdkRectangle r = { x, y, w, h };
 	int fbw, fbh;
+	cairo_surface_t *surface;
+	cairo_t *cr;
 
 	fbw = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
 	fbh = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
 
-	/* Copy pixbuf to pixmap */
-	gdk_gc_set_clip_rectangle(priv->gc, &r);
-	gdk_draw_image(priv->pixmap, priv->gc,
-		       vnc_image_framebuffer_get_image(priv->fb),
-		       x, y, x, y, w, h);
+	cr = gdk_cairo_create(priv->pixmap);
+	cairo_rectangle(cr, x, y, w, h);
+	cairo_clip(cr);
+
+	surface = vnc_cairo_framebuffer_get_surface(priv->fb);
+	cairo_set_source_surface(cr, surface, 0, 0);
+	cairo_paint(cr);
+
+	cairo_destroy(cr);
 
 	gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
 
@@ -873,8 +880,6 @@ static void do_framebuffer_init(VncDisplay *obj,
 				int width, int height, gboolean quiet)
 {
 	VncDisplayPrivate *priv = obj->priv;
-	GdkVisual *visual;
-	GdkImage *image;
 
 	if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
 		return;
@@ -888,24 +893,17 @@ static void do_framebuffer_init(VncDisplay *obj,
 		priv->pixmap = NULL;
 	}
 
-	if (priv->gc == NULL) {
-		if (!priv->null_cursor)
-			priv->null_cursor = create_null_cursor();
+	if (priv->null_cursor == NULL) {
+		priv->null_cursor = create_null_cursor();
 		if (priv->local_pointer)
 			do_pointer_show(obj);
 		else if (priv->in_pointer_grab || priv->absolute)
 			do_pointer_hide(obj);
-		priv->gc = gdk_gc_new(gtk_widget_get_window(GTK_WIDGET(obj)));
 	}
 
-	visual = gdk_drawable_get_visual(gtk_widget_get_window(GTK_WIDGET(obj)));
-	image = gdk_image_new(GDK_IMAGE_FASTEST, visual, width, height);
-
-	priv->fb = vnc_image_framebuffer_new(image, remoteFormat);
+	priv->fb = vnc_cairo_framebuffer_new(width, height, remoteFormat);
 	priv->pixmap = gdk_pixmap_new(gtk_widget_get_window(GTK_WIDGET(obj)), width, height, -1);
 
-	g_object_unref(G_OBJECT(image));
-
 	vnc_connection_set_framebuffer(priv->conn, VNC_FRAMEBUFFER(priv->fb));
 
 	if (priv->force_size)
@@ -1479,10 +1477,6 @@ static void vnc_display_finalize (GObject *obj)
 		priv->remote_cursor = NULL;
 	}
 
-	if (priv->gc) {
-		g_object_unref (priv->gc);
-		priv->gc = NULL;
-	}
 	if (priv->vncgrabseq) {
 		vnc_grab_sequence_free(priv->vncgrabseq);
 		priv->vncgrabseq = NULL;
@@ -1913,7 +1907,7 @@ gboolean vnc_display_set_credential(VncDisplay *obj, int type, const gchar *data
 
 void vnc_display_set_pointer_local(VncDisplay *obj, gboolean enable)
 {
-	if (obj->priv->gc) {
+	if (obj->priv->null_cursor) {
 		if (enable)
 			do_pointer_show(obj);
 		else if (obj->priv->in_pointer_grab || obj->priv->absolute)
@@ -1967,32 +1961,26 @@ GdkPixbuf *vnc_display_get_pixbuf(VncDisplay *obj)
 {
 	VncDisplayPrivate *priv = obj->priv;
 	GdkPixbuf *pixbuf;
-	GdkImage *image;
+	cairo_surface_t *surface;
 	gint w, h;
 
 	if (!priv->conn ||
 	    !vnc_connection_is_initialized(priv->conn))
 		return NULL;
 
-	image = vnc_image_framebuffer_get_image(priv->fb);
-	#if GTK_CHECK_VERSION(2, 21, 1)
-	w = gdk_image_get_width (image);
-	h = gdk_image_get_height (image);
-	#else
-	w = image->width;
-	h = image->height;
-	#endif
+	surface = vnc_cairo_framebuffer_get_surface(priv->fb);
+	w = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
+	h = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
 
 	pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
 				w,
 				h);
 
-	if (!gdk_pixbuf_get_from_image(pixbuf,
-				       image,
-				       gdk_colormap_get_system(),
-				       0, 0, 0, 0,
-				       w,
-				       h))
+	if (!gdk_pixbuf_get_from_drawable(pixbuf,
+					  priv->pixmap,
+					  NULL,
+					  0, 0, 0, 0,
+					  w, h))
 		return NULL;
 
 	return pixbuf;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]