[gtk-vnc-devel] [PATCH][RFC] OpenGL based scaling



Hi,

I've wanted scaling for a while now. OpenGL seems like the right approach. Using Gerd Hoffman vnc viewer from xenwatch, I hacked something up with gtk-vnc. It still needs some work. I want to add two new interfaces. One to enable scaling and another to set whether to maintain the aspect ratio when scaling. What determines the scaled size is the configured widget size.

I'm using gtkglext to improve portability. We'll probably want to optionally enable support for scaling if gtkglext is available as I don't know that we want to have it as a hard build requirement.

I've updated gvncviewer with a Fullscreen menu option to test with. The really cool thing is that on my system, even connecting to a VNC server displaying a DVD, scaling only incurred a 3-4% CPU usage (this is on top of the about 85% CPU usage already being used).

Regards,

Anthony Liguori
diff -r ec1859fcd58c configure.ac
--- a/configure.ac	Mon Jan 21 09:00:38 2008 -0600
+++ b/configure.ac	Wed Jan 23 21:37:35 2008 -0600
@@ -7,8 +7,12 @@ AC_SUBST(GTK_REQUIRED)
 AC_SUBST(GTK_REQUIRED)
 GNUTLS_REQUIRED=1.4.0
 AC_SUBST(GNUTLS_REQUIRED)
-
+GTKGLEXT_REQUIRED=1.2.0
+AC_SUBST(GTKGLEXT_REQUIRED)
+
+GTK_REQUIRED=2.0.0
 PYGTK_REQUIRED=2.0.0
+GTKGLEXT_REQUIRED=1.2.0
 GTHREAD_REQUIRED=2.0.0
 PYTHON_REQUIRED=2.4
 
@@ -79,9 +83,9 @@ AC_ARG_WITH(examples,
  esac],[withval=no])
 WITH_EXAMPLES=$withval
 
-PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $GTK_REQUIRED)
-AC_SUBST(GTK_CFLAGS)
-AC_SUBST(GTK_LIBS)
+PKG_CHECK_MODULES(GTKGLEXT, gtkglext-1.0 >= $GTKGLEXT_REQUIRED)
+AC_SUBST(GTKGLEXT_CFLAGS)
+AC_SUBST(GTKGLEXT_LIBS)
 
 PKG_CHECK_MODULES(GNUTLS, gnutls >= $GNUTLS_REQUIRED)
 AC_SUBST(GNUTLS_CFLAGS)
diff -r ec1859fcd58c examples/Makefile.am
--- a/examples/Makefile.am	Mon Jan 21 09:00:38 2008 -0600
+++ b/examples/Makefile.am	Wed Jan 23 21:37:35 2008 -0600
@@ -6,7 +6,7 @@ endif
 endif
 
 gvncviewer_SOURCES = gvncviewer.c
-gvncviewer_LDADD = ../src/libgtk-vnc-1.0.la @GTK_LIBS@
-gvncviewer_CFLAGS = @GTK_CFLAGS@ @WARNING_CFLAGS@ -I$(top_srcdir)/src/
+gvncviewer_LDADD = ../src/libgtk-vnc-1.0.la @GTKGLEXT_LIBS@
+gvncviewer_CFLAGS = @GTKGLEXT_CFLAGS@ @WARNING_CFLAGS@ -I$(top_srcdir)/src/
 
 EXTRA_DIST = gvncviewer.py
diff -r ec1859fcd58c examples/gvncviewer.c
--- a/examples/gvncviewer.c	Mon Jan 21 09:00:38 2008 -0600
+++ b/examples/gvncviewer.c	Wed Jan 23 21:37:35 2008 -0600
@@ -1,6 +1,7 @@
 #include "vncdisplay.h"
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
+#include <gtk/gtkgl.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -102,6 +103,14 @@ static void send_cab(GtkWidget *menu G_G
 	guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_BackSpace };
 	printf("Sending Ctrl+Alt+Backspace\n");
 	vnc_display_send_keys(VNC_DISPLAY(vnc), keys, sizeof(keys)/sizeof(keys[0]));
