[virt-tools-list] [PATCH 4 of 5] viewer: Add support for Spice

Marc-André Lureau marcandre.lureau at redhat.com
Tue Nov 30 15:08:32 UTC 2010


# HG changeset patch
# User Marc-André Lureau <marcandre.lureau at redhat.com>
# Date 1291121941 -3600
# Node ID 4ac66a955d7bf718ce85e8ffd0be00f0c4ad8ac6
# Parent  9f8a6e933516586fc82982cffb4721f5539c9d2f
viewer: Add support for Spice

diff -r 9f8a6e933516 -r 4ac66a955d7b AUTHORS
--- a/AUTHORS	Fri Nov 19 18:09:42 2010 +0100
+++ b/AUTHORS	Tue Nov 30 13:59:01 2010 +0100
@@ -13,6 +13,7 @@
     Guido G\374nther <agx-at-sigxcpu-dot-org>
     Hiroyuki Kaguchi <fj7025cf-at-aa-dot-jp-dot-fujitsu-dot-com>
     Ronnie Sahlberg <ronniesahlberg at gmail.com>
+    Marc-André Lureau <marcandre.lureau-at-redhat-dot-com>
 
    ...send patches to get your name here...
 
diff -r 9f8a6e933516 -r 4ac66a955d7b configure.ac
--- a/configure.ac	Fri Nov 19 18:09:42 2010 +0100
+++ b/configure.ac	Tue Nov 30 13:59:01 2010 +0100
@@ -33,6 +33,7 @@
 PKG_CHECK_MODULES(GTK2, gtk+-2.0 >= 2.10.0)
 PKG_CHECK_MODULES(LIBGLADE2, libglade-2.0 >= 2.6.0)
 PKG_CHECK_MODULES(GTKVNC, gtk-vnc-1.0 >= 0.3.8)
+PKG_CHECK_MODULES(SPICEGTK, spice-client-gtk >= 0.1.0.14)
 
 dnl Decide if this platform can support the SSH tunnel feature.
 AC_CHECK_HEADERS([sys/socket.h sys/un.h windows.h])
diff -r 9f8a6e933516 -r 4ac66a955d7b man/Makefile.am
--- a/man/Makefile.am	Fri Nov 19 18:09:42 2010 +0100
+++ b/man/Makefile.am	Tue Nov 30 13:59:01 2010 +0100
@@ -2,6 +2,7 @@
 man_MANS = virt-viewer.1
 
 EXTRA_DIST = virt-viewer.pod
+DISTCLEANFILES = $(man_MANS)
 
 %.1: %.pod
 	pod2man $< > $@
diff -r 9f8a6e933516 -r 4ac66a955d7b man/virt-viewer.pod
--- a/man/virt-viewer.pod	Fri Nov 19 18:09:42 2010 +0100
+++ b/man/virt-viewer.pod	Tue Nov 30 13:59:01 2010 +0100
@@ -10,12 +10,13 @@
 =head1 DESCRIPTION
 
 B<virt-viewer> is a minimal tool for displaying the graphical console
-of a virtual machine. The console is accessed using the VNC protocol.
-The guest can be referred to based on its name, ID, or UUID. If the
-guest is not already running, then the viewer can be told to wait
-until is starts before attempting to connect to the console The viewer
-can connect to remote hosts to lookup the console information and then
-also connect to the remote console using the same network transport.
+of a virtual machine. The console is accessed using the VNC or SPICE
+protocol. The guest can be referred to based on its name, ID, or
+UUID. If the guest is not already running, then the viewer can be told
+to wait until is starts before attempting to connect to the console
+The viewer can connect to remote hosts to lookup the console
+information and then also connect to the remote console using the same
+network transport.
 
 =head1 OPTIONS
 
@@ -49,7 +50,7 @@
 
 =item -z PCT, --zoom=PCT
 
-Zoom level of the VNC window in percentage. Range 10-200.
+Zoom level of the display window in percentage. Range 10-200.
 
 =item -d, --direct
 
diff -r 9f8a6e933516 -r 4ac66a955d7b po/POTFILES.in
--- a/po/POTFILES.in	Fri Nov 19 18:09:42 2010 +0100
+++ b/po/POTFILES.in	Tue Nov 30 13:59:01 2010 +0100
@@ -3,6 +3,8 @@
 src/main.c
 src/util.c
 src/viewer.c
+src/display-spice.c
+src/display-vnc.c
 src/about.glade
 src/auth.glade
 src/viewer.glade
diff -r 9f8a6e933516 -r 4ac66a955d7b src/Makefile.am
--- a/src/Makefile.am	Fri Nov 19 18:09:42 2010 +0100
+++ b/src/Makefile.am	Tue Nov 30 13:59:01 2010 +0100
@@ -11,10 +11,15 @@
 	util.h util.c \
 	auth.h auth.c \
 	events.h events.c \
-	viewer.h viewer.c
+	viewer.h viewer.c \
+	viewer-priv.h \
+	display.h display.c \
+	display-vnc.h display-vnc.c \
+	display-spice.h display-spice.c
 
 virt_viewer_LDADD = \
 	@GTKVNC_LIBS@ \
+	@SPICEGTK_LIBS@ \
 	@GTK2_LIBS@ \
 	@LIBXML2_LIBS@ \
 	@LIBGLADE2_LIBS@ \
@@ -22,6 +27,7 @@
 
 virt_viewer_CFLAGS = \
 	@GTKVNC_CFLAGS@ \
+	@SPICEGTK_CFLAGS@ \
 	@GTK2_CFLAGS@ \
 	@LIBXML2_CFLAGS@ \
 	@LIBGLADE2_CFLAGS@ \
diff -r 9f8a6e933516 -r 4ac66a955d7b src/auth.c
--- a/src/auth.c	Fri Nov 19 18:09:42 2010 +0100
+++ b/src/auth.c	Tue Nov 30 13:59:01 2010 +0100
@@ -30,11 +30,8 @@
 #include "auth.h"
 
 
