[gtk-vnc-devel] [RFC] VNC tunnels



Hi,

I'm looking for some feedback before submitting to QEMU that I've got the protocol extensions reasonably nailed down. Attached is the preliminary gtk-vnc and QEMU patch to enable VNC channels.

There's something of a fundamental architecture shift here that I'd also like to discuss. Originally, I had planned that gvnc would only depend on glib. Since then, I've come under the opinion that it should actually have been a proper GObject. This patch introduces a GObject called "VncTunnel" which represents an active tunnel. These tunnels really aren't dependent at all on VncDisplay so they are actually created and managed by gvnc. This effectively adds a GObject dependency on gvnc.

I've also #if 0'd out a lot of the SharedMemory extension. I'll properly remove that though until I can figure out the least racy way to do it.

Right now, I've modified the gvncviewer.py example a little bit so that if you launch QEMU with -monitor vnc:org.qemu.monitor you can see some monitor output.

The protocol extensions are as follows:

I'm using a psuedo-encoding (-258) to identify support for VNC tunnels. I'm introducing three server and three client messages based on the idea of "extended messages". This is really just a way to multiplex messages over one message type (which is all I have allocated).

The three client messages are:

TunnelOpenRequest

u8              id              255
u8              xid             0
u16             size
u8[size]        name

TunnelClientShutdown

u8              id              255
u8              xid             1
u16             tid

TunnelClientSend

u8              id              255
u8              xid             2
u16             tid
u32             size
u8[size]        data

And the three server messages are:

TunnelOpenResponse

u8              id              255
u8              xid             0
u16             tid

TunnelServerShutdown

u8              id              255
u8              xid             1
u16             tid

TunnelServerSend

u8              id              255
u8              xid             2
u16             tid
u32             size
u8[size]        data

Semantically, a client open's a VNC channel and is assigned an ID. The client can then close the channel or the server can close the connection. When the client closes the channel, the server will also close the channel. Finally, either the client or server is capable of sending a message in either direction.

I thought about enumerating supported channels but I think that it becomes less useful if channel names can be created during execution. For instance, if you sent a monitor command of "change cdrom vnc:org.qemu.cdrom" that would create a new channel on the server. So, does that mean we would need another server message to tell the client that a new channel is available? It seems to me that it's better to just ignore the enumeration problem entirely.

Any thoughts?

Regards,

Anthony Liguori
Index: qemu/sysemu.h
===================================================================
--- qemu.orig/sysemu.h	2007-12-10 12:29:40.000000000 -0600
+++ qemu/sysemu.h	2007-12-10 12:30:09.000000000 -0600
@@ -179,4 +179,6 @@
 void do_usb_del(const char *devname);
 void usb_info(void);
 
+CharDriverState *qemu_chr_open_vnc(const char *name);
+
 #endif
Index: qemu/vl.c
===================================================================
--- qemu.orig/vl.c	2007-12-10 12:28:43.000000000 -0600
+++ qemu/vl.c	2007-12-10 12:29:49.000000000 -0600
@@ -3373,6 +3373,9 @@
         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);
Index: qemu/vnc.c
===================================================================
--- qemu.orig/vnc.c	2007-12-10 12:21:02.000000000 -0600
+++ qemu/vnc.c	2007-12-11 15:49:21.000000000 -0600
@@ -40,7 +40,7 @@
 #include <gnutls/x509.h>
 #endif /* CONFIG_VNC_TLS */
 
-// #define _VNC_DEBUG 1
+#define _VNC_DEBUG 1
 
 #if _VNC_DEBUG
 #define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
@@ -65,7 +65,7 @@
 
 typedef struct VncState VncState;
 
-typedef int VncReadEvent(VncState *vs, char *data, size_t len);
+typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
 
 typedef void VncWritePixels(VncState *vs, void *data, int size);
 
@@ -118,6 +118,18 @@
 
 #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;
@@ -133,6 +145,7 @@
     int has_resize;
     int has_hextile;
     int has_pointer_type_change;
+    int has_tunnels;
     int absolute;
     int last_x;
     int last_y;
@@ -174,10 +187,24 @@
     size_t read_handler_expect;
     /* input */
     uint8_t modifiers_state[256];
+
+    VNCTunnelState *tunnel_list;
 };
 
 static VncState *vnc_state; /* needed for info vnc */
 
