[virt-tools-list] [PATCH 07/12] Add spice controller support in remote-viewer
Marc-André Lureau
marcandre.lureau at gmail.com
Tue Dec 13 19:35:05 UTC 2011
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
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
--
1.7.7.3
More information about the virt-tools-list
mailing list