-static int
-viewer_auth_collect_credentials(const char *type,
-				const char *address,
-				char **username,
-				char **password)
+int viewer_auth_collect_credentials(const char *type, const char *address,
+                                    char **username, char **password)
 {
 	GtkWidget *dialog = NULL;
 	GladeXML *creds = viewer_load_glade("auth.glade", "auth");
diff -r 9f8a6e933516 -r 4ac66a955d7b src/auth.h
--- a/src/auth.h	Fri Nov 19 18:09:42 2010 +0100
+++ b/src/auth.h	Tue Nov 30 13:59:01 2010 +0100
@@ -29,6 +29,9 @@
 
 void viewer_auth_vnc_credentials(GtkWidget *vnc, GValueArray *credList, char **message);
 
+int viewer_auth_collect_credentials(const char *type, const char *address,
+                                    char **username, char **password);
+
 int viewer_auth_libvirt_credentials(virConnectCredentialPtr cred,
 				    unsigned int ncred,
 				    void *cbdata);
diff -r 9f8a6e933516 -r 4ac66a955d7b src/display-spice.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/display-spice.c	Tue Nov 30 13:59:01 2010 +0100
@@ -0,0 +1,281 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2007-2009 Red Hat,
+ * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <spice-audio.h>
+#include "util.h"
+#include "display-spice.h"
+#include "auth.h"
+
+G_DEFINE_TYPE (VirtViewerDisplaySpice, virt_viewer_display_spice, VIRT_VIEWER_TYPE_DISPLAY)
+
+
+static void _spice_close(VirtViewerDisplay* display);
+static void _spice_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals);
+static GdkPixbuf* _spice_get_pixbuf(VirtViewerDisplay* display);
+static gboolean _spice_open_fd(VirtViewerDisplay* display, int fd);
+static gboolean _spice_open_host(VirtViewerDisplay* display, char *host, char *port);
+static gboolean _spice_channel_open_fd(VirtViewerDisplay* display, VirtViewerDisplayChannel* channel, int fd);
+
+
+static void
+virt_viewer_display_spice_class_init(VirtViewerDisplaySpiceClass *klass)
+{
+	VirtViewerDisplayClass *dclass = VIRT_VIEWER_DISPLAY_CLASS(klass);
+
+	dclass->close = _spice_close;
+	dclass->send_keys = _spice_send_keys;
+	dclass->get_pixbuf = _spice_get_pixbuf;
+	dclass->open_fd = _spice_open_fd;
+	dclass->open_host = _spice_open_host;
+	dclass->channel_open_fd = _spice_channel_open_fd;
+}
+
+static void
+virt_viewer_display_spice_init(VirtViewerDisplaySpice *self G_GNUC_UNUSED)
+{
+}
+
+static void _spice_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+
+	g_return_if_fail(self != NULL);
+	g_return_if_fail(self->display != NULL);
+
+	spice_display_send_keys(self->display, keyvals, nkeyvals, SPICE_DISPLAY_KEY_EVENT_CLICK);
+}
+
+static GdkPixbuf* _spice_get_pixbuf(VirtViewerDisplay* display)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+
+	g_return_val_if_fail(self != NULL, NULL);
+	g_return_val_if_fail(self->display != NULL, NULL);
+
+	return spice_display_get_pixbuf(self->display);
+}
+
+static void _spice_close(VirtViewerDisplay* display)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+
+	g_return_if_fail(self != NULL);
+
+	if (self->session == NULL)
+		return;
+
+	spice_session_disconnect(self->session);
+
+	if (self->session) /* let viewer_quit() be reentrant */
+		g_object_unref(self->session);
+	self->session = NULL;
+
+	if (self->audio)
+		g_object_unref(self->audio);
+	self->audio = NULL;
+}
+
+static gboolean _spice_open_host(VirtViewerDisplay* display, char *host, char *port)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+
+	g_return_val_if_fail(self != NULL, FALSE);
+	g_return_val_if_fail(self->session != NULL, FALSE);
+
+	g_object_set(self->session,
+		     "host", host,
+		     "port", port,
+		     NULL);
+
+	return spice_session_connect(self->session);
+}
+
+static gboolean _spice_open_fd(VirtViewerDisplay* display, int fd)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+
+	g_return_val_if_fail(self != NULL, FALSE);
+
+	return spice_session_open_fd(self->session, fd);
+}
+
+static gboolean _spice_channel_open_fd(VirtViewerDisplay* display,
+				       VirtViewerDisplayChannel* channel, int fd)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+
+	g_return_val_if_fail(self != NULL, FALSE);
+
+	return spice_channel_open_fd(SPICE_CHANNEL(channel), fd);
+}
+
+static void _spice_channel_open_fd_request(SpiceChannel *channel,
+                                           G_GNUC_UNUSED gint tls,
+					   VirtViewerDisplay *display)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+
+	g_return_if_fail(self != NULL);
+
+	viewer_channel_open_fd(display->viewer, (VirtViewerDisplayChannel *)channel);
+}
+
+static void _spice_main_channel_event(G_GNUC_UNUSED SpiceChannel *channel,
+				      SpiceChannelEvent event,
+				      VirtViewerDisplay *display)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+	char *password = NULL;
+
+	g_return_if_fail(self != NULL);
+	g_return_if_fail(display->viewer != NULL);
+
+	switch (event) {
+	case SPICE_CHANNEL_OPENED:
+		DEBUG_LOG("main channel: opened");
+		break;
+	case SPICE_CHANNEL_CLOSED:
+		DEBUG_LOG("main channel: closed");
+		viewer_quit(display->viewer);
+		break;
+	case SPICE_CHANNEL_ERROR_CONNECT:
+		DEBUG_LOG("main channel: failed to connect");
+		viewer_disconnected(display->viewer);
+		break;
+	case SPICE_CHANNEL_ERROR_AUTH:
+		DEBUG_LOG("main channel: auth failure (wrong password?)");
+		int ret = viewer_auth_collect_credentials("SPICE",
+							  display->viewer->pretty_address,
+							  NULL, &password);
+		if (ret < 0) {
+			viewer_quit(display->viewer);
+		} else {
+			g_object_set(self->session, "password", password, NULL);
+			spice_session_connect(self->session);
+		}
+		break;
+	default:
+		g_warning("unknown main channel event: %d", event);
+		viewer_disconnected(display->viewer);
+		break;
+	}
+
+	g_free(password);
+}
+
+static void _spice_channel_new(SpiceSession *s, SpiceChannel *channel,
+			       VirtViewerDisplay *display)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+	int id;
+
+	g_return_if_fail(self != NULL);
+
+	g_signal_connect(channel, "open-fd",
+			 G_CALLBACK(_spice_channel_open_fd_request), self);
+
+	g_object_get(channel, "channel-id", &id, NULL);
+
+	if (SPICE_IS_MAIN_CHANNEL(channel)) {
+		g_signal_connect(channel, "channel-event",
+				 G_CALLBACK(_spice_main_channel_event), self);
+	}
+
+	if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
+		DEBUG_LOG("new display channel (#%d)", id);
+		if (display->widget != NULL)
+			return;
+		self->display = spice_display_new(s, id);
+		display->widget = GTK_WIDGET(self->display);
+		g_object_set(self->display,
+			     "grab-keyboard", TRUE,
+			     "grab-mouse", TRUE,
+			     NULL);
+		viewer_add_display_and_realize(display->viewer);
+		viewer_initialized(display->viewer);
+	}
+
+	if (SPICE_IS_INPUTS_CHANNEL(channel)) {
+		DEBUG_LOG("new inputs channel");
+	}
+
+	if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
+		DEBUG_LOG("new audio channel");
+		if (self->audio != NULL)
+			return;
+		self->audio = spice_audio_new(s, NULL, NULL);
+	}
+}
+
+static void _spice_channel_destroy(G_GNUC_UNUSED SpiceSession *s, SpiceChannel *channel,
+				   VirtViewerDisplay *display)
+{
+	VirtViewerDisplaySpice *self = VIRT_VIEWER_DISPLAY_SPICE(display);
+	int id;
+
+	g_return_if_fail(self != NULL);
+
+	g_object_get(channel, "channel-id", &id, NULL);
+	if (SPICE_IS_MAIN_CHANNEL(channel)) {
+		DEBUG_LOG("zap main channel");
+	}
+
+	if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
+		DEBUG_LOG("zap display channel (#%d)", id);
+	}
+
+	if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
+		if (self->audio == NULL)
+			return;
+		DEBUG_LOG("zap audio channel");
+	}
+}
+
+VirtViewerDisplaySpice* virt_viewer_display_spice_new(VirtViewer *viewer)
+{
+	VirtViewerDisplaySpice *self;
+	VirtViewerDisplay *d;
+
+	g_return_val_if_fail(viewer != NULL, NULL);
+
+	self = g_object_new(VIRT_VIEWER_TYPE_DISPLAY_SPICE, NULL);
+	d = VIRT_VIEWER_DISPLAY(self);
+	d->viewer = viewer;
+
+	self->session = spice_session_new();
+	g_signal_connect(self->session, "channel-new",
+			 G_CALLBACK(_spice_channel_new), self);
+	g_signal_connect(self->session, "channel-destroy",
+			 G_CALLBACK(_spice_channel_destroy), self);
+
+	return self;
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r 9f8a6e933516 -r 4ac66a955d7b src/display-spice.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/display-spice.h	Tue Nov 30 13:59:01 2010 +0100
@@ -0,0 +1,78 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2007-2009 Red Hat,
+ * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+#ifndef _VIRT_VIEWER_DISPLAY_SPICE_H
+#define _VIRT_VIEWER_DISPLAY_SPICE_H
+
+#include <glib-object.h>
+#include <spice-widget.h>
+#include <spice-audio.h>
+
+#include "display.h"
+
+G_BEGIN_DECLS
+
+#define VIRT_VIEWER_TYPE_DISPLAY_SPICE virt_viewer_display_spice_get_type()
+
+#define VIRT_VIEWER_DISPLAY_SPICE(obj)					\
+	(G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_DISPLAY_SPICE, VirtViewerDisplaySpice))
+
+#define VIRT_VIEWER_DISPLAY_SPICE_CLASS(klass)				\
+	(G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_DISPLAY_SPICE, VirtViewerDisplaySpiceClass))
+
+#define VIRT_IS_VIEWER_DISPLAY_SPICE(obj)				\
+	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_DISPLAY_SPICE))
+
+#define VIRT_IS_VIEWER_DISPLAY_SPICE_CLASS(klass)			\
+	(G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_DISPLAY_SPICE))
+
+#define VIRT_VIEWER_DISPLAY_SPICE_GET_CLASS(obj)			\
+	(G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_DISPLAY_SPICE, VirtViewerDisplaySpiceClass))
+
+typedef struct {
+	VirtViewerDisplay parent;
+
+	SpiceSession *session;
+	SpiceDisplay *display;
+	SpiceAudio *audio;
+} VirtViewerDisplaySpice;
+
+typedef struct {
+	VirtViewerDisplayClass parent_class;
+} VirtViewerDisplaySpiceClass;
+
+GType virt_viewer_display_spice_get_type(void);
+
+VirtViewerDisplaySpice* virt_viewer_display_spice_new(VirtViewer *viewer);
+
+G_END_DECLS
+
+#endif /* _VIRT_VIEWER_DISPLAY_SPICE_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r 9f8a6e933516 -r 4ac66a955d7b src/display-vnc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/display-vnc.c	Tue Nov 30 13:59:01 2010 +0100
@@ -0,0 +1,333 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2007-2009 Red Hat,
+ * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include "auth.h"
+#include "display-vnc.h"
+
+G_DEFINE_TYPE(VirtViewerDisplayVNC, virt_viewer_display_vnc, VIRT_VIEWER_TYPE_DISPLAY)
+
+static void _vnc_close(VirtViewerDisplay* display);
+static void _vnc_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals);
+static GdkPixbuf* _vnc_get_pixbuf(VirtViewerDisplay* display);
+static gboolean _vnc_open_fd(VirtViewerDisplay* display, int fd);
+static gboolean _vnc_open_host(VirtViewerDisplay* display, char *host, char *port);
+static gboolean _vnc_channel_open_fd(VirtViewerDisplay* display,
+				     VirtViewerDisplayChannel* channel, int fd);
+
+static void virt_viewer_display_vnc_class_init(VirtViewerDisplayVNCClass *klass)
+{
+	VirtViewerDisplayClass *dclass = VIRT_VIEWER_DISPLAY_CLASS(klass);
+
+	dclass->close = _vnc_close;
+	dclass->send_keys = _vnc_send_keys;
+	dclass->get_pixbuf = _vnc_get_pixbuf;
+	dclass->open_fd = _vnc_open_fd;
+	dclass->open_host = _vnc_open_host;
+	dclass->channel_open_fd = _vnc_channel_open_fd;
+}
+
+static void virt_viewer_display_vnc_init(VirtViewerDisplayVNC *self G_GNUC_UNUSED)
+{
+}
+
+static void _vnc_mouse_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self)
+{
+	viewer_set_title(VIRT_VIEWER_DISPLAY(self)->viewer, TRUE);
+}
+
+static void _vnc_mouse_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self)
+{
+	viewer_set_title(VIRT_VIEWER_DISPLAY(self)->viewer, FALSE);
+}
+
+static void _vnc_key_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self)
+{
+	viewer_disable_modifiers(VIRT_VIEWER_DISPLAY(self)->viewer);
+}
+
+static void _vnc_key_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewerDisplayVNC *self)
+{
+	viewer_enable_modifiers(VIRT_VIEWER_DISPLAY(self)->viewer);
+}
+
+static void _vnc_send_keys(VirtViewerDisplay* display, const guint *keyvals, int nkeyvals)
+{
+	VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display);
+
+	g_return_if_fail(self != NULL);
+	g_return_if_fail(keyvals != NULL);
+	g_return_if_fail(self->vnc != NULL);
+
+	vnc_display_send_keys(self->vnc, keyvals, nkeyvals);
+}
+
+static GdkPixbuf* _vnc_get_pixbuf(VirtViewerDisplay* display)
+{
+	VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display);
+
+	g_return_val_if_fail(self != NULL, NULL);
+	g_return_val_if_fail(self->vnc != NULL, NULL);
+
+	return vnc_display_get_pixbuf(self->vnc);
+}
+
+static gboolean _vnc_open_fd(VirtViewerDisplay* display, int fd)
+{
+	VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display);
+
+	g_return_val_if_fail(self != NULL, FALSE);
+	g_return_val_if_fail(self->vnc != NULL, FALSE);
+
+	return vnc_display_open_fd(self->vnc, fd);
+}
+
+static gboolean _vnc_channel_open_fd(VirtViewerDisplay* display G_GNUC_UNUSED,
+				     VirtViewerDisplayChannel* channel G_GNUC_UNUSED,
+				     int fd G_GNUC_UNUSED)
+{
+	g_warning("channel_open_fd is not supported by VNC");
+	return FALSE;
+}
+
+static gboolean _vnc_open_host(VirtViewerDisplay* display, char *host, char *port)
+{
+	VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display);
+
+	g_return_val_if_fail(self != NULL, FALSE);
+	g_return_val_if_fail(self->vnc != NULL, FALSE);
+
+	return vnc_display_open_host(self->vnc, host, port);
+}
+
+static void _vnc_close(VirtViewerDisplay* display)
+{
+	VirtViewerDisplayVNC *self = VIRT_VIEWER_DISPLAY_VNC(display);
+
+	g_return_if_fail(self != NULL);
+
+	if (self->vnc != NULL)
+		vnc_display_close(self->vnc);
+ }
+
+static void viewer_bell(VirtViewer *viewer, gpointer data G_GNUC_UNUSED)
+{
+	gdk_window_beep(GTK_WIDGET(viewer->window)->window);
+}
+
+static void viewer_vnc_auth_unsupported(VirtViewer *viewer,
+					unsigned int authType, gpointer data G_GNUC_UNUSED)
+{
+	viewer_simple_message_dialog(viewer->window,
+				     _("Unable to authenticate with VNC server at %s\n"
+				       "Unsupported authentication type %d"),
+				     viewer->pretty_address, authType);
+}
+
+static void viewer_vnc_auth_failure(VirtViewer *viewer,
+				    const char *reason, gpointer data G_GNUC_UNUSED)
+{
+	GtkWidget *dialog;
+	int ret;
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(viewer->window),
+					GTK_DIALOG_MODAL |
+					GTK_DIALOG_DESTROY_WITH_PARENT,
+					GTK_MESSAGE_ERROR,
+					GTK_BUTTONS_YES_NO,
+					_("Unable to authenticate with VNC server at %s: %s\n"
+					  "Retry connection again?"),
+					viewer->pretty_address, reason);
+
+	ret = gtk_dialog_run(GTK_DIALOG(dialog));
+
+	gtk_widget_destroy(dialog);
+
+	if (ret == GTK_RESPONSE_YES)
+		viewer->authretry = TRUE;
+	else
+		viewer->authretry = FALSE;
+}
+
+/*
+ * Triggers a resize of the main container to indirectly cause
+ * the display widget to be resized to fit the available space
+ */
+static void
+viewer_resize_display_widget(VirtViewer *viewer)
+{
+	GtkWidget *align;
+	align = glade_xml_get_widget(viewer->glade, "display-align");
+	gtk_widget_queue_resize(align);
+}
+
+
+/*
+ * Called when desktop size changes.
+ *
+ * It either tries to resize the main window, or it triggers
+ * recalculation of the display within existing window size
+ */
+static void viewer_resize_desktop(VirtViewer *viewer, gint width, gint height)
+{
+	DEBUG_LOG("desktop resize %dx%d", width, height);
+	viewer->desktopWidth = width;
+	viewer->desktopHeight = height;
+
+	if (viewer->autoResize && viewer->window && !viewer->fullscreen) {
+		viewer_resize_main_window(viewer);
+	} else {
+		viewer_resize_display_widget(viewer);
+	}
+}
+
+
+/*
+ * Called when the main container widget's size has been set.
+ * It attempts to fit the display widget into this space while
+ * maintaining aspect ratio
+ */
+static gboolean viewer_resize_align(GtkWidget *widget,
+				    GtkAllocation *alloc,
+				    VirtViewer *viewer)
+{
+	double desktopAspect;
+	double scrollAspect;
+	int height, width;
+	GtkAllocation child;
+	int dx = 0, dy = 0;
+
+	if (!viewer->active) {
+		DEBUG_LOG("Skipping inactive resize");
+		return TRUE;
+	}
+
+	desktopAspect = (double)viewer->desktopWidth / (double)viewer->desktopHeight;
+	scrollAspect = (double)alloc->width / (double)alloc->height;
+
+	if (scrollAspect > desktopAspect) {
+		width = alloc->height * desktopAspect;
+		dx = (alloc->width - width) / 2;
+		height = alloc->height;
+	} else {
+		width = alloc->width;
+		height = alloc->width / desktopAspect;
+		dy = (alloc->height - height) / 2;
+	}
+
+	DEBUG_LOG("Align widget=%p is %dx%d, desktop is %dx%d, setting display to %dx%d",
+		  widget,
+		  alloc->width, alloc->height,
+		  viewer->desktopWidth, viewer->desktopHeight,
+		  width, height);
+
+	child.x = alloc->x + dx;
+	child.y = alloc->y + dy;
+	child.width = width;
+	child.height = height;
+	if (viewer->display && viewer->display->widget)
+		gtk_widget_size_allocate(viewer->display->widget, &child);
+
+	return FALSE;
+}
+
+VirtViewerDisplayVNC* virt_viewer_display_vnc_new(VirtViewer *viewer)
+{
+	VirtViewerDisplayVNC *self;
+	VirtViewerDisplay *d;
+	GtkWidget *align;
+
+	g_return_val_if_fail(viewer != NULL, NULL);
+
+	self = g_object_new(VIRT_VIEWER_TYPE_DISPLAY_VNC, NULL);
+	d = VIRT_VIEWER_DISPLAY(self);
+	d->viewer = viewer;
+	viewer->display = d;
+
+	d->widget = vnc_display_new();
+	self->vnc = VNC_DISPLAY(d->widget);
+	vnc_display_set_keyboard_grab(self->vnc, TRUE);
+	vnc_display_set_pointer_grab(self->vnc, TRUE);
+
+	/*
+	 * In auto-resize mode we have things setup so that we always
+	 * automatically resize the top level window to be exactly the
+	 * same size as the VNC desktop, except when it won't fit on
+	 * the local screen, at which point we let it scale down.
+	 * The upshot is, we always want scaling enabled.
+	 * We disable force_size because we want to allow user to
+	 * manually size the widget smaller too
+	 */
+	vnc_display_set_force_size(self->vnc, FALSE);
+	vnc_display_set_scaling(self->vnc, TRUE);
+
+	g_signal_connect_swapped(self->vnc, "vnc-connected",
+				 G_CALLBACK(viewer_connected), viewer);
+	g_signal_connect_swapped(self->vnc, "vnc-initialized",
+				 G_CALLBACK(viewer_initialized), viewer);
+	g_signal_connect_swapped(self->vnc, "vnc-disconnected",
+				 G_CALLBACK(viewer_disconnected), viewer);
+
+	/* When VNC desktop resizes, we have to resize the containing widget */
+	g_signal_connect_swapped(self->vnc, "vnc-desktop-resize",
+				 G_CALLBACK(viewer_resize_desktop), viewer);
+	g_signal_connect_swapped(self->vnc, "vnc-bell",
+				 G_CALLBACK(viewer_bell), NULL);
+	g_signal_connect_swapped(self->vnc, "vnc-auth-failure",
+				 G_CALLBACK(viewer_vnc_auth_failure), viewer);
+	g_signal_connect_swapped(self->vnc, "vnc-auth-unsupported",
+				 G_CALLBACK(viewer_vnc_auth_unsupported), viewer);
+	g_signal_connect_swapped(self->vnc, "vnc-server-cut-text",
+				 G_CALLBACK(viewer_server_cut_text), viewer);
+
+	g_signal_connect(self->vnc, "vnc-pointer-grab",
+			 G_CALLBACK(_vnc_mouse_grab), self);
+	g_signal_connect(self->vnc, "vnc-pointer-ungrab",
+			 G_CALLBACK(_vnc_mouse_ungrab), self);
+	g_signal_connect(self->vnc, "vnc-keyboard-grab",
+			 G_CALLBACK(_vnc_key_grab), self);
+	g_signal_connect(self->vnc, "vnc-keyboard-ungrab",
+			 G_CALLBACK(_vnc_key_ungrab), self);
+
+	g_signal_connect(self->vnc, "vnc-auth-credential",
+			 G_CALLBACK(viewer_auth_vnc_credentials), &viewer->pretty_address);
+
+	viewer_add_display_and_realize(viewer);
+
+	align = glade_xml_get_widget(viewer->glade, "display-align");
+	g_signal_connect(align, "size-allocate",
+			 G_CALLBACK(viewer_resize_align), viewer);
+
+	return self;
+}
+
+
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r 9f8a6e933516 -r 4ac66a955d7b src/display-vnc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/display-vnc.h	Tue Nov 30 13:59:01 2010 +0100
@@ -0,0 +1,75 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2007-2009 Red Hat,
+ * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+#ifndef _VIRT_VIEWER_DISPLAY_VNC_H
+#define _VIRT_VIEWER_DISPLAY_VNC_H
+
+#include <glib-object.h>
+#include <vncdisplay.h>
+
+#include "display.h"
+
+G_BEGIN_DECLS
+
+#define VIRT_VIEWER_TYPE_DISPLAY_VNC virt_viewer_display_vnc_get_type()
+
+#define VIRT_VIEWER_DISPLAY_VNC(obj)					\
+	(G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_DISPLAY_VNC, VirtViewerDisplayVNC))
+
+#define VIRT_VIEWER_DISPLAY_VNC_CLASS(klass)				\
+	(G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_DISPLAY_VNC, VirtViewerDisplayVNCClass))
+
+#define VIRT_IS_VIEWER_DISPLAY_VNC(obj)					\
+	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_DISPLAY_VNC))
+
+#define VIRT_IS_VIEWER_DISPLAY_VNC_CLASS(klass)				\
+	(G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_DISPLAY_VNC))
+
+#define VIRT_VIEWER_DISPLAY_VNC_GET_CLASS(obj)				\
+	(G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_DISPLAY_VNC, VirtViewerDisplayVNCClass))
+
+typedef struct {
+	VirtViewerDisplay parent;
+
+	VncDisplay *vnc;
+} VirtViewerDisplayVNC;
+
+typedef struct {
+	VirtViewerDisplayClass parent_class;
+} VirtViewerDisplayVNCClass;
+
+GType virt_viewer_display_vnc_get_type(void);
+
+VirtViewerDisplayVNC* virt_viewer_display_vnc_new(VirtViewer *viewer);
+
+G_END_DECLS
+
+#endif /* _VIRT_VIEWER_DISPLAY_VNC_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r 9f8a6e933516 -r 4ac66a955d7b src/display.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/display.c	Tue Nov 30 13:59:01 2010 +0100
@@ -0,0 +1,90 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2007-2009 Red Hat,
+ * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+#include "display.h"
+
+G_DEFINE_ABSTRACT_TYPE(VirtViewerDisplay, virt_viewer_display, G_TYPE_OBJECT)
+
+
+static void virt_viewer_display_class_init(VirtViewerDisplayClass *klass G_GNUC_UNUSED)
+{
+}
+
+static void virt_viewer_display_init(VirtViewerDisplay *self G_GNUC_UNUSED)
+{
+}
+
+void virt_viewer_display_close(VirtViewerDisplay *self)
+{
+	g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
+
+	VIRT_VIEWER_DISPLAY_GET_CLASS(self)->close(self);
+}
+
+void virt_viewer_display_send_keys(VirtViewerDisplay *self,
+				   const guint *keyvals, int nkeyvals)
+{
+	g_return_if_fail(VIRT_VIEWER_IS_DISPLAY(self));
+
+	VIRT_VIEWER_DISPLAY_GET_CLASS(self)->send_keys(self, keyvals, nkeyvals);
+}
+
+GdkPixbuf* virt_viewer_display_get_pixbuf(VirtViewerDisplay *self)
+{
+	g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), NULL);
+
+	return VIRT_VIEWER_DISPLAY_GET_CLASS(self)->get_pixbuf(self);
+}
+
+gboolean virt_viewer_display_open_fd(VirtViewerDisplay *self, int fd)
+{
+	g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE);
+
+	return VIRT_VIEWER_DISPLAY_GET_CLASS(self)->open_fd(self, fd);
+}
+
+gboolean virt_viewer_display_open_host(VirtViewerDisplay *self, char *host, char *port)
+{
+        VirtViewerDisplayClass *klass;
+
+	g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE);
+
+	klass = VIRT_VIEWER_DISPLAY_GET_CLASS(self);
+        return klass->open_host(self, host, port);
+}
+
+gboolean virt_viewer_display_channel_open_fd(VirtViewerDisplay *self,
+					     VirtViewerDisplayChannel *channel, int fd)
+{
+	g_return_val_if_fail(VIRT_VIEWER_IS_DISPLAY(self), FALSE);
+
+	return VIRT_VIEWER_DISPLAY_GET_CLASS(self)->channel_open_fd(self, channel, fd);
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r 9f8a6e933516 -r 4ac66a955d7b src/display.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/display.h	Tue Nov 30 13:59:01 2010 +0100
@@ -0,0 +1,91 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2007-2009 Red Hat,
+ * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+#ifndef _VIRT_VIEWER_DISPLAY_H
+#define _VIRT_VIEWER_DISPLAY_H
+
+#include <glib-object.h>
+#include "viewer-priv.h"
+
+G_BEGIN_DECLS
+
+#define VIRT_VIEWER_TYPE_DISPLAY virt_viewer_display_get_type()
+
+#define VIRT_VIEWER_DISPLAY(obj)					\
+	(G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_DISPLAY, VirtViewerDisplay))
+
+#define VIRT_VIEWER_DISPLAY_CLASS(klass)				\
+	(G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_DISPLAY, VirtViewerDisplayClass))
+
+#define VIRT_VIEWER_IS_DISPLAY(obj)                                     \
+	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_DISPLAY))
+
+#define VIRT_VIEWER_IS_DISPLAY_CLASS(klass)                             \
+	(G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_DISPLAY))
+
+#define VIRT_VIEWER_DISPLAY_GET_CLASS(obj)				\
+	(G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_DISPLAY, VirtViewerDisplayClass))
+
+/* perhaps this become an interface, and be pushed in gtkvnc and spice? */
+struct _VirtViewerDisplay {
+	GObject parent;
+	VirtViewer *viewer;
+	GtkWidget *widget;
+};
+
+struct _VirtViewerDisplayClass {
+	GObjectClass parent_class;
+
+	/* virtual methods */
+	void (* close) (VirtViewerDisplay* display);
+	void (* send_keys) (VirtViewerDisplay* display,
+			    const guint *keyvals, int nkeyvals);
+	GdkPixbuf* (* get_pixbuf) (VirtViewerDisplay* display);
+	gboolean (* open_fd) (VirtViewerDisplay* display, int fd);
+	gboolean (* open_host) (VirtViewerDisplay* display, char *host, char *port);
+	gboolean (* channel_open_fd) (VirtViewerDisplay* display,
+                                      VirtViewerDisplayChannel* channel, int fd);
+};
+
+GType virt_viewer_display_get_type(void);
+
+void virt_viewer_display_close(VirtViewerDisplay* display);
+void virt_viewer_display_send_keys(VirtViewerDisplay* display,
+				   const guint *keyvals, int nkeyvals);
+GdkPixbuf* virt_viewer_display_get_pixbuf(VirtViewerDisplay* display);
+gboolean virt_viewer_display_open_fd(VirtViewerDisplay* display, int fd);
+gboolean virt_viewer_display_open_host(VirtViewerDisplay* display, char *host, char *port);
+GObject* virt_viewer_display_get(VirtViewerDisplay* display);
+gboolean virt_viewer_display_channel_open_fd(VirtViewerDisplay* display,
+					     VirtViewerDisplayChannel* channel, int fd);
+
+G_END_DECLS
+
+#endif /* _VIRT_VIEWER_DISPLAY_H */
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r 9f8a6e933516 -r 4ac66a955d7b src/viewer-priv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/viewer-priv.h	Tue Nov 30 13:59:01 2010 +0100
@@ -0,0 +1,123 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2007-2009 Red Hat,
+ * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+#ifndef _VIRT_VIEWER_PRIV_H
+# define _VIRT_VIEWER_PRIV_H
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <glib/gi18n.h>
+
+#include <libvirt/libvirt.h>
+#include <libvirt/virterror.h>
+#include <libxml/xpath.h>
+#include <libxml/uri.h>
+
+typedef struct _VirtViewerDisplay VirtViewerDisplay;
+typedef struct _VirtViewerDisplayClass VirtViewerDisplayClass;
+typedef struct _VirtViewer VirtViewer;
+typedef struct _VirtViewerSize VirtViewerSize;
+typedef struct _VirtViewerDisplayChanne VirtViewerDisplayChannel;
+
+enum menuNums {
+	FILE_MENU,
+	VIEW_MENU,
+	SEND_KEY_MENU,
+	HELP_MENU,
+	LAST_MENU // sentinel
+};
+
+struct _VirtViewer {
+	char *uri;
+	virConnectPtr conn;
+	char *domkey;
+	char *domtitle;
+
+	GladeXML *glade;
+	GtkWidget *window;
+	GtkWidget *container;
+
+	char *pretty_address;
+
+	int zoomlevel;
+
+	int desktopWidth;
+	int desktopHeight;
+	gboolean autoResize;
+	gboolean fullscreen;
+	gboolean withEvents;
+
+	gboolean active;
+
+	gboolean accelEnabled;
+	GValue accelSetting;
+	GSList *accelList;
+	int accelMenuSig[LAST_MENU];
+
+	gboolean waitvm;
+	gboolean reconnect;
+	gboolean direct;
+	gboolean verbose;
+	gboolean authretry;
+	gboolean connected;
+
+	gchar *clipboard;
+
+	VirtViewerDisplay *display;
+
+	char *gport;
+	char *host;
+	char *transport;
+	char *user;
+        int port;
+};
+
+struct _VirtViewerSize {
+	VirtViewer *viewer;
+	gint width, height;
+	gulong sig_id;
+};
+
+void viewer_connected(VirtViewer *viewer);
+void viewer_initialized(VirtViewer *viewer);
+void viewer_disconnected(VirtViewer *viewer);
+void viewer_set_status(VirtViewer *viewer, const char *text);
+void viewer_set_title(VirtViewer *viewer, gboolean grabbed);
+void viewer_enable_modifiers(VirtViewer *viewer);
+void viewer_disable_modifiers(VirtViewer *viewer);
+void viewer_add_display_and_realize(VirtViewer *viewer);
+void viewer_server_cut_text(VirtViewer *viewer, const gchar *text);
+void viewer_resize_main_window(VirtViewer *viewer);
+void viewer_channel_open_fd(VirtViewer *viewer, VirtViewerDisplayChannel *channel);
+void viewer_quit(VirtViewer *viewer);
+
+void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ...);
+
+#endif // _VIRT_VIEWER_PRIV_H
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r 9f8a6e933516 -r 4ac66a955d7b src/viewer.c
--- a/src/viewer.c	Fri Nov 19 18:09:42 2010 +0100
+++ b/src/viewer.c	Tue Nov 30 13:59:01 2010 +0100
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2007-2009 Red Hat,
  * Copyright (C) 2009 Daniel P. Berrange
