[gtk-vnc-devel] [PATCH] gtkgl based scaling



I've just committed this patch (it's not in the 0.3.3 release). Please try it out and let me know what you think.

I've modified gvncviewer so that it has a Full Screen and OpenGL Scaling menu option. The dependency on gtkglext is optional and there's also a new optional dependency on libview. This is only used by gvncviewer though so I do not recommend that any distro packages make gtk-vnc depend on libview.

Regards,

Anthony Liguori
# HG changeset patch
# User Anthony Liguori <anthony codemonkey ws>
# Date 1201983249 21600
# Node ID a3bcd91c40e8d217e2c751341ff3f9619b5f5d02
# Parent  a01e9f1fa9abc01cb7f26ab7496c32f89687ee20
Support for OpenGL scaling

diff -r a01e9f1fa9ab -r a3bcd91c40e8 configure.ac
--- a/configure.ac	Sat Feb 02 14:12:33 2008 -0600
+++ b/configure.ac	Sat Feb 02 14:14:09 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)
 
 PYGTK_REQUIRED=2.0.0
+GTKGLEXT_REQUIRED=1.2.0
+VIEW_REQUIRED=0.6.0
 GTHREAD_REQUIRED=2.0.0
 PYTHON_REQUIRED=2.4
 
@@ -82,6 +86,38 @@ PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $GTK_
 PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $GTK_REQUIRED)
 AC_SUBST(GTK_CFLAGS)
 AC_SUBST(GTK_LIBS)
+
+AC_ARG_WITH(gtkglext,
+[  --with-gtkglext         enable gtkgl support],
+[case "${withval}" in
+   yes|no) ;;
+   *)	   AC_MSG_ERROR([bad value ${withval} for gtkglext option]) ;;
+ esac],[withval=yes])
+
+WITH_GTKGLEXT=0
+if test "${withval}" = "yes"; then
+  PKG_CHECK_MODULES(GTKGLEXT, gtkglext-1.0 >= $GTKGLEXT_REQUIRED,
+	            [WITH_GTKGLEXT=1], [WITH_GTKGLEXT=0])
+fi
+AC_DEFINE_UNQUOTED(WITH_GTKGLEXT,[$WITH_GTKGLEXT], [Whether to use gtkglext])
+AC_SUBST(GTKGLEXT_CFLAGS)
+AC_SUBST(GTKGLEXT_LIBS)
+
+AC_ARG_WITH(libview,
+[  --with-libview          enable libview support in gvncviewer],
+[case "${withval}" in
+   yes|no) ;;
+   *)	   AC_MSG_ERROR([bad value ${withval} for libview option]) ;;
+ esac],[withval=yes])
+
+WITH_LIBVIEW=0
+if test "${withval}" = "yes"; then
+  PKG_CHECK_MODULES(VIEW, libview >= $VIEW_REQUIRED,
+		   [WITH_LIBVIEW=1], [WITH_LIBVIEW=0])
+fi
+AC_DEFINE_UNQUOTED(WITH_LIBVIEW,[$WITH_LIBVIEW], [Whether to use libview])
+AC_SUBST(VIEW_CFLAGS)
+AC_SUBST(VIEW_LIBS)
 
 PKG_CHECK_MODULES(GNUTLS, gnutls >= $GNUTLS_REQUIRED)
 AC_SUBST(GNUTLS_CFLAGS)
diff -r a01e9f1fa9ab -r a3bcd91c40e8 examples/Makefile.am
--- a/examples/Makefile.am	Sat Feb 02 14:12:33 2008 -0600
+++ b/examples/Makefile.am	Sat Feb 02 14:14:09 2008 -0600
@@ -6,7 +6,9 @@ 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 @GTK_CFLAGS@ @GTKGLEXT_LIBS@ \
+		   @VIEW_LIBS@
+gvncviewer_CFLAGS = @GTK_CFLAGS@ @GTKGLEXT_CFLAGS@ @WARNING_CFLAGS@ \
+		    @VIEW_CFLAGS@ -I$(top_srcdir)/src/
 
 EXTRA_DIST = gvncviewer.py
