[gtk-vnc-devel] [PATCH] VNC tunnel support
- From: Anthony Liguori <anthony codemonkey ws>
- To: gtk-vnc-devel List <gtk-vnc-devel lists sourceforge net>
- Subject: [gtk-vnc-devel] [PATCH] VNC tunnel support
- Date: Thu, 07 Feb 2008 19:57:18 -0600
Hi,
The following patch implements VNC tunnel support. This patch is large
but a lot of it is the gvncviewer support. The main vision I have of
tunnels is to allow existing protocols to be tunneled through the VNC
session.
In particular, I envision tunneling sound, NBD, and text through this
interface. I've also included a QEMU patch that implements support for
this. Tunnels are disabled by default and an interface is added to
enable it. The reason for this is that I intend to make QEMU tunnel all
vc's by default when tunnel support is present.
I'll check this patch in after people have had a chance to look at it.
Regards,
Anthony Liguori
diff --git a/console.h b/console.h
index 1ac74fa..8d58983 100644
--- a/console.h
+++ b/console.h
@@ -123,6 +123,7 @@ void vnc_display_close(DisplayState *ds);
int vnc_display_open(DisplayState *ds, const char *display);
int vnc_display_password(DisplayState *ds, const char *password);
void do_info_vnc(void);
+CharDriverState *qemu_chr_open_vnc(const char *name);
/* x_keymap.c */
extern uint8_t _translate_keycode(const int key);
diff --git a/vl.c b/vl.c
index deff149..d76a40d 100644
--- a/vl.c
+++ b/vl.c
@@ -3402,6 +3402,9 @@ CharDriverState *qemu_chr_open(const char *filename)
printf("Unable to open driver: %s\n", p);
return 0;
} else
+ if (strstart(filename, "vnc:", &p)) {
+ return qemu_chr_open_vnc(p);
+ } else
#ifndef _WIN32
if (strstart(filename, "unix:", &p)) {
return qemu_chr_open_tcp(p, 0, 1);
diff --git a/vnc.c b/vnc.c
index 2e1ca34..ad751b8 100644
--- a/vnc.c
+++ b/vnc.c
@@ -125,6 +125,18 @@ enum {
#endif /* CONFIG_VNC_TLS */
+typedef struct VNCTunnelState VNCTunnelState;
+
+struct VNCTunnelState
+{
+ CharDriverState chr;
+ VncState *vs;
+ char *name;
+ int16_t id;
+ int connected;
+ VNCTunnelState *next;
+};
+
struct VncState
{
QEMUTimer *timer;
@@ -140,6 +152,7 @@ struct VncState
int has_resize;
int has_hextile;
int has_pointer_type_change;
+ int has_tunnels;
int absolute;
int last_x;
int last_y;
@@ -186,6 +199,8 @@ struct VncState
pid_t script_pid;
int script_fd;
#endif
+
+ VNCTunnelState *tunnel_list;
};
static VncState *vnc_state; /* needed for info vnc */
@@ -222,6 +237,128 @@ static void vnc_flush(VncState *vs);
static void vnc_update_client(void *opaque);
static void vnc_client_read(void *opaque);
+/* VNC Tunnel functions */
+
+static void tunnel_find_rsp(VncState *vs, int16_t id);
+static void tunnel_write(VncState *vs, int16_t id, const void *data, size_t size);
+
+static int vnc_tunnel_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ VNCTunnelState *s = chr->opaque;
+
+ if (s->vs->csock != -1 && s->vs->has_tunnels) {
+ tunnel_write(s->vs, s->id, buf, len);
+ return len;
+ }
+
+ return 0;
+}
+
+CharDriverState *qemu_chr_open_vnc(const char *name)
+{
+ VNCTunnelState *s;
+ static int16_t max_tunnel_id;
+
+ s = qemu_mallocz(sizeof(VNCTunnelState));
+ if (!s)
+ return NULL;
+
+ s->name = strdup(name);
+ if (s->name == NULL) {
+ qemu_free(s);
+ return NULL;
+ }
+
+ s->vs = vnc_state;
+ s->id = max_tunnel_id++;
+ s->chr.opaque = s;
+ s->chr.chr_write = vnc_tunnel_write;
+ qemu_chr_reset(&s->chr);
+
+ s->next = s->vs->tunnel_list;
+ s->vs->tunnel_list = s;
+
+ return &s->chr;
+}
+
+static VNCTunnelState *find_tunnel_by_tid(VncState *vs, uint16_t tid)
+{
+ VNCTunnelState *s;
+
+ for (s = vs->tunnel_list; s; s = s->next) {
+ if (s->id == tid)
+ return s;
+ }
+
+ return NULL;
+}
+
+static void vnc_tunnel_open_request(VncState *vs,
+ const char *name, size_t size)
+{
+ VNCTunnelState *s;
+
+ for (s = vs->tunnel_list; s; s = s->next) {
+ if (strlen(s->name) == size && memcmp(s->name, name, size) == 0)
+ break;
+ }
+
+ if (s == NULL || s->connected) {
+ tunnel_find_rsp(vs, -1);
+ } else {
+ s->connected = 1;
+ tunnel_find_rsp(vs, s->id);
+ qemu_chr_reset(&s->chr);
+ }
+}
+
+static void vnc_server_tunnel_shutdown(VncState *vs, VNCTunnelState *s)
+{
+ vnc_write_u8(vs, 255);
+ vnc_write_u8(vs, 1);
+ vnc_write_u16(vs, s->id);
+ vnc_flush(vs);
+}
+
+static void vnc_tunnel_client_shutdown(VncState *vs, uint16_t tid)
+{
+ VNCTunnelState *s;
+
+ s = find_tunnel_by_tid(vs, tid);
+ if (s) {
+ s->connected = 0;
+ vnc_server_tunnel_shutdown(vs, s);
+ }
+}
+
+static void vnc_tunnel_client_send(VncState *vs, int16_t tid,
+ const void *data, size_t size)
+{
+ VNCTunnelState *s;
+ CharDriverState *chr;
+
+ s = find_tunnel_by_tid(vs, tid);
+ if (s == NULL || !s->connected)
+ return;
+
+ chr = (CharDriverState *)s;
+
+ if (qemu_chr_can_read(chr))
+ qemu_chr_read(chr, (char *)data, size);
+}
+
+static void vnc_close_tunnels(VncState *vs)
+{
+ VNCTunnelState *s;
+
+ for (s = vs->tunnel_list; s; s = s->next) {
+ if (s->connected) {
+ /* FIXME signal disconnection */
+ s->connected = 0;
+ }
+ }
+}
+
static inline void vnc_set_bit(uint32_t *d, int k)
{
d[k >> 5] |= 1 << (k & 0x1f);
@@ -655,6 +792,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
buffer_reset(&vs->input);
buffer_reset(&vs->output);
vs->need_update = 0;
+ vnc_close_tunnels(vs);
#if CONFIG_VNC_TLS
if (vs->tls_session) {
gnutls_deinit(vs->tls_session);
@@ -1088,6 +1226,33 @@ static void send_ext_key_event_ack(VncState *vs)
vnc_flush(vs);
}
+static void send_tunnels_ack(VncState *vs)
+{
+ vnc_write_u8(vs, 0);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, 0, 0, vs->ds->width, vs->ds->height, -259);
+ vnc_flush(vs);
+}
+
+static void tunnel_find_rsp(VncState *vs, int16_t id)
+{
+ vnc_write_u8(vs, 255);
+ vnc_write_u8(vs, 0); /* find response */
+ vnc_write_u16(vs, id);
+ vnc_flush(vs);
+}
+
+static void tunnel_write(VncState *vs, int16_t id, const void *data, size_t size)
+{
+ vnc_write_u8(vs, 255);
+ vnc_write_u8(vs, 2); /* tunnel server send */
+ vnc_write_u16(vs, id);
+ vnc_write_u32(vs, size);
+ vnc_write(vs, data, size);
+ vnc_flush(vs);
+}
+
static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
{
int i;
@@ -1095,6 +1260,7 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
vs->has_hextile = 0;
vs->has_resize = 0;
vs->has_pointer_type_change = 0;
+ vs->has_tunnels = 0;
vs->absolute = -1;
vs->ds->dpy_copy = NULL;
@@ -1118,6 +1284,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
case -258:
send_ext_key_event_ack(vs);
break;
+ case -259:
+ vs->has_tunnels = 1;
+ send_tunnels_ack(vs);
default:
break;
}
@@ -1283,6 +1452,31 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
ext_key_event(vs, read_u16(data, 2),
read_u32(data, 4), read_u32(data, 8));
break;
+ case 1:
+ if (len < 4)
+ return 4;
+
+ if (len == 4)
+ return 4 + read_u16(data, 2);
+
+ vnc_tunnel_open_request(vs, data + 4, read_u16(data, 2));
+ break;
+ case 2:
+ if (len < 4)
+ return 4;
+
+ vnc_tunnel_client_shutdown(vs, read_u16(data, 2));
+ break;
+ case 3:
+ if (len < 8)
+ return 8;
+
+ if (len == 8)
+ return 8 + read_u32(data, 4);
+
+ vnc_tunnel_client_send(vs, read_u16(data, 2),
+ data + 8, read_u32(data, 4));
+ break;
default:
printf("Msg: %d\n", read_u16(data, 0));
vnc_client_error(vs);
@@ -2117,6 +2311,7 @@ void vnc_display_close(DisplayState *ds)
vs->script_pid = -1;
}
#endif
+ vnc_close_tunnels(vs);
vs->auth = VNC_AUTH_INVALID;
#if CONFIG_VNC_TLS
vs->subauth = VNC_AUTH_INVALID;
diff -r 1e6a5272ef00 configure.ac
--- a/configure.ac Wed Feb 06 22:20:39 2008 -0600
+++ b/configure.ac Thu Feb 07 19:51:08 2008 -0600
@@ -13,6 +13,7 @@ PYGTK_REQUIRED=2.0.0
PYGTK_REQUIRED=2.0.0
GTKGLEXT_REQUIRED=1.2.0
VIEW_REQUIRED=0.6.0
+VTE_REQUIRED=0.16.0
GTHREAD_REQUIRED=2.0.0
PYTHON_REQUIRED=2.4
@@ -118,6 +119,23 @@ AC_DEFINE_UNQUOTED(WITH_LIBVIEW,[$WITH_L
AC_DEFINE_UNQUOTED(WITH_LIBVIEW,[$WITH_LIBVIEW], [Whether to use libview])
AC_SUBST(VIEW_CFLAGS)
AC_SUBST(VIEW_LIBS)
+
+AC_ARG_WITH(libvte,
+[ --with-libvte enable libvte support in gvncviewer],
+[case "${withval}" in
+ yes|no) ;;
+ *) AC_MSG_ERROR([bad value ${withval} for libvte option]) ;;
+ esac],[withval=yes])
+
+WITH_LIBVTE=0
+if test "${withval}" = "yes"; then
+ PKG_CHECK_MODULES(VTE, vte >= $VTE_REQUIRED,
+ [WITH_LIBVTE=1], [WITH_LIBVTE=0])
+ VTE_LIBS="$VTE_LIBS -lutil"
+fi
+AC_DEFINE_UNQUOTED(WITH_LIBVTE,[$WITH_LIBVTE], [Whether to use libvte])
+AC_SUBST(VTE_CFLAGS)
+AC_SUBST(VTE_LIBS)
PKG_CHECK_MODULES(GNUTLS, gnutls >= $GNUTLS_REQUIRED)
AC_SUBST(GNUTLS_CFLAGS)
diff -r 1e6a5272ef00 examples/Makefile.am
--- a/examples/Makefile.am Wed Feb 06 22:20:39 2008 -0600
+++ b/examples/Makefile.am Thu Feb 07 19:51:08 2008 -0600
@@ -7,8 +7,8 @@ endif
gvncviewer_SOURCES = gvncviewer.c
gvncviewer_LDADD = ../src/libgtk-vnc-1.0.la @GTK_CFLAGS@ @GTKGLEXT_LIBS@ \
- @VIEW_LIBS@
+ @VIEW_LIBS@ @VTE_LIBS@
gvncviewer_CFLAGS = @GTK_CFLAGS@ @GTKGLEXT_CFLAGS@ @WARNING_CFLAGS@ \
- @VIEW_CFLAGS@ -I$(top_srcdir)/src/
+ @VIEW_CFLAGS@ @VTE_CFLAGS@ -I$(top_srcdir)/src/
EXTRA_DIST = gvncviewer.py
diff -r 1e6a5272ef00 examples/gvncviewer.c
--- a/examples/gvncviewer.c Wed Feb 06 22:20:39 2008 -0600
+++ b/examples/gvncviewer.c Thu Feb 07 19:51:08 2008 -0600
@@ -14,6 +14,27 @@
#if WITH_LIBVIEW
#include <libview/autoDrawer.h>
#endif
+
+#if WITH_LIBVTE
+#include <vte/vte.h>
+#endif
+
+#include <pty.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+
+static GtkWidget *notebook;
+
+struct tunnel_state
+{
+ VncTunnel *tunnel;
+ GtkWidget *notebook;
+ GtkWidget *terminal;
+ int slave;
+ gchar *name;
+ int io_watch;
+};
#if WITH_GTKGLEXT
#include <gtk/gtkgl.h>
@@ -257,6 +278,187 @@ static gboolean window_state_event(GtkWi
}
#endif
+static void do_tunnel_init(GtkWidget *widget G_GNUC_UNUSED, GtkWidget *tunnel)
+{
+ gtk_widget_set_sensitive(tunnel, TRUE);
+}
+
+static void do_close_tab(GtkButton *button, struct tunnel_state *s)
+{
+ gint page_no;
+
+ g_object_unref(s->tunnel);
+ s->tunnel = NULL;
+
+ g_source_remove(s->io_watch);
+ s->io_watch = -1;
+
+ page_no = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook),
+ s->terminal);
+ gtk_notebook_remove_page(GTK_NOTEBOOK(s->notebook), page_no);
+
+ g_free(s->name);
+ g_free(s);
+}
+
+static gboolean do_slave_input(GIOChannel *channel, GIOCondition cond, gpointer data)
+{
+ struct tunnel_state *s = data;
+ char buffer[1024];
+ ssize_t len;
+
+ do {
+ len = read(s->slave, buffer, sizeof(buffer));
+ } while (len == -1 && errno == EINTR);
+
+ if (len > 0) {
+ vnc_tunnel_send(s->tunnel, buffer, len);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void do_tunnel_connected(VncTunnel *tunnel, struct tunnel_state *s)
+{
+ GtkWidget *label, *hbox, *button, *image;
+ GtkWidget *terminal;
+ int master, slave, err;
+ struct termios term;
+ GIOChannel *chan;
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ label = gtk_label_new(s->name);
+ button = gtk_button_new();
+ image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+#if WITH_LIBVTE
+ terminal = vte_terminal_new();
+#else
+ terminal = NULL;
+#endif
+
+ err = openpty(&master, &slave, NULL, NULL, NULL);
+ g_assert(err != -1);
+
+ err = tcgetattr(slave, &term);
+ g_assert(err != -1);
+ cfmakeraw(&term);
+ err = tcsetattr(slave, 0, &term);
+ g_assert(err != -1);
+
+ err = tcgetattr(master, &term);
+ g_assert(err != -1);
+ cfmakeraw(&term);
+ err = tcsetattr(master, 0, &term);
+ g_assert(err != -1);
+
+#if WITH_LIBVTE
+ vte_terminal_set_pty(VTE_TERMINAL(terminal), master);
+ vte_terminal_set_size(VTE_TERMINAL(terminal), 1, 1);
+#endif
+
+ gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+ gtk_button_set_image(GTK_BUTTON(button), image);
+ gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
+
+ gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, TRUE, 0);
+
+ gtk_widget_show_all(hbox);
+ gtk_widget_show(terminal);
+
+ gtk_signal_connect(GTK_OBJECT(button), "clicked",
+ GTK_SIGNAL_FUNC(do_close_tab), s);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), terminal, hbox);
+ s->terminal = terminal;
+ s->slave = slave;
+
+ chan = g_io_channel_unix_new(slave);
+ s->io_watch = g_io_add_watch(chan, G_IO_IN, do_slave_input, s);
+}
+
+static void do_tunnel_io_watch(VncTunnel *tunnel, struct tunnel_state *s)
+{
+ guchar *data;
+ guint size;
+
+ data = vnc_tunnel_recv(tunnel, &size);
+ if (data) {
+ ssize_t ret;
+ ret = write(s->slave, data, size);
+ g_assert(ret == size);
+ }
+
+ g_free(data);
+}
+
+static void do_tunnel_shutdown(VncTunnel *tunnel, struct tunnel_state *s)
+{
+ g_free(s->name);
+ g_free(s);
+}
+
+static void do_open_tunnel(GtkWidget *menu, VncDisplay *vnc)
+{
+ GtkWidget *label, *entry, *vbox, *dialog;
+ int response;
+
+ dialog = gtk_dialog_new_with_buttons("Enter tunnel name",
+ NULL,
+ 0,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+ label = gtk_label_new("Tunnel Name:");
+ entry = gtk_entry_new();
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+
+ vbox = gtk_bin_get_child(GTK_BIN(dialog));
+
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
+
+ gtk_widget_show_all(dialog);
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_hide(GTK_WIDGET(dialog));
+
+ if (response == GTK_RESPONSE_OK) {
+ struct tunnel_state *s;
+ const gchar *name;
+
+ name = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ s = g_new0(struct tunnel_state, 1);
+ s->name = g_strdup(name);
+ s->notebook = notebook;
+
+ s->tunnel = vnc_display_tunnel_open(vnc, name);
+ g_signal_connect(G_OBJECT(s->tunnel), "vnc-tunnel-connected",
+ G_CALLBACK(do_tunnel_connected), s);
+ g_signal_connect(G_OBJECT(s->tunnel), "vnc-tunnel-io-watch",
+ G_CALLBACK(do_tunnel_io_watch), s);
+ g_signal_connect(G_OBJECT(s->tunnel), "vnc-tunnel-shutdown",
+ G_CALLBACK(do_tunnel_shutdown), s);
+
+ }
+
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+static void do_show_tabs(GtkNotebook *notebook, GtkWidget *child,
+ guint page_num, gpointer data)
+{
+ if (gtk_notebook_get_n_pages(notebook) == 1)
+ gtk_notebook_set_show_tabs(notebook, FALSE);
+ else
+ gtk_notebook_set_show_tabs(notebook, TRUE);
+}
+
int main(int argc, char **argv)
{
char port[1024], hostname[1024];
@@ -272,6 +474,9 @@ int main(int argc, char **argv)
GtkWidget *cab;
GtkWidget *fullscreen;
GtkWidget *scaling;
+ GtkWidget *sep;
+ GtkWidget *tunnel;
+ GtkWidget *label;
if (argc != 2 && argc != 3) {
fprintf(stderr, "Usage: %s hostname[:display] [password]\n",
@@ -292,6 +497,7 @@ int main(int argc, char **argv)
layout = gtk_vbox_new(FALSE, 0);
#endif
menubar = gtk_menu_bar_new();
+ notebook = gtk_notebook_new();
gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
@@ -319,19 +525,31 @@ int main(int argc, char **argv)
fullscreen = gtk_check_menu_item_new_with_mnemonic("_Full Screen");
scaling = gtk_check_menu_item_new_with_mnemonic("OpenGL _Scaling");
+ sep = gtk_separator_menu_item_new();
+ tunnel = gtk_menu_item_new_with_mnemonic("Open _Tunnel..");
gtk_menu_append(GTK_MENU(submenu), fullscreen);
gtk_menu_append(GTK_MENU(submenu), scaling);
+ gtk_menu_append(GTK_MENU(submenu), sep);
+ gtk_menu_append(GTK_MENU(submenu), tunnel);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), submenu);
+
+ gtk_widget_set_sensitive(tunnel, FALSE);
+
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
+
+ label = gtk_label_new("Display");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vnc, label);
#if WITH_LIBVIEW
ViewAutoDrawer_SetActive(VIEW_AUTODRAWER(layout), FALSE);
ViewOvBox_SetOver(VIEW_OV_BOX(layout), menubar);
- ViewOvBox_SetUnder(VIEW_OV_BOX(layout), vnc);
+ ViewOvBox_SetUnder(VIEW_OV_BOX(layout), notebook);
#else
gtk_box_pack_start(GTK_BOX(layout), menubar, FALSE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(layout), vnc, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(layout), notebook, TRUE, TRUE, 0);
#endif
gtk_container_add(GTK_CONTAINER(window), layout);
gtk_widget_realize(vnc);
@@ -352,6 +570,7 @@ int main(int argc, char **argv)
vnc_display_set_keyboard_grab(VNC_DISPLAY(vnc), TRUE);
vnc_display_set_pointer_grab(VNC_DISPLAY(vnc), TRUE);
//vnc_display_set_pointer_local(VNC_DISPLAY(vnc), TRUE);
+ vnc_display_set_tunnels(VNC_DISPLAY(vnc), TRUE);
gtk_signal_connect(GTK_OBJECT(window), "delete-event",
GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
@@ -393,6 +612,16 @@ int main(int argc, char **argv)
gtk_signal_connect(GTK_OBJECT(window), "window-state-event",
GTK_SIGNAL_FUNC(window_state_event), layout);
#endif
+ gtk_signal_connect(GTK_OBJECT(tunnel), "activate",
+ GTK_SIGNAL_FUNC(do_open_tunnel), vnc);
+#if WITH_LIBVTE
+ gtk_signal_connect(GTK_OBJECT(vnc), "vnc-tunnel-init",
+ GTK_SIGNAL_FUNC(do_tunnel_init), tunnel);
+#endif
+ gtk_signal_connect(GTK_OBJECT(notebook), "page-added",
+ GTK_SIGNAL_FUNC(do_show_tabs), NULL);
+ gtk_signal_connect(GTK_OBJECT(notebook), "page-removed",
+ GTK_SIGNAL_FUNC(do_show_tabs), NULL);
gtk_main();
diff -r 1e6a5272ef00 src/Makefile.am
--- a/src/Makefile.am Wed Feb 06 22:20:39 2008 -0600
+++ b/src/Makefile.am Thu Feb 07 19:51:08 2008 -0600
@@ -13,7 +13,7 @@ libgtk_vnc_1_0_la_LDFLAGS = -Wl,--versio
-version-info 0:1:0
gtk_vnc_includedir = $(includedir)/gtk-vnc-1.0/
-gtk_vnc_include_HEADERS = vncdisplay.h
+gtk_vnc_include_HEADERS = vncdisplay.h vnctunnel.h
libgtk_vnc_1_0_la_SOURCES = blt.h blt1.h \
coroutine.h \
@@ -21,6 +21,7 @@ libgtk_vnc_1_0_la_SOURCES = blt.h blt1.h
gvnc.h gvnc.c \
vncdisplay.h vncdisplay.c \
vncmarshal.h vncmarshal.c \
+ vnctunnel.h vnctunnel.c \
x_keymap.h x_keymap.c vnc_keycodes.h \
utils.h
@@ -56,8 +57,8 @@ CODEGENDIR = $(shell pkg-config --variab
CODEGENDIR = $(shell pkg-config --variable=codegendir pygtk-2.0)
DEFSDIR = $(shell pkg-config --variable=defsdir pygtk-2.0)
-vnc.defs: vncdisplay.h
- $(PYTHON) $(CODEGENDIR)/h2def.py $< > $@
+vnc.defs: vncdisplay.h vnctunnel.h
+ $(PYTHON) $(CODEGENDIR)/h2def.py $^ > $@
vncmodule.defs.c: vnc.override vnc.defs
pygtk-codegen-2.0 --prefix gtkvnc \
diff -r 1e6a5272ef00 src/gvnc.c
--- a/src/gvnc.c Wed Feb 06 22:20:39 2008 -0600
+++ b/src/gvnc.c Thu Feb 07 19:51:08 2008 -0600
@@ -42,6 +42,7 @@
#include <gdk/gdkkeysyms.h>
#include "vnc_keycodes.h"
+#include "vnctunnel.h"
struct wait_queue
{
@@ -166,6 +167,10 @@ struct gvnc
int zrle_pi_bits;
gboolean has_ext_key_event;
+
+ gboolean has_tunnels;
+ GList *pending_tunnels;
+ GList *open_tunnels;
};
#define nibhi(a) (((a) >> 4) & 0x0F)
@@ -926,6 +931,56 @@ gboolean gvnc_pointer_event(struct gvnc
return !gvnc_has_error(gvnc);
}
+gboolean gvnc_tunnel_open_request(struct gvnc *gvnc, const char *name)
+{
+ size_t size = strlen(name);
+
+ if (!gvnc->has_tunnels) {
+ printf("open request when tunnel support is not available\n");
+ gvnc->has_error = TRUE;
+ return FALSE;
+ }
+
+ gvnc_buffered_write_u8(gvnc, 255);
+ gvnc_buffered_write_u8(gvnc, 1);
+ gvnc_buffered_write_u16(gvnc, size);
+ gvnc_buffered_write(gvnc, name, size);
+ gvnc_buffered_flush(gvnc);
+ return !gvnc_has_error(gvnc);
+}
+
+gboolean gvnc_tunnel_client_shutdown(struct gvnc *gvnc, uint16_t id)
+{
+ if (!gvnc->has_tunnels) {
+ printf("shutdown request when tunnel support is not available\n");
+ gvnc->has_error = TRUE;
+ return FALSE;
+ }
+
+ gvnc_buffered_write_u8(gvnc, 255);
+ gvnc_buffered_write_u8(gvnc, 2);
+ gvnc_buffered_write_u16(gvnc, id);
+ return !gvnc_has_error(gvnc);
+}
+
+gboolean gvnc_tunnel_client_send(struct gvnc *gvnc, uint16_t id,
+ const void *data, size_t length)
+{
+ if (!gvnc->has_tunnels) {
+ printf("send request when tunnel support is not available\n");
+ gvnc->has_error = TRUE;
+ return FALSE;
+ }
+
+ gvnc_buffered_write_u8(gvnc, 255);
+ gvnc_buffered_write_u8(gvnc, 3);
+ gvnc_buffered_write_u16(gvnc, id);
+ gvnc_buffered_write_u32(gvnc, length);
+ gvnc_buffered_write(gvnc, data, length);
+ gvnc_buffered_flush(gvnc);
+ return !gvnc_has_error(gvnc);
+}
+
gboolean gvnc_client_cut_text(struct gvnc *gvnc,
const void *data, size_t length)
{
@@ -1761,6 +1816,114 @@ static void gvnc_pointer_type_change(str
gvnc->has_error = TRUE;
}
+static void gvnc_tunnels_available(struct gvnc *gvnc)
+{
+ if (gvnc->has_error || !gvnc->ops.tunnels_available)
+ return;
+
+ if (!gvnc->ops.tunnels_available(gvnc->ops_data))
+ gvnc->has_error = TRUE;
+}
+
+VncTunnel *gvnc_tunnel_open(struct gvnc *gvnc, const char *name)
+{
+ VncTunnel *tunnel;
+
+ if (!gvnc_tunnel_open_request(gvnc, name))
+ return NULL;
+
+ tunnel = vnc_tunnel_new(gvnc);
+ gvnc->pending_tunnels = g_list_append(gvnc->pending_tunnels, tunnel);
+
+ return tunnel;
+}
+
+void gvnc_tunnel_remove(struct gvnc *gvnc, VncTunnel *tunnel)
+{
+ gvnc->pending_tunnels = g_list_remove(gvnc->pending_tunnels, tunnel);
+ gvnc->open_tunnels = g_list_remove(gvnc->open_tunnels, tunnel);
+}
+
+static gint tunnel_cmp(gconstpointer a, gconstpointer b)
+{
+ VncTunnel *lhs = VNC_TUNNEL(a);
+ uint16_t *ptid = (void *)b;
+ return vnc_tunnel_get_tid(lhs) - *ptid;
+}
+
+static VncTunnel *find_tunnel_by_tid(struct gvnc *gvnc, uint16_t tid)
+{
+ GList *obj;
+ obj = g_list_find_custom(gvnc->open_tunnels, &tid, tunnel_cmp);
+ if (obj)
+ return VNC_TUNNEL(obj->data);
+ return NULL;
+}
+
+static void gvnc_tunnel_open_response(struct gvnc *gvnc, uint16_t id)
+{
+ VncTunnel *tunnel;
+
+ if (gvnc->has_error)
+ return;
+
+ GVNC_DEBUG("Tunnel open response\n");
+
+ if (!gvnc->pending_tunnels) {
+ printf("open response with no pending tunnels\n");
+ return;
+ }
+
+ tunnel = gvnc->pending_tunnels->data;
+ gvnc->pending_tunnels = g_list_remove(gvnc->pending_tunnels, tunnel);
+ if ((int16_t)id == -1)
+ vnc_tunnel_feed_shutdown(tunnel);
+ else {
+ vnc_tunnel_set_tid(tunnel, id);
+ gvnc->open_tunnels = g_list_prepend(gvnc->open_tunnels,
+ tunnel);
+ }
+}
+
+static void gvnc_tunnel_server_shutdown(struct gvnc *gvnc, uint16_t id)
+{
+ VncTunnel *tunnel;
+
+ if (gvnc->has_error)
+ return;
+
+ GVNC_DEBUG("Tunnel server shutdown\n");
+
+ tunnel = find_tunnel_by_tid(gvnc, id);
+ if (!tunnel) {
+ printf("shutdown request with no open tunnel\n");
+ return;
+ }
+
+ vnc_tunnel_feed_shutdown(tunnel);
+ gvnc->open_tunnels = g_list_remove(gvnc->open_tunnels, tunnel);
+}
+
+static void gvnc_tunnel_server_send(struct gvnc *gvnc, uint16_t id,
+ const void *data, size_t len)
+{
+ VncTunnel *tunnel;
+
+ if (gvnc->has_error)
+ return;
+
+ GVNC_DEBUG("Tunnel server send\n");
+
+ tunnel = find_tunnel_by_tid(gvnc, id);
+ if (!tunnel) {
+ GVNC_DEBUG("Unable to find tunnel %d\n", id);
+ printf("unable to find tunnel for send\n");
+ return;
+ }
+
+ vnc_tunnel_feed_recv(tunnel, data, len);
+}
+
static void gvnc_rich_cursor_blt(struct gvnc *gvnc, uint8_t *pixbuf,
uint8_t *image, uint8_t *mask,
int pitch, uint16_t width, uint16_t height)
@@ -1910,6 +2073,10 @@ static void gvnc_framebuffer_update(stru
break;
case GVNC_ENCODING_EXT_KEY_EVENT:
gvnc->has_ext_key_event = TRUE;
+ break;
+ case GVNC_ENCODING_TUNNEL_IO:
+ gvnc->has_tunnels = TRUE;
+ gvnc_tunnels_available(gvnc);
break;
default:
GVNC_DEBUG("Received an unknown encoding type: %d\n", etype);
@@ -2011,6 +2178,42 @@ gboolean gvnc_server_message(struct gvnc
gvnc_server_cut_text(gvnc, data, n_text);
free(data);
} break;
+ case 255: /* ExtServerMessage */
+ switch (gvnc_read_u8(gvnc)) {
+ case 0: /* TunnelOpenResponse */
+ gvnc_tunnel_open_response(gvnc, gvnc_read_u16(gvnc));
+ break;
+ case 1: /* TunnelServerShutdown */
+ gvnc_tunnel_server_shutdown(gvnc, gvnc_read_u16(gvnc));
+ break;
+ case 2: { /* TunnelServerSend */
+ uint8_t *data;
+ uint16_t id;
+ size_t size;
+
+ id = gvnc_read_u16(gvnc);
+ size = gvnc_read_u32(gvnc);
+ if ((size + 1) < size || (size + 1) < 1) {
+ gvnc->has_error = TRUE;
+ break;
+ }
+
+ data = malloc(size + 1);
+ if (data == NULL) {
+ gvnc->has_error = TRUE;
+ break;
+ }
+ gvnc_read(gvnc, data, size);
+ data[size] = 0;
+
+ gvnc_tunnel_server_send(gvnc, id, data, size);
+ free(data);
+ } break;
+ default:
+ gvnc->has_error = TRUE;
+ break;
+ }
+ break;
default:
GVNC_DEBUG("Received an unknown message: %u\n", msg);
gvnc->has_error = TRUE;
diff -r 1e6a5272ef00 src/gvnc.h
--- a/src/gvnc.h Wed Feb 06 22:20:39 2008 -0600
+++ b/src/gvnc.h Thu Feb 07 19:51:08 2008 -0600
@@ -3,6 +3,8 @@
#include <glib.h>
#include <stdint.h>
+
+#include "vnctunnel.h"
struct gvnc;
@@ -24,6 +26,7 @@ struct gvnc_ops
gboolean (*auth_unsupported)(void *, unsigned int);
gboolean (*render_jpeg)(void *, rgb24_render_func *render, void *,
int, int, int, int, uint8_t *, int);
+ gboolean (*tunnels_available)(void *);
};
struct gvnc_pixel_format
@@ -90,6 +93,7 @@ typedef enum {
GVNC_ENCODING_POINTER_CHANGE = -257,
GVNC_ENCODING_EXT_KEY_EVENT = -258,
+ GVNC_ENCODING_TUNNEL_IO = -259,
} gvnc_encoding;
typedef enum {
@@ -153,6 +157,13 @@ gboolean gvnc_key_event(struct gvnc *gvn
gboolean gvnc_key_event(struct gvnc *gvnc, uint8_t down_flag,
uint32_t key, uint16_t scancode);
+gboolean gvnc_tunnel_open_request(struct gvnc *gvnc, const char *name);
+
+gboolean gvnc_tunnel_client_shutdown(struct gvnc *gvnc, uint16_t id);
+
+gboolean gvnc_tunnel_client_send(struct gvnc *gvnc, uint16_t id,
+ const void *data, size_t length);
+
gboolean gvnc_framebuffer_update_request(struct gvnc *gvnc,
uint8_t incremental,
uint16_t x, uint16_t y,
@@ -176,6 +187,10 @@ int gvnc_get_height(struct gvnc *gvnc);
/* HACK this is temporary */
gboolean gvnc_using_raw_keycodes(struct gvnc *gvnc);
+VncTunnel *gvnc_tunnel_open(struct gvnc *gvnc, const char *name);
+
+void gvnc_tunnel_remove(struct gvnc *gvnc, VncTunnel *tunnel);
+
#endif
/*
* Local variables:
diff -r 1e6a5272ef00 src/libgtk-vnc_sym.version
--- a/src/libgtk-vnc_sym.version Wed Feb 06 22:20:39 2008 -0600
+++ b/src/libgtk-vnc_sym.version Thu Feb 07 19:51:08 2008 -0600
@@ -33,6 +33,14 @@
vnc_display_set_scaling;
vnc_display_force_grab;
+ vnc_display_tunnel_open;
+ vnc_display_set_tunnels;
+
+ vnc_tunnel_get_type;
+ vnc_tunnel_recv;
+ vnc_tunnel_send;
+ vnc_tunnel_new;
+
local:
*;
};
diff -r 1e6a5272ef00 src/vnc.override
--- a/src/vnc.override Wed Feb 06 22:20:39 2008 -0600
+++ b/src/vnc.override Thu Feb 07 19:51:08 2008 -0600
@@ -3,14 +3,20 @@ headers
#include <Python.h>
#include "pygobject.h"
#include "vncdisplay.h"
+#include "vnctunnel.h"
%%
modulename gtkvnc
%%
import gtk.DrawingArea as PyGtkDrawingArea_Type
+import gobject.GObject as PyGObject_Type
%%
ignore-glob
*_get_type
vnc_display_send_keys_ex
+ vnc_tunnel_get_tid
+ vnc_tunnel_set_tid
+ vnc_tunnel_feed_recv
+ vnc_tunnel_feed_shutdown
%%
override vnc_display_send_keys kwargs
static PyObject*
@@ -51,3 +57,45 @@ _wrap_vnc_display_send_keys(PyGObject *s
Py_INCREF(Py_None);
return Py_None;
}
+
+%%
+override vnc_tunnel_recv noargs
+static PyObject *
+_wrap_vnc_tunnel_recv(PyGObject *self)
+{
+ gchar *str;
+ guint size;
+ PyObject *ret;
+
+ str = vnc_tunnel_recv(VNC_TUNNEL(self->obj), &size);
+ if (str == NULL) {
+ Py_INCREF(Py_None);
+ ret = Py_None;
+ } else {
+ ret = PyString_FromStringAndSize(str, size);
+ g_free(str);
+ }
+
+ return ret;
+}
+
+%%
+override vnc_tunnel_send onearg
+static PyObject *
+_wrap_vnc_tunnel_send(PyGObject *self, PyObject *arg)
+{
+ GString *str;
+ char *data;
+ Py_ssize_t length;
+ PyObject *ret;
+ int err;
+
+ err = PyString_AsStringAndSize(arg, &data, &length);
+ if (err == -1)
+ return NULL;
+
+ vnc_tunnel_send(VNC_TUNNEL(self->obj), data, length);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
diff -r 1e6a5272ef00 src/vncdisplay.c
--- a/src/vncdisplay.c Wed Feb 06 22:20:39 2008 -0600
+++ b/src/vncdisplay.c Thu Feb 07 19:51:08 2008 -0600
@@ -81,6 +81,7 @@ struct _VncDisplayPrivate
gboolean read_only;
gboolean allow_lossy;
gboolean allow_scaling;
+ gboolean allow_tunnels;
};
/* Delayed signal emmission.
@@ -128,12 +129,12 @@ typedef enum
VNC_SERVER_CUT_TEXT,
VNC_BELL,
+ VNC_TUNNEL_INIT,
+
LAST_SIGNAL
} vnc_display_signals;
-static guint signals[LAST_SIGNAL] = { 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0, 0,};
+static guint signals[LAST_SIGNAL] = { 0 };
static GParamSpec *signalCredParam;
GtkWidget *vnc_display_new(void)
@@ -1293,6 +1294,17 @@ static gboolean on_render_jpeg(void *opa
return TRUE;
}
+static gboolean on_tunnels_available(void *opaque)
+{
+ VncDisplay *obj = VNC_DISPLAY(opaque);
+
+ g_signal_emit (G_OBJECT (obj),
+ signals[VNC_TUNNEL_INIT],
+ 0);
+
+ return TRUE;
+}
+
static const struct gvnc_ops vnc_display_ops = {
.auth_cred = on_auth_cred,
.auth_type = on_auth_type,
@@ -1306,6 +1318,7 @@ static const struct gvnc_ops vnc_display
.server_cut_text = on_server_cut_text,
.bell = on_bell,
.render_jpeg = on_render_jpeg,
+ .tunnels_available = on_tunnels_available,
};
/* we use an idle function to allow the coroutine to exit before we actually
@@ -1325,19 +1338,7 @@ static void *vnc_coroutine(void *opaque)
VncDisplayPrivate *priv = obj->priv;
/* this order is extremely important! */
- int32_t encodings[] = { GVNC_ENCODING_TIGHT_JPEG5,
- GVNC_ENCODING_TIGHT,
- GVNC_ENCODING_EXT_KEY_EVENT,
- GVNC_ENCODING_DESKTOP_RESIZE,
- GVNC_ENCODING_RICH_CURSOR,
- GVNC_ENCODING_XCURSOR,
- GVNC_ENCODING_POINTER_CHANGE,
- GVNC_ENCODING_ZRLE,
- GVNC_ENCODING_HEXTILE,
- GVNC_ENCODING_RRE,
- GVNC_ENCODING_COPY_RECT,
- GVNC_ENCODING_RAW };
- int32_t *encodingsp;
+ int32_t encodings[32];
int n_encodings;
int ret;
struct signal_data s;
@@ -1364,20 +1365,28 @@ static void *vnc_coroutine(void *opaque)
emit_signal_delayed(obj, VNC_INITIALIZED, &s);
- encodingsp = encodings;
- n_encodings = ARRAY_SIZE(encodings);
-
+ n_encodings = 0;
if (check_pixbuf_support("jpeg")) {
- if (!priv->allow_lossy) {
- encodingsp++;
- n_encodings--;
- }
- } else {
- encodingsp += 2;
- n_encodings -= 2;
+ if (priv->allow_lossy)
+ encodings[n_encodings++] = GVNC_ENCODING_TIGHT_JPEG5;
+ encodings[n_encodings++] = GVNC_ENCODING_TIGHT;
}
- if (!gvnc_set_encodings(priv->gvnc, n_encodings, encodingsp))
+ if (priv->allow_tunnels)
+ encodings[n_encodings++] = GVNC_ENCODING_TUNNEL_IO;
+
+ encodings[n_encodings++] = GVNC_ENCODING_EXT_KEY_EVENT;
+ encodings[n_encodings++] = GVNC_ENCODING_DESKTOP_RESIZE;
+ encodings[n_encodings++] = GVNC_ENCODING_RICH_CURSOR;
+ encodings[n_encodings++] = GVNC_ENCODING_XCURSOR;
+ encodings[n_encodings++] = GVNC_ENCODING_POINTER_CHANGE;
+ encodings[n_encodings++] = GVNC_ENCODING_ZRLE;
+ encodings[n_encodings++] = GVNC_ENCODING_HEXTILE;
+ encodings[n_encodings++] = GVNC_ENCODING_RRE;
+ encodings[n_encodings++] = GVNC_ENCODING_COPY_RECT;
+ encodings[n_encodings++] = GVNC_ENCODING_RAW;
+
+ if (!gvnc_set_encodings(priv->gvnc, n_encodings, encodings))
goto cleanup;
if (!gvnc_framebuffer_update_request(priv->gvnc, 0, 0, 0, priv->fb.width, priv->fb.height))
@@ -1747,6 +1756,16 @@ static void vnc_display_class_init(VncDi
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE,
0);
+
+ signals[VNC_TUNNEL_INIT] =
+ g_signal_new ("vnc-tunnel-init",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (VncDisplayClass, vnc_tunnel_init),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
g_type_class_add_private(klass, sizeof(VncDisplayPrivate));
}
@@ -2048,6 +2067,18 @@ gboolean vnc_display_set_scaling(VncDisp
}
#endif
+VncTunnel *vnc_display_tunnel_open(VncDisplay *obj, const gchar *name)
+{
+ g_return_val_if_fail(VNC_IS_DISPLAY(obj), NULL);
+
+ return gvnc_tunnel_open(obj->priv->gvnc, name);
+}
+
+gboolean vnc_display_set_tunnels(VncDisplay *obj, gboolean enable)
+{
+ obj->priv->allow_tunnels = enable;
+}
+
/*
* Local variables:
* c-indent-level: 8
diff -r 1e6a5272ef00 src/vncdisplay.h
--- a/src/vncdisplay.h Wed Feb 06 22:20:39 2008 -0600
+++ b/src/vncdisplay.h Thu Feb 07 19:51:08 2008 -0600
@@ -17,6 +17,8 @@ typedef struct _VncDisplayPrivate VncDis
#include <gtk/gtkdrawingarea.h>
#include <glib.h>
+
+#include "vnctunnel.h"
#define VNC_TYPE_DISPLAY (vnc_display_get_type())
#define VNC_TYPE_DISPLAY_CREDENTIAL (vnc_display_credential_get_type())
@@ -53,6 +55,7 @@ struct _VncDisplayClass
void (* vnc_initialized) (VncDisplay *display);
void (* vnc_disconnected) (VncDisplay *display);
void (* vnc_auth_credential) (VncDisplay *display, GValueArray *credList);
+ void (* vnc_tunnel_init) (VncDisplay *display);
};
typedef enum
@@ -109,6 +112,10 @@ gboolean vnc_display_set_scaling(VncDisp
void vnc_display_force_grab(VncDisplay *obj, gboolean enable);
+VncTunnel * vnc_display_tunnel_open(VncDisplay *obj, const gchar *name);
+
+gboolean vnc_display_set_tunnels(VncDisplay *obj, gboolean enable);
+
G_END_DECLS
#endif
diff -r 1e6a5272ef00 src/vnctunnel.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vnctunnel.c Thu Feb 07 19:51:08 2008 -0600
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2007 Anthony Liguori <anthony codemonkey ws>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * GTK VNC Widget
+ */
+
+#include "vnctunnel.h"
+#include "gvnc.h"
+
+#include <string.h>
+#include <stdint.h>
+
+#define VNC_TUNNEL_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_TUNNEL, VncTunnelPrivate))
+
+struct _VncTunnelPrivate
+{
+ GList *send_queue;
+ GList *recv_queue;
+ gboolean connected;
+ struct gvnc *gvnc;
+ uint16_t tid;
+};
+
+enum
+{
+ VNC_TUNNEL_CONNECTED,
+ VNC_TUNNEL_IO_WATCH,
+ VNC_TUNNEL_SHUTDOWN,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+VncTunnel *vnc_tunnel_new(struct gvnc *gvnc)
+{
+ VncTunnel *obj;
+
+ obj = VNC_TUNNEL(g_object_new(VNC_TYPE_TUNNEL, NULL));
+ if (obj) {
+ VncTunnelPrivate *priv = obj->priv;
+
+ priv->send_queue = NULL;
+ priv->recv_queue = NULL;
+ priv->connected = FALSE;
+ priv->gvnc = gvnc;
+ priv->tid = -1;
+ }
+
+ return obj;
+}
+
+static void vnc_tunnel_finalize(GObject *gobj)
+{
+ VncTunnel *obj = VNC_TUNNEL(gobj);
+ VncTunnelPrivate *priv = obj->priv;
+
+ if (priv->connected) {
+ gvnc_tunnel_client_shutdown(priv->gvnc, priv->tid);
+ priv->connected = FALSE;
+ }
+
+ while (priv->send_queue) {
+ GString *str = priv->send_queue->data;
+ priv->send_queue = g_list_remove(priv->send_queue, str);
+ g_string_free(str, TRUE);
+ }
+
+ while (priv->recv_queue) {
+ GString *str = priv->recv_queue->data;
+ priv->recv_queue = g_list_remove(priv->recv_queue, str);
+ g_string_free(str, TRUE);
+ }
+
+ gvnc_tunnel_remove(priv->gvnc, obj);
+}
+
+static void vnc_tunnel_class_init(VncTunnelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->finalize = vnc_tunnel_finalize;
+
+ signals[VNC_TUNNEL_CONNECTED] =
+ g_signal_new ("vnc-tunnel-connected",
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(VncTunnelClass, vnc_tunnel_connected),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals[VNC_TUNNEL_IO_WATCH] =
+ g_signal_new ("vnc-tunnel-io-watch",
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(VncTunnelClass, vnc_tunnel_io_watch),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals[VNC_TUNNEL_SHUTDOWN] =
+ g_signal_new ("vnc-tunnel-shutdown",
+ G_OBJECT_CLASS_TYPE(object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(VncTunnelClass, vnc_tunnel_shutdown),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_type_class_add_private(klass, sizeof(VncTunnelPrivate));
+}
+
+static void vnc_tunnel_init(GTypeInstance *instance,
+ gpointer klass G_GNUC_UNUSED)
+{
+ VncTunnel *obj = VNC_TUNNEL(instance);
+ obj->priv = VNC_TUNNEL_GET_PRIVATE(obj);
+ memset(obj->priv, 0, sizeof(VncTunnelPrivate));
+}
+
+GType vnc_tunnel_get_type(void)
+{
+ static GType type;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof(VncTunnelClass),
+ NULL,
+ NULL,
+ (GClassInitFunc)vnc_tunnel_class_init,
+ NULL,
+ NULL,
+ sizeof(VncTunnel),
+ 0,
+ vnc_tunnel_init,
+ NULL
+ };
+
+ type = g_type_register_static(G_TYPE_OBJECT,
+ "VncTunnel",
+ &info,
+ 0);
+ }
+
+ return type;
+}
+
+void vnc_tunnel_send(VncTunnel *obj, const guchar *data, guint size)
+{
+ VncTunnelPrivate *priv = obj->priv;
+
+ if (priv->connected)
+ gvnc_tunnel_client_send(priv->gvnc, priv->tid,
+ data, size);
+ else {
+ GString *copy = g_string_new_len(data, size);
+ priv->send_queue = g_list_append(priv->send_queue, copy);
+ }
+}
+
+guchar *vnc_tunnel_recv(VncTunnel *obj, guint *size)
+{
+ VncTunnelPrivate *priv = obj->priv;
+ GString *str;
+
+ if (!priv->connected || !priv->recv_queue)
+ return NULL;
+
+ str = priv->recv_queue->data;
+ priv->recv_queue = g_list_remove(priv->recv_queue, str);
+ *size = str->len;
+ return g_string_free(str, FALSE);
+}
+
+void vnc_tunnel_set_tid(VncTunnel *obj, guint tid)
+{
+ VncTunnelPrivate *priv = obj->priv;
+
+ priv->tid = tid;
+ priv->connected = TRUE;
+
+ while (priv->send_queue) {
+ GString *str = priv->send_queue->data;
+ priv->send_queue = g_list_remove(priv->send_queue, str);
+ vnc_tunnel_send(obj, str->str, str->len);
+ g_string_free(str, TRUE);
+ }
+
+ g_signal_emit(obj, signals[VNC_TUNNEL_CONNECTED], 0);
+}
+
+guint vnc_tunnel_get_tid(VncTunnel *obj)
+{
+ return obj->priv->tid;
+}
+
+void vnc_tunnel_feed_recv(VncTunnel *obj, const guchar *data, guint size)
+{
+ VncTunnelPrivate *priv = obj->priv;
+ priv->recv_queue = g_list_append(priv->recv_queue,
+ g_string_new_len(data, size));
+ g_signal_emit(obj, signals[VNC_TUNNEL_IO_WATCH], 0);
+}
+
+void vnc_tunnel_feed_shutdown(VncTunnel *obj)
+{
+ obj->priv->connected = FALSE;
+ g_signal_emit(obj, signals[VNC_TUNNEL_SHUTDOWN], 0);
+}
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
diff -r 1e6a5272ef00 src/vnctunnel.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/vnctunnel.h Thu Feb 07 19:51:08 2008 -0600
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007 Anthony Liguori <anthony codemonkey ws>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * GTK VNC Widget
+ */
+
+#ifndef _VNC_TUNNEL_H_
+#define _VNC_TUNNEL_H_
+
+typedef struct _VncTunnel VncTunnel;
+typedef struct _VncTunnelClass VncTunnelClass;
+typedef struct _VncTunnelPrivate VncTunnelPrivate;
+
+#include <glib-object.h>
+
+#define VNC_TYPE_TUNNEL (vnc_tunnel_get_type())
+
+#define VNC_TUNNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), VNC_TYPE_TUNNEL, VncTunnel))
+
+#define VNC_TUNNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), VNC_TYPE_TUNNEL, VncTunnelClass))
+
+#define VNC_IS_TUNNEL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), VNC_TYPE_TUNNEL))
+
+#define VNC_IS_TUNNEL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), VNC_TYPE_TUNNEL))
+
+#define VNC_TUNNEL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), VNC_TYPE_TUNNEL, VncTunnelClass))
+
+struct _VncTunnel
+{
+ GObject parent;
+
+ VncTunnelPrivate *priv;
+};
+
+struct _VncTunnelClass
+{
+ GObjectClass parent;
+
+ /* Signals */
+ void (* vnc_tunnel_connected) (VncTunnel *obj);
+ void (* vnc_tunnel_io_watch) (VncTunnel *obj);
+ void (* vnc_tunnel_shutdown) (VncTunnel *obj);
+};
+
+struct gvnc;
+
+G_BEGIN_DECLS
+
+GType vnc_tunnel_get_type(void);
+
+VncTunnel * vnc_tunnel_new(struct gvnc *gvnc);
+
+guchar * vnc_tunnel_recv(VncTunnel *obj, guint *size);
+
+void vnc_tunnel_send(VncTunnel *obj, const guchar *data, guint size);
+
+guint vnc_tunnel_get_tid(VncTunnel *obj);
+
+void vnc_tunnel_set_tid(VncTunnel *obj, guint tid);
+
+void vnc_tunnel_feed_recv(VncTunnel *obj, const guchar *data, guint size);
+
+void vnc_tunnel_feed_shutdown(VncTunnel *obj);
+
+G_END_DECLS
+
+#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]