+}
+
+static void do_fullscreen(GtkWidget *menu, GtkWidget *window)
+{
+	if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)))
+		gtk_window_fullscreen(GTK_WINDOW(window));
+	else
+		gtk_window_unfullscreen(GTK_WINDOW(window));
 }
 
 static void vnc_credential(GtkWidget *vnc, GValueArray *credList)
@@ -215,12 +224,13 @@ int main(int argc, char **argv)
 	GtkWidget *vnc;
 	GtkWidget *layout;
 	GtkWidget *menubar;
-	GtkWidget *sendkey;
+	GtkWidget *sendkey, *view;
 	GtkWidget *submenu;
 	GtkWidget *caf1;
 	GtkWidget *caf7;
 	GtkWidget *cad;
 	GtkWidget *cab;
+	GtkWidget *fullscreen;
 
 	if (argc != 2 && argc != 3) {
 		fprintf(stderr, "Usage: %s hostname[:display] [password]\n",
@@ -229,11 +239,14 @@ int main(int argc, char **argv)
 	}
 
 	gtk_init(&argc, &argv);
+	gtk_gl_init(&argc, &argv);
 
 	vnc = vnc_display_new();
 	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	layout = gtk_vbox_new(FALSE, 3);
+	layout = gtk_vbox_new(FALSE, 0);
 	menubar = gtk_menu_bar_new();
+
+	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
 
 	sendkey = gtk_menu_item_new_with_mnemonic("_Send Key");
 	gtk_menu_bar_append(GTK_MENU_BAR(menubar), sendkey);
@@ -252,10 +265,20 @@ int main(int argc, char **argv)
 
 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(sendkey), submenu);
 
+	view = gtk_menu_item_new_with_mnemonic("_View");
+	gtk_menu_bar_append(GTK_MENU_BAR(menubar), view);
+
+	submenu = gtk_menu_new();
+
+	fullscreen = gtk_check_menu_item_new_with_mnemonic("_Full Screen");
+
+	gtk_menu_append(GTK_MENU(submenu), fullscreen);
+
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), submenu);
+
+	gtk_box_pack_start(GTK_BOX(layout), menubar, FALSE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(layout), vnc, TRUE, TRUE, 0);
 	gtk_container_add(GTK_CONTAINER(window), layout);
-	gtk_container_add(GTK_CONTAINER(layout), menubar);
-	gtk_container_add(GTK_CONTAINER(layout), vnc);
-	gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
 	gtk_widget_realize(vnc);
 
 	if (argc == 3)
@@ -307,6 +330,8 @@ int main(int argc, char **argv)
 			   GTK_SIGNAL_FUNC(send_cad), vnc);
 	gtk_signal_connect(GTK_OBJECT(cab), "activate",
 			   GTK_SIGNAL_FUNC(send_cab), vnc);
+	gtk_signal_connect(GTK_OBJECT(fullscreen), "toggled",
+			   GTK_SIGNAL_FUNC(do_fullscreen), window);
 
 	gtk_main();
 
diff -r ec1859fcd58c plugin/Makefile.am
--- a/plugin/Makefile.am	Mon Jan 21 09:00:38 2008 -0600
+++ b/plugin/Makefile.am	Wed Jan 23 21:37:35 2008 -0600
@@ -6,11 +6,11 @@ gtk_vnc_plugin_la_SOURCES = \
 gtk_vnc_plugin_la_SOURCES = \
 	gtk-vnc-plugin.c gtk-vnc-plugin.h npshell.c npunix.c
 gtk_vnc_plugin_la_LIBADD = \
-	../src/libgtk-vnc-1.0.la @GTK_LIBS@ @FIREFOX_PLUGIN_LIBS@
+	../src/libgtk-vnc-1.0.la @GTKGLEXT_LIBS@ @FIREFOX_PLUGIN_LIBS@
 gtk_vnc_plugin_la_LDFLAGS = \
 	-module -avoid-version
 gtk_vnc_plugin_la_CFLAGS = \
