[vinagre] plugins: add spice plugin
- From: Marc-Andre Lureau <malureau src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vinagre] plugins: add spice plugin
- Date: Wed, 23 Feb 2011 23:22:07 +0000 (UTC)
commit df39ca2a0b8bcbf6aad39202b433049ecc65e4e3
Author: Marc-André Lureau <marcandre lureau redhat com>
Date: Sat Feb 19 00:50:17 2011 +0100
plugins: add spice plugin
Use --enable-spice configure argument.
https://bugzilla.gnome.org/show_bug.cgi?id=642844
configure.ac | 18 +
data/.gitignore | 2 -
plugins/Makefile.am | 35 +-
plugins/spice/Makefile.am | 33 ++
plugins/spice/spice.plugin.desktop.in | 9 +
plugins/spice/vinagre-spice-connection.c | 551 ++++++++++++++++++++++
plugins/spice/vinagre-spice-connection.h | 92 ++++
plugins/spice/vinagre-spice-plugin.c | 360 ++++++++++++++
plugins/spice/vinagre-spice-plugin.h | 62 +++
plugins/spice/vinagre-spice-tab.c | 753 ++++++++++++++++++++++++++++++
plugins/spice/vinagre-spice-tab.h | 76 +++
plugins/spice/vinagre-spice-tunnel.c | 155 ++++++
plugins/spice/vinagre-spice-tunnel.h | 46 ++
13 files changed, 2177 insertions(+), 15 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 8a5a9f1..82f5145 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,6 +75,7 @@ GLIB_REQUIRED=2.25.11
GTK_REQUIRED=2.99.3
GTHREAD_REQUIRED=2.0.0
GTK_VNC_REQUIRED=0.4.3
+SPICE_GTK_REQUIRED=0.5
AVAHI_REQUIRED=0.6.26
PANEL_REQUIRED_VERSION=2.0.0
TELEPATHY_GLIB_REQUIRED=0.11.6
@@ -215,6 +216,21 @@ if test "x$enable_applet" = "xyes";then
fi
AM_CONDITIONAL(APPLET, test "x$enable_applet" = "xyes")
+AC_ARG_ENABLE(spice,
+ AS_HELP_STRING([--enable-spice],
+ [Enable SPICE plugin (default=yes)]),
+ enable_spice=${enableval},
+ enable_spice=yes)
+
+if test "x$enable_spice" = "xyes" ; then
+ PKG_CHECK_MODULES(SPICE,\
+ spice-client-gtk-3.0 >= $SPICE_GTK_REQUIRED)
+ AC_SUBST(SPICE_CFLAGS)
+ AC_SUBST(SPICE_LIBS)
+ AC_DEFINE([VINAGRE_ENABLE_SPICE],[1],[We want spice support])
+fi
+AM_CONDITIONAL(SPICE, test "x$enable_spice" = "xyes")
+
GLIB_GSETTINGS
dnl Check for bind in libsocket (needed on Solaris)
@@ -303,6 +319,7 @@ plugins/Makefile
plugins/vnc/Makefile
plugins/ssh/Makefile
plugins/rdp/Makefile
+plugins/spice/Makefile
plugins/reverse-vnc/Makefile
plugins/im-status/Makefile
vinagre/Makefile
@@ -327,4 +344,5 @@ Configure summary:
Telepathy support ..........: ${have_telepathy}
SSH plugin .................: ${enable_ssh}
RDP plugin .................: ${enable_rdp}
+ Spice plugin ...............: ${enable_spice}
"
diff --git a/data/.gitignore b/data/.gitignore
index a5fd0a8..3ee935c 100644
--- a/data/.gitignore
+++ b/data/.gitignore
@@ -28,5 +28,3 @@
/vinagre.desktop
/vinagre.desktop.in
/vinagre.pc
-/vinagre.schemas.in
-/*.gschema.valid
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index ce85df8..bcf40af 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -1,21 +1,30 @@
-DIST_SUBDIRS = \
- vnc \
- ssh \
- rdp \
- reverse-vnc \
- im-status
+NULL =
-SUBDIRS = \
- vnc \
- reverse-vnc \
- im-status
+DIST_SUBDIRS = \
+ im-status \
+ rdp \
+ reverse-vnc \
+ spice \
+ ssh \
+ vnc \
+ $(NULL)
-if SSH
-SUBDIRS += ssh
-endif
+SUBDIRS = \
+ im-status \
+ reverse-vnc \
+ vnc \
+ $(NULL)
if RDP
SUBDIRS += rdp
endif
+if SPICE
+SUBDIRS += spice
+endif
+
+if SSH
+SUBDIRS += ssh
+endif
+
-include $(top_srcdir)/git.mk
diff --git a/plugins/spice/Makefile.am b/plugins/spice/Makefile.am
new file mode 100644
index 0000000..36b3afa
--- /dev/null
+++ b/plugins/spice/Makefile.am
@@ -0,0 +1,33 @@
+# spice plugin
+plugindir = $(VINAGRE_PLUGINS_LIBS_DIR)
+
+NULL =
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ $(SPICE_CFLAGS) \
+ $(VINAGRE_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(NULL)
+
+plugin_LTLIBRARIES = libspice.la
+
+libspice_la_SOURCES = \
+ vinagre-spice-plugin.h vinagre-spice-plugin.c \
+ vinagre-spice-connection.h vinagre-spice-connection.c \
+ vinagre-spice-tab.h vinagre-spice-tab.c \
+ vinagre-spice-tunnel.h vinagre-spice-tunnel.c \
+ $(NULL)
+
+libspice_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+libspice_la_LIBADD = $(SPICE_LIBS)
+
+plugin_in_files = spice.plugin.desktop.in
+%.plugin: %.plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(AM_V_GEN)$(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+plugin_DATA = $(plugin_in_files:.plugin.desktop.in=.plugin)
+
+EXTRA_DIST = $(plugin_in_files)
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/spice/spice.plugin.desktop.in b/plugins/spice/spice.plugin.desktop.in
new file mode 100644
index 0000000..2416f41
--- /dev/null
+++ b/plugins/spice/spice.plugin.desktop.in
@@ -0,0 +1,9 @@
+[Plugin]
+Module=spice
+IAge=1
+_Name=Spice
+_Description=Spice support
+Authors=Marc-André Lureau
+Copyright=Copyright © 2011 Red Hat, Inc.
+Website=http://www.spice-space.org
+Version=1.0
diff --git a/plugins/spice/vinagre-spice-connection.c b/plugins/spice/vinagre-spice-connection.c
new file mode 100644
index 0000000..210b462
--- /dev/null
+++ b/plugins/spice/vinagre-spice-connection.c
@@ -0,0 +1,551 @@
+/*
+ * vinagre-spice-connection.c
+ * Child class of abstract VinagreConnection, specific to SPICE protocol
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Author: Marc-Andre Lureau <marcandre lureau redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <stdlib.h>
+#include <vinagre/vinagre-utils.h>
+#include <vinagre/vinagre-cache-prefs.h>
+
+#include "vinagre-spice-connection.h"
+
+struct _VinagreSpiceConnectionPrivate
+{
+ gchar *desktop_name;
+ gboolean view_only;
+ gboolean scaling;
+ gboolean resize_guest;
+ gboolean auto_clipboard;
+ gint fd;
+ gchar *ssh_tunnel_host;
+ GSocket *socket;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DESKTOP_NAME,
+ PROP_VIEW_ONLY,
+ PROP_SCALING,
+ PROP_RESIZE_GUEST,
+ PROP_AUTO_CLIPBOARD,
+ PROP_FD,
+ PROP_SSH_TUNNEL_HOST,
+ PROP_SOCKET
+};
+
+#define VINAGRE_SPICE_CONNECTION_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), VINAGRE_TYPE_SPICE_CONNECTION, VinagreSpiceConnectionPrivate))
+G_DEFINE_TYPE (VinagreSpiceConnection, vinagre_spice_connection, VINAGRE_TYPE_CONNECTION);
+
+static void
+vinagre_spice_connection_init (VinagreSpiceConnection *conn)
+{
+ conn->priv = G_TYPE_INSTANCE_GET_PRIVATE (conn, VINAGRE_TYPE_SPICE_CONNECTION, VinagreSpiceConnectionPrivate);
+
+ conn->priv->desktop_name = NULL;
+ conn->priv->view_only = FALSE;
+ conn->priv->scaling = FALSE;
+ conn->priv->resize_guest = TRUE;
+ conn->priv->auto_clipboard = TRUE;
+ conn->priv->fd = 0;
+ conn->priv->ssh_tunnel_host = NULL;
+ conn->priv->socket = NULL;
+}
+
+static void
+vinagre_spice_connection_constructed (GObject *object)
+{
+ vinagre_connection_set_protocol (VINAGRE_CONNECTION (object), "spice");
+}
+
+static void
+vinagre_spice_connection_finalize (GObject *object)
+{
+ VinagreSpiceConnection *conn = VINAGRE_SPICE_CONNECTION (object);
+
+ g_free (conn->priv->desktop_name);
+ g_free (conn->priv->ssh_tunnel_host);
+
+ G_OBJECT_CLASS (vinagre_spice_connection_parent_class)->finalize (object);
+}
+
+static void
+vinagre_spice_connection_dispose (GObject *object)
+{
+ VinagreSpiceConnection *conn = VINAGRE_SPICE_CONNECTION (object);
+
+ if (conn->priv->socket)
+ {
+ g_object_unref (conn->priv->socket);
+ conn->priv->socket = NULL;
+ }
+
+ G_OBJECT_CLASS (vinagre_spice_connection_parent_class)->dispose (object);
+}
+
+static void
+vinagre_spice_connection_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ VinagreSpiceConnection *conn;
+
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (object));
+
+ conn = VINAGRE_SPICE_CONNECTION (object);
+
+ switch (prop_id)
+ {
+ case PROP_DESKTOP_NAME:
+ vinagre_spice_connection_set_desktop_name (conn, g_value_get_string (value));
+ break;
+
+ case PROP_VIEW_ONLY:
+ vinagre_spice_connection_set_view_only (conn, g_value_get_boolean (value));
+ break;
+
+ case PROP_SCALING:
+ vinagre_spice_connection_set_scaling (conn, g_value_get_boolean (value));
+ break;
+
+ case PROP_RESIZE_GUEST:
+ vinagre_spice_connection_set_resize_guest (conn, g_value_get_boolean (value));
+ break;
+
+ case PROP_AUTO_CLIPBOARD:
+ vinagre_spice_connection_set_auto_clipboard (conn, g_value_get_boolean (value));
+ break;
+
+ case PROP_SSH_TUNNEL_HOST:
+ vinagre_spice_connection_set_ssh_tunnel_host (conn, g_value_get_string (value));
+ break;
+
+ case PROP_FD:
+ vinagre_spice_connection_set_fd (conn, g_value_get_int (value));
+ break;
+
+ case PROP_SOCKET:
+ vinagre_spice_connection_set_socket (conn, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+vinagre_spice_connection_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ VinagreSpiceConnection *conn;
+
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (object));
+
+ conn = VINAGRE_SPICE_CONNECTION (object);
+
+ switch (prop_id)
+ {
+ case PROP_DESKTOP_NAME:
+ g_value_set_string (value, conn->priv->desktop_name);
+ break;
+
+ case PROP_VIEW_ONLY:
+ g_value_set_boolean (value, conn->priv->view_only);
+ break;
+
+ case PROP_SCALING:
+ g_value_set_boolean (value, conn->priv->scaling);
+ break;
+
+ case PROP_RESIZE_GUEST:
+ g_value_set_boolean (value, conn->priv->resize_guest);
+ break;
+
+ case PROP_AUTO_CLIPBOARD:
+ g_value_set_boolean (value, conn->priv->auto_clipboard);
+ break;
+
+ case PROP_SSH_TUNNEL_HOST:
+ g_value_set_string (value, conn->priv->ssh_tunnel_host);
+ break;
+
+ case PROP_FD:
+ g_value_set_int (value, vinagre_spice_connection_get_fd (conn));
+ break;
+
+ case PROP_SOCKET:
+ g_value_set_object (value, conn->priv->socket);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+spice_fill_writer (VinagreConnection *conn, xmlTextWriter *writer)
+{
+ VinagreSpiceConnection *spice_conn = VINAGRE_SPICE_CONNECTION (conn);
+ VINAGRE_CONNECTION_CLASS (vinagre_spice_connection_parent_class)->impl_fill_writer (conn, writer);
+
+ xmlTextWriterWriteFormatElement (writer, (const xmlChar *)"view_only", "%d", spice_conn->priv->view_only);
+ xmlTextWriterWriteFormatElement (writer, (const xmlChar *)"scaling", "%d", spice_conn->priv->scaling);
+ xmlTextWriterWriteFormatElement (writer, (const xmlChar *)"resize_guest", "%d", spice_conn->priv->resize_guest);
+ xmlTextWriterWriteFormatElement (writer, (const xmlChar *)"auto_clipboard", "%d", spice_conn->priv->auto_clipboard);
+
+ if (spice_conn->priv->ssh_tunnel_host && *spice_conn->priv->ssh_tunnel_host)
+ xmlTextWriterWriteFormatElement (writer, (const xmlChar *)"ssh_tunnel_host", "%s", spice_conn->priv->ssh_tunnel_host);
+}
+
+static void
+spice_parse_item (VinagreConnection *conn, xmlNode *root)
+{
+ xmlNode *curr;
+ xmlChar *s_value;
+ VinagreSpiceConnection *spice_conn = VINAGRE_SPICE_CONNECTION (conn);
+
+ VINAGRE_CONNECTION_CLASS (vinagre_spice_connection_parent_class)->impl_parse_item (conn, root);
+
+ for (curr = root->children; curr; curr = curr->next)
+ {
+ s_value = xmlNodeGetContent (curr);
+
+ if (!xmlStrcmp(curr->name, (const xmlChar *)"view_only"))
+ {
+ vinagre_spice_connection_set_view_only (spice_conn, vinagre_utils_parse_boolean ((const gchar *)s_value));
+ }
+ else if (!xmlStrcmp(curr->name, (const xmlChar *)"scaling"))
+ {
+ vinagre_spice_connection_set_scaling (spice_conn, vinagre_utils_parse_boolean ((const gchar *)s_value));
+ }
+ else if (!xmlStrcmp(curr->name, (const xmlChar *)"resize_guest"))
+ {
+ vinagre_spice_connection_set_resize_guest (spice_conn, vinagre_utils_parse_boolean ((const gchar *)s_value));
+ }
+ else if (!xmlStrcmp(curr->name, (const xmlChar *)"auto_clipboard"))
+ {
+ vinagre_spice_connection_set_auto_clipboard (spice_conn, vinagre_utils_parse_boolean ((const gchar *)s_value));
+ }
+ else if (!xmlStrcmp(curr->name, (const xmlChar *)"ssh_tunnel_host"))
+ {
+ vinagre_spice_connection_set_ssh_tunnel_host (spice_conn, (const gchar *)s_value);
+ }
+
+ xmlFree (s_value);
+ }
+}
+
+static gchar *
+spice_get_best_name (VinagreConnection *conn)
+{
+ VinagreSpiceConnection *spice_conn = VINAGRE_SPICE_CONNECTION (conn);
+
+ if (vinagre_connection_get_name (conn))
+ return g_strdup (vinagre_connection_get_name (conn));
+
+ if (spice_conn->priv->desktop_name)
+ return g_strdup (spice_conn->priv->desktop_name);
+
+ if (vinagre_connection_get_host (conn))
+ return vinagre_connection_get_string_rep (conn, FALSE);
+
+ return NULL;
+}
+
+static void
+spice_parse_options_widget (VinagreConnection *conn, GtkWidget *widget)
+{
+ GtkWidget *view_only, *scaling, *ssh_host, *resize_guest, *auto_clipboard;
+
+ view_only = g_object_get_data (G_OBJECT (widget), "view_only");
+ scaling = g_object_get_data (G_OBJECT (widget), "scaling");
+ resize_guest = g_object_get_data (G_OBJECT (widget), "resize_guest");
+ auto_clipboard = g_object_get_data (G_OBJECT (widget), "auto_clipboard");
+ ssh_host = g_object_get_data (G_OBJECT (widget), "ssh_host");
+ if (!view_only || !scaling || !ssh_host || !resize_guest || !auto_clipboard)
+ {
+ g_warning ("Wrong widget passed to spice_parse_options_widget()");
+ return;
+ }
+
+ vinagre_cache_prefs_set_boolean ("spice-connection", "view-only", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (view_only)));
+ vinagre_cache_prefs_set_boolean ("spice-connection", "scaling", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (scaling)));
+ vinagre_cache_prefs_set_boolean ("spice-connection", "resize-guest", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (resize_guest)));
+ vinagre_cache_prefs_set_boolean ("spice-connection", "auto-clipboard", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (auto_clipboard)));
+ vinagre_cache_prefs_set_string ("spice-connection", "ssh-tunnel-host", gtk_entry_get_text (GTK_ENTRY (ssh_host)));
+
+ g_object_set (conn,
+ "resize-guest", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (resize_guest)),
+ "auto-clipboard", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (auto_clipboard)),
+ "view-only", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (view_only)),
+ "scaling", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (scaling)),
+ "ssh-tunnel-host", gtk_entry_get_text (GTK_ENTRY (ssh_host)),
+ NULL);
+}
+
+static void
+vinagre_spice_connection_class_init (VinagreSpiceConnectionClass *klass)
+{
+ GObjectClass* object_class = G_OBJECT_CLASS (klass);
+ VinagreConnectionClass* parent_class = VINAGRE_CONNECTION_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (VinagreSpiceConnectionPrivate));
+
+ object_class->finalize = vinagre_spice_connection_finalize;
+ object_class->dispose = vinagre_spice_connection_dispose;
+ object_class->set_property = vinagre_spice_connection_set_property;
+ object_class->get_property = vinagre_spice_connection_get_property;
+ object_class->constructed = vinagre_spice_connection_constructed;
+
+ parent_class->impl_fill_writer = spice_fill_writer;
+ parent_class->impl_parse_item = spice_parse_item;
+ parent_class->impl_get_best_name = spice_get_best_name;
+ parent_class->impl_parse_options_widget = spice_parse_options_widget;
+
+ g_object_class_install_property (object_class,
+ PROP_DESKTOP_NAME,
+ g_param_spec_string ("desktop-name",
+ "desktop-name",
+ "name of this connection as reported by the server",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_VIEW_ONLY,
+ g_param_spec_boolean ("view-only",
+ "View-only connection",
+ "Whether this connection is a view-only one",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SCALING,
+ g_param_spec_boolean ("scaling",
+ "Use scaling",
+ "Whether to use scaling on this connection",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_RESIZE_GUEST,
+ g_param_spec_boolean ("resize-guest",
+ "Resize guest",
+ "Whether to use guest resize on this connection",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_RESIZE_GUEST,
+ g_param_spec_boolean ("auto-clipboard",
+ "Auto clipboard",
+ "Whether clipboard sharing is automatic",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_FD,
+ g_param_spec_int ("fd",
+ "file descriptor",
+ "the file descriptor for this connection",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SSH_TUNNEL_HOST,
+ g_param_spec_string ("ssh-tunnel-host",
+ "SSH Tunnel Host",
+ "hostname used to create the SSH tunnel",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class,
+ PROP_SOCKET,
+ g_param_spec_object ("socket",
+ "Socket",
+ "A GSocket for this connection",
+ G_TYPE_SOCKET,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+}
+
+VinagreConnection *
+vinagre_spice_connection_new (void)
+{
+ return VINAGRE_CONNECTION (g_object_new (VINAGRE_TYPE_SPICE_CONNECTION, NULL));
+}
+
+void
+vinagre_spice_connection_set_desktop_name (VinagreSpiceConnection *conn,
+ const gchar *desktop_name)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+
+ g_free (conn->priv->desktop_name);
+ conn->priv->desktop_name = g_strdup (desktop_name);
+}
+
+const gchar *
+vinagre_spice_connection_get_desktop_name (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), NULL);
+
+ return conn->priv->desktop_name;
+}
+
+void
+vinagre_spice_connection_set_view_only (VinagreSpiceConnection *conn,
+ gboolean value)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+
+ conn->priv->view_only = value;
+}
+gboolean
+vinagre_spice_connection_get_view_only (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), FALSE);
+
+ return conn->priv->view_only;
+}
+
+void
+vinagre_spice_connection_set_scaling (VinagreSpiceConnection *conn,
+ gboolean value)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+
+ conn->priv->scaling = value;
+}
+gboolean
+vinagre_spice_connection_get_scaling (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), FALSE);
+
+ return conn->priv->scaling;
+}
+
+void
+vinagre_spice_connection_set_resize_guest (VinagreSpiceConnection *conn,
+ gboolean value)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+
+ conn->priv->resize_guest = value;
+}
+gboolean
+vinagre_spice_connection_get_resize_guest (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), FALSE);
+
+ return conn->priv->resize_guest;
+}
+
+void
+vinagre_spice_connection_set_auto_clipboard (VinagreSpiceConnection *conn,
+ gboolean value)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+
+ conn->priv->auto_clipboard = value;
+}
+
+gboolean
+vinagre_spice_connection_get_auto_clipboard (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), FALSE);
+
+ return conn->priv->auto_clipboard;
+}
+
+void
+vinagre_spice_connection_set_fd (VinagreSpiceConnection *conn,
+ gint value)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+ g_return_if_fail (value >= 0);
+
+ conn->priv->fd = value;
+}
+
+gint
+vinagre_spice_connection_get_fd (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), 0);
+
+ if (conn->priv->socket)
+ return g_socket_get_fd (conn->priv->socket);
+ else
+ return conn->priv->fd;
+}
+
+void
+vinagre_spice_connection_set_socket (VinagreSpiceConnection *conn,
+ GSocket *socket)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+
+ if (socket)
+ conn->priv->socket = g_object_ref (socket);
+}
+
+GSocket *
+vinagre_spice_connection_get_socket (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), 0);
+
+ return conn->priv->socket;
+}
+
+void
+vinagre_spice_connection_set_ssh_tunnel_host (VinagreSpiceConnection *conn,
+ const gchar *host)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn));
+
+ g_free (conn->priv->ssh_tunnel_host);
+ conn->priv->ssh_tunnel_host = g_strdup (host);
+}
+
+const gchar *
+vinagre_spice_connection_get_ssh_tunnel_host (VinagreSpiceConnection *conn)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_CONNECTION (conn), NULL);
+
+ return conn->priv->ssh_tunnel_host;
+}
+
+/* vim: set ts=8: */
diff --git a/plugins/spice/vinagre-spice-connection.h b/plugins/spice/vinagre-spice-connection.h
new file mode 100644
index 0000000..31165a7
--- /dev/null
+++ b/plugins/spice/vinagre-spice-connection.h
@@ -0,0 +1,92 @@
+/*
+ * vinagre-spice-connection.h
+ * Child class of abstract VinagreConnection, specific to SPICE protocol
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Author: Marc-Andre Lureau <marcandre lureau redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_SPICE_CONNECTION_H__
+#define __VINAGRE_SPICE_CONNECTION_H__
+
+#include <vinagre/vinagre-connection.h>
+
+G_BEGIN_DECLS
+
+#define VINAGRE_TYPE_SPICE_CONNECTION (vinagre_spice_connection_get_type ())
+#define VINAGRE_SPICE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VINAGRE_TYPE_SPICE_CONNECTION, VinagreSpiceConnection))
+#define VINAGRE_SPICE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VINAGRE_TYPE_SPICE_CONNECTION, VinagreSpiceConnectionClass))
+#define VINAGRE_IS_SPICE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VINAGRE_TYPE_SPICE_CONNECTION))
+#define VINAGRE_IS_SPICE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VINAGRE_TYPE_SPICE_CONNECTION))
+#define VINAGRE_SPICE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VINAGRE_TYPE_SPICE_CONNECTION, VinagreSpiceConnectionClass))
+
+typedef struct _VinagreSpiceConnectionClass VinagreSpiceConnectionClass;
+typedef struct _VinagreSpiceConnection VinagreSpiceConnection;
+typedef struct _VinagreSpiceConnectionPrivate VinagreSpiceConnectionPrivate;
+
+struct _VinagreSpiceConnectionClass
+{
+ VinagreConnectionClass parent_class;
+};
+
+struct _VinagreSpiceConnection
+{
+ VinagreConnection parent_instance;
+ VinagreSpiceConnectionPrivate *priv;
+};
+
+
+GType vinagre_spice_connection_get_type (void) G_GNUC_CONST;
+
+VinagreConnection* vinagre_spice_connection_new (void);
+
+const gchar* vinagre_spice_connection_get_desktop_name (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_desktop_name (VinagreSpiceConnection *conn,
+ const gchar *desktop_name);
+
+gboolean vinagre_spice_connection_get_view_only (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_view_only (VinagreSpiceConnection *conn,
+ gboolean value);
+
+gboolean vinagre_spice_connection_get_scaling (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_scaling (VinagreSpiceConnection *conn,
+ gboolean value);
+
+gboolean vinagre_spice_connection_get_resize_guest (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_resize_guest (VinagreSpiceConnection *conn,
+ gboolean value);
+
+gint vinagre_spice_connection_get_fd (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_fd (VinagreSpiceConnection *conn,
+ gint value);
+
+GSocket * vinagre_spice_connection_get_socket (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_socket (VinagreSpiceConnection *conn,
+ GSocket *socket);
+
+const gchar* vinagre_spice_connection_get_ssh_tunnel_host (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_ssh_tunnel_host (VinagreSpiceConnection *conn,
+ const gchar *host);
+
+gboolean vinagre_spice_connection_get_auto_clipboard (VinagreSpiceConnection *conn);
+void vinagre_spice_connection_set_auto_clipboard (VinagreSpiceConnection *conn,
+ gboolean value);
+
+G_END_DECLS
+
+#endif /* __VINAGRE_SPICE_CONNECTION_H__ */
+/* vim: set ts=8: */
diff --git a/plugins/spice/vinagre-spice-plugin.c b/plugins/spice/vinagre-spice-plugin.c
new file mode 100644
index 0000000..2a1c182
--- /dev/null
+++ b/plugins/spice/vinagre-spice-plugin.c
@@ -0,0 +1,360 @@
+/*
+ * vinagre-spice-plugin.c
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Author: Marc-Andre Lureau <marcandre lureau redhat com>
+ *
+ * vinagre-spice-plugin.c 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.
+ *
+ * vinagre-spice-plugin.c 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include <vinagre/vinagre-cache-prefs.h>
+#include <vinagre/vinagre-protocol.h>
+
+#include "vinagre-spice-plugin.h"
+#include "vinagre-spice-connection.h"
+#include "vinagre-spice-tab.h"
+
+static void vinagre_spice_protocol_iface_init (VinagreProtocolInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (VinagreSpicePlugin,
+ vinagre_spice_plugin,
+ PEAS_TYPE_EXTENSION_BASE,
+ 0,
+ G_IMPLEMENT_INTERFACE_DYNAMIC (VINAGRE_TYPE_PROTOCOL,
+ vinagre_spice_protocol_iface_init))
+
+static const gchar *
+impl_get_protocol (VinagreProtocol *plugin)
+{
+ return "spice";
+}
+
+static const gchar *
+impl_get_mdns_service (VinagreProtocol *plugin)
+{
+ return "_spice._tcp";
+}
+
+static VinagreConnection *
+impl_new_connection (VinagreProtocol *plugin)
+{
+ VinagreConnection *conn;
+
+ conn = vinagre_spice_connection_new ();
+
+ return conn;
+}
+
+static VinagreConnection *
+impl_new_connection_from_file (VinagreProtocol *plugin,
+ const gchar *data,
+ gboolean use_bookmarks,
+ gchar **error_msg)
+{
+ GKeyFile *file;
+ GError *error;
+ gboolean loaded;
+ gchar *host, *actual_host, *protocol;
+ gint port;
+ VinagreConnection *conn;
+
+ *error_msg = NULL;
+ conn = NULL;
+ host = NULL;
+ protocol = NULL;
+ error = NULL;
+
+ file = g_key_file_new ();
+ loaded = g_key_file_load_from_data (file,
+ data,
+ -1,
+ 0,
+ &error);
+ if (!loaded)
+ {
+ if (error)
+ {
+ *error_msg = g_strdup (error->message);
+ g_error_free (error);
+ }
+ else
+ *error_msg = g_strdup (_("Could not parse the file."));
+
+ goto the_end;
+ }
+ if (!g_key_file_has_group (file, "connection"))
+ {
+ /* Translators: Do not translate "connection". It's the name of a group in the .spice (.ini like) file. */
+ *error_msg = g_strdup (_("The file is not a Spice one: Missing the group \"connection\"."));
+ goto the_end;
+ }
+
+ if (!g_key_file_has_key (file, "connection", "host", NULL))
+ {
+ /* Translators: Do not translate "host". It's the name of a key in the .spice (.ini like) file. */
+ *error_msg = g_strdup (_("The file is not a Spice one: Missing the key \"host\"."));
+ goto the_end;
+ }
+
+ host = g_key_file_get_string (file, "connection", "host", NULL);
+ port = g_key_file_get_integer (file, "connection", "port", NULL);
+ if (!port)
+ {
+ if (!vinagre_connection_split_string (host, "spice", &protocol, &actual_host, &port, error_msg))
+ goto the_end;
+
+ g_free (host);
+ host = actual_host;
+ }
+
+ if (use_bookmarks)
+ conn = vinagre_bookmarks_exists (vinagre_bookmarks_get_default (), "spice", host, port);
+ if (!conn)
+ {
+ gchar *s_value;
+
+ conn = vinagre_spice_connection_new ();
+ vinagre_connection_set_host (conn, host);
+ vinagre_connection_set_port (conn, port);
+
+ s_value = g_key_file_get_string (file, "connection", "password", NULL);
+ vinagre_connection_set_password (conn, s_value);
+ g_free (s_value);
+ }
+
+ the_end:
+
+ g_free (host);
+ g_free (protocol);
+ g_key_file_free (file);
+ return conn;
+
+}
+
+static gchar **
+impl_get_public_description (VinagreProtocol *plugin)
+{
+ gchar **result = g_new (gchar *, 3);
+
+ result[0] = g_strdup (_("SPICE"));
+ /* Translators: This is a description of the SPICE protocol. It appears at Connect dialog. */
+ result[1] = g_strdup (_("Access Spice desktop server"));
+ result[2] = NULL;
+
+ return result;
+}
+
+static gint
+impl_get_default_port (VinagreProtocol *plugin)
+{
+ return 5900;
+}
+
+static void
+vinagre_spice_plugin_init (VinagreSpicePlugin *plugin)
+{
+}
+
+static GtkWidget *
+impl_new_tab (VinagreProtocol *plugin,
+ VinagreConnection *conn,
+ VinagreWindow *window)
+{
+ return vinagre_spice_tab_new (conn, window);
+}
+
+static void
+ssh_tunnel_check_toggled_cb (GtkToggleButton *button, GObject *box)
+{
+ gboolean active = gtk_toggle_button_get_active (button);
+ GtkWidget *ssh_host_entry = g_object_get_data (G_OBJECT (box), "ssh_host");
+
+ gtk_widget_set_sensitive (ssh_host_entry, active);
+
+ if (active)
+ gtk_widget_grab_focus (ssh_host_entry);
+ else
+ gtk_entry_set_text (GTK_ENTRY (ssh_host_entry), "");
+}
+
+static GtkWidget *
+impl_get_connect_widget (VinagreProtocol *plugin, VinagreConnection *conn)
+{
+ GtkWidget *box, *label, *check, *p_entry, *box2, *ssh_host_entry;
+ GtkTable *table;
+ gchar *str, *ssh_host;
+ gboolean has_conn = VINAGRE_IS_SPICE_CONNECTION (conn);
+
+ box = gtk_vbox_new (FALSE, 0);
+
+ str = g_strdup_printf ("<b>%s</b>", _("SPICE Options"));
+ label = gtk_label_new (str);
+ g_free (str);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_misc_set_padding (GTK_MISC (label), 0, 6);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+
+ table = GTK_TABLE (gtk_table_new (6, 2, FALSE));
+ label = gtk_label_new (" ");
+ gtk_table_attach (table, label, 0, 1, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ /* View only check button - not fully ready on spice-gtk side */
+ check = gtk_check_button_new_with_mnemonic (_("_View only"));
+ g_object_set_data (G_OBJECT (box), "view_only", check);
+ /* gtk_table_attach_defaults (table, check, 1, 2, 0, 1); */
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ has_conn ? vinagre_spice_connection_get_view_only (VINAGRE_SPICE_CONNECTION (conn))
+ : vinagre_cache_prefs_get_boolean ("spice-connection", "view-only", FALSE));
+
+ /* Resize guest check button */
+ check = gtk_check_button_new_with_mnemonic (_("_Resize guest"));
+ g_object_set_data (G_OBJECT (box), "resize_guest", check);
+ gtk_table_attach_defaults (table, check, 1, 2, 0, 1);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ has_conn ? vinagre_spice_connection_get_resize_guest (VINAGRE_SPICE_CONNECTION (conn))
+ : vinagre_cache_prefs_get_boolean ("spice-connection", "resize-guest", TRUE));
+
+ /* Clipboard sharing check button */
+ check = gtk_check_button_new_with_mnemonic (_("_Share clipboard"));
+ g_object_set_data (G_OBJECT (box), "auto_clipboard", check);
+ gtk_table_attach_defaults (table, check, 1, 2, 1, 2);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ has_conn ? vinagre_spice_connection_get_auto_clipboard (VINAGRE_SPICE_CONNECTION (conn))
+ : vinagre_cache_prefs_get_boolean ("spice-connection", "auto-clipboard", TRUE));
+
+ /* Scaling check button */
+ check = gtk_check_button_new_with_mnemonic (_("_Scaling"));
+ g_object_set_data (G_OBJECT (box), "scaling", check);
+ gtk_table_attach_defaults (table, check, 1, 2, 2, 3);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ has_conn ? vinagre_spice_connection_get_scaling (VINAGRE_SPICE_CONNECTION (conn))
+ : vinagre_cache_prefs_get_boolean ("spice-connection", "scaling", FALSE));
+
+ /* SSH Tunneling */
+ box2 = gtk_hbox_new (FALSE, 5);
+
+ /* Translators: the whole sentence will be: Use host <hostname> as a SSH tunnel*/
+ check = gtk_check_button_new_with_mnemonic (_("Use h_ost"));
+ g_object_set_data (G_OBJECT (box), "use_ssh", check);
+ gtk_box_pack_start (GTK_BOX (box2), check, FALSE, FALSE, 0);
+
+ ssh_host_entry = gtk_entry_new ();
+ gtk_widget_set_sensitive (ssh_host_entry, FALSE);
+ g_object_set_data (G_OBJECT (box), "ssh_host", ssh_host_entry);
+ /* Translators: This is the tooltip of the SSH tunneling entry */
+ str = g_strdup_printf ("%s\n%s\n%s",
+ _("hostname or user hostname"),
+ _("Supply an alternative port using colon"),
+ _("For instance: joe domain com:5022"));
+ gtk_widget_set_tooltip_text (ssh_host_entry, str);
+ g_free (str);
+ gtk_box_pack_start (GTK_BOX (box2), ssh_host_entry, FALSE, FALSE, 0);
+
+ /* Translators: the whole sentence will be: Use host <hostname> as a SSH tunnel*/
+ label = gtk_label_new_with_mnemonic (_("as a SSH tunnel"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
+
+ g_signal_connect (check,
+ "toggled",
+ G_CALLBACK (ssh_tunnel_check_toggled_cb),
+ box);
+
+ ssh_host = has_conn ? g_strdup (vinagre_spice_connection_get_ssh_tunnel_host (VINAGRE_SPICE_CONNECTION (conn)))
+ : vinagre_cache_prefs_get_string ("spice-connection", "ssh-tunnel-host", NULL);
+ if (ssh_host)
+ gtk_entry_set_text (GTK_ENTRY (ssh_host_entry), ssh_host);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), ssh_host && *ssh_host);
+ g_free (ssh_host);
+
+ gtk_table_attach_defaults (table, box2, 1, 2, 3, 4);
+
+ /* Password */
+ box2 = gtk_hbox_new (FALSE, 4);
+
+ label = gtk_label_new_with_mnemonic (_("_Password:"));
+ gtk_box_pack_start (GTK_BOX (box2), label, FALSE, FALSE, 0);
+
+ p_entry = gtk_entry_new ();
+ /* Translators: This is the tooltip for the password field in a SPICE connection */
+ gtk_widget_set_tooltip_text (p_entry, _("Optional"));
+ g_object_set_data (G_OBJECT (box), "password_entry", p_entry);
+ gtk_box_pack_start (GTK_BOX (box2), p_entry, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), p_entry);
+ str = g_strdup (vinagre_cache_prefs_get_string ("spice-connection", "password", ""));
+ gtk_entry_set_text (GTK_ENTRY (p_entry), str);
+ gtk_entry_set_activates_default (GTK_ENTRY (p_entry), TRUE);
+ g_free (str);
+ /* FIXME: how do you set the password here? gtk_table_attach_defaults (table, box2, 1, 2, 4, 5); */
+
+ gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (table), FALSE, FALSE, 0);
+ return box;
+}
+
+static GtkFileFilter *
+impl_get_file_filter (VinagreProtocol *plugin)
+{
+ GtkFileFilter *filter;
+
+ filter = gtk_file_filter_new ();
+ /* Translators: this is a pattern to open *.spice files in a open dialog. */
+ gtk_file_filter_set_name (filter, _("Spice Files"));
+ gtk_file_filter_add_pattern (filter, "*.spice");
+
+ return filter;
+}
+
+static void
+vinagre_spice_protocol_iface_init (VinagreProtocolInterface *iface)
+{
+ iface->get_protocol = impl_get_protocol;
+ iface->get_public_description = impl_get_public_description;
+ iface->get_default_port = impl_get_default_port;
+ iface->get_connect_widget = impl_get_connect_widget;
+ iface->get_mdns_service = impl_get_mdns_service;
+ iface->get_file_filter = impl_get_file_filter;
+ iface->new_connection = impl_new_connection;
+ iface->new_tab = impl_new_tab;
+ iface->new_connection_from_file = impl_new_connection_from_file;
+}
+
+static void
+vinagre_spice_plugin_class_finalize (VinagreSpicePluginClass *klass)
+{
+}
+
+static void
+vinagre_spice_plugin_class_init (VinagreSpicePluginClass *klass)
+{
+}
+
+G_MODULE_EXPORT void
+peas_register_types (PeasObjectModule *module)
+{
+ vinagre_spice_plugin_register_type (G_TYPE_MODULE (module));
+ peas_object_module_register_extension_type (module,
+ VINAGRE_TYPE_PROTOCOL,
+ VINAGRE_TYPE_SPICE_PLUGIN);
+}
+
+/* vim: set ts=8: */
diff --git a/plugins/spice/vinagre-spice-plugin.h b/plugins/spice/vinagre-spice-plugin.h
new file mode 100644
index 0000000..55bdf72
--- /dev/null
+++ b/plugins/spice/vinagre-spice-plugin.h
@@ -0,0 +1,62 @@
+/*
+ * vinagre-spice-plugin.h
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Author: Marc-Andre Lureau <marcandre lureau redhat com>
+ *
+ * vinagre-spice-plugin.h 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.
+ *
+ * vinagre-spice-plugin.h 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_SPICE_PLUGIN_H__
+#define __VINAGRE_SPICE_PLUGIN_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libpeas/peas.h>
+
+G_BEGIN_DECLS
+
+/*
+ * Type checking and casting macros
+ */
+#define VINAGRE_TYPE_SPICE_PLUGIN (vinagre_spice_plugin_get_type ())
+#define VINAGRE_SPICE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), VINAGRE_TYPE_SPICE_PLUGIN, VinagreSpicePlugin))
+#define VINAGRE_SPICE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), VINAGRE_TYPE_SPICE_PLUGIN, VinagreSpicePluginClass))
+#define VINAGRE_IS_SPICE_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), VINAGRE_TYPE_SPICE_PLUGIN))
+#define VINAGRE_IS_SPICE_PLUGIN_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), VINAGRE_TYPE_SPICE_PLUGIN))
+#define VINAGRE_SPICE_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), VINAGRE_TYPE_SPICE_PLUGIN, VinagreSpicePluginClass))
+
+typedef struct _VinagreSpicePlugin VinagreSpicePlugin;
+typedef struct _VinagreSpicePluginClass VinagreSpicePluginClass;
+
+struct _VinagreSpicePlugin
+{
+ PeasExtensionBase parent_instance;
+};
+
+struct _VinagreSpicePluginClass
+{
+ PeasExtensionBaseClass parent_class;
+};
+
+GType vinagre_spice_plugin_get_type (void) G_GNUC_CONST;
+
+G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module);
+
+G_END_DECLS
+
+#endif /* __VINAGRE_SPICE_PLUGIN_H__ */
+
+/* vim: set ts=8: */
diff --git a/plugins/spice/vinagre-spice-tab.c b/plugins/spice/vinagre-spice-tab.c
new file mode 100644
index 0000000..921a3d6
--- /dev/null
+++ b/plugins/spice/vinagre-spice-tab.c
@@ -0,0 +1,753 @@
+/*
+ * vinagre-spice-tab.c
+ * SPICE Implementation for VinagreSpiceTab widget
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Author: Marc-Andre Lureau <marcandre lureau redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <spice-client-glib-2.0/spice-session.h>
+#include <spice-client-glib-2.0/spice-audio.h>
+#include <spice-client-gtk-3.0/spice-widget.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <vinagre/vinagre-utils.h>
+#include <vinagre/vinagre-prefs.h>
+
+#include "vinagre-spice-tab.h"
+#include "vinagre-spice-connection.h"
+#include "vinagre-spice-tunnel.h"
+
+#define VINAGRE_SPICE_TAB_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), VINAGRE_TYPE_SPICE_TAB, VinagreSpiceTabPrivate))
+
+typedef struct _VinagreSpiceDisplay
+{
+ GtkWidget *display;
+} VinagreSpiceDisplay;
+
+struct _VinagreSpiceTabPrivate
+{
+ SpiceSession *spice;
+ SpiceAudio *audio;
+ GtkWidget *display, *align;
+ gboolean mouse_grabbed;
+ GSList *connected_actions, *initialized_actions;
+ GtkWidget *viewonly_button, *scaling_button;
+ GtkAction *scaling_action, *viewonly_action, *original_size_action, *resize_guest_action, *auto_clipboard_action;
+ gulong signal_align;
+ VinagreSpiceDisplay *wins[4]; /* TODO: handle multi-display */
+};
+
+G_DEFINE_TYPE (VinagreSpiceTab, vinagre_spice_tab, VINAGRE_TYPE_TAB)
+
+/* Properties */
+enum
+{
+ PROP_0,
+};
+
+static void open_spice (VinagreSpiceTab *spice_tab);
+static void setup_toolbar (VinagreSpiceTab *spice_tab);
+
+static void
+vinagre_spice_tab_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+view_scaling_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
+{
+ vinagre_spice_tab_set_scaling (spice_tab,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+static void
+view_resize_guest_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
+{
+ vinagre_spice_tab_set_resize_guest (spice_tab,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+static void
+view_auto_clipboard_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
+{
+ vinagre_spice_tab_set_auto_clipboard (spice_tab,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+static void
+view_viewonly_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
+{
+ vinagre_spice_tab_set_viewonly (spice_tab,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+static void
+send_ctrlaltdel_cb (GtkAction *action, VinagreSpiceTab *spice_tab)
+{
+ vinagre_spice_tab_send_ctrlaltdel (spice_tab);
+}
+
+const static GSList *
+spice_get_connected_actions (VinagreTab *tab)
+{
+ VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (tab);
+
+ return spice_tab->priv->connected_actions;
+}
+
+const static GSList *
+spice_get_initialized_actions (VinagreTab *tab)
+{
+ VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (tab);
+
+ return spice_tab->priv->initialized_actions;
+}
+
+static gchar *
+spice_tab_get_tooltip (VinagreTab *tab)
+{
+ VinagreConnection *conn = vinagre_tab_get_conn (tab);
+
+ return g_markup_printf_escaped ("<b>%s</b> %s\n\n"
+ "<b>%s</b> %d\n",
+ _("Host:"), vinagre_connection_get_host (conn),
+ _("Port:"), vinagre_connection_get_port (conn));
+}
+
+static void
+vinagre_spice_tab_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->finalize (object);
+}
+
+static void
+vinagre_spice_tab_dispose (GObject *object)
+{
+ VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (object);
+
+ if (spice_tab->priv->connected_actions)
+ {
+ vinagre_tab_free_actions (spice_tab->priv->connected_actions);
+ spice_tab->priv->connected_actions = NULL;
+ }
+
+ if (spice_tab->priv->initialized_actions)
+ {
+ vinagre_tab_free_actions (spice_tab->priv->initialized_actions);
+ spice_tab->priv->initialized_actions = NULL;
+ }
+
+ if (spice_tab->priv->audio)
+ {
+ g_object_unref (spice_tab->priv->audio);
+ spice_tab->priv->audio = NULL;
+ }
+
+ if (spice_tab->priv->spice)
+ {
+ spice_session_disconnect (spice_tab->priv->spice);
+ g_object_unref (spice_tab->priv->spice);
+ spice_tab->priv->spice = NULL;
+ }
+
+ G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->dispose (object);
+}
+
+static void
+vinagre_spice_tab_constructed (GObject *object)
+{
+ VinagreSpiceTab *spice_tab = VINAGRE_SPICE_TAB (object);
+
+ if (G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->constructed)
+ G_OBJECT_CLASS (vinagre_spice_tab_parent_class)->constructed (object);
+
+ setup_toolbar (spice_tab);
+ open_spice (spice_tab);
+}
+
+static void
+vinagre_spice_tab_class_init (VinagreSpiceTabClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ VinagreTabClass* tab_class = VINAGRE_TAB_CLASS (klass);
+
+ object_class->finalize = vinagre_spice_tab_finalize;
+ object_class->dispose = vinagre_spice_tab_dispose;
+ object_class->get_property = vinagre_spice_tab_get_property;
+ object_class->constructed = vinagre_spice_tab_constructed;
+
+ tab_class->impl_get_tooltip = spice_tab_get_tooltip;
+ tab_class->impl_get_connected_actions = spice_get_connected_actions;
+ tab_class->impl_get_initialized_actions = spice_get_initialized_actions;
+
+ g_type_class_add_private (object_class, sizeof (VinagreSpiceTabPrivate));
+}
+
+static gboolean
+idle_close (VinagreTab *tab)
+{
+ vinagre_notebook_close_tab (vinagre_tab_get_notebook (tab), tab);
+ return FALSE;
+}
+
+static void
+open_spice (VinagreSpiceTab *spice_tab)
+{
+ gchar *host, *port_str, *ssh_tunnel_host;
+ gint port, fd;
+ gboolean scaling, success;
+ GError *error;
+ SpiceSession *spice = spice_tab->priv->spice;
+ VinagreTab *tab = VINAGRE_TAB (spice_tab);
+ GtkWindow *window = GTK_WINDOW (vinagre_tab_get_window (tab));
+ VinagreConnection *conn = vinagre_tab_get_conn (tab);
+
+ success = TRUE;
+ error = NULL;
+
+ g_object_get (conn,
+ "port", &port,
+ "host", &host,
+ "scaling", &scaling,
+ "fd", &fd,
+ "ssh-tunnel-host", &ssh_tunnel_host,
+ NULL);
+
+ port_str = g_strdup_printf ("%d", port);
+
+ if (fd > 0)
+ success = spice_session_open_fd (spice, fd);
+ else
+ {
+ if (ssh_tunnel_host && *ssh_tunnel_host)
+ if (!vinagre_spice_tunnel_create (window, &host, &port_str, ssh_tunnel_host, &error))
+ {
+ success = FALSE;
+ vinagre_utils_show_error (_("Error creating the SSH tunnel"),
+ error ? error->message : _("Unknown reason"),
+ window);
+ goto out;
+ }
+ g_object_set (spice, "host", host, "port", port_str,
+ "password", vinagre_connection_get_password (conn),
+ NULL);
+ success = spice_session_connect (spice);
+ }
+
+ if (!success)
+ vinagre_utils_show_error (_("Error connecting to host."),
+ error ? error->message : _("Unknown reason"),
+ window);
+
+ out:
+ g_free (port_str);
+ g_free (host);
+ g_free (ssh_tunnel_host);
+ g_clear_error (&error);
+
+ if (!success)
+ g_idle_add ((GSourceFunc)idle_close, spice_tab);
+}
+
+static void
+spice_main_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event,
+ VinagreSpiceTab *spice_tab)
+{
+ switch (event) {
+ case SPICE_CHANNEL_OPENED:
+ g_signal_emit_by_name (G_OBJECT (spice_tab), "tab-connected");
+ break;
+ case SPICE_CHANNEL_CLOSED:
+ g_signal_emit_by_name (G_OBJECT (spice_tab), "tab-disconnected");
+ break;
+ case SPICE_CHANNEL_ERROR_AUTH: {
+ VinagreTab *tab = VINAGRE_TAB (spice_tab);
+ VinagreConnection *conn = vinagre_tab_get_conn (tab);
+ gchar *name = vinagre_connection_get_best_name (conn), *password = NULL;
+ GtkWindow *window = GTK_WINDOW (vinagre_tab_get_window (tab));
+ gboolean save_in_keyring = FALSE;
+
+ vinagre_tab_remove_credentials_from_keyring (VINAGRE_TAB (tab));
+ if (!vinagre_utils_ask_credential (window,
+ "SPICE",
+ name,
+ FALSE,
+ TRUE,
+ 8,
+ NULL,
+ &password,
+ &save_in_keyring))
+ {
+ vinagre_tab_remove_from_notebook (tab);
+ goto out;
+ }
+ vinagre_connection_set_password (conn, password);
+ vinagre_tab_set_save_credentials (tab, save_in_keyring);
+
+ g_object_set (spice_tab->priv->spice, "password", password, NULL);
+ spice_session_connect (spice_tab->priv->spice);
+
+ out:
+ g_free (password);
+ break;
+ }
+ case SPICE_CHANNEL_ERROR_IO:
+ case SPICE_CHANNEL_ERROR_TLS:
+ case SPICE_CHANNEL_ERROR_LINK:
+ case SPICE_CHANNEL_ERROR_CONNECT:
+ g_signal_emit_by_name (G_OBJECT (spice_tab), "tab-disconnected");
+ break;
+ default:
+ g_warning("unhandled main channel event: %d", event);
+ break;
+ }
+}
+
+static void
+spice_mouse_grab_cb(GtkWidget *widget, gint grabbed, VinagreSpiceTab *spice_tab)
+{
+ spice_tab->priv->mouse_grabbed = grabbed;
+}
+
+static VinagreSpiceDisplay *
+create_spice_display (VinagreSpiceTab *spice_tab, int id)
+{
+ VinagreSpiceDisplay *d;
+ GtkLabel *label;
+ gchar *name;
+ gboolean scaling, resize_guest, view_only, fullscreen, auto_clipboard;
+ VinagreTab *tab = VINAGRE_TAB (spice_tab);
+ VinagreConnection *conn = vinagre_tab_get_conn (tab);
+
+ d = g_new0(VinagreSpiceDisplay, 1);
+
+ /* Create the display widget */
+ spice_tab->priv->display = d->display = GTK_WIDGET (spice_display_new (spice_tab->priv->spice, id));
+
+ vinagre_tab_add_view (tab, d->display);
+ vinagre_tab_set_has_screenshot (tab, TRUE);
+
+ g_signal_connect (spice_tab->priv->display, "mouse-grab",
+ G_CALLBACK (spice_mouse_grab_cb), spice_tab);
+
+ g_object_get (conn,
+ "fullscreen", &fullscreen,
+ "resize-guest", &resize_guest,
+ "auto-clipboard", &auto_clipboard,
+ "scaling", &scaling,
+ "view-only", &view_only,
+ NULL);
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->scaling_action), scaling);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->resize_guest_action), resize_guest);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->auto_clipboard_action), auto_clipboard);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->viewonly_action), view_only);
+ g_object_set (d->display,
+ "grab-keyboard", TRUE,
+ "grab-mouse", TRUE,
+ "resize-guest", resize_guest,
+ "auto-clipboard", auto_clipboard,
+ "scaling", scaling,
+ NULL);
+ /* TODO: add view-only here when spice-gtk ready */
+
+ g_object_get (spice_tab->priv->spice, "uri", &name, NULL); /* TODO: a better friendly name? */
+ vinagre_spice_connection_set_desktop_name (VINAGRE_SPICE_CONNECTION (conn), name);
+ g_free (name);
+
+ name = vinagre_connection_get_best_name (conn);
+ label = g_object_get_data (G_OBJECT (tab), "label");
+ g_return_val_if_fail (label != NULL, d);
+ gtk_label_set_label (label, name);
+ g_free (name);
+
+ vinagre_tab_save_credentials_in_keyring (tab);
+ vinagre_tab_add_recent_used (tab);
+ vinagre_tab_set_state (tab, VINAGRE_TAB_STATE_CONNECTED);
+
+ g_signal_emit_by_name (G_OBJECT (tab), "tab-initialized");
+
+ gtk_widget_show_all (GTK_WIDGET (tab));
+
+ return d;
+}
+
+static void
+destroy_spice_display (VinagreSpiceTab *tab, VinagreSpiceDisplay *display)
+{
+}
+
+static void
+spice_channel_new_cb (SpiceSession *s, SpiceChannel *channel, VinagreSpiceTab *tab)
+{
+ int id;
+
+ g_object_get (channel, "channel-id", &id, NULL);
+ g_object_ref (tab);
+
+ if (SPICE_IS_MAIN_CHANNEL (channel)) {
+ g_signal_connect (channel, "channel-event",
+ G_CALLBACK (spice_main_channel_event_cb), tab);
+ }
+
+ if (SPICE_IS_DISPLAY_CHANNEL (channel)) {
+ if (id >= G_N_ELEMENTS (tab->priv->wins))
+ return;
+ if (tab->priv->wins[id] != NULL)
+ return;
+ tab->priv->wins[id] = create_spice_display (tab, id);
+ }
+
+ if (SPICE_IS_PLAYBACK_CHANNEL (channel) ||
+ SPICE_IS_RECORD_CHANNEL (channel)) {
+ if (tab->priv->audio != NULL)
+ return;
+ tab->priv->audio = spice_audio_new (s, NULL, NULL);
+ }
+}
+
+static void
+spice_channel_destroy_cb (SpiceSession *s, SpiceChannel *channel, VinagreSpiceTab *tab)
+{
+ int id;
+
+ g_object_get (channel, "channel-id", &id, NULL);
+ g_object_unref (tab);
+
+ if (SPICE_IS_DISPLAY_CHANNEL (channel)) {
+ if (id >= G_N_ELEMENTS (tab->priv->wins))
+ return;
+ if (tab->priv->wins[id] == NULL)
+ return;
+ destroy_spice_display (tab, tab->priv->wins[id]);
+ tab->priv->wins[id] = NULL;
+ }
+}
+
+static GSList *
+create_connected_actions (VinagreSpiceTab *tab)
+{
+ GSList *list = NULL;
+ VinagreTabUiAction *a;
+
+ /* View->Scaling */
+ a = g_new (VinagreTabUiAction, 1);
+ a->paths = g_new (gchar *, 3);
+ a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
+ a->paths[1] = g_strdup ("/ToolBar");
+ a->paths[2] = NULL;
+ a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewScaling",
+ _("S_caling"),
+ _("Fits the remote screen into the current window size"),
+ "zoom-fit-best"));
+ gtk_action_set_icon_name (a->action, "zoom-fit-best");
+ g_signal_connect (a->action, "activate", G_CALLBACK (view_scaling_cb), tab);
+ list = g_slist_append (list, a);
+ tab->priv->scaling_action = a->action;
+
+ /* View->Resize Guest */
+ a = g_new (VinagreTabUiAction, 1);
+ a->paths = g_new (gchar *, 2);
+ a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
+ a->paths[1] = NULL;
+ a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewResizeGuest",
+ _("_Resize guest"),
+ _("Resize the screen guest to best fit"),
+ NULL));
+ g_signal_connect (a->action, "activate", G_CALLBACK (view_resize_guest_cb), tab);
+ list = g_slist_append (list, a);
+ tab->priv->resize_guest_action = a->action;
+
+ /* View->Share clipboard */
+ a = g_new (VinagreTabUiAction, 1);
+ a->paths = g_new (gchar *, 2);
+ a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
+ a->paths[1] = NULL;
+ a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewAutoClipboard",
+ _("_Share clipboard"),
+ _("Automatically share clipboard between client and guest"),
+ NULL));
+ g_signal_connect (a->action, "activate", G_CALLBACK (view_auto_clipboard_cb), tab);
+ list = g_slist_append (list, a);
+ tab->priv->auto_clipboard_action = a->action;
+
+ /* View->View Only TODO: not ready in spice-gtk yet */
+ a = g_new (VinagreTabUiAction, 1);
+ a->paths = g_new (gchar *, 3);
+ a->paths[0] = g_strdup ("/MenuBar/ViewMenu");
+ a->paths[1] = g_strdup ("/ToolBar");
+ a->paths[2] = NULL;
+ a->action = GTK_ACTION (gtk_toggle_action_new ("SPICEViewViewOnly",
+ _("_View only"),
+ _("Does not send mouse and keyboard events"),
+ "emblem-readonly"));
+ gtk_action_set_icon_name (a->action, "emblem-readonly");
+ g_signal_connect (a->action, "activate", G_CALLBACK (view_viewonly_cb), tab);
+ /* list = g_slist_append (list, a); */
+ tab->priv->viewonly_action = a->action;
+
+ return list;
+}
+
+static GSList *
+create_initialized_actions (VinagreSpiceTab *tab)
+{
+ GSList *list = NULL;
+ VinagreTabUiAction *a;
+
+ /* Machine->Send CTRL-ALT-DEL */
+ a = g_new (VinagreTabUiAction, 1);
+ a->paths = g_new (gchar *, 3);
+ a->paths[0] = g_strdup ("/MenuBar/MachineMenu/MachineOps_1");
+ a->paths[1] = g_strdup ("/ToolBar");
+ a->paths[2] = NULL;
+ a->action = gtk_action_new ("SPICEMachineSendCtrlAltDel",
+ _("_Send Ctrl-Alt-Del"),
+ _("Sends Ctrl+Alt+Del to the remote machine"),
+ "preferences-desktop-keyboard-shortcuts");
+ gtk_action_set_is_important (a->action, TRUE);
+ gtk_action_set_icon_name (a->action, "preferences-desktop-keyboard-shortcuts");
+ g_signal_connect (a->action, "activate", G_CALLBACK (send_ctrlaltdel_cb), tab);
+ list = g_slist_append (list, a);
+
+ return list;
+}
+
+static void
+viewonly_button_clicked (GtkToggleToolButton *button,
+ VinagreSpiceTab *spice_tab)
+{
+ vinagre_spice_tab_set_viewonly (spice_tab, gtk_toggle_tool_button_get_active (button));
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->viewonly_action),
+ vinagre_spice_tab_get_viewonly (spice_tab));
+}
+
+static void
+scaling_button_clicked (GtkToggleToolButton *button,
+ VinagreSpiceTab *spice_tab)
+{
+ if (!vinagre_spice_tab_set_scaling (spice_tab, gtk_toggle_tool_button_get_active (button)))
+ gtk_toggle_tool_button_set_active (button, FALSE);
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (spice_tab->priv->scaling_action),
+ vinagre_spice_tab_get_scaling (spice_tab));
+}
+
+static void
+cad_button_clicked (GtkToolButton *button,
+ VinagreSpiceTab *spice_tab)
+{
+ vinagre_spice_tab_send_ctrlaltdel (spice_tab);
+}
+
+static void
+setup_toolbar (VinagreSpiceTab *spice_tab)
+{
+ GtkWidget *toolbar = vinagre_tab_get_toolbar (VINAGRE_TAB (spice_tab));
+ GtkWidget *button;
+
+ /* Space */
+ button = GTK_WIDGET (gtk_separator_tool_item_new ());
+ gtk_tool_item_set_expand (GTK_TOOL_ITEM (button), TRUE);
+ gtk_widget_show (GTK_WIDGET (button));
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
+
+ /* Scaling */
+ button = GTK_WIDGET (gtk_toggle_tool_button_new ());
+ gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), _("Scaling"));
+ gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (button), _("Scaling"));
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (button), "zoom-fit-best");
+ gtk_widget_show (GTK_WIDGET (button));
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
+ g_signal_connect (button, "toggled", G_CALLBACK (scaling_button_clicked), spice_tab);
+ spice_tab->priv->scaling_button = button;
+
+ /* Read only */
+ button = GTK_WIDGET (gtk_toggle_tool_button_new ());
+ gtk_tool_button_set_label (GTK_TOOL_BUTTON (button), _("Read only"));
+ gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (button), _("Read only"));
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (button), "emblem-readonly");
+ gtk_widget_show (GTK_WIDGET (button));
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
+ g_signal_connect (button, "toggled", G_CALLBACK (viewonly_button_clicked), spice_tab);
+ spice_tab->priv->viewonly_button = button;
+
+ /* Send Ctrl-alt-del */
+ button = GTK_WIDGET (gtk_tool_button_new (NULL, _("Send Ctrl-Alt-Del")));
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (button), "preferences-desktop-keyboard-shortcuts");
+ gtk_tool_item_set_tooltip_text (GTK_TOOL_ITEM (button), _("Send Ctrl-Alt-Del"));
+ g_signal_connect (button, "clicked", G_CALLBACK (cad_button_clicked), spice_tab);
+ gtk_widget_show (GTK_WIDGET (button));
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (button), -1);
+}
+
+static void
+vinagre_spice_tab_init (VinagreSpiceTab *spice_tab)
+{
+ SpiceSession *spice;
+
+ spice_tab->priv = VINAGRE_SPICE_TAB_GET_PRIVATE (spice_tab);
+ spice_tab->priv->connected_actions = create_connected_actions (spice_tab);
+ spice_tab->priv->initialized_actions = create_initialized_actions (spice_tab);
+
+ /* Create the spice widget */
+ spice = spice_tab->priv->spice = spice_session_new ();
+ g_signal_connect(spice, "channel-new",
+ G_CALLBACK(spice_channel_new_cb), spice_tab);
+ g_signal_connect(spice, "channel-destroy",
+ G_CALLBACK(spice_channel_destroy_cb), spice_tab);
+}
+
+GtkWidget *
+vinagre_spice_tab_new (VinagreConnection *conn,
+ VinagreWindow *window)
+{
+ return GTK_WIDGET (g_object_new (VINAGRE_TYPE_SPICE_TAB,
+ "conn", conn,
+ "window", window,
+ NULL));
+}
+
+void
+vinagre_spice_tab_send_ctrlaltdel (VinagreSpiceTab *tab)
+{
+ guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
+
+ g_return_if_fail (VINAGRE_IS_SPICE_TAB (tab));
+
+ spice_display_send_keys (SPICE_DISPLAY (tab->priv->display), keys, sizeof (keys) / sizeof (keys[0]), SPICE_DISPLAY_KEY_EVENT_CLICK);
+}
+
+gboolean
+vinagre_spice_tab_set_scaling (VinagreSpiceTab *tab, gboolean active)
+{
+ gboolean scaling;
+
+ g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
+
+ g_object_get (tab->priv->display, "scaling", &scaling, NULL);
+ if (scaling == active)
+ return TRUE;
+
+ g_object_set (tab->priv->display, "scaling", active, NULL);
+
+ gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (tab->priv->scaling_button),
+ active);
+
+ return TRUE;
+}
+
+gboolean
+vinagre_spice_tab_get_scaling (VinagreSpiceTab *tab)
+{
+ gboolean scaling;
+
+ g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
+
+ g_object_get (tab->priv->display, "scaling", &scaling, NULL);
+
+ return scaling;
+}
+
+void
+vinagre_spice_tab_set_viewonly (VinagreSpiceTab *tab, gboolean active)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_TAB (tab));
+
+#if 0
+ g_object_set (tab->priv->display, "read-only", active, NULL); /* only in future version of spice-gtk */
+#endif
+
+ gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (tab->priv->viewonly_button),
+ active);
+}
+
+gboolean
+vinagre_spice_tab_get_viewonly (VinagreSpiceTab *tab)
+{
+ gboolean active = FALSE;
+
+ g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
+#if 0
+ g_object_get (tab->priv->display, "read-only", &active, NULL); /* only in future version of spice-gtk */
+#endif
+ return active;
+}
+
+gboolean
+vinagre_spice_tab_set_resize_guest (VinagreSpiceTab *tab, gboolean active)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
+
+ g_object_set (tab->priv->display, "resize-guest", active, NULL);
+
+ return TRUE;
+}
+
+gboolean
+vinagre_spice_tab_get_resize_guest (VinagreSpiceTab *tab)
+{
+ gboolean active;
+
+ g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
+
+ g_object_get (tab->priv->display, "resize-guest", &active, NULL);
+
+ return active;
+}
+
+void
+vinagre_spice_tab_set_auto_clipboard (VinagreSpiceTab *tab, gboolean active)
+{
+ g_return_if_fail (VINAGRE_IS_SPICE_TAB (tab));
+
+ g_object_set (tab->priv->display, "auto-clipboard", active, NULL);
+}
+
+gboolean
+vinagre_spice_tab_get_auto_clipboard (VinagreSpiceTab *tab)
+{
+ gboolean active;
+
+ g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
+
+ g_object_get (tab->priv->display, "auto-clipboard", &active, NULL);
+
+ return active;
+}
+
+gboolean
+vinagre_spice_tab_is_mouse_grab (VinagreSpiceTab *tab)
+{
+ g_return_val_if_fail (VINAGRE_IS_SPICE_TAB (tab), FALSE);
+
+ return tab->priv->mouse_grabbed;
+}
+
+/* vim: set ts=8: */
diff --git a/plugins/spice/vinagre-spice-tab.h b/plugins/spice/vinagre-spice-tab.h
new file mode 100644
index 0000000..5d3fffa
--- /dev/null
+++ b/plugins/spice/vinagre-spice-tab.h
@@ -0,0 +1,76 @@
+/*
+ * vinagre-spice-tab.h
+ * SPICE Tab
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Author: Marc-Andre Lureau <marcandre lureau redhat com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_SPICE_TAB_H__
+#define __VINAGRE_SPICE_TAB_H__
+
+#include <vinagre/vinagre-tab.h>
+
+G_BEGIN_DECLS
+
+#define VINAGRE_TYPE_SPICE_TAB (vinagre_spice_tab_get_type())
+#define VINAGRE_SPICE_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), VINAGRE_TYPE_SPICE_TAB, VinagreSpiceTab))
+#define VINAGRE_SPICE_TAB_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), VINAGRE_TYPE_SPICE_TAB, VinagreSpiceTab const))
+#define VINAGRE_SPICE_TAB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), VINAGRE_TYPE_SPICE_TAB, VinagreSpiceTabClass))
+#define VINAGRE_IS_SPICE_TAB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), VINAGRE_TYPE_SPICE_TAB))
+#define VINAGRE_IS_SPICE_TAB_CLASS(klass)...(G_TYPE_CHECK_CLASS_TYPE ((klass), VINAGRE_TYPE_SPICE_TAB))
+#define VINAGRE_SPICE_TAB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), VINAGRE_TYPE_SPICE_TAB, VinagreSpiceTabClass))
+
+typedef struct _VinagreSpiceTabPrivate VinagreSpiceTabPrivate;
+typedef struct _VinagreSpiceTab VinagreSpiceTab;
+typedef struct _VinagreSpiceTabClass VinagreSpiceTabClass;
+
+
+struct _VinagreSpiceTab
+{
+ VinagreTab tab;
+ VinagreSpiceTabPrivate *priv;
+};
+
+struct _VinagreSpiceTabClass
+{
+ VinagreTabClass parent_class;
+};
+
+GType vinagre_spice_tab_get_type (void) G_GNUC_CONST;
+
+GtkWidget * vinagre_spice_tab_new (VinagreConnection *conn,
+ VinagreWindow *window);
+
+void vinagre_spice_tab_send_ctrlaltdel (VinagreSpiceTab *tab);
+void vinagre_spice_tab_paste_text (VinagreSpiceTab *tab,
+ const gchar *text);
+
+gboolean vinagre_spice_tab_set_scaling (VinagreSpiceTab *tab, gboolean active);
+gboolean vinagre_spice_tab_get_scaling (VinagreSpiceTab *tab);
+gboolean vinagre_spice_tab_set_resize_guest (VinagreSpiceTab *tab, gboolean active);
+gboolean vinagre_spice_tab_get_resize_guest (VinagreSpiceTab *tab);
+void vinagre_spice_tab_set_viewonly (VinagreSpiceTab *tab, gboolean active);
+gboolean vinagre_spice_tab_get_viewonly (VinagreSpiceTab *tab);
+void vinagre_spice_tab_set_auto_clipboard (VinagreSpiceTab *tab, gboolean active);
+gboolean vinagre_spice_tab_get_auto_clipboard (VinagreSpiceTab *tab);
+
+gboolean vinagre_spice_tab_is_mouse_grab (VinagreSpiceTab *tab);
+G_END_DECLS
+
+#endif /* __VINAGRE_SPICE_TAB_H__ */
+/* vim: set ts=8: */
diff --git a/plugins/spice/vinagre-spice-tunnel.c b/plugins/spice/vinagre-spice-tunnel.c
new file mode 100644
index 0000000..2759651
--- /dev/null
+++ b/plugins/spice/vinagre-spice-tunnel.c
@@ -0,0 +1,155 @@
+/*
+ * vinagre-spice-tunnel.c
+ * SSH Tunneling for Vinagre
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2009 - Jonh Wendell <wendell bani com br>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include <vinagre/vinagre-ssh.h>
+#include "vinagre-spice-tunnel.h"
+
+#define TUNNEL_PORT_OFFSET 5500
+
+static int
+find_free_port (void)
+{
+ int sock, port;
+ struct sockaddr_in6 addr;
+
+ memset (&addr, 0, sizeof (addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = in6addr_any;
+
+ sock = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0)
+ return 0;
+
+ for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--)
+ {
+ addr.sin6_port = htons (port);
+ if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == 0)
+ {
+ close (sock);
+ return port;
+ }
+ }
+
+ close (sock);
+ return 0;
+}
+
+static void
+split_gateway (const gchar *gateway, gchar **host, gint *port)
+{
+ if (g_strrstr (gateway, ":") == NULL)
+ {
+ *host = g_strdup (gateway);
+ *port = 22;
+ }
+ else
+ {
+ gchar **server = g_strsplit (gateway, ":", 2);
+ *host = g_strdup (server[0]);
+ *port = server[1] ? atoi (server[1]) : 22;
+ g_strfreev (server);
+ }
+}
+
+
+gboolean
+vinagre_spice_tunnel_create (GtkWindow *parent,
+ gchar **original_host,
+ gchar **original_port,
+ gchar *gateway,
+ GError **error)
+{
+ int local_port, gateway_port;
+ gchar **tunnel_str, **command_str, *gateway_host;
+
+ local_port = find_free_port ();
+ if (local_port == 0)
+ {
+ g_set_error (error,
+ VINAGRE_SPICE_TUNNEL_ERROR,
+ VINAGRE_SPICE_TUNNEL_ERROR_NO_FREE_PORT,
+ _("Unable to find a free TCP port"));
+ return FALSE;
+ }
+
+ tunnel_str = g_new (gchar *, 4);
+ tunnel_str[0] = g_strdup ("-f");
+ tunnel_str[1] = g_strdup ("-L");
+ tunnel_str[2] = g_strdup_printf ("%d:%s:%s",
+ local_port,
+ *original_host,
+ *original_port);
+ tunnel_str[3] = NULL;
+
+ command_str = g_new (gchar *, 5);
+ command_str[0] = g_strdup ("echo");
+ command_str[1] = g_strdup_printf ("%s;", VINAGRE_SSH_CHECK);
+ command_str[2] = g_strdup ("sleep");
+ command_str[3] = g_strdup ("15");
+ command_str[4] = NULL;
+
+ split_gateway (gateway, &gateway_host, &gateway_port);
+
+ if (!vinagre_ssh_connect (parent,
+ gateway_host,
+ gateway_port,
+ NULL,
+ tunnel_str,
+ command_str,
+ NULL,
+ error))
+ {
+ g_strfreev (tunnel_str);
+ g_strfreev (command_str);
+ g_free (gateway_host);
+ return FALSE;
+ }
+
+ g_strfreev (tunnel_str);
+ g_strfreev (command_str);
+ g_free (gateway_host);
+ g_free (*original_host);
+ *original_host = g_strdup ("localhost");
+ g_free (*original_port);
+ *original_port = g_strdup_printf ("%d", local_port);
+
+ return TRUE;
+}
+
+GQuark
+vinagre_spice_tunnel_error_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (!quark)
+ quark = g_quark_from_string ("vinagre_spice_tunnel_error");
+
+ return quark;
+}
+
+/* vim: set ts=8: */
diff --git a/plugins/spice/vinagre-spice-tunnel.h b/plugins/spice/vinagre-spice-tunnel.h
new file mode 100644
index 0000000..4327f82
--- /dev/null
+++ b/plugins/spice/vinagre-spice-tunnel.h
@@ -0,0 +1,46 @@
+/*
+ * vinagre-spice-tunnel.h
+ * SSH Tunneling for Vinagre
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2009 - Jonh Wendell <wendell bani com br>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_SPICE_TUNNEL_H__
+#define __VINAGRE_SPICE_TUNNEL_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ VINAGRE_SPICE_TUNNEL_ERROR_NO_FREE_PORT = 1
+} VinagreSpiceTunnelError;
+
+#define VINAGRE_SPICE_TUNNEL_ERROR vinagre_spice_tunnel_error_quark()
+GQuark vinagre_spice_tunnel_error_quark (void);
+
+gboolean vinagre_spice_tunnel_create (GtkWindow *parent,
+ gchar **original_host,
+ gchar **original_port,
+ gchar *gateway,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __VINAGRE_SPICE_TUNNEL_H__ */
+/* vim: set ts=8: */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]