+ * Copyright (C) 2010 Marc-André Lureau
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,7 +24,6 @@
 
 #include <config.h>
 
-#include <vncdisplay.h>
 #include <gdk/gdkkeysyms.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -31,11 +31,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <locale.h>
-#include <glib/gi18n.h>
-#include <libvirt/libvirt.h>
-#include <libvirt/virterror.h>
-#include <libxml/xpath.h>
-#include <libxml/uri.h>
 
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
@@ -50,105 +45,54 @@
 #endif
 
 #include "viewer.h"
+#include "viewer-priv.h"
 #include "events.h"
 #include "auth.h"
+#include "display-vnc.h"
+#include "display-spice.h"
 
 #define SCALE(x) do { x = viewer->fullscreen ? x : x * viewer->zoomlevel / 100; } while (0);
 
 gboolean doDebug = FALSE;
 
-enum menuNums {
-        FILE_MENU,
-        VIEW_MENU,
-        SEND_KEY_MENU,
-        HELP_MENU,
-        LAST_MENU // sentinel
-};
-
 static const char * const menuNames[LAST_MENU] = {
 	"menu-file", "menu-view", "menu-send", "menu-help"
 };
 
 
 #define MAX_KEY_COMBO 3
-struct  keyComboDef {
+struct	keyComboDef {
 	guint keys[MAX_KEY_COMBO];
 	guint nkeys;
 	const char *label;
 };
 
 static const struct keyComboDef keyCombos[] = {
-        { { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+_Del"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+_Backspace"},
-        { {}, 0, "" },
-        { { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_9"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F1_0"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F11"},
-        { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F12"},
-        { {}, 0, "" },
-        { { GDK_Print }, 1, "_PrintScreen"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+_Del"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+_Backspace"},
+	{ {}, 0, "" },
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_9"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F1_0"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F11"},
+	{ { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F12"},
+	{ {}, 0, "" },
+	{ { GDK_Print }, 1, "_PrintScreen"},
 };
 
 
-
-typedef struct VirtViewer {
-	char *uri;
-	virConnectPtr conn;
-	char *domkey;
-	char *domtitle;
-
-	GladeXML *glade;
-	GtkWidget *window;
-	GtkWidget *container;
-	GtkWidget *vnc;
-
-	int zoomlevel;
-
-	int desktopWidth;
-	int desktopHeight;
-	gboolean autoResize;
-	gboolean fullscreen;
-	gboolean withEvents;
-
-	gboolean active;
-	char *vncAddress;
-
-	gboolean accelEnabled;
-	GValue accelSetting;
-	GSList *accelList;
-	int accelMenuSig[LAST_MENU];
-
-	gboolean waitvm;
-	gboolean reconnect;
-	gboolean direct;
-	gboolean verbose;
-	gboolean authretry;
-	gboolean connected;
-
-	gchar *clipboard;
-} VirtViewer;
-
-typedef struct VirtViewerSize {
-	VirtViewer *viewer;
-	gint width, height;
-	gulong sig_id;
-} VirtViewerSize;
-
-
 static gboolean viewer_connect_timer(void *opaque);
 static int viewer_initial_connect(VirtViewer *viewer);
-static void viewer_init_vnc_display(VirtViewer *viewer);
 
 
-static void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ...)
+void viewer_simple_message_dialog(GtkWidget *window, const char *fmt, ...)
 {
 	GtkWidget *dialog;
 	char *msg;
@@ -176,6 +120,27 @@
 }
 
 
+void viewer_add_display_and_realize(VirtViewer *viewer)
+{
+	GtkWidget *notebook;
+	GtkWidget *align;
+
+	g_return_if_fail(viewer != NULL);
+	g_return_if_fail(viewer->display != NULL);
+	g_return_if_fail(viewer->display->widget != NULL);
+
+	notebook = glade_xml_get_widget(viewer->glade, "notebook");
+	align = glade_xml_get_widget(viewer->glade, "display-align");
+	gtk_container_add(GTK_CONTAINER(align), viewer->display->widget);
+
+	if (!viewer->window) {
+		gtk_container_add(GTK_CONTAINER(viewer->container), GTK_WIDGET(notebook));
+		gtk_widget_show_all(viewer->container);
+	}
+
+	gtk_widget_realize(viewer->display->widget);
+}
+
 /* Now that the size is set to our preferred sizing, this
  * triggers another resize calculation but without our
  * scrolled window callback active. This is the key that
@@ -241,72 +206,12 @@
 
 
 /*
- * Called when the main container widget's size has been set.
- * It attempts to fit the VNC widget into this space while
- * maintaining aspect ratio
- */
-static gboolean viewer_resize_align(GtkWidget *widget,
-				    GtkAllocation *alloc,
-				    VirtViewer *viewer)
-{
-	double desktopAspect = (double)viewer->desktopWidth / (double)viewer->desktopHeight;
-	double scrollAspect = (double)alloc->width / (double)alloc->height;
-	int height, width;
-	GtkAllocation child;
-	int dx = 0, dy = 0;
-
-	if (!viewer->active) {
-		DEBUG_LOG("Skipping inactive resize");
-		return TRUE;
-	}
-
-	if (scrollAspect > desktopAspect) {
-		width = alloc->height * desktopAspect;
-		dx = (alloc->width - width) / 2;
-		height = alloc->height;
-	} else {
-		width = alloc->width;
-		height = alloc->width / desktopAspect;
-		dy = (alloc->height - height) / 2;
-	}
-
-	DEBUG_LOG("Align widget=%p is %dx%d, desktop is %dx%d, setting VNC to %dx%d",
-		  widget,
-		  alloc->width, alloc->height,
-		  viewer->desktopWidth, viewer->desktopHeight,
-		  width, height);
-
-	child.x = alloc->x + dx;
-	child.y = alloc->y + dy;
-	child.width = width;
-	child.height = height;
-	gtk_widget_size_allocate(viewer->vnc, &child);
-
-	return FALSE;
-}
-
-
-/*
- * Triggers a resize of the main container to indirectly cause
- * the VNC widget to be resized to fit the available space
- */
-static void
-viewer_resize_vnc_widget(VirtViewer *viewer)
-{
-	GtkWidget *align;
-	align = glade_xml_get_widget(viewer->glade, "vnc-align");
-	gtk_widget_queue_resize(align);
-}
-
-
-/*
  * This code attempts to resize the top level window to be large enough
- * to contain the entire VNC desktop at 1:1 ratio. If the local desktop
- * isn't large enough that it goes as large as possible and lets VNC
+ * to contain the entire display desktop at 1:1 ratio. If the local desktop
+ * isn't large enough that it goes as large as possible and lets the display
  * scale down to fit, maintaining aspect ratio
  */
-static void
-viewer_resize_main_window(VirtViewer *viewer)
+void viewer_resize_main_window(VirtViewer *viewer)
 {
 	GdkRectangle fullscreen;
 	GdkScreen *screen;
@@ -351,7 +256,7 @@
 	SCALE(height);
 
 	viewer_set_widget_size(viewer,
-			       glade_xml_get_widget(viewer->glade, "vnc-align"),
+			       glade_xml_get_widget(viewer->glade, "display-align"),
 			       width,
 			       height);
 }
@@ -381,27 +286,7 @@
 	viewer_resize_main_window(viewer);
 }
 
-/*
- * Called when VNC desktop size changes.
- *
- * It either tries to resize the main window, or it triggers
- * recalculation of VNC within existing window size
- */
-static void viewer_resize_desktop(GtkWidget *vnc G_GNUC_UNUSED, gint width, gint height, VirtViewer *viewer)
-{
-	DEBUG_LOG("VNC desktop resize %dx%d", width, height);
-	viewer->desktopWidth = width;
-	viewer->desktopHeight = height;
-
-	if (viewer->autoResize && viewer->window && !viewer->fullscreen) {
-		viewer_resize_main_window(viewer);
-	} else {
-		viewer_resize_vnc_widget(viewer);
-	}
-}
-
-
-static void viewer_set_title(VirtViewer *viewer, gboolean grabbed)
+void viewer_set_title(VirtViewer *viewer, gboolean grabbed)
 {
 	char *title;
 	const char *subtitle;
@@ -430,7 +315,7 @@
 }
 
 
-static void viewer_disable_modifiers(VirtViewer *viewer)
+void viewer_disable_modifiers(VirtViewer *viewer)
 {
 	GtkSettings *settings = gtk_settings_get_default();
 	GValue empty;
@@ -466,7 +351,7 @@
 }
 
 
-static void viewer_enable_modifiers(VirtViewer *viewer)
+void viewer_enable_modifiers(VirtViewer *viewer)
 {
 	GtkSettings *settings = gtk_settings_get_default();
 	GSList *accels;
@@ -495,38 +380,23 @@
 	viewer->accelEnabled = TRUE;
 }
 
+void viewer_quit(VirtViewer *viewer)
+{
+	g_return_if_fail(viewer != NULL);
 
-
-static void viewer_mouse_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	viewer_set_title(viewer, TRUE);
+	if (viewer->display)
+		virt_viewer_display_close(viewer->display);
+	gtk_main_quit();
 }
 
-static void viewer_mouse_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
+static void viewer_delete(GtkWidget *src G_GNUC_UNUSED, void *dummy G_GNUC_UNUSED, VirtViewer *viewer)
 {
-	viewer_set_title(viewer, FALSE);
-}
-
-static void viewer_key_grab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	viewer_disable_modifiers(viewer);
-}
-
-static void viewer_key_ungrab(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	viewer_enable_modifiers(viewer);
-}
-
-
-static void viewer_shutdown(GtkWidget *src G_GNUC_UNUSED, void *dummy G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	vnc_display_close(VNC_DISPLAY(viewer->vnc));
-	gtk_main_quit();
+	viewer_quit(viewer);
 }
 
 static void viewer_menu_file_quit(GtkWidget *src G_GNUC_UNUSED, VirtViewer *viewer)
 {
-	viewer_shutdown(src, NULL, viewer);
+	viewer_quit(viewer);
 }
 
 static void viewer_menu_view_fullscreen(GtkWidget *menu, VirtViewer *viewer)
@@ -558,26 +428,26 @@
 
 static void viewer_menu_send(GtkWidget *menu G_GNUC_UNUSED, VirtViewer *viewer)
 {
-        int i;
-        GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu));
-        const char *text = gtk_label_get_label(GTK_LABEL(label));
+	int i;
+	GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu));
+	const char *text = gtk_label_get_label(GTK_LABEL(label));
 
-        for (i = 0 ; i < G_N_ELEMENTS(keyCombos) ; i++) {
-                if (!strcmp(text, keyCombos[i].label)) {
-                        DEBUG_LOG("Sending key combo %s", gtk_label_get_text(GTK_LABEL(label)));
-                        vnc_display_send_keys(VNC_DISPLAY(viewer->vnc),
-                                              keyCombos[i].keys,
-                                              keyCombos[i].nkeys);
-                        return;
-                }
-        }
+	for (i = 0 ; i < G_N_ELEMENTS(keyCombos) ; i++) {
+		if (!strcmp(text, keyCombos[i].label)) {
+			DEBUG_LOG("Sending key combo %s", gtk_label_get_text(GTK_LABEL(label)));
+			virt_viewer_display_send_keys(viewer->display,
+						      keyCombos[i].keys,
+						      keyCombos[i].nkeys);
+			return;
+		}
+	}
 	DEBUG_LOG("Failed to find key combo %s", gtk_label_get_text(GTK_LABEL(label)));
 }
 
 