-	-I$(top_srcdir)/src @GTK_CFLAGS@ @FIREFOX_PLUGIN_CFLAGS@ \
+	-I$(top_srcdir)/src @GTKGLEXT_CFLAGS@ @FIREFOX_PLUGIN_CFLAGS@ \
 	@WARNING_CFLAGS@ @WERROR_CFLAGS@ @DEBUG_CFLAGS@
 
 all-local: gtk-vnc-plugin.so
diff -r ec1859fcd58c src/Makefile.am
--- a/src/Makefile.am	Mon Jan 21 09:00:38 2008 -0600
+++ b/src/Makefile.am	Wed Jan 23 21:37:35 2008 -0600
@@ -3,10 +3,9 @@ EXTRA_DIST = libgtk-vnc_sym.version vncm
 
 lib_LTLIBRARIES = libgtk-vnc-1.0.la
 
-libgtk_vnc_1_0_la_LIBADD = @GTK_LIBS@ @GNUTLS_LIBS@ @GTHREAD_LIBS@
-libgtk_vnc_1_0_la_CFLAGS = @GTK_CFLAGS@ @GNUTLS_CFLAGS@ @GTHREAD_CFLAGS@ \
-			   @WARNING_CFLAGS@ \
-                           -DSYSCONFDIR=\""$(sysconfdir)"\" \
+libgtk_vnc_1_0_la_LIBADD = @GTKGLEXT_LIBS@ @GNUTLS_LIBS@ @GTHREAD_LIBS@
+libgtk_vnc_1_0_la_CFLAGS = @GTKGLEXT_CFLAGS@ @GNUTLS_CFLAGS@ @GTHREAD_CFLAGS@ \
+			   @WARNING_CFLAGS@ -DSYSCONFDIR=\""$(sysconfdir)"\" \
                            @DEBUG_CFLAGS@
 libgtk_vnc_1_0_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libgtk-vnc_sym.version \
                             -version-info 0:1:0
@@ -46,7 +45,8 @@ gtkvnc_la_LIBADD = libgtk-vnc-1.0.la @PY
 gtkvnc_la_LIBADD = libgtk-vnc-1.0.la @PYGTK_LIBS@
 # Auto-generated C code for Python binding is full of compiler warnings :-(
 #gtkvnc_la_CFLAGS = @GTK_CFLAGS@ @WARNING_CFLAGS@ @PYTHON_INCLUDES@ @PYGTK_CFLAGS@
-gtkvnc_la_CFLAGS = @GTK_CFLAGS@ @PYTHON_INCLUDES@ @PYGTK_CFLAGS@ @DEBUG_CFLAGS@
+gtkvnc_la_CFLAGS = @GTKGLEXT_CFLAGS@ @PYTHON_INCLUDES@ @PYGTK_CFLAGS@ \
+		   @DEBUG_CFLAGS@
 gtkvnc_la_LDFLAGS = -module -avoid-version -fPIC
 gtkvnc_la_SOURCES = vncmodule.c vncmodule.defs.c
 
diff -r ec1859fcd58c src/vncdisplay.c
--- a/src/vncdisplay.c	Mon Jan 21 09:00:38 2008 -0600
+++ b/src/vncdisplay.c	Wed Jan 23 21:37:35 2008 -0600
@@ -23,6 +23,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <pwd.h>
+#include <gtk/gtkgl.h>
+#include <GL/gl.h>
 
 #define VNC_DISPLAY_GET_PRIVATE(obj) \
       (G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_DISPLAY, VncDisplayPrivate))
@@ -37,6 +39,18 @@ struct _VncDisplayPrivate
 	GdkCursor *null_cursor;
 	GdkCursor *remote_cursor;
 
+	int gl_enabled;
+	GdkGLConfig *gl_config;
+	GdkGLDrawable *gl_drawable;
+	GdkGLContext *gl_context;
+	int gl_tex_max;
+	uint8_t *gl_tex_data;
+	int gl_texture_width;
+	int gl_texture_height;
+	int gl_width;
+	int gl_height;
+	GLuint gl_tex;
+
 	struct gvnc_framebuffer fb;
 	struct coroutine coroutine;
 	struct gvnc *gvnc;
