[gtk-vnc-devel] [PATCH] gtkgl based scaling
- From: Anthony Liguori <anthony codemonkey ws>
- To: gtk-vnc-devel List <gtk-vnc-devel lists sourceforge net>
- Subject: [gtk-vnc-devel] [PATCH] gtkgl based scaling
- Date: Sat, 02 Feb 2008 14:16:42 -0600
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]