[virt-tools-list] [PATCH 07/12] Add spice controller support in remote-viewer

Daniel P. Berrange berrange at redhat.com
Thu Jan 26 16:30:06 UTC 2012


On Tue, Dec 13, 2011 at 08:35:05PM +0100, Marc-André Lureau wrote:
> Usage is simply "remote-viewer --spice-controller"
> ---
>  configure.ac                    |    3 +-
>  src/remote-viewer-main.c        |   20 ++-
>  src/remote-viewer.c             |  374 ++++++++++++++++++++++++++++++++++++--
>  src/remote-viewer.h             |    4 +-
>  src/virt-viewer-app.c           |    7 +
>  src/virt-viewer-app.h           |    1 +
>  src/virt-viewer-session-spice.c |   47 +++++-
>  src/virt-viewer-window.c        |    9 +
>  src/virt-viewer-window.h        |    1 +
>  9 files changed, 435 insertions(+), 31 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index e47d60a..b2d7e8f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -92,7 +92,8 @@ AC_ARG_WITH([spice-gtk],
>  
>  AS_IF([test "x$with_spice_gtk" != "xno"],
>        [PKG_CHECK_MODULES(SPICE_GTK,
> -                         spice-client-gtk-$SPICE_GTK_API_VERSION >= $SPICE_GTK_REQUIRED,
> +                         [spice-client-gtk-$SPICE_GTK_API_VERSION >= $SPICE_GTK_REQUIRED
> +                          spice-controller],
>                           [have_spice_gtk=yes], [have_spice_gtk=no])],
>        [have_spice_gtk=no])
>  
> diff --git a/src/remote-viewer-main.c b/src/remote-viewer-main.c
> index 54670d1..2256528 100644
> --- a/src/remote-viewer-main.c
> +++ b/src/remote-viewer-main.c
> @@ -56,6 +56,7 @@ main(int argc, char **argv)
>  	gboolean direct = FALSE;
>  	gboolean fullscreen = FALSE;
>  	RemoteViewer *viewer = NULL;
> +	gboolean controller = FALSE;
>  	VirtViewerApp *app;
>  	const char *help_msg = N_("Run '" PACKAGE " --help' to see a full list of available command line options");
>  	const GOptionEntry options [] = {
> @@ -71,6 +72,8 @@ main(int argc, char **argv)
>  		  N_("Display debugging information"), NULL },
>  		{ "full-screen", 'f', 0, G_OPTION_ARG_NONE, &fullscreen,
>  		  N_("Open in full screen mode"), NULL },
> +		{ "spice-controller", '\0', 0, G_OPTION_ARG_NONE, &controller,
> +		  N_("Open connection using Spice controller communication"), NULL },
>  		{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &args,
>  		  NULL, "URI" },
>  		{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
> @@ -99,7 +102,8 @@ main(int argc, char **argv)
>  
>  	g_option_context_free(context);
>  
> -	if (!args || (g_strv_length(args) != 1)) {
> +	if ((!args || (g_strv_length(args) != 1)) &&
> +	    !controller) {
>  		g_printerr(_("\nUsage: %s [OPTIONS] URI\n\n%s\n\n"), argv[0], help_msg);
>  		goto cleanup;
>  	}
> @@ -111,15 +115,19 @@ main(int argc, char **argv)
>  
>  	virt_viewer_app_set_debug(debug);
>  
> -	viewer = remote_viewer_new(args[0], verbose);
> +	if (controller) {
> +		viewer = remote_viewer_new_with_controller(verbose);
> +		g_object_set(viewer, "guest-name", "defined by Spice controller", NULL);
> +	} else {
> +		viewer = remote_viewer_new(args[0], verbose);
> +		g_object_set(viewer, "guest-name", args[0], NULL);
> +
> +	}
>  	if (viewer == NULL)
>  		goto cleanup;
>  
>  	app = VIRT_VIEWER_APP(viewer);
> -	g_object_set(app,
> -		     "fullscreen", fullscreen,
> -		     "guest-name", args[0],
> -		     NULL);
> +	g_object_set(app, "fullscreen", fullscreen, NULL);
>  	virt_viewer_window_set_zoom_level(virt_viewer_app_get_main_window(app), zoom);
>  	virt_viewer_app_set_direct(app, direct);
>  
> diff --git a/src/remote-viewer.c b/src/remote-viewer.c
> index d5c9824..388531b 100644
> --- a/src/remote-viewer.c
> +++ b/src/remote-viewer.c
> @@ -27,24 +27,41 @@
>  #include <glib/gprintf.h>
>  #include <glib/gi18n.h>
>  
> +#include <spice-controller/spice-controller.h>
> +
> +#include "virt-viewer-session-spice.h"
>  #include "virt-viewer-app.h"
>  #include "remote-viewer.h"
>  
>  struct _RemoteViewerPrivate {
> -	int _dummy;
> +	SpiceCtrlController *controller;
> +	GtkWidget *controller_menu;
>  };
>  
>  G_DEFINE_TYPE (RemoteViewer, remote_viewer, VIRT_VIEWER_TYPE_APP)
>  #define GET_PRIVATE(o)							\
>          (G_TYPE_INSTANCE_GET_PRIVATE ((o), REMOTE_VIEWER_TYPE, RemoteViewerPrivate))
>  
> +enum {
> +	PROP_0,
> +	PROP_CONTROLLER,
> +};
> +
>  static gboolean remote_viewer_start(VirtViewerApp *self);
> +static int remote_viewer_activate(VirtViewerApp *self);
> +static void remote_viewer_window_added(VirtViewerApp *self, VirtViewerWindow *win);
>  
>  static void
>  remote_viewer_get_property (GObject *object, guint property_id,
> -			    GValue *value G_GNUC_UNUSED, GParamSpec *pspec)
> +			   GValue *value, GParamSpec *pspec)
>  {
> +	RemoteViewer *self = REMOTE_VIEWER(object);
> +	RemoteViewerPrivate *priv = self->priv;
> +
>  	switch (property_id) {
> +	case PROP_CONTROLLER:
> +		g_value_set_object(value, priv->controller);
> +		break;
>  	default:
>  		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
>  	}
> @@ -52,9 +69,16 @@ remote_viewer_get_property (GObject *object, guint property_id,
>  
>  static void
>  remote_viewer_set_property (GObject *object, guint property_id,
> -			    const GValue *value G_GNUC_UNUSED, GParamSpec *pspec)
> +			   const GValue *value, GParamSpec *pspec)
>  {
> +	RemoteViewer *self = REMOTE_VIEWER(object);
> +	RemoteViewerPrivate *priv = self->priv;
> +
>  	switch (property_id) {
> +	case PROP_CONTROLLER:
> +		g_return_if_fail(priv->controller == NULL);
> +		priv->controller = g_value_dup_object(value);
> +		break;
>  	default:
>  		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
>  	}
> @@ -63,6 +87,14 @@ remote_viewer_set_property (GObject *object, guint property_id,
>  static void
>  remote_viewer_dispose (GObject *object)
>  {
> +	RemoteViewer *self = REMOTE_VIEWER(object);
> +	RemoteViewerPrivate *priv = self->priv;
> +
> +	if (priv->controller) {
> +		g_object_unref(priv->controller);
> +		priv->controller = NULL;
> +	}
> +
>  	G_OBJECT_CLASS(remote_viewer_parent_class)->dispose (object);
>  }
>  
> @@ -79,6 +111,18 @@ remote_viewer_class_init (RemoteViewerClass *klass)
>  	object_class->dispose = remote_viewer_dispose;
>  
>  	app_class->start = remote_viewer_start;
> +	app_class->activate = remote_viewer_activate;
> +	app_class->window_added = remote_viewer_window_added;
> +
> +	g_object_class_install_property(object_class,
> +					PROP_CONTROLLER,
> +					g_param_spec_object("controller",
> +							    "Controller",
> +							    "Spice controller",
> +							    SPICE_CTRL_TYPE_CONTROLLER,
> +							    G_PARAM_READWRITE |
> +							    G_PARAM_CONSTRUCT_ONLY |
> +							    G_PARAM_STATIC_STRINGS));
>  }
>  
>  static void
> @@ -96,36 +140,326 @@ remote_viewer_new(const gchar *uri, gboolean verbose)
>  			    NULL);
>  }
>  
> +RemoteViewer *
> +remote_viewer_new_with_controller(gboolean verbose)
> +{
> +	RemoteViewer *self;
> +	SpiceCtrlController *ctrl = spice_ctrl_controller_new();
> +
> +	self =  g_object_new(REMOTE_VIEWER_TYPE,
> +			     "controller", ctrl,
> +			     "verbose", verbose,
> +			     NULL);
> +	g_object_unref(ctrl);
> +
> +	return self;
> +}
> +
> +static void
> +spice_ctrl_do_connect(SpiceCtrlController *ctrl G_GNUC_UNUSED,
> +		      VirtViewerApp *self)
> +{
> +	if (virt_viewer_app_initial_connect(self) < 0) {
> +		virt_viewer_app_simple_message_dialog(self, _("Failed to initiate connection"));
> +	}
> +}
> +
> +static void
> +spice_ctrl_show(SpiceCtrlController *ctrl G_GNUC_UNUSED, RemoteViewer *self)
> +{
> +	virt_viewer_app_show_display(VIRT_VIEWER_APP(self));
> +}
> +
> +static void
> +spice_ctrl_hide(SpiceCtrlController *ctrl G_GNUC_UNUSED, RemoteViewer *self)
> +{
> +	virt_viewer_app_show_status(VIRT_VIEWER_APP(self), _("Display disabled by controller"));
> +}
> +
> +static void
> +spice_menuitem_activate_cb(GtkMenuItem *mi, RemoteViewer *self)
> +{
> +	SpiceCtrlMenuItem *menuitem = g_object_get_data(G_OBJECT(mi), "spice-menuitem");
> +
> +	g_return_if_fail(menuitem != NULL);
> +	if (gtk_menu_item_get_submenu(mi))
> +		return;
> +
> +	spice_ctrl_controller_menu_item_click_msg(self->priv->controller, menuitem->id);
> +}
> +
> +static GtkWidget *
> +ctrlmenu_to_gtkmenu (RemoteViewer *self, SpiceCtrlMenu *ctrlmenu)
> +{
> +	GList *l;
> +	GtkWidget *menu = gtk_menu_new();
> +	guint n = 0;
> +
> +	for (l = ctrlmenu->items; l != NULL; l = l->next) {
> +		SpiceCtrlMenuItem *menuitem = l->data;
> +		GtkWidget *item;
> +		char *s;
> +		if (menuitem->text == NULL) {
> +			g_warn_if_reached();
> +			continue;
> +		}
> +
> +		for (s = menuitem->text; *s; s++)
> +			if (*s == '&')
> +				*s = '_';
> +
> +		if (g_str_equal(menuitem->text, "-")){
> +			item = gtk_separator_menu_item_new();
> +		} else {
> +			item = gtk_menu_item_new_with_mnemonic(menuitem->text);
> +		}
> +
> +		g_object_set_data_full(G_OBJECT(item), "spice-menuitem",
> +					g_object_ref(menuitem), g_object_unref);
> +		g_signal_connect(item, "activate", G_CALLBACK(spice_menuitem_activate_cb), self);
> +		gtk_menu_attach(GTK_MENU (menu), item, 0, 1, n, n + 1);
> +		n += 1;
> +
> +		if (menuitem->submenu) {
> +			gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
> +						  ctrlmenu_to_gtkmenu(self, menuitem->submenu));
> +		}
> +	}
> +
> +	if (n == 0) {
> +		g_object_ref_sink(menu);
> +		g_object_unref(menu);
> +		menu = NULL;
> +	}
> +
> +	gtk_widget_show_all(menu);
> +	return menu;
> +}
> +
> +static void
> +spice_menu_set_visible(gpointer key G_GNUC_UNUSED,
> +		       gpointer value,
> +		       gpointer user_data)
> +{
> +	gboolean visible = GPOINTER_TO_INT(user_data);
> +	GtkWidget *menu = g_object_get_data(value, "spice-menu");
> +
> +	gtk_widget_set_visible(menu, visible);
> +}
> +
> +static void
> +remote_viewer_window_spice_menu_set_visible(RemoteViewer *self,
> +					   gboolean visible)
> +{
> +	GHashTable *windows = virt_viewer_app_get_windows(VIRT_VIEWER_APP(self));
> +
> +	g_hash_table_foreach(windows, spice_menu_set_visible, GINT_TO_POINTER(visible));
> +}
> +
> +static void
> +spice_menu_update(gpointer key G_GNUC_UNUSED,
> +		  gpointer value,
> +		  gpointer user_data)
> +{
> +	RemoteViewer *self = REMOTE_VIEWER(user_data);
> +	GtkWidget *menuitem = g_object_get_data(value, "spice-menu");
> +	SpiceCtrlMenu *menu;
> +
> +	g_object_get(self->priv->controller, "menu", &menu, NULL);
> +	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), ctrlmenu_to_gtkmenu(self, menu));
> +	g_object_unref(menu);
> +}
> +
> +static void
> +spice_ctrl_menu_updated(RemoteViewer *self,
> +			SpiceCtrlMenu *menu)
> +{
> +	GHashTable *windows = virt_viewer_app_get_windows(VIRT_VIEWER_APP(self));
> +	RemoteViewerPrivate *priv = self->priv;
> +	gboolean visible;
> +
> +	DEBUG_LOG("Spice controller menu updated");
> +
> +	if (priv->controller_menu != NULL) {
> +		g_object_unref (priv->controller_menu);
> +		priv->controller_menu = NULL;
> +	}
> +
> +	if (menu && g_list_length(menu->items) > 0) {
> +		priv->controller_menu = ctrlmenu_to_gtkmenu(self, menu);
> +		g_hash_table_foreach(windows, spice_menu_update, self);
> +	}
> +
> +	visible = priv->controller_menu != NULL;
> +
> +	remote_viewer_window_spice_menu_set_visible(self, visible);
> +}
> +
> +static SpiceSession *
> +remote_viewer_get_spice_session(RemoteViewer *self)
> +{
> +	VirtViewerSession *vsession = NULL;
> +	SpiceSession *session = NULL;
> +
> +	g_object_get(self, "session", &vsession, NULL);
> +	g_return_val_if_fail(vsession != NULL, NULL);
> +
> +	g_object_get(vsession, "spice-session", &session, NULL);
> +
> +	g_object_unref(vsession);
> +
> +	return session;
> +}
> +
> +#ifndef G_VALUE_INIT /* see bug https://bugzilla.gnome.org/show_bug.cgi?id=654793 */
> +#define G_VALUE_INIT  { 0, { { 0 } } }
> +#endif
> +
> +static void
> +spice_ctrl_notified(SpiceCtrlController *ctrl,
> +		    GParamSpec *pspec,
> +		    RemoteViewer *self)
> +{
> +	SpiceSession *session = remote_viewer_get_spice_session(self);
> +	GValue value = G_VALUE_INIT;
> +	VirtViewerApp *app = VIRT_VIEWER_APP(self);
> +
> +	g_return_if_fail(session != NULL);
> +
> +	g_value_init(&value, pspec->value_type);
> +	g_object_get_property(G_OBJECT(ctrl), pspec->name, &value);
> +
> +	if (g_str_equal(pspec->name, "host") ||
> +	    g_str_equal(pspec->name, "port") ||
> +	    g_str_equal(pspec->name, "password") ||
> +	    g_str_equal(pspec->name, "ca-file")) {
> +		g_object_set_property(G_OBJECT(session), pspec->name, &value);
> +	} else if (g_str_equal(pspec->name, "sport")) {
> +		g_object_set_property(G_OBJECT(session), "tls-port", &value);
> +	} else if (g_str_equal(pspec->name, "tls-ciphers")) {
> +		g_object_set_property(G_OBJECT(session), "ciphers", &value);
> +	} else if (g_str_equal(pspec->name, "host-subject")) {
> +		g_object_set_property(G_OBJECT(session), "cert-subject", &value);
> +	} else if (g_str_equal(pspec->name, "title")) {
> +		g_object_set_property(G_OBJECT(app), "title", &value);
> +	} else if (g_str_equal(pspec->name, "display-flags")) {
> +		guint flags = g_value_get_uint(&value);
> +		gboolean fullscreen = flags & CONTROLLER_SET_FULL_SCREEN;
> +		gboolean auto_res = flags & CONTROLLER_AUTO_DISPLAY_RES;
> +		g_object_set(G_OBJECT(self), "fullscreen", fullscreen, NULL);
> +		g_debug("unimplemented resize-guest %d", auto_res);
> +		/* g_object_set(G_OBJECT(self), "resize-guest", auto_res, NULL); */
> +	} else if (g_str_equal(pspec->name, "menu")) {
> +		spice_ctrl_menu_updated(self, g_value_get_object(&value));
> +	} else {
> +		gchar *content = g_strdup_value_contents(&value);
> +
> +		g_debug("unimplemented property: %s=%s", pspec->name, content);
> +		g_free(content);
> +	}
> +
> +	g_object_unref(session);
> +	g_value_unset(&value);
> +}
> +
> +static void
> +spice_ctrl_listen_async_cb(GObject *object,
> +			   GAsyncResult *res,
> +			   gpointer user_data)
> +{
> +	GError *error = NULL;
> +
> +	spice_ctrl_controller_listen_finish(SPICE_CTRL_CONTROLLER(object), res, &error);
> +
> +	if (error != NULL) {
> +		virt_viewer_app_simple_message_dialog(VIRT_VIEWER_APP(user_data),
> +						      _("Controller connection failed: %s"),
> +						      error->message);
> +		g_clear_error(&error);
> +		exit(1); /* TODO: make start async? */
> +	}
> +}
> +
> +static int
> +remote_viewer_activate(VirtViewerApp *app)
> +{
> +	g_return_val_if_fail(REMOTE_VIEWER_IS(app), -1);
> +	RemoteViewer *self = REMOTE_VIEWER(app);
> +	int ret = -1;
> +
> +	if (self->priv->controller) {
> +		SpiceSession *session = remote_viewer_get_spice_session(self);
> +		ret = spice_session_connect(session);
> +		g_object_unref(session);
> +	} else {
> +		ret = VIRT_VIEWER_APP_CLASS(remote_viewer_parent_class)->activate(app);
> +	}
> +
> +	return ret;
> +}
> +
> +static void
> +remote_viewer_window_added(VirtViewerApp *self G_GNUC_UNUSED,
> +			  VirtViewerWindow *win)
> +{
> +	GtkMenuShell *shell = GTK_MENU_SHELL(gtk_builder_get_object(virt_viewer_window_get_builder(win), "top-menu"));
> +	GtkWidget *spice = gtk_menu_item_new_with_label("Spice");
> +
> +	gtk_menu_shell_append(shell, spice);
> +	g_object_set_data(G_OBJECT(win), "spice-menu", spice);
> +}
> +
>  static gboolean
>  remote_viewer_start(VirtViewerApp *app)
>  {
> -	gchar *guri;
> -	gchar *type;
> +	g_return_val_if_fail(REMOTE_VIEWER_IS(app), FALSE);
> +
> +	RemoteViewer *self = REMOTE_VIEWER(app);
> +	RemoteViewerPrivate *priv = self->priv;
>  	gboolean ret = FALSE;
> +	gchar *guri = NULL;
> +	gchar *type = NULL;
>  
> -	g_object_get(app, "guri", &guri, NULL);
> -	g_return_val_if_fail(guri != NULL, FALSE);
> +	if (priv->controller) {
> +		if (virt_viewer_app_create_session(app, "spice") < 0) {
> +			virt_viewer_app_simple_message_dialog(app, _("Couldn't create a Spice session"));
> +			goto cleanup;
> +		}
>  
> -	DEBUG_LOG("Opening display to %s", guri);
> +		g_signal_connect(priv->controller, "notify", G_CALLBACK(spice_ctrl_notified), self);
> +		g_signal_connect(priv->controller, "do_connect", G_CALLBACK(spice_ctrl_do_connect), self);
> +		g_signal_connect(priv->controller, "show", G_CALLBACK(spice_ctrl_show), self);
> +		g_signal_connect(priv->controller, "hide", G_CALLBACK(spice_ctrl_hide), self);
>  
> -	if (virt_viewer_util_extract_host(guri, &type, NULL, NULL, NULL, NULL) < 0) {
> -		virt_viewer_app_simple_message_dialog(app, _("Cannot determine the connection type from URI"));
> -		goto cleanup;
> -	}
> +		spice_ctrl_controller_listen(priv->controller, NULL, spice_ctrl_listen_async_cb, self);
> +		virt_viewer_app_show_status(VIRT_VIEWER_APP(self), _("Setting up Spice session..."));
> +	} else {
> +		g_object_get(app, "guri", &guri, NULL);
> +		g_return_val_if_fail(guri != NULL, FALSE);
>  
> -	if (virt_viewer_app_create_session(app, type) < 0) {
> -		virt_viewer_app_simple_message_dialog(app, _("Couldn't create a session for this type: %s"), type);
> -		goto cleanup;
> -	}
> +		DEBUG_LOG("Opening display to %s", guri);
> +		g_object_set(app, "title", guri, NULL);
> +
> +		if (virt_viewer_util_extract_host(guri, &type, NULL, NULL, NULL, NULL) < 0) {
> +			virt_viewer_app_simple_message_dialog(app, _("Cannot determine the connection type from URI"));
> +			goto cleanup;
> +		}
> +
> +		if (virt_viewer_app_create_session(app, type) < 0) {
> +			virt_viewer_app_simple_message_dialog(app, _("Couldn't create a session for this type: %s"), type);
> +			goto cleanup;
> +		}
> +
> +		if (virt_viewer_app_initial_connect(app) < 0) {
> +			virt_viewer_app_simple_message_dialog(app, _("Failed to initiate connection"));
> +			goto cleanup;
> +		}
>  
> -	if (virt_viewer_app_activate(app) < 0) {
> -		virt_viewer_app_simple_message_dialog(app, _("Failed to initiate connection"));
> -		goto cleanup;
>  	}
>  
>  	ret = VIRT_VIEWER_APP_CLASS(remote_viewer_parent_class)->start(app);
>  
> - cleanup:
> +cleanup:
>  	g_free(guri);
>  	g_free(type);
>  	return ret;
> diff --git a/src/remote-viewer.h b/src/remote-viewer.h
> index 1ff6d8d..3d02315 100644
> --- a/src/remote-viewer.h
> +++ b/src/remote-viewer.h
> @@ -48,8 +48,8 @@ typedef struct {
>  
>  GType remote_viewer_get_type (void);
>  
> -RemoteViewer *
> -remote_viewer_new(const gchar *uri, gboolean verbose);
> +RemoteViewer* remote_viewer_new(const gchar *uri, gboolean verbose);
> +RemoteViewer* remote_viewer_new_with_controller(gboolean verbose);
>  
>  G_END_DECLS
>  


ACK, but the bits that follow this point should probably go in a separate patch...


> diff --git a/src/virt-viewer-app.c b/src/virt-viewer-app.c
> index 97b53c2..352f206 100644
> --- a/src/virt-viewer-app.c
> +++ b/src/virt-viewer-app.c
> @@ -1528,6 +1528,13 @@ virt_viewer_app_show_display(VirtViewerApp *self)
>  	g_hash_table_foreach(self->priv->windows, show_display_cb, self);
>  }
>  
> +GHashTable*
> +virt_viewer_app_get_windows(VirtViewerApp *self)
> +{
> +	g_return_val_if_fail(VIRT_VIEWER_IS_APP(self), NULL);
> +	return self->priv->windows;
> +}
> +
>  /*
>   * Local variables:
>   *  c-indent-level: 8
> diff --git a/src/virt-viewer-app.h b/src/virt-viewer-app.h
> index 7c3f0a7..320e75c 100644
> --- a/src/virt-viewer-app.h
> +++ b/src/virt-viewer-app.h
> @@ -86,6 +86,7 @@ void virt_viewer_app_set_connect_info(VirtViewerApp *self,
>  gboolean virt_viewer_app_window_set_visible(VirtViewerApp *self, VirtViewerWindow *window, gboolean visible);
>  void virt_viewer_app_show_status(VirtViewerApp *self, const gchar *fmt, ...);
>  void virt_viewer_app_show_display(VirtViewerApp *self);
> +GHashTable* virt_viewer_app_get_windows(VirtViewerApp *self);
>  
>  G_END_DECLS
>  
> diff --git a/src/virt-viewer-session-spice.c b/src/virt-viewer-session-spice.c
> index 066f922..f89d042 100644
> --- a/src/virt-viewer-session-spice.c
> +++ b/src/virt-viewer-session-spice.c
> @@ -41,6 +41,12 @@ struct _VirtViewerSessionSpicePrivate {
>  
>  #define VIRT_VIEWER_SESSION_SPICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_SESSION_SPICE, VirtViewerSessionSpicePrivate))
>  
> +enum {
> +	PROP_0,
> +	PROP_SPICE_SESSION,
> +};
> +
> +
>  static void virt_viewer_session_spice_close(VirtViewerSession *session);
>  static gboolean virt_viewer_session_spice_open_fd(VirtViewerSession *session, int fd);
>  static gboolean virt_viewer_session_spice_open_host(VirtViewerSession *session, char *host, char *port);
> @@ -55,7 +61,33 @@ static void virt_viewer_session_spice_channel_destroy(SpiceSession *s,
>  
>  
>  static void
> -virt_viewer_session_spice_finalize(GObject *obj)
> +virt_viewer_session_spice_get_property(GObject *object, guint property_id,
> +				       GValue *value, GParamSpec *pspec)
> +{
> +	VirtViewerSessionSpice *self = VIRT_VIEWER_SESSION_SPICE(object);
> +	VirtViewerSessionSpicePrivate *priv = self->priv;
> +
> +	switch (property_id) {
> +	case PROP_SPICE_SESSION:
> +		g_value_set_object(value, priv->session);
> +		break;
> +	default:
> +		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
> +	}
> +}
> +
> +static void
> +virt_viewer_session_spice_set_property(GObject *object, guint property_id,
> +				       const GValue *value G_GNUC_UNUSED, GParamSpec *pspec)
> +{
> +	switch (property_id) {
> +	default:
> +		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
> +	}
> +}
> +
> +static void
> +virt_viewer_session_spice_dispose(GObject *obj)
>  {
>  	VirtViewerSessionSpice *spice = VIRT_VIEWER_SESSION_SPICE(obj);
>  
> @@ -76,7 +108,9 @@ virt_viewer_session_spice_class_init(VirtViewerSessionSpiceClass *klass)
>  	VirtViewerSessionClass *dclass = VIRT_VIEWER_SESSION_CLASS(klass);
>  	GObjectClass *oclass = G_OBJECT_CLASS(klass);
>  
> -	oclass->finalize = virt_viewer_session_spice_finalize;
> +	oclass->get_property = virt_viewer_session_spice_get_property;
> +	oclass->set_property = virt_viewer_session_spice_set_property;
> +	oclass->dispose = virt_viewer_session_spice_dispose;
>  
>  	dclass->close = virt_viewer_session_spice_close;
>  	dclass->open_fd = virt_viewer_session_spice_open_fd;
> @@ -85,6 +119,15 @@ virt_viewer_session_spice_class_init(VirtViewerSessionSpiceClass *klass)
>  	dclass->channel_open_fd = virt_viewer_session_spice_channel_open_fd;
>  
>  	g_type_class_add_private(oclass, sizeof(VirtViewerSessionSpicePrivate));
> +
> +	g_object_class_install_property(oclass,
> +					PROP_SPICE_SESSION,
> +					g_param_spec_object("spice-session",
> +							    "Spice session",
> +							    "Spice session",
> +							    SPICE_TYPE_SESSION,
> +							    G_PARAM_READABLE |
> +							    G_PARAM_STATIC_STRINGS));
>  }
>  
>  static void
> diff --git a/src/virt-viewer-window.c b/src/virt-viewer-window.c
> index 324e37f..d80b456 100644
> --- a/src/virt-viewer-window.c
> +++ b/src/virt-viewer-window.c
> @@ -912,9 +912,18 @@ GtkMenuItem*
>  virt_viewer_window_get_menu_displays(VirtViewerWindow *self)
>  {
>  	g_return_val_if_fail(VIRT_VIEWER_IS_WINDOW(self), NULL);
> +
>  	return GTK_MENU_ITEM(gtk_builder_get_object(self->priv->builder, "menu-displays"));
>  }
>  
> +GtkBuilder*
> +virt_viewer_window_get_builder(VirtViewerWindow *self)
> +{
> +	g_return_val_if_fail(VIRT_VIEWER_IS_WINDOW(self), NULL);
> +
> +	return self->priv->builder;
> +}
> +
>  /*
>   * Local variables:
>   *  c-indent-level: 8
> diff --git a/src/virt-viewer-window.h b/src/virt-viewer-window.h
> index 9baab76..cf66f5e 100644
> --- a/src/virt-viewer-window.h
> +++ b/src/virt-viewer-window.h
> @@ -69,6 +69,7 @@ gint virt_viewer_window_get_zoom_level(VirtViewerWindow *self);
>  void virt_viewer_window_leave_fullscreen(VirtViewerWindow *self);
>  void virt_viewer_window_enter_fullscreen(VirtViewerWindow *self, gboolean move, gint x, gint y);
>  GtkMenuItem *virt_viewer_window_get_menu_displays(VirtViewerWindow *self);
> +GtkBuilder* virt_viewer_window_get_builder(VirtViewerWindow *window);
>  
>  G_END_DECLS


Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the virt-tools-list mailing list