@@ -120,38 +134,90 @@ static gboolean expose_event(GtkWidget *
 	VncDisplay *obj = VNC_DISPLAY(widget);
 	VncDisplayPrivate *priv = obj->priv;
 	int x, y, w, h;
-	GdkRectangle drawn;
-	GdkRegion *clear, *copy;
-
-	if (priv->image == NULL)
+
+	if (priv->image == NULL && priv->gl_tex_data == NULL)
 		return TRUE;
 
-	x = MIN(expose->area.x, priv->fb.width);
-	y = MIN(expose->area.y, priv->fb.height);
-	w = MIN(expose->area.x + expose->area.width, priv->fb.width);
-	h = MIN(expose->area.y + expose->area.height, priv->fb.height);
-	w -= x;
-	h -= y;
-
-	drawn.x = x;
-	drawn.y = y;
-	drawn.width = w;
-	drawn.height = h;
-
-	clear = gdk_region_rectangle(&expose->area);
-	copy = gdk_region_rectangle(&drawn);
-	gdk_region_subtract(clear, copy);
-
-	gdk_gc_set_clip_region(priv->gc, copy);
-	gdk_draw_image(widget->window, priv->gc, priv->image,
-		       x, y, x, y, w, h);
-
-	gdk_gc_set_clip_region(priv->gc, clear);
-	gdk_draw_rectangle(widget->window, priv->gc, TRUE, expose->area.x, expose->area.y,
-			   expose->area.width, expose->area.height);
-
-	gdk_region_destroy(clear);
-	gdk_region_destroy(copy);
+	if (priv->gl_enabled) {
+		float rx, ry;
+		int wx = 0, wy = 0;
+		int ww = priv->gl_width, wh = priv->gl_height;
+		double scale_x, scale_y;
+
+		scale_x = (double)priv->gl_width / priv->fb.width;
+		scale_y = (double)priv->gl_height / priv->fb.height;
+
+		x = expose->area.x / scale_x;
+		y = expose->area.y / scale_y;
+		w = expose->area.width / scale_x;
+		h = expose->area.height / scale_y;
+
+		y -= 5;
+		h += 10;
+		if (y < 0)
+			y = 0;
+
+		x = MIN(x, priv->fb.width);
+		y = MIN(y, priv->fb.height);
+		w = MIN(x + w, priv->fb.width);
+		h = MIN(y + h, priv->fb.height);
+		w -= x;
+		h -= y;
+
+		gdk_gl_drawable_gl_begin(priv->gl_drawable, priv->gl_context);
+		glBindTexture(GL_TEXTURE_2D, priv->gl_tex);
+		glPixelStorei(GL_UNPACK_ROW_LENGTH, priv->fb.width);
+		glTexSubImage2D(GL_TEXTURE_2D, 0,
+				x, y, w, h,
+				GL_BGRA_EXT /* GL_RGB */,
+				GL_UNSIGNED_BYTE,
+				priv->gl_tex_data +
+				y * 4 * priv->fb.width +
+				x * 4);
+		rx = (float)priv->fb.width  / priv->gl_texture_width;
+		ry = (float)priv->fb.height / priv->gl_texture_height;
+		
+		glEnable(GL_TEXTURE_2D);
+		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+		glBegin(GL_QUADS);
+		glTexCoord2f(0,ry);  glVertex3f(wx, wy, 0);
+		glTexCoord2f(0,0);  glVertex3f(wx, wy+wh, 0);
+		glTexCoord2f(rx,0);  glVertex3f(wx+ww, wy+wh, 0);
+		glTexCoord2f(rx,ry);  glVertex3f(wx+ww, wy, 0);
+		glEnd();
+		glDisable(GL_TEXTURE_2D);
+		gdk_gl_drawable_gl_end(priv->gl_drawable);
+	} else {
+		GdkRectangle drawn;
+		GdkRegion *clear, *copy;
+
+		x = MIN(expose->area.x, priv->fb.width);
+		y = MIN(expose->area.y, priv->fb.height);
+		w = MIN(expose->area.x + expose->area.width, priv->fb.width);
+		h = MIN(expose->area.y + expose->area.height, priv->fb.height);
+		w -= x;
+		h -= y;
+
+		drawn.x = x;
+		drawn.y = y;
+		drawn.width = w;
+		drawn.height = h;
+
+		clear = gdk_region_rectangle(&expose->area);
+		copy = gdk_region_rectangle(&drawn);
+		gdk_region_subtract(clear, copy);
+
+		gdk_gc_set_clip_region(priv->gc, copy);
+		gdk_draw_image(widget->window, priv->gc, priv->image,
+			       x, y, x, y, w, h);
+
+		gdk_gc_set_clip_region(priv->gc, clear);
+		gdk_draw_rectangle(widget->window, priv->gc, TRUE, expose->area.x, expose->area.y,
+				   expose->area.width, expose->area.height);
+
+		gdk_region_destroy(clear);
+		gdk_region_destroy(copy);
+	}
 
 	return TRUE;
 }