-static void viewer_save_screenshot(GtkWidget *vnc, const char *file)
+static void viewer_save_screenshot(VirtViewer *viewer, const char *file)
 {
-	GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(vnc));
+	GdkPixbuf *pix = virt_viewer_display_get_pixbuf(viewer->display);
 	gdk_pixbuf_save(pix, file, "png", NULL,
 			"tEXt::Generator App", PACKAGE, NULL);
 	gdk_pixbuf_unref(pix);
@@ -587,6 +457,8 @@
 {
 	GtkWidget *dialog;
 
+	g_return_if_fail(viewer->display != NULL);
+
 	dialog = gtk_file_chooser_dialog_new ("Save screenshot",
 					      NULL,
 					      GTK_FILE_CHOOSER_ACTION_SAVE,
@@ -602,7 +474,7 @@
 		char *filename;
 
 		filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-		viewer_save_screenshot(viewer->vnc, filename);
+		viewer_save_screenshot(viewer, filename);
 		g_free (filename);
 	}
 
@@ -811,37 +683,37 @@
 
 static int viewer_open_tunnel(const char **cmd)
 {
-        int fd[2];
-        pid_t pid;
+	int fd[2];
+	pid_t pid;
 
-        if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0)
-                return -1;
+	if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd) < 0)
+		return -1;
 