+static void vnc_write(VncState *vs, const void *data, size_t len);
+static void vnc_write_u32(VncState *vs, uint32_t value);
+static void vnc_write_s32(VncState *vs, int32_t value);
+static void vnc_write_u16(VncState *vs, uint16_t value);
+static void vnc_write_u8(VncState *vs, uint8_t value);
+static void vnc_flush(VncState *vs);
+static void vnc_update_client(void *opaque);
+static void vnc_client_read(void *opaque);
+
+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);
+
 void do_info_vnc(void)
 {
     if (vnc_state == NULL)
@@ -194,6 +221,112 @@
     }
 }
 
+/* VNC Tunnel functions */
+
+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
+	tunnel_find_rsp(vs, s->id);
+
+    qemu_chr_reset(&s->chr);
+}
+
+static void vnc_server_tunnel_shutdown(VncState *vs, VNCTunnelState *s)
+{
+    s->connected = 0;
+    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)
+	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;
+
+    /* FIXME signal disconnection */
+    chr = (CharDriverState *)s;
+
+    if (qemu_chr_can_read(chr))
+	qemu_chr_read(chr, (char *)data, size);
+}
+
 /* TODO
    1) Get the queue working for IO.
    2) there is some weirdness when using the -S option (the screen is grey
@@ -201,15 +334,6 @@
    3) resolutions > 1024
 */
 
-static void vnc_write(VncState *vs, const void *data, size_t len);
-static void vnc_write_u32(VncState *vs, uint32_t value);
-static void vnc_write_s32(VncState *vs, int32_t value);
-static void vnc_write_u16(VncState *vs, uint16_t value);
-static void vnc_write_u8(VncState *vs, uint8_t value);
-static void vnc_flush(VncState *vs);
-static void vnc_update_client(void *opaque);
-static void vnc_client_read(void *opaque);
-
 static inline void vnc_set_bit(uint32_t *d, int k)
 {
     d[k >> 5] |= 1 << (k & 0x1f);
@@ -1064,6 +1188,37 @@
     }
 }
 
+static void advertise_tunnel_support(VncState *vs)
+{
+    printf("%d\n", __LINE__);
+    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, -258);
+    vnc_flush(vs);
+}
+
+static void tunnel_find_rsp(VncState *vs, int16_t id)
+{
+    printf("%d\n", id);
+    printf("%d\n", __LINE__);
+    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)
+{
+    printf("%d\n", __LINE__);
+    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;
@@ -1071,6 +1226,7 @@
     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;
 
@@ -1091,6 +1247,10 @@
 	case -257:
 	    vs->has_pointer_type_change = 1;
 	    break;
+	case -258:
+	    vs->has_tunnels = 1;
+	    advertise_tunnel_support(vs);
+	    break;
 	default:
 	    break;
 	}
@@ -1181,7 +1341,7 @@
     vga_hw_update();
 }
 
-static int protocol_client_msg(VncState *vs, char *data, size_t len)
+static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
 {
     int i;
     uint16_t limit;
@@ -1244,6 +1404,38 @@
 
 	client_cut_text(vs, read_u32(data, 4), data + 8);
 	break;
+    case 255:
+	if (len == 1)
+	    return 2;
+
+	switch (read_u8(data, 1)) {
+	case 0:
+	    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 1:
+	    if (len < 4)
+		return 4;
+
+	    vnc_tunnel_client_shutdown(vs, read_u16(data, 2));
+	    break;
+	case 2:
+	    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;
+	}
+	break;
     default:
 	printf("Msg: %d\n", data[0]);
 	vnc_client_error(vs);
@@ -1254,7 +1446,7 @@
     return 0;
 }
 
-static int protocol_client_init(VncState *vs, char *data, size_t len)
+static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
 {
     char pad[3] = { 0, 0, 0 };
     char buf[1024];
@@ -1327,7 +1519,7 @@
         vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
 }
 
-static int protocol_client_auth_vnc(VncState *vs, char *data, size_t len)
+static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
 {
     char response[VNC_AUTH_CHALLENGE_SIZE];
     int i, j, pwlen;
@@ -1738,7 +1930,7 @@
     return vnc_continue_handshake(vs);
 }
 
-static int protocol_client_vencrypt_auth(VncState *vs, char *data, size_t len)
+static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
 {
     int auth = read_u32(data, 0);
 
@@ -1768,7 +1960,7 @@
     return 0;
 }
 