@@ -341,6 +407,12 @@ static gboolean motion_event(GtkWidget *
 
 	if (priv->last_x != -1) {
 		if (priv->absolute) {
+			if (priv->gl_enabled) {
+				motion->x *= priv->fb.width;
+				motion->x /= priv->gl_width;
+				motion->y *= priv->fb.height;
+				motion->y /= priv->gl_height;
+			}
 			dx = (int)motion->x;
 			dy = (int)motion->y;
 		} else {
@@ -517,36 +589,45 @@ static gboolean focus_event(GtkWidget *w
         return TRUE;
 }
 
+static void realize_event(GtkWidget *widget, gpointer data G_GNUC_UNUSED)
+{
+	VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
+
+	priv->gl_drawable = gtk_widget_get_gl_drawable(widget);
+	priv->gl_context = gtk_widget_get_gl_context(widget);
+
+	gdk_gl_drawable_make_current(priv->gl_drawable, priv->gl_context);
+	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &priv->gl_tex_max);
+	printf("tex_max - %d\n", priv->gl_tex_max);
+}
 
 static gboolean on_update(void *opaque, int x, int y, int w, int h)
 {
-	GtkWidget *obj = GTK_WIDGET(opaque);
-
-	gtk_widget_queue_draw_area(obj, x, y, w, h);
+	GtkWidget *widget = GTK_WIDGET(opaque);
+	VncDisplay *obj = VNC_DISPLAY(widget);
+	VncDisplayPrivate *priv = obj->priv;
+
+	if (priv->gl_enabled) {
+		double scale_x, scale_y;
+
+		scale_x = (double)priv->gl_width / priv->fb.width;
+		scale_y = (double)priv->gl_height / priv->fb.height;
+
+		x *= scale_x;
+		y *= scale_y;
+		w *= scale_x;
+		h *= scale_y;
+	}
+
+	gtk_widget_queue_draw_area(widget, x, y, w, h);
 
 	return TRUE;
 }
 
-static gboolean on_resize(void *opaque, int width, int height)
-{
-	VncDisplay *obj = VNC_DISPLAY(opaque);
+static void setup_gdk_image(VncDisplay *obj, gint width, gint height)
+{
 	VncDisplayPrivate *priv = obj->priv;
 	GdkVisual *visual;
-
-	if (priv->gvnc == NULL || !gvnc_is_initialized(priv->gvnc))
-		return TRUE;
-
-	if (priv->image)
-		g_object_unref(priv->image);
-
-	if (priv->gc == NULL) {
-		priv->null_cursor = create_null_cursor();
-		if (priv->local_pointer)
-			do_pointer_show(obj);
-		else
-			do_pointer_hide(obj);
-		priv->gc = gdk_gc_new(GTK_WIDGET(obj)->window);
-	}
 
 	visual = gdk_drawable_get_visual(GTK_WIDGET(obj)->window);
 	
@@ -565,6 +646,92 @@ static gboolean on_resize(void *opaque, 
 	priv->fb.data = (uint8_t *)priv->image->mem;
 
 	gtk_widget_set_size_request(GTK_WIDGET(obj), width, height);
+}
+
+static int pow_of_2(int value)
+{
+	int i;
+	for (i = 0; value >= (1 << i); i++);
+	return (1 << i);
+}
+
+static void setup_gl_image(VncDisplay *obj, gint width, gint height)
+{
+	VncDisplayPrivate *priv = VNC_DISPLAY(obj)->priv;
+	void *dummy;
+
+	priv->gl_texture_width = pow_of_2(width);
+	priv->gl_texture_height = pow_of_2(height);
+
+	gdk_gl_drawable_gl_begin(priv->gl_drawable, priv->gl_context);
+
+	glGenTextures(1, &priv->gl_tex);
+	glBindTexture(GL_TEXTURE_2D, priv->gl_tex);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+	dummy = g_malloc(priv->gl_texture_width*priv->gl_texture_height*4);
+	memset(dummy, 0, priv->gl_texture_width*priv->gl_texture_height*4);
+	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+		     priv->gl_texture_width, priv->gl_texture_height, 0,
+		     GL_RGB, GL_UNSIGNED_BYTE,
+		     dummy);
+	g_free(dummy);
+	
+	gdk_gl_drawable_gl_end(priv->gl_drawable);
+
+	priv->gl_tex_data = g_malloc(width * height * 4);
+
+	priv->fb.red_mask = 0xFF;
+	priv->fb.green_mask = 0xFF;
+	priv->fb.blue_mask = 0xFF;
+	priv->fb.red_shift = 16;
+	priv->fb.green_shift = 8;
+	priv->fb.blue_shift = 0;
+	priv->fb.depth = 32;
+	priv->fb.bpp = 4;
+	priv->fb.width = width;
+	priv->fb.height = height;
+	priv->fb.linesize = priv->fb.width * priv->fb.bpp;
+	priv->fb.data = (uint8_t *)priv->gl_tex_data;
+}
+
+static gboolean on_resize(void *opaque, int width, int height)
+{
+	VncDisplay *obj = VNC_DISPLAY(opaque);
+	VncDisplayPrivate *priv = obj->priv;
+
+	if (priv->gvnc == NULL || !gvnc_is_initialized(priv->gvnc))
+		return TRUE;
+
+	if (priv->image) {
+		g_object_unref(priv->image);
+		priv->image = NULL;
+	}
+
+	if (priv->gl_tex_data) {
+		printf("Resizing\n");
+		gdk_gl_drawable_gl_begin(priv->gl_drawable,
+					 priv->gl_context);
+		glDeleteTextures(1, &priv->gl_tex);
+		gdk_gl_drawable_gl_end(priv->gl_drawable);
+		g_free(priv->gl_tex_data);
+		priv->gl_tex_data = NULL;
+	}
+
+	if (priv->gc == NULL) {
+		priv->null_cursor = create_null_cursor();
+		if (priv->local_pointer)
+			do_pointer_show(obj);
+		else
+			do_pointer_hide(obj);
+		priv->gc = gdk_gc_new(GTK_WIDGET(obj)->window);
+	}
+
+	if (priv->gl_enabled)
+		setup_gl_image(obj, width, height);
+	else
+		setup_gdk_image(obj, width, height);
 
 	gvnc_set_local(priv->gvnc, &priv->fb);
 
@@ -574,6 +741,151 @@ static gboolean on_resize(void *opaque, 
 		       width, height);
 
 	return TRUE;
+}
+
+static void build_gl_image_from_gdk(uint32_t *data, GdkImage *image)
+{
+	GdkVisual *visual;
+	int i, j;
+	uint8_t *row;
+
+	visual = image->visual;
+	row = image->mem;
+	for (j = 0; j < image->height; j++) {
+		uint8_t *src = row;
+		for (i = 0; i < image->width; i++) {
+			uint32_t pixel = 0;
+			switch (image->bpp) {
+			case 1:
+				pixel = *(uint8_t *)src;
+				break;
+			case 2:
+				pixel = *(uint16_t *)src;
+				break;
+			case 4:
+				pixel = *(uint32_t *)src;
+				break;
+			}
+			*data = ((pixel & visual->red_mask) >> visual->red_shift) << (24 - visual->red_prec) |
+				((pixel & visual->green_mask) >> visual->green_shift) << (16 - visual->green_prec) |
+				((pixel & visual->blue_mask) >> visual->blue_shift) << (8 - visual->blue_prec);
+			src += image->bpp;
+			data++;
+		}
+		row += image->bpl;
+
+	}
+}
+
+static void build_gdk_image_from_gl(GdkImage *image, uint32_t *data)
+{
+	GdkVisual *visual;
+	int i, j;
+	uint8_t *row;
+
+	visual = image->visual;
+	row = image->mem;
+	for (j = 0; j < image->height; j++) {
+		uint8_t *dst = row;
+		for (i = 0; i < image->width; i++) {
+			uint32_t pixel;
+
+			pixel = (((*data >> (24 - visual->red_prec)) << visual->red_shift) & visual->red_mask) |
+				(((*data >> (16 - visual->green_prec)) << visual->green_shift) & visual->green_mask) |
+				(((*data >> (8 - visual->blue_prec)) << visual->blue_shift) & visual->blue_mask);
+
+			switch (image->bpp) {
+			case 1:
+				*(uint8_t *)dst = pixel;
+				break;
+			case 2:
+				*(uint16_t *)dst = pixel;
+				break;
+			case 4:
+				*(uint32_t *)dst = pixel;
+				break;
+			}
+			dst += image->bpp;
+			data++;
+		}
+		row += image->bpl;
+	}
+}
+
+static void scale_display(VncDisplay *obj, gint width, gint height)
+{
+	VncDisplayPrivate *priv = VNC_DISPLAY(obj)->priv;
+
+	if (priv->gl_drawable == NULL)
+		return;
+
+	if (priv->gl_enabled == 0) {
+		GdkImage *image;
+
+		printf("enabling gl\n");
+		priv->gl_enabled = 1;
+
+		image = priv->image;
+		priv->image = NULL;
+	
+		on_resize(obj, priv->fb.width, priv->fb.height);
+		build_gl_image_from_gdk((uint32_t *)priv->fb.data, image);
+
+		g_object_unref(image);
+	}
+
+	priv->gl_width = width;
+	priv->gl_height = height;
+
+	gdk_gl_drawable_gl_begin(priv->gl_drawable, priv->gl_context);
+	glClearColor (0.0, 0.0, 0.0, 0.0);
+	glShadeModel(GL_FLAT);
+	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    
+	glViewport(0, 0, priv->gl_width, priv->gl_height);
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrtho(0.0, priv->gl_width, 0.0, priv->gl_height, -1, 1);
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+	gdk_gl_drawable_gl_end(priv->gl_drawable);
+}
+
+static int absi(int value)
+{
+	return (value < 0) ? -value : value;
+}
+
+static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *configure,
+				gpointer data G_GNUC_UNUSED)
+{
+	VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
+
+	printf("size is: (%d, %d)\n", configure->width, configure->height);
+
+	if (absi(priv->fb.width - configure->width) > 20 ||
+	    absi(priv->fb.height - configure->height) > 20)
+		scale_display(VNC_DISPLAY(widget),
+			      configure->width, configure->height);
+	else if (priv->gl_enabled) {
+		void *data;
+		printf("disabling gl\n");
+		priv->gl_enabled = 0;
+
+		data = priv->gl_tex_data;
+		priv->gl_tex_data = NULL;
+
+		on_resize(widget, priv->fb.width, priv->fb.height);
+
+		build_gdk_image_from_gl(priv->image, (uint32_t *)data);
+		gdk_gl_drawable_gl_begin(priv->gl_drawable,
+					 priv->gl_context);
+		glDeleteTextures(1, &priv->gl_tex);
+		gdk_gl_drawable_gl_end(priv->gl_drawable);
+		g_free(data);
+	}
+
+	return FALSE;
 }
 
 static gboolean on_pointer_type_change(void *opaque, int absolute)
@@ -1027,12 +1339,34 @@ static void vnc_display_finalize (GObjec
 static void vnc_display_finalize (GObject *obj)
 {
 	VncDisplay *display = VNC_DISPLAY (obj);
+	VncDisplayPrivate *priv = display->priv;
+
 	GVNC_DEBUG("Releasing VNC widget\n");
-	if (gvnc_is_open(display->priv->gvnc)) {
+	if (gvnc_is_open(priv->gvnc)) {
 		g_warning("VNC widget finalized before the connection finished shutting down");
 	}
-	gvnc_free(display->priv->gvnc);
+	gvnc_free(priv->gvnc);
 	display->priv->gvnc = NULL;
+
+	if (priv->gl_enabled) {
+		gdk_gl_drawable_gl_begin(priv->gl_drawable,
+					 priv->gl_context);
+		glDeleteTextures(1, &priv->gl_tex);
+		gdk_gl_drawable_gl_end(priv->gl_drawable);
+		g_free(priv->gl_tex_data);
+		priv->gl_tex_data = NULL;
+	}
+
+	if (priv->image) {
+		g_object_unref(priv->image);
+		priv->image = NULL;
+	}
+
+	if (priv->gl_config) {
+		g_object_unref(G_OBJECT(priv->gl_config));
+		priv->gl_config = NULL;
+	}
+
 	G_OBJECT_CLASS (vnc_display_parent_class)->finalize (obj);
 }
 
@@ -1204,6 +1538,12 @@ static void vnc_display_init(VncDisplay 
 {
 	GtkObject *obj = GTK_OBJECT(display);
 	GtkWidget *widget = GTK_WIDGET(display);
+	VncDisplayPrivate *priv;
+	static const int attrib[] = { GDK_GL_RGBA,
+				      GDK_GL_RED_SIZE, 1,
+				      GDK_GL_GREEN_SIZE, 1,
+				      GDK_GL_BLUE_SIZE, 1,
+				      GDK_GL_ATTRIB_LIST_NONE };
 
 	g_signal_connect(obj, "expose-event",
 			 G_CALLBACK(expose_event), NULL);
@@ -1225,6 +1565,10 @@ static void vnc_display_init(VncDisplay 
 			 G_CALLBACK(leave_event), NULL);
 	g_signal_connect(obj, "focus-out-event",
 			 G_CALLBACK(focus_event), NULL);
+	g_signal_connect(obj, "realize",
+			 G_CALLBACK(realize_event), NULL);
+	g_signal_connect(obj, "configure-event",
+			 G_CALLBACK(configure_event), NULL);
 
 	GTK_WIDGET_SET_FLAGS(obj, GTK_CAN_FOCUS);
 
@@ -1239,14 +1583,25 @@ static void vnc_display_init(VncDisplay 
 			      GDK_KEY_PRESS_MASK);
 	gtk_widget_set_double_buffered(widget, FALSE);
 
-	display->priv = VNC_DISPLAY_GET_PRIVATE(display);
-	memset(display->priv, 0, sizeof(VncDisplayPrivate));
-	display->priv->last_x = -1;
-	display->priv->last_y = -1;
-	display->priv->absolute = 1;
-	display->priv->fd = -1;
-
-	display->priv->gvnc = gvnc_new(&vnc_display_ops, obj);
+	priv = display->priv = VNC_DISPLAY_GET_PRIVATE(display);
+	memset(priv, 0, sizeof(VncDisplayPrivate));
+	priv->last_x = -1;
+	priv->last_y = -1;
+	priv->absolute = 1;
+	priv->fd = -1;
+
+	priv->gl_config = gdk_gl_config_new(attrib);
+	if (!gtk_widget_set_gl_capability(widget,
+					  priv->gl_config,
+					  NULL,
+					  TRUE,
+					  GDK_GL_RGBA_TYPE)) {
+		g_warning("Could not enable OpenGL");
+		g_object_unref(G_OBJECT(priv->gl_config));
+		priv->gl_config = NULL;
+	}
+
+	priv->gvnc = gvnc_new(&vnc_display_ops, obj);
 }
 
 static int vnc_display_best_path(char *buf,


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