-        pid = fork();
-        if (pid == -1) {
+	pid = fork();
+	if (pid == -1) {
 		close(fd[0]);
 		close(fd[1]);
 		return -1;
 	}
 
-        if (pid == 0) { /* child */
-                close(fd[0]);
-                close(0);
-                close(1);
-                if (dup(fd[1]) < 0)
-                        _exit(1);
-                if (dup(fd[1]) < 0)
-                        _exit(1);
-                close(fd[1]);
-                execvp("ssh", (char *const*)cmd);
-                _exit(1);
-        }
-        close(fd[1]);
+	if (pid == 0) { /* child */
+		close(fd[0]);
+		close(0);
+		close(1);
+		if (dup(fd[1]) < 0)
+			_exit(1);
+		if (dup(fd[1]) < 0)
+			_exit(1);
+		close(fd[1]);
+		execvp("ssh", (char *const*)cmd);
+		_exit(1);
+	}
+	close(fd[1]);
 	return fd[0];
 }
 
 
-static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char *sshuser, const char *vncport)
+static int viewer_open_tunnel_ssh(const char *sshhost, int sshport, const char *sshuser, const char *port)
 {
 	const char *cmd[10];
 	char portstr[50];
@@ -862,7 +734,7 @@
 	cmd[n++] = sshhost;
 	cmd[n++] = "nc";
 	cmd[n++] = "localhost";
-	cmd[n++] = vncport;
+	cmd[n++] = port;
 	cmd[n++] = NULL;
 
 	return viewer_open_tunnel(cmd);
@@ -870,7 +742,7 @@
 
 #endif /* defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK) */
 
-static void viewer_set_status(VirtViewer *viewer, const char *text)
+void viewer_set_status(VirtViewer *viewer, const char *text)
 {
 	GtkWidget *status, *notebook;
 
@@ -882,63 +754,136 @@
 }
 
 
-static void viewer_set_vnc(VirtViewer *viewer)
+static void viewer_show_display(VirtViewer *viewer)
 {
 	GtkWidget *notebook;
 
+	g_return_if_fail(viewer != NULL);
+	g_return_if_fail(viewer->display != NULL);
+	g_return_if_fail(viewer->display->widget != NULL);
+
 	notebook = glade_xml_get_widget(viewer->glade, "notebook");
 	gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 1);
 
-	gtk_widget_show(viewer->vnc);
+	gtk_widget_show(viewer->display->widget);
 }
 