diff -r a01e9f1fa9ab -r a3bcd91c40e8 examples/gvncviewer.c
--- a/examples/gvncviewer.c	Sat Feb 02 14:12:33 2008 -0600
+++ b/examples/gvncviewer.c	Sat Feb 02 14:14:09 2008 -0600
@@ -8,6 +8,16 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
+
+#include "config.h"
+
+#if WITH_LIBVIEW
+#include <libview/autoDrawer.h>
+#endif
+
+#if WITH_GTKGLEXT
+#include <gtk/gtkgl.h>
+#endif
 
 static void set_title(VncDisplay *vnc, GtkWidget *window, gboolean grabbed)
 {
@@ -104,6 +114,22 @@ static void send_cab(GtkWidget *menu G_G
 	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 do_scaling(GtkWidget *menu, GtkWidget *vnc)
+{
+	if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)))
+		vnc_display_set_scaling(VNC_DISPLAY(vnc), TRUE);
+	else
+		vnc_display_set_scaling(VNC_DISPLAY(vnc), FALSE);
+}
+
 static void vnc_credential(GtkWidget *vnc, GValueArray *credList)
 {
 	GtkWidget *dialog = NULL;
@@ -148,17 +174,18 @@ static void vnc_credential(GtkWidget *vn
 
 		for (i = 0, row =0 ; i < credList->n_values ; i++) {
 			GValue *cred = g_value_array_get_nth(credList, i);
+			entry[row] = gtk_entry_new();
 			switch (g_value_get_enum(cred)) {
 			case VNC_DISPLAY_CREDENTIAL_USERNAME:
 				label[row] = gtk_label_new("Username:");
 				break;
 			case VNC_DISPLAY_CREDENTIAL_PASSWORD:
 				label[row] = gtk_label_new("Password:");
+				gtk_entry_set_activates_default(GTK_ENTRY(entry[row]), TRUE);
 				break;
 			default:
 				continue;
 			}
-			entry[row] = gtk_entry_new();
 			if (g_value_get_enum (cred) == VNC_DISPLAY_CREDENTIAL_PASSWORD)
 				gtk_entry_set_visibility (GTK_ENTRY (entry[row]), FALSE);
 
@@ -207,6 +234,24 @@ static void vnc_credential(GtkWidget *vn
 		gtk_widget_destroy(GTK_WIDGET(dialog));
 }
 
+#if WITH_LIBVIEW
+static gboolean window_state_event(GtkWidget *widget,
+				   GdkEventWindowState *event,
+				   gpointer data)
+{
+	ViewAutoDrawer *drawer = VIEW_AUTODRAWER(data);
+
+	if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
+		if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
+			ViewAutoDrawer_SetPinned(drawer, FALSE);
+		else
+			ViewAutoDrawer_SetPinned(drawer, TRUE);
+	}
+
+	return FALSE;
+}
+#endif
+
 int main(int argc, char **argv)
 {
 	char port[1024], hostname[1024];
@@ -215,12 +260,14 @@ 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;
+	GtkWidget *scaling;
 
 	if (argc != 2 && argc != 3) {
 		fprintf(stderr, "Usage: %s hostname[:display] [password]\n",
@@ -229,11 +276,20 @@ int main(int argc, char **argv)
 	}
 
 	gtk_init(&argc, &argv);
+#if WITH_GTKGLEXT
+	gtk_gl_init(&argc, &argv);
+#endif
 
 	vnc = vnc_display_new();
 	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	layout = gtk_vbox_new(FALSE, 3);
+#if WITH_LIBVIEW
+	layout = ViewAutoDrawer_New();
+#else
+	layout = gtk_vbox_new(FALSE, 0);
+#endif
 	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 +308,28 @@ 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");
+	scaling = gtk_check_menu_item_new_with_mnemonic("OpenGL _Scaling");
+
+	gtk_menu_append(GTK_MENU(submenu), fullscreen);
+	gtk_menu_append(GTK_MENU(submenu), scaling);
+
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), submenu);
+
+#if WITH_LIBVIEW
+	ViewAutoDrawer_SetPinned(VIEW_AUTODRAWER(layout), TRUE);
+	ViewOvBox_SetOver(VIEW_OV_BOX(layout), menubar);
+	ViewOvBox_SetUnder(VIEW_OV_BOX(layout), vnc);
+#else
+	gtk_box_pack_start(GTK_BOX(layout), menubar, FALSE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(layout), vnc, TRUE, TRUE, 0);
+#endif
 	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 +381,14 @@ 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_signal_connect(GTK_OBJECT(scaling), "toggled",
+			   GTK_SIGNAL_FUNC(do_scaling), vnc);
+#if WITH_LIBVIEW
+	gtk_signal_connect(GTK_OBJECT(window), "window-state-event",
+			   GTK_SIGNAL_FUNC(window_state_event), layout);
+#endif
 
 	gtk_main();
 
diff -r a01e9f1fa9ab -r a3bcd91c40e8 plugin/Makefile.am
--- a/plugin/Makefile.am	Sat Feb 02 14:12:33 2008 -0600
+++ b/plugin/Makefile.am	Sat Feb 02 14:14:09 2008 -0600
@@ -6,12 +6,13 @@ 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 @GTK_LIBS@ @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@ \
-	@WARNING_CFLAGS@ @WERROR_CFLAGS@ @DEBUG_CFLAGS@
+	-I$(top_srcdir)/src @GTK_CFLAGS@ @GTKGLEXT_CFLAGS@ \
+	@FIREFOX_PLUGIN_CFLAGS@ @WARNING_CFLAGS@ @WERROR_CFLAGS@ @DEBUG_CFLAGS@
 
 all-local: gtk-vnc-plugin.so
 
diff -r a01e9f1fa9ab -r a3bcd91c40e8 src/Makefile.am
--- a/src/Makefile.am	Sat Feb 02 14:12:33 2008 -0600
+++ b/src/Makefile.am	Sat Feb 02 14:14:09 2008 -0600
@@ -3,10 +3,11 @@ 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 = @GTK_LIBS@ @GTKGLEXT_LIBS@ @GNUTLS_LIBS@ \
+			   @GTHREAD_LIBS@
+libgtk_vnc_1_0_la_CFLAGS = @GTK_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 +47,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 = @GTK_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 a01e9f1fa9ab -r a3bcd91c40e8 src/vncdisplay.c
--- a/src/vncdisplay.c	Sat Feb 02 14:12:33 2008 -0600
+++ b/src/vncdisplay.c	Sat Feb 02 14:14:09 2008 -0600
@@ -13,6 +13,7 @@
 #include "gvnc.h"
 #include "utils.h"
 #include "vncmarshal.h"
+#include "config.h"
 
 #include <gtk/gtk.h>
 #include <string.h>
@@ -23,6 +24,11 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <pwd.h>
+
+#if WITH_GTKGLEXT
+#include <gtk/gtkgl.h>
+#include <GL/gl.h>
+#endif
 
 #define VNC_DISPLAY_GET_PRIVATE(obj) \
       (G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_DISPLAY, VncDisplayPrivate))
@@ -36,6 +42,20 @@ struct _VncDisplayPrivate
 	GdkImage *image;
 	GdkCursor *null_cursor;
 	GdkCursor *remote_cursor;
+
+#if WITH_GTKGLEXT
+	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;
+#endif
 
 	struct gvnc_framebuffer fb;
 	struct coroutine coroutine;
@@ -59,6 +79,7 @@ struct _VncDisplayPrivate
 	gboolean local_pointer;
 	gboolean read_only;
 	gboolean allow_lossy;
+	gboolean allow_scaling;
 };
 
 G_DEFINE_TYPE(VncDisplay, vnc_display, GTK_TYPE_DRAWING_AREA)