-static int protocol_client_vencrypt_init(VncState *vs, char *data, size_t len)
+static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
 {
     if (data[0] != 0 ||
 	data[1] != 2) {
@@ -1798,7 +1990,7 @@
 }
 #endif /* CONFIG_VNC_TLS */
 
-static int protocol_client_auth(VncState *vs, char *data, size_t len)
+static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
 {
     /* We only advertise 1 auth scheme at a time, so client
      * must pick the one we sent. Verify this */
@@ -1847,7 +2039,7 @@
     return 0;
 }
 
-static int protocol_version(VncState *vs, char *version, size_t len)
+static int protocol_version(VncState *vs, uint8_t *version, size_t len)
 {
     char local[13];
 
diff -r d6f1a50dbb0b examples/gvncviewer.py
--- a/examples/gvncviewer.py	Sun Oct 21 17:11:07 2007 -0300
+++ b/examples/gvncviewer.py	Tue Dec 11 20:44:49 2007 -0600
@@ -34,10 +34,19 @@ def vnc_connected(src):
 def vnc_connected(src):
     print "Connected to server"
 
+def tun_io_watch(tun):
+    sys.stdout.write(tun.recv())
+    sys.stdout.flush()
+
 def vnc_initialized(src, window):
     print "Connection initialized"
     set_title(src, window, False)
     window.show_all()
+
+def vnc_tunnel_init(src):
+    print "Server supports tunnels"
+    tunnel = vnc.tunnel_open("org.qemu.monitor")
+    tunnel.connect('vnc-tunnel-io-watch', tun_io_watch)
 
 def vnc_disconnected(src):
     print "Disconnected from server"
@@ -170,6 +179,7 @@ vnc.connect("vnc-pointer-ungrab", vnc_un
 
 vnc.connect("vnc-connected", vnc_connected)
 vnc.connect("vnc-initialized", vnc_initialized, window)
+vnc.connect("vnc-tunnel-init", vnc_tunnel_init)
 vnc.connect("vnc-disconnected", vnc_disconnected)
 vnc.connect("vnc-auth-credential", vnc_auth_cred)
 
diff -r d6f1a50dbb0b src/Makefile.am
--- a/src/Makefile.am	Sun Oct 21 17:11:07 2007 -0300
+++ b/src/Makefile.am	Tue Dec 11 20:44:49 2007 -0600
@@ -20,6 +20,7 @@ libgtk_vnc_1_0_la_SOURCES = blt.h blt1.h
 	gvnc.h gvnc.c \
 	vncdisplay.h vncdisplay.c \
 	vncshmimage.h vncshmimage.c \
+	vnctunnel.h vnctunnel.c \
         vncmarshal.h vncmarshal.c \
 	utils.h
 
@@ -46,8 +47,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 d6f1a50dbb0b src/gvnc.c
--- a/src/gvnc.c	Sun Oct 21 17:11:07 2007 -0300
+++ b/src/gvnc.c	Tue Dec 11 20:44:49 2007 -0600
@@ -9,6 +9,8 @@
  */
 
 #include "gvnc.h"
+
+#include <glib.h>
 
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -31,6 +33,8 @@
 #include "coroutine.h"
 #include "d3des.h"
 
+#include "vnctunnel.h"
+
 #include "utils.h"
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
@@ -124,6 +128,10 @@ struct gvnc
 	char *xmit_buffer;
 	int xmit_buffer_capacity;
 	int xmit_buffer_size;
+
+	gboolean has_extmsg;
+	GList *pending_tunnels;
+	GList *open_tunnels;
 };
 
 #define nibhi(a) (((a) >> 4) & 0x0F)
@@ -710,6 +718,7 @@ gboolean gvnc_set_pixel_format(struct gv
 	return !gvnc_has_error(gvnc);
 }
 
+#if 0
 gboolean gvnc_set_shared_buffer(struct gvnc *gvnc, int line_size, int shmid)
 {
 	gvnc_write_u8(gvnc, 255);
@@ -720,6 +729,7 @@ gboolean gvnc_set_shared_buffer(struct g
 
 	return !gvnc_has_error(gvnc);
 }
+#endif
 
 gboolean gvnc_set_encodings(struct gvnc *gvnc, int n_encoding, int32_t *encoding)
 {
@@ -818,11 +828,71 @@ gboolean gvnc_client_cut_text(struct gvn
 {
 	uint8_t pad[3] = {0};
 
-	gvnc_write_u8(gvnc, 6);
-	gvnc_write(gvnc, pad, 3);
-	gvnc_write_u32(gvnc, length);
-	gvnc_write(gvnc, data, length);
-	gvnc_flush(gvnc);
+	gvnc_buffered_write_u8(gvnc, 6);
+	gvnc_buffered_write(gvnc, pad, 3);
+	gvnc_buffered_write_u32(gvnc, length);
+	gvnc_buffered_write(gvnc, data, length);
+	gvnc_buffered_flush(gvnc);
+	return !gvnc_has_error(gvnc);
+}
+
+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;
+}
+
+gboolean gvnc_tunnel_open_request(struct gvnc *gvnc, const char *name)
+{
+	size_t size = strlen(name);
+
+	if (!gvnc->has_extmsg) {
+		gvnc->has_error = TRUE;
+		return FALSE;
+	}
+
+	gvnc_buffered_write_u8(gvnc, 255);
+	gvnc_buffered_write_u8(gvnc, 0);
+	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_extmsg) {
+		gvnc->has_error = TRUE;
+		return FALSE;
+	}
+
+	gvnc_buffered_write_u8(gvnc, 255);
+	gvnc_buffered_write_u8(gvnc, 1);
+	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_extmsg) {
+		gvnc->has_error = TRUE;
+		return FALSE;
+	}
+
+	gvnc_buffered_write_u8(gvnc, 255);
+	gvnc_buffered_write_u8(gvnc, 2);
+	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);
 }
 
@@ -1010,6 +1080,86 @@ static void gvnc_server_cut_text(struct 
 		gvnc->has_error = TRUE;
 }
 
+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) {
+		gvnc->has_error = TRUE;
+		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) {
+		gvnc->has_error = TRUE;
+		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);
+		gvnc->has_error = TRUE;
+		return;
+	}
+
+	vnc_tunnel_feed_recv(tunnel, data, len);
+}
+
 static void gvnc_resize(struct gvnc *gvnc, int width, int height)
 {
 	if (gvnc->has_error || !gvnc->ops.resize)
@@ -1026,6 +1176,7 @@ static void gvnc_pointer_type_change(str
 		gvnc->has_error = TRUE;
 }
 
+#if 0
 static void gvnc_shared_memory_rmid(struct gvnc *gvnc, int shmid)
 {
 	if (gvnc->has_error || !gvnc->ops.shared_memory_rmid)
@@ -1033,6 +1184,7 @@ static void gvnc_shared_memory_rmid(stru
 	if (!gvnc->ops.shared_memory_rmid(gvnc->ops_data, shmid))
 		gvnc->has_error = TRUE;
 }
+#endif
 
 #define RICH_CURSOR_BLIT(gvnc, pixbuf, image, mask, pitch, width, height, src_pixel_t) \
 	do {								\
@@ -1185,6 +1337,7 @@ static void gvnc_framebuffer_update(stru
 	case GVNC_ENCODING_POINTER_CHANGE:
 		gvnc_pointer_type_change(gvnc, x);
 		break;
+#if 0
 	case GVNC_ENCODING_SHARED_MEMORY:
 		switch (gvnc_read_u32(gvnc)) {
 		case 0:
@@ -1200,6 +1353,12 @@ static void gvnc_framebuffer_update(stru
 			break;
 		}
 		break;
+#endif
+	case GVNC_ENCODING_TUNNEL_IO:
+		gvnc->has_extmsg = TRUE;
+		if (gvnc->ops.tunnels_available)
+			gvnc->ops.tunnels_available(gvnc->ops_data);
+		break;
 	case GVNC_ENCODING_RICH_CURSOR:
 		gvnc_rich_cursor(gvnc, x, y, width, height);
 		break;
@@ -1305,6 +1464,42 @@ gboolean gvnc_server_message(struct gvnc
 		gvnc_server_cut_text(gvnc, data, n_text);
 		free(data);
 	}	break;
+	case 255: /* ExtClientMessage */
+		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->has_error = TRUE;
 		break;
@@ -1842,7 +2037,6 @@ struct gvnc *gvnc_new(const struct gvnc_
 	gvnc->ops_data = ops_data;
 	gvnc->auth_type = GVNC_AUTH_INVALID;
 	gvnc->auth_subtype = GVNC_AUTH_INVALID;
-
 	return gvnc;
 }
 
@@ -2305,11 +2499,12 @@ gboolean gvnc_set_local(struct gvnc *gvn
 	return !gvnc_has_error(gvnc);
 }
 
+#if 0
 gboolean gvnc_shared_memory_enabled(struct gvnc *gvnc)
 {
 	return gvnc->shared_memory_enabled;
 }
-
+#endif
 
 const char *gvnc_get_name(struct gvnc *gvnc)
 {
diff -r d6f1a50dbb0b src/gvnc.h
--- a/src/gvnc.h	Sun Oct 21 17:11:07 2007 -0300
+++ b/src/gvnc.h	Tue Dec 11 20:44:49 2007 -0600
@@ -3,6 +3,8 @@
 
 #include <glib.h>
 #include <stdint.h>
+
+#include "vnctunnel.h"
 
 struct gvnc;
 
@@ -18,9 +20,12 @@ struct gvnc_ops
 	gboolean (*server_cut_text)(void *, const void *, size_t);
 	gboolean (*resize)(void *, int, int);
 	gboolean (*pointer_type_change)(void *, int);
+#if 0
 	gboolean (*shared_memory_rmid)(void *, int);
+#endif
 	gboolean (*local_cursor)(void *, int, int, int, int, uint8_t *);
 	gboolean (*auth_unsupported)(void *, unsigned int);
+	gboolean (*tunnels_available)(void *);
 };
 
 struct gvnc_pixel_format
@@ -74,7 +79,10 @@ typedef enum {
 	GVNC_ENCODING_XCURSOR = -240,
 
 	GVNC_ENCODING_POINTER_CHANGE = -257,
+#if 0
 	GVNC_ENCODING_SHARED_MEMORY = -258,
+#endif
+	GVNC_ENCODING_TUNNEL_IO = -258,
 } gvnc_encoding;
 
 typedef enum {
@@ -129,6 +137,13 @@ gboolean gvnc_is_initialized(struct gvnc
 
 gboolean gvnc_server_message(struct gvnc *gvnc);
 
+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_client_cut_text(struct gvnc *gvnc,
 			      const void *data, size_t length);
 
@@ -147,17 +162,23 @@ gboolean gvnc_set_pixel_format(struct gv
 gboolean gvnc_set_pixel_format(struct gvnc *gvnc,
 			       const struct gvnc_pixel_format *fmt);
 
+#if 0
 gboolean gvnc_set_shared_buffer(struct gvnc *gvnc, int line_size, int shmid);
+#endif
 
 gboolean gvnc_has_error(struct gvnc *gvnc);
 
 gboolean gvnc_set_local(struct gvnc *gvnc, struct gvnc_framebuffer *fb);
 
+#if 0
 gboolean gvnc_shared_memory_enabled(struct gvnc *gvnc);
+#endif
 
 const char *gvnc_get_name(struct gvnc *gvnc);
 int gvnc_get_width(struct gvnc *gvnc);
 int gvnc_get_height(struct gvnc *gvnc);
+
+VncTunnel *gvnc_tunnel_open(struct gvnc *gvnc, const char *name);
 
 #endif
 /*
diff -r d6f1a50dbb0b src/libgtk-vnc_sym.version
--- a/src/libgtk-vnc_sym.version	Sun Oct 21 17:11:07 2007 -0300
+++ b/src/libgtk-vnc_sym.version	Tue Dec 11 20:44:49 2007 -0600
@@ -25,6 +25,13 @@
     vnc_display_get_name;
 
     vnc_display_client_cut_text;
+
+    vnc_display_tunnel_open;
+
+    vnc_tunnel_get_type;
+    vnc_tunnel_recv;
+    vnc_tunnel_send;
+    vnc_tunnel_new;
 
     gvnc_new;
     gvnc_free;
diff -r d6f1a50dbb0b src/vnc.override
--- a/src/vnc.override	Sun Oct 21 17:11:07 2007 -0300
+++ b/src/vnc.override	Tue Dec 11 20:44:49 2007 -0600
@@ -3,13 +3,19 @@ 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_tunnel_get_tid
+  vnc_tunnel_set_tid
+  vnc_tunnel_feed_recv
+  vnc_tunnel_feed_shutdown
 %%
 override vnc_display_send_keys kwargs 
 static PyObject*
@@ -46,3 +52,45 @@ _wrap_vnc_display_send_keys(PyGObject *s
     vnc_display_send_keys(VNC_DISPLAY(self->obj), keys, len);
     free(keys);
 }
+
+%%
+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 d6f1a50dbb0b src/vncdisplay.c
--- a/src/vncdisplay.c	Sun Oct 21 17:11:07 2007 -0300
+++ b/src/vncdisplay.c	Tue Dec 11 20:44:49 2007 -0600
@@ -80,6 +80,8 @@ typedef enum
 
 	VNC_SERVER_CUT_TEXT,
 	VNC_BELL,
+
+	VNC_TUNNEL_INIT,
 
 	LAST_SIGNAL
 } vnc_display_signals;
@@ -482,9 +484,11 @@ static gboolean on_resize(void *opaque, 
 	priv->fb.linesize = priv->shm_image->bytes_per_line;
 	priv->fb.data = (uint8_t *)priv->shm_image->pixels;
 
+#if 0
 	if (gvnc_shared_memory_enabled(priv->gvnc) && priv->fb.shm_id != -1)
 		gvnc_set_shared_buffer(priv->gvnc,
 				       priv->fb.linesize, priv->fb.shm_id);
+#endif
 
 	gtk_widget_set_size_request(GTK_WIDGET(obj), width, height);
 
@@ -510,6 +514,7 @@ static gboolean on_pointer_type_change(v
 	return TRUE;
 }
 
+#if 0
 static gboolean on_shared_memory_rmid(void *opaque, int shmid)
 {
 	VncDisplay *obj = VNC_DISPLAY(opaque);
@@ -525,6 +530,7 @@ static gboolean on_shared_memory_rmid(vo
 		priv->fb.shm_id = -1;
 	return TRUE;
 }
+#endif
 
 
 static gboolean on_auth_cred(void *opaque)
@@ -674,6 +680,17 @@ static gboolean on_local_cursor(void *op
 	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,
@@ -682,7 +699,7 @@ static const struct gvnc_ops vnc_display
 	.update = on_update,
 	.resize = on_resize,
 	.pointer_type_change = on_pointer_type_change,
-	.shared_memory_rmid = on_shared_memory_rmid,
+	.tunnels_available = on_tunnels_available,
 	.local_cursor = on_local_cursor,
 	.auth_unsupported = on_auth_unsupported,
 	.server_cut_text = on_server_cut_text,
@@ -696,7 +713,7 @@ static void *vnc_coroutine(void *opaque)
 	int32_t encodings[] = { GVNC_ENCODING_DESKTOP_RESIZE,
 				GVNC_ENCODING_RICH_CURSOR,
 				GVNC_ENCODING_XCURSOR,
-				GVNC_ENCODING_SHARED_MEMORY,
+				GVNC_ENCODING_TUNNEL_IO,
 				GVNC_ENCODING_POINTER_CHANGE,
 				GVNC_ENCODING_HEXTILE,
 				GVNC_ENCODING_COPY_RECT,
@@ -730,7 +747,7 @@ static void *vnc_coroutine(void *opaque)
 		       signals[VNC_INITIALIZED],
 		       0);
 
-	if (!gvnc_set_encodings(priv->gvnc, 6, encodings))
+	if (!gvnc_set_encodings(priv->gvnc, 8, encodings))
 		goto cleanup;
 
 	if (!gvnc_framebuffer_update_request(priv->gvnc, 0, 0, 0, priv->fb.width, priv->fb.height))
@@ -855,7 +872,7 @@ static void vnc_display_destroy (GtkObje
 }
 
 
-static void vnc_display_finalize (GObject *obj)
+static void vnc_display_finalize(GObject *obj)
 {
 	VncDisplay *display = VNC_DISPLAY (obj);
 	GVNC_DEBUG("Releasing VNC widget\n");
@@ -923,6 +940,16 @@ static void vnc_display_class_init(VncDi
 			      1,
 			      G_TYPE_VALUE_ARRAY);
 
+
+	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);
 
 	signals[VNC_POINTER_GRAB] =
 		g_signal_new("vnc-pointer-grab",
@@ -1245,6 +1272,13 @@ void vnc_display_client_cut_text(VncDisp
 	gvnc_client_cut_text(obj->priv->gvnc, text, strlen (text));
 }
 
+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);
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8
diff -r d6f1a50dbb0b src/vncdisplay.h
--- a/src/vncdisplay.h	Sun Oct 21 17:11:07 2007 -0300
+++ b/src/vncdisplay.h	Tue Dec 11 20:44:49 2007 -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())
@@ -52,6 +54,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
@@ -89,6 +92,8 @@ const char *	vnc_display_get_name(VncDis
 
 void		vnc_display_client_cut_text(VncDisplay *obj, const gchar *text);
 
+VncTunnel *	vnc_display_tunnel_open(VncDisplay *obj, const gchar *name);
+
 G_END_DECLS
 
 #endif


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