+static void viewer_connect_info_free(VirtViewer *viewer)
+{
+	free(viewer->host);
+	free(viewer->gport);
+	free(viewer->transport);
+	free(viewer->user);
+
+	viewer->host = NULL;
+	viewer->gport = NULL;
+	viewer->transport = NULL;
+	viewer->user = NULL;
+	viewer->port = 0;
+}
+
+static gboolean viewer_extract_connect_info(VirtViewer *viewer,
+					    virDomainPtr dom)
+{
+	char *type = NULL;
+	char *xpath = NULL;
+	gboolean retval = FALSE;
+
+	viewer_connect_info_free(viewer);
+
+	if ((type = viewer_extract_xpath_string(dom, "string(/domain/devices/graphics/@type)")) == NULL) {
+		viewer_simple_message_dialog(viewer->window, _("Cannot determine the graphic type for the guest %s"),
+					     viewer->domkey);
+		goto cleanup;
+	}
+
+	if (g_strcasecmp(type, "vnc") == 0)
+		viewer->display = VIRT_VIEWER_DISPLAY(virt_viewer_display_vnc_new(viewer));
+	else if (g_strcasecmp(type, "spice") == 0)
+		viewer->display = VIRT_VIEWER_DISPLAY(virt_viewer_display_spice_new(viewer));
+	else {
+		viewer_simple_message_dialog(viewer->window, _("Unknown graphic type for the guest %s"),
+					     viewer->domkey);
+		goto cleanup;
+	}
+
+	xpath = g_strdup_printf("string(/domain/devices/graphics[@type='%s']/@port)", type);
+	if ((viewer->gport = viewer_extract_xpath_string(dom, xpath)) == NULL) {
+		viewer_simple_message_dialog(viewer->window, _("Cannot determine the graphic port for the guest %s"),
+					     viewer->domkey);
+		goto cleanup;
+	}
+
+	if (viewer_extract_host(viewer->uri, &viewer->host, &viewer->transport, &viewer->user, &viewer->port) < 0) {
+		viewer_simple_message_dialog(viewer->window, _("Cannot determine the host for the guest %s"),
+					     viewer->domkey);
+		goto cleanup;
+	}
+
+	DEBUG_LOG("Remote host is %s and transport %s user %s",
+		  viewer->host, viewer->transport ? viewer->transport : "", viewer->user ? viewer->user : "");
+
+	retval = TRUE;
+
+cleanup:
+	free(xpath);
+	return retval;
+}
+
+void viewer_channel_open_fd(VirtViewer *viewer, VirtViewerDisplayChannel *channel)
+{
+#if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK)
+	int fd = -1;
+
+	g_return_if_fail(viewer != NULL);
+	g_return_if_fail(viewer->display != NULL);
+
+	if (viewer->transport && g_strcasecmp(viewer->transport, "ssh") == 0 &&
+	    !viewer->direct) {
+		if ((fd = viewer_open_tunnel_ssh(viewer->host, viewer->port, viewer->user, viewer->gport)) < 0)
+			viewer_simple_message_dialog(viewer->window, _("Connect to ssh failed."));
+	} else
+		viewer_simple_message_dialog(viewer->window, _("Can't connect to channel, SSH only supported."));
+
+	if (fd >= 0)
+		virt_viewer_display_channel_open_fd(viewer->display, channel, fd);
+#else
+	viewer_simple_message_dialog(viewer->window, _("Connect to channel unsupported."));
+#endif
+}
 
 static int viewer_activate(VirtViewer *viewer,
 			   virDomainPtr dom)
 {
-	char *vncport = NULL;
-	char *host = NULL;
-	char *transport = NULL;
-	char *user = NULL;
-	int port, fd = -1;
+	int fd = -1;
 	int ret = -1;
 
+	g_return_val_if_fail(viewer->display == NULL, -1);
+
 	if (viewer->active)
 		goto cleanup;
 
-	viewer_init_vnc_display(viewer);
+	if (!viewer_extract_connect_info(viewer, dom))
+		goto cleanup;
 
-	if ((vncport = viewer_extract_xpath_string(dom, "string(/domain/devices/graphics[@type='vnc']/@port)")) == NULL) {
-		viewer_simple_message_dialog(viewer->window, _("Cannot determine the VNC port for the guest %s"),
-					     viewer->domkey);
-		goto cleanup;
-	}
-
-        if (viewer_extract_host(viewer->uri, &host, &transport, &user, &port) < 0) {
-		viewer_simple_message_dialog(viewer->window, _("Cannot determine the VNC host for the guest %s"),
-					     viewer->domkey);
-		goto cleanup;
-	}
-
-        DEBUG_LOG("Remote host is %s and transport %s user %s",
-		  host, transport ? transport : "", user ? user : "");
-
-	viewer->vncAddress = g_strdup_printf("%s:%s", host, vncport);
+	viewer->pretty_address = g_strdup_printf("%s:%s", viewer->host, viewer->gport);
 
 #if defined(HAVE_SOCKETPAIR) && defined(HAVE_FORK)
-        if (transport && g_strcasecmp(transport, "ssh") == 0 &&
+	if (viewer->transport && g_strcasecmp(viewer->transport, "ssh") == 0 &&
 	    !viewer->direct)
-                if ((fd = viewer_open_tunnel_ssh(host, port, user, vncport)) < 0)
+		if ((fd = viewer_open_tunnel_ssh(viewer->host, viewer->port,
+						 viewer->user, viewer->gport)) < 0)
 			return -1;
 #endif
 
 	if (fd >= 0) {
-		vnc_display_open_fd(VNC_DISPLAY(viewer->vnc), fd);
+		ret = virt_viewer_display_open_fd(viewer->display, fd);
 	} else {
-		vnc_display_open_host(VNC_DISPLAY(viewer->vnc), host, vncport);
+		ret = virt_viewer_display_open_host(viewer->display,
+						    viewer->host, viewer->gport);
 	}
 
-	viewer_set_status(viewer, "Connecting to VNC server");
+	viewer_set_status(viewer, "Connecting to graphic server");
 
 	free(viewer->domtitle);
 	viewer->domtitle = g_strdup(virDomainGetName(dom));
@@ -947,121 +892,10 @@
 	viewer->active = TRUE;
 	viewer_set_title(viewer, FALSE);
 
-	ret = 0;
- cleanup:
-	free(host);
-	free(transport);
-	free(user);
-	free(vncport);
+cleanup:
 	return ret;
-
 }
 
-
-static gboolean viewer_retryauth(gpointer opaque)
-{
-	VirtViewer *viewer = opaque;
-	viewer_initial_connect(viewer);
-
-	return FALSE;
-}
-
-static void viewer_deactivate(VirtViewer *viewer)
-{
-	if (!viewer->active)
-		return;
-
-	vnc_display_close(VNC_DISPLAY(viewer->vnc));
-	free(viewer->domtitle);
-	viewer->domtitle = NULL;
-
-	viewer->connected = FALSE;
-	viewer->active = FALSE;
-	g_free(viewer->vncAddress);
-	viewer->vncAddress = NULL;
-	viewer_set_title(viewer, FALSE);
-
-	if (viewer->authretry) {
-		viewer->authretry = FALSE;
-		g_idle_add(viewer_retryauth, viewer);
-	} else if (viewer->reconnect) {
-		if (!viewer->withEvents) {
-			DEBUG_LOG("No domain events, falling back to polling");
-			g_timeout_add(500,
-				      viewer_connect_timer,
-				      viewer);
-		}
-
-		viewer_set_status(viewer, "Waiting for guest domain to re-start");
-	} else {
-		viewer_set_status(viewer, "Guest domain has shutdown");
-		gtk_main_quit();
-	}
-}
-
-static void viewer_connected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	viewer->connected = TRUE;
-	viewer_set_status(viewer, "Connected to VNC server");
-}
-
-static void viewer_initialized(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	viewer_set_vnc(viewer);
-        viewer_set_title(viewer, FALSE);
-}
-
-
-static void viewer_disconnected(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	if (!viewer->connected) {
-		viewer_simple_message_dialog(viewer->window, _("Unable to connect to the VNC server %s"),
-					     viewer->vncAddress);
-	}
-	viewer_deactivate(viewer);
-}
-
-
-static void viewer_vnc_auth_failure(GtkWidget *vnc G_GNUC_UNUSED, const char *reason, VirtViewer *viewer)
-{
-	GtkWidget *dialog;
-	int ret;
-
-	dialog = gtk_message_dialog_new(GTK_WINDOW(viewer->window),
-					GTK_DIALOG_MODAL |
-					GTK_DIALOG_DESTROY_WITH_PARENT,
-					GTK_MESSAGE_ERROR,
-					GTK_BUTTONS_YES_NO,
-					_("Unable to authenticate with VNC server at %s: %s\n"
-					  "Retry connection again?"),
-					viewer->vncAddress, reason);
-
-	ret = gtk_dialog_run(GTK_DIALOG(dialog));
-
-	gtk_widget_destroy(dialog);
-
-	if (ret == GTK_RESPONSE_YES)
-		viewer->authretry = TRUE;
-	else
-		viewer->authretry = FALSE;
-}
-
-
-static void viewer_vnc_auth_unsupported(GtkWidget *vnc G_GNUC_UNUSED, unsigned int authType, VirtViewer *viewer)
-{
-	viewer_simple_message_dialog(viewer->window,
-				     _("Unable to authenticate with VNC server at %s\n"
-				       "Unsupported authentication type %d"),
-				     viewer->vncAddress, authType);
-}
-
-
-static void viewer_vnc_bell(GtkWidget *vnc G_GNUC_UNUSED, VirtViewer *viewer)
-{
-	gdk_window_beep(GTK_WIDGET(viewer->window)->window);
-}
-
-
 /* text was actually requested */
 static void viewer_vnc_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED,
 				      GtkSelectionData *data,