@@ -123,35 +144,113 @@ static gboolean expose_event(GtkWidget *
 	GdkRectangle drawn;
 	GdkRegion *clear, *copy;
 
+#if WITH_GTKGLEXT
+	if (priv->image == NULL && priv->gl_tex_data == NULL)
+		return TRUE;
+#else
 	if (priv->image == NULL)
 		return TRUE;
+#endif
 
-	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;
+#if WITH_GTKGLEXT
+	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;
 
-	drawn.x = x;
-	drawn.y = y;
-	drawn.width = w;
-	drawn.height = h;
+		scale_x = (double)priv->gl_width / priv->fb.width;
+		scale_y = (double)priv->gl_height / priv->fb.height;
 
-	clear = gdk_region_rectangle(&expose->area);
-	copy = gdk_region_rectangle(&drawn);
-	gdk_region_subtract(clear, copy);
+		x = expose->area.x / scale_x;
+		y = expose->area.y / scale_y;
+		w = expose->area.width / scale_x;
+		h = expose->area.height / scale_y;
 
-	gdk_gc_set_clip_region(priv->gc, copy);
-	gdk_draw_image(widget->window, priv->gc, priv->image,
-		       x, y, x, y, w, h);
+		y -= 5;
+		h += 10;
+		if (y < 0)
+			y = 0;
 
-	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);
+		x -= 5;
+		w += 10;
+		if (x < 0)
+			x = 0;
 
-	gdk_region_destroy(clear);
-	gdk_region_destroy(copy);
+		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
+#endif
+	{
+		int mx = 0, my = 0;
+		int ww, wh;
+
+		gdk_drawable_get_size(widget->window, &ww, &wh);
+		if (ww > priv->fb.width)
+			mx = (ww - priv->fb.width) / 2;
+		if (wh > priv->fb.height)
+			my = (wh - priv->fb.height) / 2;
+
+		x = MIN(expose->area.x - mx, priv->fb.width);
+		y = MIN(expose->area.y - my, priv->fb.height);
+		w = MIN(expose->area.x + expose->area.width - mx, priv->fb.width);
+		h = MIN(expose->area.y + expose->area.height - my, priv->fb.height);
+		x = MAX(0, x);
+		y = MAX(0, y);
+		w = MAX(0, w);
+		h = MAX(0, h);
+
+		w -= x;
+		h -= y;
+
+		drawn.x = x + mx;
+		drawn.y = y + my;
+		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 + mx, y + my, 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;
 }
@@ -318,6 +417,32 @@ static gboolean motion_event(GtkWidget *
 
 	if (priv->read_only)
 		return FALSE;
+
+#if WITH_GTKGLEXT
+	if (priv->gl_enabled) {
+		motion->x *= priv->fb.width;
+		motion->x /= priv->gl_width;
+		motion->y *= priv->fb.height;
+		motion->y /= priv->gl_height;
+	} else
+#endif
+	{
+		int ww, wh;
+		int mw = 0, mh = 0;
+
+		gdk_drawable_get_size(widget->window, &ww, &wh);
+		if (ww > priv->fb.width)
+			mw = (ww - priv->fb.width) / 2;
+		if (wh > priv->fb.height)
+			mh = (wh - priv->fb.height) / 2;
+
+		motion->x -= mw;
+		motion->y -= mh;
+
+		if (motion->x < 0 || motion->x >= priv->fb.width ||
+		    motion->y < 0 || motion->y >= priv->fb.height)
+			return FALSE;
+	}
 
 	if (!priv->absolute && priv->in_pointer_grab) {
 		GdkDrawable *drawable = GDK_DRAWABLE(widget->window);
@@ -517,36 +642,61 @@ static gboolean focus_event(GtkWidget *w
         return TRUE;
 }
 
+#if WITH_GTKGLEXT
+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);
+}
+#endif
 
 static gboolean on_update(void *opaque, int x, int y, int w, int h)
 {
-	GtkWidget *obj = GTK_WIDGET(opaque);
+	GtkWidget *widget = GTK_WIDGET(opaque);
+	VncDisplay *obj = VNC_DISPLAY(widget);
+	VncDisplayPrivate *priv = obj->priv;
 
-	gtk_widget_queue_draw_area(obj, x, y, w, h);
+#if WITH_GTKGLEXT
+	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;
+	} else
+#endif
+	{
+		int ww, wh;
+		int mw = 0, mh = 0;
+
+		gdk_drawable_get_size(widget->window, &ww, &wh);
+		if (ww > priv->fb.width)
+			mw = (ww - priv->fb.width) / 2;
+		if (wh > priv->fb.height)
+			mh = (wh - priv->fb.height) / 2;
+
+		x += mw;
+		y += mh;
+	}
+
+	gtk_widget_queue_draw_area(widget, x, y, w, h);
 
 	return TRUE;
 }
 
-static gboolean on_resize(void *opaque, int width, int height)
+static void setup_gdk_image(VncDisplay *obj, gint width, gint height)
 {
-	VncDisplay *obj = VNC_DISPLAY(opaque);
 	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 +715,97 @@ static gboolean on_resize(void *opaque, 
 	priv->fb.data = (uint8_t *)priv->image->mem;
 
 	gtk_widget_set_size_request(GTK_WIDGET(obj), width, height);
+}
+
+#if WITH_GTKGLEXT
+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;
+}
+#endif
+
+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 WITH_GTKGLEXT
+	if (priv->gl_tex_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(priv->gl_tex_data);
+		priv->gl_tex_data = NULL;
+	}
+#endif
+
+	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 WITH_GTKGLEXT
+	if (priv->gl_enabled)
+		setup_gl_image(obj, width, height);
+	else
+#endif
+		setup_gdk_image(obj, width, height);
 
 	gvnc_set_local(priv->gvnc, &priv->fb);
 