@@ -1071,12 +905,11 @@
 	gtk_selection_data_set_text(data, viewer->clipboard, -1);
 }
 
-static void viewer_vnc_server_cut_text(VncDisplay *vnc G_GNUC_UNUSED,
-				       const gchar *text, VirtViewer *viewer)
+void viewer_server_cut_text(VirtViewer *viewer, const gchar *text)
 {
 	GtkClipboard *cb;
 	gsize a, b;
-        GtkTargetEntry targets[] = {
+	GtkTargetEntry targets[] = {
 		{g_strdup("UTF8_STRING"), 0, 0},
 		{g_strdup("COMPOUND_TEXT"), 0, 0},
 		{g_strdup("TEXT"), 0, 0},
@@ -1101,6 +934,69 @@
 	}
 }
 
+static gboolean viewer_retryauth(gpointer opaque)
+{
+	VirtViewer *viewer = opaque;
+	viewer_initial_connect(viewer);
+
+	return FALSE;
+}
+
+static void viewer_deactivate(VirtViewer *viewer)
+{
+	if (!viewer->active)
+		return;
+
+	if (viewer->display)
+		virt_viewer_display_close(viewer->display);
+	free(viewer->domtitle);
+	viewer->domtitle = NULL;
+
+	viewer->connected = FALSE;
+	viewer->active = FALSE;
+	g_free(viewer->pretty_address);
+	viewer->pretty_address = NULL;
+	viewer_set_title(viewer, FALSE);
+
+	if (viewer->authretry) {
+		viewer->authretry = FALSE;
+		g_idle_add(viewer_retryauth, viewer);
+	} else if (viewer->reconnect) {
+		if (!viewer->withEvents) {
+			DEBUG_LOG("No domain events, falling back to polling");
+			g_timeout_add(500,
+				      viewer_connect_timer,
+				      viewer);
+		}
+
+		viewer_set_status(viewer, "Waiting for guest domain to re-start");
+	} else {
+		viewer_set_status(viewer, "Guest domain has shutdown");
+		gtk_main_quit();
+	}
+}
+
+void viewer_connected(VirtViewer *viewer)
+{
+	viewer->connected = TRUE;
+	viewer_set_status(viewer, "Connected to graphic server");
+}
+
+void viewer_initialized(VirtViewer *viewer)
+{
+	viewer_show_display(viewer);
+	viewer_set_title(viewer, FALSE);
+}
+
+void viewer_disconnected(VirtViewer *viewer)
+{
+	if (!viewer->connected) {
+		viewer_simple_message_dialog(viewer->window, _("Unable to connect to the graphic server %s"),
+					     viewer->pretty_address);
+	}
+	viewer_deactivate(viewer);
+}
+
 
 static int viewer_domain_event(virConnectPtr conn G_GNUC_UNUSED,
 			       virDomainPtr dom,
@@ -1158,13 +1054,18 @@
 	if (info.state == VIR_DOMAIN_SHUTOFF) {
 		viewer_set_status(viewer, "Waiting for guest domain to start");
 	} else {
-		if (viewer_activate(viewer, dom) < 0) {
+		ret = viewer_activate(viewer, dom);
+		if (ret < 0) {
 			if (viewer->waitvm) {
-				viewer_set_status(viewer, "Waiting for guest domain to start VNC");
+				viewer_set_status(viewer, "Waiting for guest domain to start server");
 			} else {
 				DEBUG_LOG("Failed to activate viewer");
 				goto cleanup;
 			}
+		} else if (ret == 0) {
+			DEBUG_LOG("Failed to activate viewer");
+			ret = -1;
+			goto cleanup;
 		}
 	}
 
@@ -1197,72 +1098,6 @@
 	/* nada */
 }
 
-static void viewer_init_vnc_display(VirtViewer *viewer)
-{
-	GtkWidget *notebook;
-	GtkWidget *align;
-
-	g_return_if_fail(viewer != NULL);
-
-	viewer->vnc = vnc_display_new();
-	vnc_display_set_keyboard_grab(VNC_DISPLAY(viewer->vnc), TRUE);
-	vnc_display_set_pointer_grab(VNC_DISPLAY(viewer->vnc), TRUE);
-
-	/*
-	 * In auto-resize mode we have things setup so that we always
-	 * automatically resize the top level window to be exactly the
-	 * same size as the VNC desktop, except when it won't fit on
-	 * the local screen, at which point we let it scale down.
-	 * The upshot is, we always want scaling enabled.
-	 * We disable force_size because we want to allow user to
-	 * manually size the widget smaller too
-	 */
-	vnc_display_set_force_size(VNC_DISPLAY(viewer->vnc), FALSE);
-	vnc_display_set_scaling(VNC_DISPLAY(viewer->vnc), TRUE);
-
-	g_signal_connect(viewer->vnc, "vnc-connected",
-			 G_CALLBACK(viewer_connected), viewer);
-	g_signal_connect(viewer->vnc, "vnc-initialized",
-			 G_CALLBACK(viewer_initialized), viewer);
-	g_signal_connect(viewer->vnc, "vnc-disconnected",
-			 G_CALLBACK(viewer_disconnected), viewer);
-
-	/* When VNC desktop resizes, we have to resize the containing widget */
-	g_signal_connect(viewer->vnc, "vnc-desktop-resize",
-			 G_CALLBACK(viewer_resize_desktop), viewer);
-	g_signal_connect(viewer->vnc, "vnc-pointer-grab",
-			 G_CALLBACK(viewer_mouse_grab), viewer);
-	g_signal_connect(viewer->vnc, "vnc-pointer-ungrab",
-			 G_CALLBACK(viewer_mouse_ungrab), viewer);
-	g_signal_connect(viewer->vnc, "vnc-keyboard-grab",
-			 G_CALLBACK(viewer_key_grab), viewer);
-	g_signal_connect(viewer->vnc, "vnc-keyboard-ungrab",
-			 G_CALLBACK(viewer_key_ungrab), viewer);
-
-	g_signal_connect(viewer->vnc, "vnc-auth-credential",
-			 G_CALLBACK(viewer_auth_vnc_credentials), &viewer->vncAddress);
-	g_signal_connect(viewer->vnc, "vnc-auth-failure",
-			 G_CALLBACK(viewer_vnc_auth_failure), viewer);
-	g_signal_connect(viewer->vnc, "vnc-auth-unsupported",
-			 G_CALLBACK(viewer_vnc_auth_unsupported), viewer);
-
-	g_signal_connect(viewer->vnc, "vnc-bell",
-			 G_CALLBACK(viewer_vnc_bell), viewer);
-	g_signal_connect(viewer->vnc, "vnc-server-cut-text",
-			 G_CALLBACK(viewer_vnc_server_cut_text), viewer);
-
-	notebook = glade_xml_get_widget(viewer->glade, "notebook");
-	align = glade_xml_get_widget(viewer->glade, "vnc-align");
-	gtk_container_add(GTK_CONTAINER(align), viewer->vnc);
-
-	if (!viewer->window) {
-		gtk_container_add(GTK_CONTAINER(viewer->container), GTK_WIDGET(notebook));
-		gtk_widget_show_all(viewer->container);
-	}
-
-	gtk_widget_realize(viewer->vnc);
-}
-
 int
 viewer_start (const char *uri,
 	      const char *name,
@@ -1276,7 +1111,6 @@
 {
 	VirtViewer *viewer;
 	GtkWidget *notebook;
-	GtkWidget *align;
 	GtkWidget *menu;
 	int cred_types[] =
 		{ VIR_CRED_AUTHNAME, VIR_CRED_PASSPHRASE };
@@ -1291,7 +1125,7 @@
 
 	viewer = g_new0(VirtViewer, 1);
 
-	viewer->active = 0;
+	viewer->active = FALSE;
 	viewer->autoResize = TRUE;
 	viewer->direct = direct;
 	viewer->waitvm = waitvm;
@@ -1347,9 +1181,6 @@
 
 	notebook = glade_xml_get_widget(viewer->glade, "notebook");
 	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
-	align = glade_xml_get_widget(viewer->glade, "vnc-align");
-	g_signal_connect(align, "size-allocate",
-			 G_CALLBACK(viewer_resize_align), viewer);
 
 	if (container) {
 		viewer->container = container;
@@ -1359,7 +1190,7 @@
 		viewer->container = window;
 		viewer->window = window;
 		g_signal_connect(window, "delete-event",
-				 G_CALLBACK(viewer_shutdown), viewer);
+				 G_CALLBACK(viewer_delete), viewer);
 		gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
 		viewer->accelEnabled = TRUE;
 		accels = gtk_accel_groups_from_object(G_OBJECT(window));
diff -r 9f8a6e933516 -r 4ac66a955d7b src/viewer.glade
--- a/src/viewer.glade	Fri Nov 19 18:09:42 2010 +0100
+++ b/src/viewer.glade	Tue Nov 30 13:59:01 2010 +0100
@@ -309,7 +309,7 @@
               </packing>
             </child>
             <child>
-              <widget class="GtkAlignment" id="vnc-align">
+              <widget class="GtkAlignment" id="display-align">
                 <property name="visible">True</property>
                 <property name="xscale">0</property>
                 <property name="yscale">0</property>




More information about the virt-tools-list mailing list