@@ -575,6 +816,155 @@ static gboolean on_resize(void *opaque, 
 
 	return TRUE;
 }
+
+#if WITH_GTKGLEXT
+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;
+
+		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 void rescale_display(VncDisplay *obj, gint width, gint height)
+{
+	VncDisplayPrivate *priv = obj->priv;
+
+	if (priv->allow_scaling &&
+	    (priv->fb.height != width ||
+	     priv->fb.height != height))
+		scale_display(obj, width, height);
+	else if (priv->gl_enabled) {
+		void *data;
+		priv->gl_enabled = 0;
+
+		data = priv->gl_tex_data;
+		priv->gl_tex_data = NULL;
+
+		on_resize(GTK_WIDGET(obj), 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);
+	}
+}
+
+static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *configure,
+				gpointer data G_GNUC_UNUSED)
+{
+	rescale_display(VNC_DISPLAY(widget),
+			configure->width, configure->height);
+	
+	return FALSE;
+}
+#endif
 
 static gboolean on_pointer_type_change(void *opaque, int absolute)
 {
@@ -1027,12 +1417,36 @@ 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 WITH_GTKGLEXT
+	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->gl_config) {
+		g_object_unref(G_OBJECT(priv->gl_config));
+		priv->gl_config = NULL;
+	}
+#endif
+
+	if (priv->image) {
+		g_object_unref(priv->image);
+		priv->image = NULL;
+	}
+
 	G_OBJECT_CLASS (vnc_display_parent_class)->finalize (obj);
 }
 
@@ -1204,6 +1618,14 @@ static void vnc_display_init(VncDisplay 
 {
 	GtkObject *obj = GTK_OBJECT(display);
 	GtkWidget *widget = GTK_WIDGET(display);
+	VncDisplayPrivate *priv;
+#if WITH_GTKGLEXT
+	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 };
+#endif
 
 	g_signal_connect(obj, "expose-event",
 			 G_CALLBACK(expose_event), NULL);
@@ -1225,6 +1647,12 @@ static void vnc_display_init(VncDisplay 
 			 G_CALLBACK(leave_event), NULL);
 	g_signal_connect(obj, "focus-out-event",
 			 G_CALLBACK(focus_event), NULL);
+#if WITH_GTKGLEXT
+	g_signal_connect(obj, "realize",
+			 G_CALLBACK(realize_event), NULL);
+	g_signal_connect(obj, "configure-event",
+			 G_CALLBACK(configure_event), NULL);
+#endif
 
 	GTK_WIDGET_SET_FLAGS(obj, GTK_CAN_FOCUS);
 
@@ -1239,14 +1667,27 @@ 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;
+	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;
 
-	display->priv->gvnc = gvnc_new(&vnc_display_ops, obj);
+#if WITH_GTKGLEXT
+	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;
+	}
+#endif
+
+	priv->gvnc = gvnc_new(&vnc_display_ops, obj);
 }
 
 static int vnc_display_best_path(char *buf,
@@ -1452,6 +1893,29 @@ void vnc_display_set_lossy_encoding(VncD
 	obj->priv->allow_lossy = enable;
 }
 
+#if WITH_GTKGLEXT
+gboolean vnc_display_set_scaling(VncDisplay *obj, gboolean enable)
+{
+	GtkWidget *widget = GTK_WIDGET(obj);
+	gint width, height;
+
+	g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
+	obj->priv->allow_scaling = enable;
+	if (widget->window) {
+		gdk_drawable_get_size(widget->window, &width, &height);
+		rescale_display(obj, width, height);
+		gtk_widget_queue_draw_area(widget, 0, 0, width, height);
+	}
+
+	return TRUE;
+}
+#else
+gboolean vnc_display_set_scaling(VncDisplay *obj, gboolean enable)
+{
+	return FALSE;
+}
+#endif
+
 /*
  * Local variables:
  *  c-indent-level: 8
diff -r a01e9f1fa9ab -r a3bcd91c40e8 src/vncdisplay.h
--- a/src/vncdisplay.h	Sat Feb 02 14:12:33 2008 -0600
+++ b/src/vncdisplay.h	Sat Feb 02 14:14:09 2008 -0600
@@ -105,6 +105,8 @@ void		vnc_display_client_cut_text(VncDis
 
 void		vnc_display_set_lossy_encoding(VncDisplay *obj, gboolean enable);
 
+gboolean	vnc_display_set_scaling(VncDisplay *obj, gboolean enable);
+
 G_END_DECLS
 
 #endif


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