[Spice-commits] configure.ac Makefile.am po/POTFILES.in src/Makefile.am src/spice-cmdline.c src/spice-cmdline.h src/spicy.c src/spicy-connect.c src/spicy-connect.h src/spicy-screenshot.c src/spicy-stats.c tools/Makefile.am tools/spice-cmdline.c tools/spice-cmdline.h tools/spicy.c tools/spicy-connect.c tools/spicy-connect.h tools/spicy-screenshot.c tools/spicy-stats.c
Victor Toso de Carvalho
victortoso at kemper.freedesktop.org
Mon Jan 23 08:34:15 UTC 2017
Makefile.am | 2
configure.ac | 1
po/POTFILES.in | 2
src/Makefile.am | 53 -
src/spice-cmdline.c | 98 --
src/spice-cmdline.h | 29
src/spicy-connect.c | 248 ------
src/spicy-connect.h | 26
src/spicy-screenshot.c | 194 ----
src/spicy-stats.c | 136 ---
src/spicy.c | 1938 -----------------------------------------------
tools/Makefile.am | 69 +
tools/spice-cmdline.c | 98 ++
tools/spice-cmdline.h | 29
tools/spicy-connect.c | 248 ++++++
tools/spicy-connect.h | 26
tools/spicy-screenshot.c | 194 ++++
tools/spicy-stats.c | 136 +++
tools/spicy.c | 1938 +++++++++++++++++++++++++++++++++++++++++++++++
19 files changed, 2741 insertions(+), 2724 deletions(-)
New commits:
commit 47e37e7c9db5f70a0ade527fe6045ba742cec8fa
Author: Victor Toso <me at victortoso.com>
Date: Tue Jan 10 16:36:48 2017 +0100
Move spicy tools to its own folder
So we can have the tools and the libraries in different folders.
In the src/Makefile.am I've only removed the lines related to the
tools but not all lines were copied into tools/Makefile.am as we
don't really need them. Other lines were adjusted to have the paths
correctly;
Signed-off-by: Victor Toso <victortoso at redhat.com>
Acked-by: Christophe Fergeau <cfergeau at redhat.com>
diff --git a/Makefile.am b/Makefile.am
index 47cf840..31d4707 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
ACLOCAL_AMFLAGS = -I m4
NULL =
-SUBDIRS = spice-common src man po doc data
+SUBDIRS = spice-common src man po doc data tools
if BUILD_TESTS
SUBDIRS += tests
diff --git a/configure.ac b/configure.ac
index f3e7f8d..4fd0bd7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -590,6 +590,7 @@ po/Makefile.in
src/Makefile
src/spice-version.h
src/controller/Makefile
+tools/Makefile
doc/Makefile
doc/reference/Makefile
man/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index db42281..d1033f9 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,8 +2,8 @@ src/channel-main.c
src/channel-usbredir.c
src/desktop-integration.c
src/spice-channel.c
-src/spice-cmdline.c
src/spice-option.c
src/usb-device-manager.c
src/usb-device-widget.c
src/usbutil.c
+tools/spice-cmdline.c
diff --git a/src/Makefile.am b/src/Makefile.am
index e43cee0..b991a5f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,10 +45,6 @@ EXTRA_DIST = \
DISTCLEANFILES = spice-version.h
-bin_PROGRAMS = spicy-stats spicy-screenshot
-if WITH_GTK
-bin_PROGRAMS += spicy
-endif
if WITH_POLKIT
acldir = $(ACL_HELPER_DIR)
acl_PROGRAMS = spice-client-glib-usb-acl-helper
@@ -383,31 +379,6 @@ endif
libspice_client_glib_2_0_la_LIBADD += -lws2_32 -lgdi32
endif
-spicy_SOURCES = \
- spicy.c \
- spicy-connect.h \
- spicy-connect.c \
- spice-cmdline.h \
- spice-cmdline.c \
- $(NULL)
-
-spicy_LDADD = \
- libspice-client-gtk-3.0.la \
- libspice-client-glib-2.0.la \
- $(GTHREAD_LIBS) \
- $(GTK_LIBS) \
- $(LIBM) \
- $(NULL)
-
-# FIXME: GtkAction and lots of GtkUIManager APIs are deprecated
-spicy_CPPFLAGS = \
- $(AM_CPPFLAGS) \
- $(GTHREAD_CFLAGS) \
- -DSPICE_DISABLE_DEPRECATED \
- -Wno-deprecated-declarations \
- $(NULL)
-
-
if WITH_POLKIT
spice_client_glib_usb_acl_helper_SOURCES = \
spice-client-glib-usb-acl-helper.c \
@@ -436,30 +407,6 @@ install-data-hook:
endif
-spicy_screenshot_SOURCES = \
- spicy-screenshot.c \
- spice-cmdline.h \
- spice-cmdline.c \
- $(NULL)
-
-spicy_screenshot_LDADD = \
- libspice-client-glib-2.0.la \
- $(GOBJECT2_LIBS) \
- $(NULL)
-
-spicy_stats_SOURCES = \
- spicy-stats.c \
- spice-cmdline.h \
- spice-cmdline.c \
- $(NULL)
-
-spicy_stats_LDADD = \
- libspice-client-glib-2.0.la \
- $(GOBJECT2_LIBS) \
- $(NULL)
-
-
-
$(libspice_client_glib_2_0_la_SOURCES): spice-glib-enums.h spice-marshal.h
if WITH_GTK
diff --git a/src/spice-cmdline.c b/src/spice-cmdline.c
deleted file mode 100644
index 4b6f4c2..0000000
--- a/src/spice-cmdline.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2010 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#include "config.h"
-#include <glib/gi18n-lib.h>
-
-#include "spice-client.h"
-#include "spice-common.h"
-#include "spice-cmdline.h"
-
-static char *host;
-static char *port;
-static char *tls_port;
-static char *password;
-static char *uri;
-
-static GOptionEntry spice_entries[] = {
- {
- .long_name = "uri",
- .arg = G_OPTION_ARG_STRING,
- .arg_data = &uri,
- .description = N_("Spice server uri"),
- .arg_description = N_("<uri>"),
- },{
- .long_name = "host",
- .short_name = 'h',
- .arg = G_OPTION_ARG_STRING,
- .arg_data = &host,
- .description = N_("Spice server address"),
- .arg_description = N_("<host>"),
- },{
- .long_name = "port",
- .short_name = 'p',
- .arg = G_OPTION_ARG_STRING,
- .arg_data = &port,
- .description = N_("Spice server port"),
- .arg_description = N_("<port>"),
- },{
- .long_name = "secure-port",
- .short_name = 's',
- .arg = G_OPTION_ARG_STRING,
- .arg_data = &tls_port,
- .description = N_("Spice server secure port"),
- .arg_description = N_("<port>"),
- },{
- .long_name = "password",
- .short_name = 'w',
- .arg = G_OPTION_ARG_STRING,
- .arg_data = &password,
- .description = N_("Server password"),
- .arg_description = N_("<password>"),
- },{
- /* end of list */
- }
-};
-
-GOptionGroup *spice_cmdline_get_option_group(void)
-{
- GOptionGroup *grp;
-
- grp = g_option_group_new("spice",
- _("Spice connection options:"),
- _("Show Spice options"),
- NULL, NULL);
- g_option_group_add_entries(grp, spice_entries);
-
- return grp;
-}
-
-void spice_cmdline_session_setup(SpiceSession *session)
-{
- g_return_if_fail(SPICE_IS_SESSION(session));
-
- if (uri)
- g_object_set(session, "uri", uri, NULL);
- if (host)
- g_object_set(session, "host", host, NULL);
- if (port)
- g_object_set(session, "port", port, NULL);
- if (tls_port)
- g_object_set(session, "tls-port", tls_port, NULL);
- if (password)
- g_object_set(session, "password", password, NULL);
-}
diff --git a/src/spice-cmdline.h b/src/spice-cmdline.h
deleted file mode 100644
index 11a8086..0000000
--- a/src/spice-cmdline.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2010 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef SPICE_CMDLINE_H_
-# define SPICE_CMDLINE_H_
-
-G_BEGIN_DECLS
-
-GOptionGroup *spice_cmdline_get_option_group(void);
-void spice_cmdline_session_setup(SpiceSession *session);
-
-G_END_DECLS
-
-#endif // SPICE_CMDLINE_H_
diff --git a/src/spicy-connect.c b/src/spicy-connect.c
deleted file mode 100644
index 39555a6..0000000
--- a/src/spicy-connect.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2010-2015 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-#include "spice-common.h"
-#include "spicy-connect.h"
-
-typedef struct
-{
- gboolean connecting;
- GMainLoop *loop;
- SpiceSession *session;
-} ConnectionInfo;
-
-static struct {
- const char *text;
- const char *prop;
- GtkWidget *entry;
-} connect_entries[] = {
- { .text = "Hostname", .prop = "host" },
- { .text = "Port", .prop = "port" },
- { .text = "TLS Port", .prop = "tls-port" },
-};
-
-static gboolean can_connect(void)
-{
- if ((gtk_entry_get_text_length(GTK_ENTRY(connect_entries[0].entry)) > 0) &&
- ((gtk_entry_get_text_length(GTK_ENTRY(connect_entries[1].entry)) > 0) ||
- (gtk_entry_get_text_length(GTK_ENTRY(connect_entries[2].entry)) > 0)))
- return TRUE;
-
- return FALSE;
-}
-
-static void set_connection_info(SpiceSession *session)
-{
- const gchar *txt;
- int i;
-
- for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
- txt = gtk_entry_get_text(GTK_ENTRY(connect_entries[i].entry));
- g_object_set(session, connect_entries[i].prop, txt, NULL);
- }
-}
-
-static gboolean close_cb(gpointer data)
-{
- ConnectionInfo *info = data;
- info->connecting = FALSE;
- if (g_main_loop_is_running(info->loop))
- g_main_loop_quit(info->loop);
-
- return TRUE;
-}
-
-static void entry_changed_cb(GtkEditable* entry, gpointer data)
-{
- GtkButton *connect_button = data;
- gtk_widget_set_sensitive(GTK_WIDGET(connect_button), can_connect());
-}
-
-static gboolean entry_focus_in_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
-{
- GtkRecentChooser *recent = GTK_RECENT_CHOOSER(data);
- gtk_recent_chooser_unselect_all(recent);
- return TRUE;
-}
-
-static gboolean key_pressed_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
-{
- gboolean tst;
- if (event->type == GDK_KEY_PRESS) {
- switch (event->key.keyval) {
- case GDK_KEY_Escape:
- g_signal_emit_by_name(GTK_WIDGET(data), "delete-event", NULL, &tst);
- return TRUE;
- default:
- return FALSE;
- }
- }
-
- return FALSE;
-}
-
-static void recent_selection_changed_dialog_cb(GtkRecentChooser *chooser, gpointer data)
-{
- GtkRecentInfo *info;
- gchar *txt = NULL;
- const gchar *uri;
- SpiceSession *session = data;
- int i;
-
- info = gtk_recent_chooser_get_current_item(chooser);
- if (info == NULL)
- return;
-
- uri = gtk_recent_info_get_uri(info);
- g_return_if_fail(uri != NULL);
-
- g_object_set(session, "uri", uri, NULL);
-
- for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
- g_object_get(session, connect_entries[i].prop, &txt, NULL);
- gtk_entry_set_text(GTK_ENTRY(connect_entries[i].entry), txt ? txt : "");
- g_free(txt);
- }
-
- gtk_recent_info_unref(info);
-}
-
-static void connect_cb(gpointer data)
-{
- ConnectionInfo *info = data;
- if (can_connect())
- {
- info->connecting = TRUE;
- set_connection_info(info->session);
- if (g_main_loop_is_running(info->loop))
- g_main_loop_quit(info->loop);
- }
-}
-
-gboolean spicy_connect_dialog(SpiceSession *session)
-{
- GtkWidget *connect_button, *cancel_button, *label;
- GtkBox *main_box, *recent_box, *button_box;
- GtkWindow *window;
- GtkGrid *grid;
- int i;
-
- ConnectionInfo info = {
- FALSE,
- NULL,
- session
- };
-
- /* Create the widgets */
- window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
- gtk_window_set_title(window, "Connect to SPICE");
- gtk_window_set_resizable(window, FALSE);
- gtk_container_set_border_width(GTK_CONTAINER(window), 5);
-
- main_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
- gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(main_box));
-
- grid = GTK_GRID(gtk_grid_new());
- gtk_box_pack_start(main_box, GTK_WIDGET(grid), FALSE, TRUE, 0);
- gtk_container_set_border_width(GTK_CONTAINER(grid), 5);
- gtk_grid_set_row_spacing(grid, 5);
- gtk_grid_set_column_spacing(grid, 5);
-
- for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
- gchar *txt;
- label = gtk_label_new(connect_entries[i].text);
- gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
- gtk_grid_attach(grid, label, 0, i, 1, 1);
- connect_entries[i].entry = GTK_WIDGET(gtk_entry_new());
- gtk_grid_attach(grid, connect_entries[i].entry, 1, i, 1, 1);
- g_object_get(session, connect_entries[i].prop, &txt, NULL);
- SPICE_DEBUG("%s: #%i [%s]: \"%s\"",
- __FUNCTION__, i, connect_entries[i].prop, txt);
- if (txt) {
- gtk_entry_set_text(GTK_ENTRY(connect_entries[i].entry), txt);
- g_free(txt);
- }
- }
-
- recent_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
- gtk_box_pack_start(main_box, GTK_WIDGET(recent_box), TRUE, TRUE, 0);
- gtk_container_set_border_width(GTK_CONTAINER(recent_box), 5);
-
- label = gtk_label_new("Recent connections:");
- gtk_box_pack_start(recent_box, label, FALSE, TRUE, 0);
- gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
-
- button_box = GTK_BOX(gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL));
- gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_END);
- gtk_box_set_spacing(button_box, 5);
- gtk_container_set_border_width(GTK_CONTAINER(button_box), 5);
- connect_button = gtk_button_new_with_label("Connect");
- cancel_button = gtk_button_new_with_label("Cancel");
- gtk_box_pack_start(button_box, cancel_button, FALSE, TRUE, 0);
- gtk_box_pack_start(button_box, connect_button, FALSE, TRUE, 1);
-
- gtk_box_pack_start(main_box, GTK_WIDGET(button_box), FALSE, TRUE, 0);
-
- gtk_widget_set_sensitive(GTK_WIDGET(connect_button), can_connect());
-
- g_signal_connect(window, "key-press-event",
- G_CALLBACK(key_pressed_cb), window);
- g_signal_connect_swapped(window, "delete-event",
- G_CALLBACK(close_cb), &info);
- g_signal_connect_swapped(connect_button, "clicked",
- G_CALLBACK(connect_cb), &info);
- g_signal_connect_swapped(cancel_button, "clicked",
- G_CALLBACK(close_cb), &info);
-
- GtkRecentFilter *rfilter;
- GtkWidget *recent;
-
- recent = GTK_WIDGET(gtk_recent_chooser_widget_new());
- gtk_recent_chooser_set_show_icons(GTK_RECENT_CHOOSER(recent), FALSE);
- gtk_box_pack_start(recent_box, recent, TRUE, TRUE, 0);
-
- rfilter = gtk_recent_filter_new();
- gtk_recent_filter_add_mime_type(rfilter, "application/x-spice");
- gtk_recent_chooser_set_filter(GTK_RECENT_CHOOSER(recent), rfilter);
- gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(recent), FALSE);
- g_signal_connect(recent, "selection-changed",
- G_CALLBACK(recent_selection_changed_dialog_cb), session);
- g_signal_connect_swapped(recent, "item-activated",
- G_CALLBACK(connect_cb), &info);
-
- for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
- g_signal_connect_swapped(connect_entries[i].entry, "activate",
- G_CALLBACK(connect_cb), &info);
- g_signal_connect(connect_entries[i].entry, "changed",
- G_CALLBACK(entry_changed_cb), connect_button);
- g_signal_connect(connect_entries[i].entry, "focus-in-event",
- G_CALLBACK(entry_focus_in_cb), recent);
- }
-
- /* show and wait for response */
- gtk_widget_show_all(GTK_WIDGET(window));
-
- info.loop = g_main_loop_new(NULL, FALSE);
- g_main_loop_run(info.loop);
-
- gtk_widget_destroy(GTK_WIDGET(window));
-
- return info.connecting;
-}
diff --git a/src/spicy-connect.h b/src/spicy-connect.h
deleted file mode 100644
index 56b2d80..0000000
--- a/src/spicy-connect.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2010-2015 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef SPICY_CONNECT_H
-#define SPICY_CONNECT_H
-
-#include "spice-widget.h"
-
-gboolean spicy_connect_dialog(SpiceSession *session);
-
-#endif
diff --git a/src/spicy-screenshot.c b/src/spicy-screenshot.c
deleted file mode 100644
index 68f9335..0000000
--- a/src/spicy-screenshot.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2010 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#include "config.h"
-
-#include "spice-client.h"
-#include "spice-common.h"
-#include "spice-cmdline.h"
-
-/* config */
-static const char *outf = "spicy-screenshot.ppm";
-static gboolean version = FALSE;
-
-/* state */
-static SpiceSession *session;
-static GMainLoop *mainloop;
-
-enum SpiceSurfaceFmt d_format;
-gint d_width, d_height, d_stride;
-gpointer d_data;
-
-/* ------------------------------------------------------------------ */
-
-static void primary_create(SpiceChannel *channel, gint format,
- gint width, gint height, gint stride,
- gint shmid, gpointer imgdata, gpointer data)
-{
- SPICE_DEBUG("%s: %dx%d, format %d", __FUNCTION__, width, height, format);
- d_format = format;
- d_width = width;
- d_height = height;
- d_stride = stride;
- d_data = imgdata;
-}
-
-static int write_ppm_32(void)
-{
- FILE *fp;
- uint8_t *p;
- int n;
-
- fp = fopen(outf,"w");
- if (NULL == fp) {
- fprintf(stderr, "%s: can't open %s: %s\n", g_get_prgname(), outf, strerror(errno));
- return -1;
- }
- fprintf(fp, "P6\n%d %d\n255\n",
- d_width, d_height);
- n = d_width * d_height;
- p = d_data;
- while (n > 0) {
-#ifdef WORDS_BIGENDIAN
- fputc(p[1], fp);
- fputc(p[2], fp);
- fputc(p[3], fp);
-#else
- fputc(p[2], fp);
- fputc(p[1], fp);
- fputc(p[0], fp);
-#endif
- p += 4;
- n--;
- }
- fclose(fp);
- return 0;
-}
-
-static void invalidate(SpiceChannel *channel,
- gint x, gint y, gint w, gint h, gpointer *data)
-{
- int rc;
-
- switch (d_format) {
- case SPICE_SURFACE_FMT_32_xRGB:
- rc = write_ppm_32();
- break;
- default:
- fprintf(stderr, "unsupported spice surface format %u\n", d_format);
- rc = -1;
- break;
- }
- if (rc == 0)
- fprintf(stderr, "wrote screen shot to %s\n", outf);
- g_main_loop_quit(mainloop);
-}
-
-static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
- gpointer data)
-{
- switch (event) {
- case SPICE_CHANNEL_OPENED:
- break;
- default:
- g_warning("main channel event: %u", event);
- g_main_loop_quit(mainloop);
- }
-}
-
-static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer *data)
-{
- int id;
-
- if (SPICE_IS_MAIN_CHANNEL(channel)) {
- g_signal_connect(channel, "channel-event",
- G_CALLBACK(main_channel_event), data);
- return;
- }
-
- if (!SPICE_IS_DISPLAY_CHANNEL(channel))
- return;
-
- g_object_get(channel, "channel-id", &id, NULL);
- if (id != 0)
- return;
-
- g_signal_connect(channel, "display-primary-create",
- G_CALLBACK(primary_create), NULL);
- g_signal_connect(channel, "display-invalidate",
- G_CALLBACK(invalidate), NULL);
- spice_channel_connect(channel);
-}
-
-/* ------------------------------------------------------------------ */
-
-static GOptionEntry app_entries[] = {
- {
- .long_name = "out-file",
- .short_name = 'o',
- .arg = G_OPTION_ARG_FILENAME,
- .arg_data = &outf,
- .description = "Output file name (default spicy-screenshot.ppm)",
- .arg_description = "<filename>",
- },
- {
- .long_name = "version",
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &version,
- .description = "Display version and quit",
- },
- {
- /* end of list */
- }
-};
-
-int main(int argc, char *argv[])
-{
- GError *error = NULL;
- GOptionContext *context;
-
- /* parse opts */
- context = g_option_context_new(" - make screen shots");
- g_option_context_set_summary(context, "A Spice server client to take screenshots in ppm format.");
- g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
- g_option_context_set_main_group(context, spice_cmdline_get_option_group());
- g_option_context_add_main_entries(context, app_entries, NULL);
- if (!g_option_context_parse (context, &argc, &argv, &error)) {
- g_print("option parsing failed: %s\n", error->message);
- exit(1);
- }
-
- if (version) {
- g_print("%s " PACKAGE_VERSION "\n", g_get_prgname());
- exit(0);
- }
-
- mainloop = g_main_loop_new(NULL, false);
-
- session = spice_session_new();
- g_signal_connect(session, "channel-new",
- G_CALLBACK(channel_new), NULL);
- spice_cmdline_session_setup(session);
-
- if (!spice_session_connect(session)) {
- fprintf(stderr, "spice_session_connect failed\n");
- exit(1);
- }
-
- g_main_loop_run(mainloop);
- return 0;
-}
diff --git a/src/spicy-stats.c b/src/spicy-stats.c
deleted file mode 100644
index 8ca4cc1..0000000
--- a/src/spicy-stats.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2010 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-#include "config.h"
-
-#include "spice-client.h"
-#include "spice-common.h"
-#include "spice-cmdline.h"
-
-/* config */
-static gboolean version = FALSE;
-
-/* state */
-static SpiceSession *session;
-static GMainLoop *mainloop;
-
-/* ------------------------------------------------------------------ */
-static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
- gpointer data)
-{
- switch (event) {
- case SPICE_CHANNEL_OPENED:
- break;
- default:
- g_warning("main channel event: %u", event);
- g_main_loop_quit(mainloop);
- }
-}
-
-static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer *data)
-{
- int id;
-
- if (SPICE_IS_MAIN_CHANNEL(channel)) {
- SPICE_DEBUG("new main channel");
- g_signal_connect(channel, "channel-event",
- G_CALLBACK(main_channel_event), data);
- }
-
- if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
- g_object_get(channel, "channel-id", &id, NULL);
- if (id != 0)
- return;
- }
-
- spice_channel_connect(channel);
-}
-
-/* ------------------------------------------------------------------ */
-
-static GOptionEntry app_entries[] = {
- {
- .long_name = "version",
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &version,
- .description = "Display version and quit",
- },
- {
- /* end of list */
- }
-};
-
-static void
-signal_handler(int signum)
-{
- g_main_loop_quit(mainloop);
-}
-
-int main(int argc, char *argv[])
-{
- GError *error = NULL;
- GOptionContext *context;
-
- signal(SIGINT, signal_handler);
-
- /* parse opts */
- context = g_option_context_new(NULL);
- g_option_context_set_summary(context, "A Spice client used for testing and measurements.");
- g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
- g_option_context_set_main_group(context, spice_cmdline_get_option_group());
- g_option_context_add_main_entries(context, app_entries, NULL);
- if (!g_option_context_parse (context, &argc, &argv, &error)) {
- g_print("option parsing failed: %s\n", error->message);
- exit(1);
- }
-
- if (version) {
- g_print("spicy-stats " PACKAGE_VERSION "\n");
- exit(0);
- }
-
- mainloop = g_main_loop_new(NULL, false);
-
- session = spice_session_new();
- g_signal_connect(session, "channel-new",
- G_CALLBACK(channel_new), NULL);
- spice_cmdline_session_setup(session);
-
- if (!spice_session_connect(session)) {
- fprintf(stderr, "spice_session_connect failed\n");
- exit(1);
- }
-
- g_main_loop_run(mainloop);
- {
- GList *iter, *list = spice_session_get_channels(session);
- gulong total_read_bytes;
- gint channel_type;
- printf("total bytes read:\n");
- for (iter = list ; iter ; iter = iter->next) {
- g_object_get(iter->data,
- "total-read-bytes", &total_read_bytes,
- "channel-type", &channel_type,
- NULL);
- printf("%s: %lu\n",
- spice_channel_type_to_string(channel_type),
- total_read_bytes);
- }
- g_list_free(list);
- }
- return 0;
-}
diff --git a/src/spicy.c b/src/spicy.c
deleted file mode 100644
index c502428..0000000
--- a/src/spicy.c
+++ /dev/null
@@ -1,1938 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
- Copyright (C) 2010-2011 Red Hat, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "config.h"
-#include <glib.h>
-
-#include <sys/stat.h>
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#endif
-
-#ifdef USE_SMARTCARD_012
-#include <vreader.h>
-#endif
-
-#include "spice-widget.h"
-#include "spice-gtk-session.h"
-#include "spice-audio.h"
-#include "spice-common.h"
-#include "spice-cmdline.h"
-#include "spice-option.h"
-#include "usb-device-widget.h"
-
-#include "spicy-connect.h"
-
-typedef struct spice_connection spice_connection;
-
-enum {
- STATE_SCROLL_LOCK,
- STATE_CAPS_LOCK,
- STATE_NUM_LOCK,
- STATE_MAX,
-};
-
-#define SPICE_TYPE_WINDOW (spice_window_get_type ())
-#define SPICE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_WINDOW, SpiceWindow))
-#define SPICE_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_WINDOW))
-#define SPICE_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_WINDOW, SpiceWindowClass))
-#define SPICE_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_WINDOW))
-#define SPICE_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_WINDOW, SpiceWindowClass))
-
-typedef struct _SpiceWindow SpiceWindow;
-typedef struct _SpiceWindowClass SpiceWindowClass;
-
-struct _SpiceWindow {
- GObject object;
- spice_connection *conn;
- gint id;
- gint monitor_id;
- GtkWidget *toplevel, *spice;
- GtkWidget *menubar, *toolbar;
- GtkWidget *ritem, *rmenu;
- GtkWidget *statusbar, *status, *st[STATE_MAX];
- GtkActionGroup *ag;
- GtkUIManager *ui;
- bool fullscreen;
- bool mouse_grabbed;
- SpiceChannel *display_channel;
-#ifdef G_OS_WIN32
- gint win_x;
- gint win_y;
-#endif
- bool enable_accels_save;
- bool enable_mnemonics_save;
-};
-
-struct _SpiceWindowClass
-{
- GObjectClass parent_class;
-};
-
-static GType spice_window_get_type(void);
-
-G_DEFINE_TYPE (SpiceWindow, spice_window, G_TYPE_OBJECT);
-
-#define CHANNELID_MAX 4
-#define MONITORID_MAX 4
-
-
-// FIXME: turn this into an object, get rid of fixed wins array, use
-// signals to replace the various callback that iterate over wins array
-struct spice_connection {
- SpiceSession *session;
- SpiceGtkSession *gtk_session;
- SpiceMainChannel *main;
- SpiceWindow *wins[CHANNELID_MAX * MONITORID_MAX];
- SpiceAudio *audio;
- const char *mouse_state;
- const char *agent_state;
- gboolean agent_connected;
- int channels;
- int disconnecting;
-
- /* key: SpiceFileTransferTask, value: TransferTaskWidgets */
- GHashTable *transfers;
- GtkWidget *transfer_dialog;
-};
-
-static spice_connection *connection_new(void);
-static void connection_connect(spice_connection *conn);
-static void connection_disconnect(spice_connection *conn);
-static void connection_destroy(spice_connection *conn);
-static void usb_connect_failed(GObject *object,
- SpiceUsbDevice *device,
- GError *error,
- gpointer data);
-static gboolean is_gtk_session_property(const gchar *property);
-static void del_window(spice_connection *conn, SpiceWindow *win);
-
-/* options */
-static gboolean fullscreen = false;
-static gboolean version = false;
-static char *spicy_title = NULL;
-/* globals */
-static GMainLoop *mainloop = NULL;
-static int connections = 0;
-static GKeyFile *keyfile = NULL;
-static SpicePortChannel*stdin_port = NULL;
-
-/* ------------------------------------------------------------------ */
-
-static int ask_user(GtkWidget *parent, char *title, char *message,
- char *dest, int dlen, int hide)
-{
- GtkWidget *dialog, *area, *label, *entry;
- const char *txt;
- int retval;
-
- /* Create the widgets */
- dialog = gtk_dialog_new_with_buttons(title,
- parent ? GTK_WINDOW(parent) : NULL,
- GTK_DIALOG_DESTROY_WITH_PARENT,
- "_OK",
- GTK_RESPONSE_ACCEPT,
- "_Cancel",
- GTK_RESPONSE_REJECT,
- NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
- area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
-
- label = gtk_label_new(message);
- gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
- gtk_box_pack_start(GTK_BOX(area), label, FALSE, FALSE, 5);
-
- entry = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(entry), dest);
- gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
- if (hide)
- gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
- gtk_box_pack_start(GTK_BOX(area), entry, FALSE, FALSE, 5);
-
- /* show and wait for response */
- gtk_widget_show_all(dialog);
- switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
- case GTK_RESPONSE_ACCEPT:
- txt = gtk_entry_get_text(GTK_ENTRY(entry));
- snprintf(dest, dlen, "%s", txt);
- retval = 0;
- break;
- default:
- retval = -1;
- break;
- }
- gtk_widget_destroy(dialog);
- return retval;
-}
-
-static void update_status_window(SpiceWindow *win)
-{
- gchar *status;
-
- if (win == NULL)
- return;
-
- if (win->mouse_grabbed) {
- SpiceGrabSequence *sequence = spice_display_get_grab_keys(SPICE_DISPLAY(win->spice));
- gchar *seq = spice_grab_sequence_as_string(sequence);
- status = g_strdup_printf("Use %s to ungrab mouse.", seq);
- g_free(seq);
- } else {
- status = g_strdup_printf("mouse: %s, agent: %s",
- win->conn->mouse_state, win->conn->agent_state);
- }
-
- gtk_label_set_text(GTK_LABEL(win->status), status);
- g_free(status);
-}
-
-static void update_status(struct spice_connection *conn)
-{
- int i;
-
- for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
- if (conn->wins[i] == NULL)
- continue;
- update_status_window(conn->wins[i]);
- }
-}
-
-static const char *spice_edit_properties[] = {
- "CopyToGuest",
- "PasteFromGuest",
-};
-
-static void update_edit_menu_window(SpiceWindow *win)
-{
- int i;
- GtkAction *toggle;
-
- if (win == NULL) {
- return;
- }
-
- /* Make "CopyToGuest" and "PasteFromGuest" insensitive if spice
- * agent is not connected */
- for (i = 0; i < G_N_ELEMENTS(spice_edit_properties); i++) {
- toggle = gtk_action_group_get_action(win->ag, spice_edit_properties[i]);
- if (toggle) {
- gtk_action_set_sensitive(toggle, win->conn->agent_connected);
- }
- }
-}
-
-static void update_edit_menu(struct spice_connection *conn)
-{
- int i;
-
- for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
- if (conn->wins[i]) {
- update_edit_menu_window(conn->wins[i]);
- }
- }
-}
-
-static void menu_cb_connect(GtkAction *action, void *data)
-{
- struct spice_connection *conn;
-
- conn = connection_new();
- connection_connect(conn);
-}
-
-static void menu_cb_close(GtkAction *action, void *data)
-{
- SpiceWindow *win = data;
-
- connection_disconnect(win->conn);
-}
-
-static void menu_cb_copy(GtkAction *action, void *data)
-{
- SpiceWindow *win = data;
-
- spice_gtk_session_copy_to_guest(win->conn->gtk_session);
-}
-
-static void menu_cb_paste(GtkAction *action, void *data)
-{
- SpiceWindow *win = data;
-
- spice_gtk_session_paste_from_guest(win->conn->gtk_session);
-}
-
-static void window_set_fullscreen(SpiceWindow *win, gboolean fs)
-{
- if (fs) {
-#ifdef G_OS_WIN32
- gtk_window_get_position(GTK_WINDOW(win->toplevel), &win->win_x, &win->win_y);
-#endif
- gtk_window_fullscreen(GTK_WINDOW(win->toplevel));
- } else {
- gtk_window_unfullscreen(GTK_WINDOW(win->toplevel));
-#ifdef G_OS_WIN32
- gtk_window_move(GTK_WINDOW(win->toplevel), win->win_x, win->win_y);
-#endif
- }
-}
-
-static void menu_cb_fullscreen(GtkAction *action, void *data)
-{
- SpiceWindow *win = data;
-
- window_set_fullscreen(win, !win->fullscreen);
-}
-
-#ifdef USE_SMARTCARD
-static void enable_smartcard_actions(SpiceWindow *win, VReader *reader,
- gboolean can_insert, gboolean can_remove)
-{
- GtkAction *action;
-
- if ((reader != NULL) && (!spice_smartcard_reader_is_software((SpiceSmartcardReader*)reader)))
- {
- /* Having menu actions to insert/remove smartcards only makes sense
- * for software smartcard readers, don't do anything when the event
- * we received was for a "real" smartcard reader.
- */
- return;
- }
- action = gtk_action_group_get_action(win->ag, "InsertSmartcard");
- g_return_if_fail(action != NULL);
- gtk_action_set_sensitive(action, can_insert);
- action = gtk_action_group_get_action(win->ag, "RemoveSmartcard");
- g_return_if_fail(action != NULL);
- gtk_action_set_sensitive(action, can_remove);
-}
-
-
-static void reader_added_cb(SpiceSmartcardManager *manager, VReader *reader,
- gpointer user_data)
-{
- enable_smartcard_actions(user_data, reader, TRUE, FALSE);
-}
-
-static void reader_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
- gpointer user_data)
-{
- enable_smartcard_actions(user_data, reader, FALSE, FALSE);
-}
-
-static void card_inserted_cb(SpiceSmartcardManager *manager, VReader *reader,
- gpointer user_data)
-{
- enable_smartcard_actions(user_data, reader, FALSE, TRUE);
-}
-
-static void card_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
- gpointer user_data)
-{
- enable_smartcard_actions(user_data, reader, TRUE, FALSE);
-}
-
-static void menu_cb_insert_smartcard(GtkAction *action, void *data)
-{
- spice_smartcard_manager_insert_card(spice_smartcard_manager_get());
-}
-
-static void menu_cb_remove_smartcard(GtkAction *action, void *data)
-{
- spice_smartcard_manager_remove_card(spice_smartcard_manager_get());
-}
-#endif
-
-static void menu_cb_mouse_mode(GtkAction *action, void *data)
-{
- SpiceWindow *win = data;
- SpiceMainChannel *cmain = win->conn->main;
- int mode;
-
- g_object_get(cmain, "mouse-mode", &mode, NULL);
- if (mode == SPICE_MOUSE_MODE_CLIENT)
- mode = SPICE_MOUSE_MODE_SERVER;
- else
- mode = SPICE_MOUSE_MODE_CLIENT;
-
- spice_main_request_mouse_mode(cmain, mode);
-}
-
-#ifdef USE_USBREDIR
-static void remove_cb(GtkContainer *container, GtkWidget *widget, void *data)
-{
- gtk_window_resize(GTK_WINDOW(data), 1, 1);
-}
-
-static void menu_cb_select_usb_devices(GtkAction *action, void *data)
-{
- GtkWidget *dialog, *area, *usb_device_widget;
- SpiceWindow *win = data;
-
- /* Create the widgets */
- dialog = gtk_dialog_new_with_buttons(
- "Select USB devices for redirection",
- GTK_WINDOW(win->toplevel),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- "_Close", GTK_RESPONSE_ACCEPT,
- NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
- gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
- gtk_box_set_spacing(GTK_BOX(gtk_bin_get_child(GTK_BIN(dialog))), 12);
-
- area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
-
- usb_device_widget = spice_usb_device_widget_new(win->conn->session,
- NULL); /* default format */
- g_signal_connect(usb_device_widget, "connect-failed",
- G_CALLBACK(usb_connect_failed), NULL);
- gtk_box_pack_start(GTK_BOX(area), usb_device_widget, TRUE, TRUE, 0);
-
- /* This shrinks the dialog when USB devices are unplugged */
- g_signal_connect(usb_device_widget, "remove",
- G_CALLBACK(remove_cb), dialog);
-
- /* show and run */
- gtk_widget_show_all(dialog);
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
-}
-#endif
-
-static void menu_cb_bool_prop(GtkToggleAction *action, gpointer data)
-{
- SpiceWindow *win = data;
- gboolean state = gtk_toggle_action_get_active(action);
- const char *name;
- gpointer object;
-
- name = gtk_action_get_name(GTK_ACTION(action));
- SPICE_DEBUG("%s: %s = %s", __FUNCTION__, name, state ? "yes" : "no");
-
- g_key_file_set_boolean(keyfile, "general", name, state);
-
- if (is_gtk_session_property(name)) {
- object = win->conn->gtk_session;
- } else {
- object = win->spice;
- }
- g_object_set(object, name, state, NULL);
-}
-
-static void menu_cb_conn_bool_prop_changed(GObject *gobject,
- GParamSpec *pspec,
- gpointer user_data)
-{
- SpiceWindow *win = user_data;
- const gchar *property = g_param_spec_get_name(pspec);
- GtkAction *toggle;
- gboolean state;
-
- toggle = gtk_action_group_get_action(win->ag, property);
- g_object_get(win->conn->gtk_session, property, &state, NULL);
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
-}
-
-static void menu_cb_toolbar(GtkToggleAction *action, gpointer data)
-{
- SpiceWindow *win = data;
- gboolean state = gtk_toggle_action_get_active(action);
-
- gtk_widget_set_visible(win->toolbar, state);
- g_key_file_set_boolean(keyfile, "ui", "toolbar", state);
-}
-
-static void menu_cb_statusbar(GtkToggleAction *action, gpointer data)
-{
- SpiceWindow *win = data;
- gboolean state = gtk_toggle_action_get_active(action);
-
- gtk_widget_set_visible(win->statusbar, state);
- g_key_file_set_boolean(keyfile, "ui", "statusbar", state);
-}
-
-static void menu_cb_about(GtkAction *action, void *data)
-{
- char *comments = "gtk test client app for the\n"
- "spice remote desktop protocol";
- static const char *copyright = "(c) 2010 Red Hat";
- static const char *website = "http://www.spice-space.org";
- static const char *authors[] = { "Gerd Hoffmann <kraxel at redhat.com>",
- "Marc-André Lureau <marcandre.lureau at redhat.com>",
- NULL };
- SpiceWindow *win = data;
-
- gtk_show_about_dialog(GTK_WINDOW(win->toplevel),
- "authors", authors,
- "comments", comments,
- "copyright", copyright,
- "logo-icon-name", "help-about",
- "website", website,
- "version", PACKAGE_VERSION,
- "license", "LGPLv2.1",
- NULL);
-}
-
-static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
-{
- SpiceWindow *win = data;
-
- if (win->monitor_id == 0)
- connection_disconnect(win->conn);
- else
- del_window(win->conn, win);
-
- return true;
-}
-
-static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event,
- gpointer data)
-{
- SpiceWindow *win = data;
- if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
- win->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
- if (win->fullscreen) {
- gtk_widget_hide(win->menubar);
- gtk_widget_hide(win->toolbar);
- gtk_widget_hide(win->statusbar);
- gtk_widget_grab_focus(win->spice);
- } else {
- gboolean state;
- GtkAction *toggle;
-
- gtk_widget_show(win->menubar);
- toggle = gtk_action_group_get_action(win->ag, "Toolbar");
- state = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(toggle));
- gtk_widget_set_visible(win->toolbar, state);
- toggle = gtk_action_group_get_action(win->ag, "Statusbar");
- state = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(toggle));
- gtk_widget_set_visible(win->statusbar, state);
- }
- }
- return TRUE;
-}
-
-static void grab_keys_pressed_cb(GtkWidget *widget, gpointer data)
-{
- SpiceWindow *win = data;
-
- /* since mnemonics are disabled, we leave fullscreen when
- ungrabbing mouse. Perhaps we should have a different handling
- of fullscreen key, or simply use a UI, like vinagre */
- window_set_fullscreen(win, FALSE);
-}
-
-static void mouse_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
-{
- SpiceWindow *win = data;
-
- win->mouse_grabbed = grabbed;
- update_status(win->conn);
-}
-
-static void keyboard_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
-{
- SpiceWindow *win = data;
- GtkSettings *settings = gtk_widget_get_settings (widget);
-
- if (grabbed) {
- /* disable mnemonics & accels */
- g_object_get(settings,
- "gtk-enable-accels", &win->enable_accels_save,
- "gtk-enable-mnemonics", &win->enable_mnemonics_save,
- NULL);
- g_object_set(settings,
- "gtk-enable-accels", FALSE,
- "gtk-enable-mnemonics", FALSE,
- NULL);
- } else {
- g_object_set(settings,
- "gtk-enable-accels", win->enable_accels_save,
- "gtk-enable-mnemonics", win->enable_mnemonics_save,
- NULL);
- }
-}
-
-static void restore_configuration(SpiceWindow *win)
-{
- gboolean state;
- gchar *str;
- gchar **keys = NULL;
- gsize nkeys, i;
- GError *error = NULL;
- gpointer object;
-
- keys = g_key_file_get_keys(keyfile, "general", &nkeys, &error);
- if (error != NULL) {
- if (error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
- g_warning("Failed to read configuration file keys: %s", error->message);
- g_clear_error(&error);
- return;
- }
-
- if (nkeys > 0)
- g_return_if_fail(keys != NULL);
-
- for (i = 0; i < nkeys; ++i) {
- if (g_str_equal(keys[i], "grab-sequence"))
- continue;
- state = g_key_file_get_boolean(keyfile, "general", keys[i], &error);
- if (error != NULL) {
- g_clear_error(&error);
- continue;
- }
-
- if (is_gtk_session_property(keys[i])) {
- object = win->conn->gtk_session;
- } else {
- object = win->spice;
- }
- g_object_set(object, keys[i], state, NULL);
- }
-
- g_strfreev(keys);
-
- str = g_key_file_get_string(keyfile, "general", "grab-sequence", &error);
- if (error == NULL) {
- SpiceGrabSequence *seq = spice_grab_sequence_new_from_string(str);
- spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
- spice_grab_sequence_free(seq);
- g_free(str);
- }
- g_clear_error(&error);
-
-
- state = g_key_file_get_boolean(keyfile, "ui", "toolbar", &error);
- if (error == NULL)
- gtk_widget_set_visible(win->toolbar, state);
- g_clear_error(&error);
-
- state = g_key_file_get_boolean(keyfile, "ui", "statusbar", &error);
- if (error == NULL)
- gtk_widget_set_visible(win->statusbar, state);
- g_clear_error(&error);
-}
-
-/* ------------------------------------------------------------------ */
-
-static const GtkActionEntry entries[] = {
- {
- .name = "FileMenu",
- .label = "_File",
- },{
- .name = "FileRecentMenu",
- .label = "_Recent",
- },{
- .name = "EditMenu",
- .label = "_Edit",
- },{
- .name = "ViewMenu",
- .label = "_View",
- },{
- .name = "InputMenu",
- .label = "_Input",
- },{
- .name = "OptionMenu",
- .label = "_Options",
- },{
- .name = "CompressionMenu",
- .label = "_Preferred image compression",
- },{
- .name = "HelpMenu",
- .label = "_Help",
- },{
-
- /* File menu */
- .name = "Connect",
- .stock_id = "_Connect",
- .label = "_Connect ...",
- .callback = G_CALLBACK(menu_cb_connect),
- },{
- .name = "Close",
- .stock_id = "window-close",
- .label = "_Close",
- .callback = G_CALLBACK(menu_cb_close),
- .accelerator = "", /* none (disable default "<control>W") */
- },{
-
- /* Edit menu */
- .name = "CopyToGuest",
- .stock_id = "edit-copy",
- .label = "_Copy to guest",
- .callback = G_CALLBACK(menu_cb_copy),
- .accelerator = "", /* none (disable default "<control>C") */
- },{
- .name = "PasteFromGuest",
- .stock_id = "edit-paste",
- .label = "_Paste from guest",
- .callback = G_CALLBACK(menu_cb_paste),
- .accelerator = "", /* none (disable default "<control>V") */
- },{
-
- /* View menu */
- .name = "Fullscreen",
- .stock_id = "view-fullscreen",
- .label = "_Fullscreen",
- .callback = G_CALLBACK(menu_cb_fullscreen),
- .accelerator = "<shift>F11",
- },{
-#ifdef USE_SMARTCARD
- .name = "InsertSmartcard",
- .label = "_Insert Smartcard",
- .callback = G_CALLBACK(menu_cb_insert_smartcard),
- .accelerator = "<shift>F8",
- },{
- .name = "RemoveSmartcard",
- .label = "_Remove Smartcard",
- .callback = G_CALLBACK(menu_cb_remove_smartcard),
- .accelerator = "<shift>F9",
- },{
-#endif
-
-#ifdef USE_USBREDIR
- .name = "SelectUsbDevices",
- .label = "_Select USB Devices for redirection",
- .callback = G_CALLBACK(menu_cb_select_usb_devices),
- .accelerator = "<shift>F10",
- },{
-#endif
-
- .name = "MouseMode",
- .label = "Toggle _mouse mode",
- .callback = G_CALLBACK(menu_cb_mouse_mode),
- .accelerator = "<shift>F7",
-
- },{
- /* Help menu */
- .name = "About",
- .stock_id = "help-about",
- .label = "_About ...",
- .callback = G_CALLBACK(menu_cb_about),
- }
-};
-
-static const char *spice_display_properties[] = {
- "grab-keyboard",
- "grab-mouse",
- "resize-guest",
- "scaling",
- "disable-inputs",
-};
-
-static const char *spice_gtk_session_properties[] = {
- "auto-clipboard",
- "auto-usbredir",
- "sync-modifiers",
-};
-
-static const GtkToggleActionEntry tentries[] = {
- {
- .name = "grab-keyboard",
- .label = "Grab keyboard when active and focused",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "grab-mouse",
- .label = "Grab mouse in server mode (no tablet/vdagent)",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "resize-guest",
- .label = "Resize guest to match window size",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "scaling",
- .label = "Scale display",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "disable-inputs",
- .label = "Disable inputs",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "sync-modifiers",
- .label = "Sync modifiers",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "auto-clipboard",
- .label = "Automatic clipboard sharing between host and guest",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "auto-usbredir",
- .label = "Auto redirect newly plugged in USB devices",
- .callback = G_CALLBACK(menu_cb_bool_prop),
- },{
- .name = "Statusbar",
- .label = "Statusbar",
- .callback = G_CALLBACK(menu_cb_statusbar),
- },{
- .name = "Toolbar",
- .label = "Toolbar",
- .callback = G_CALLBACK(menu_cb_toolbar),
- }
-};
-
-static const GtkRadioActionEntry compression_entries[] = {
- {
- .name = "auto-glz",
- .label = "auto-glz",
- .value = SPICE_IMAGE_COMPRESSION_AUTO_GLZ,
- },{
- .name = "auto-lz",
- .label = "auto-lz",
- .value = SPICE_IMAGE_COMPRESSION_AUTO_LZ,
- },{
- .name = "quic",
- .label = "quic",
- .value = SPICE_IMAGE_COMPRESSION_QUIC,
- },{
- .name = "glz",
- .label = "glz",
- .value = SPICE_IMAGE_COMPRESSION_GLZ,
- },{
- .name = "lz",
- .label = "lz",
- .value = SPICE_IMAGE_COMPRESSION_LZ,
- },{
-#ifdef USE_LZ4
- .name = "lz4",
- .label = "lz4",
- .value = SPICE_IMAGE_COMPRESSION_LZ4,
- },{
-#endif
- .name = "off",
- .label = "off",
- .value = SPICE_IMAGE_COMPRESSION_OFF,
- }
-};
-
-static char ui_xml[] =
-"<ui>\n"
-" <menubar action='MainMenu'>\n"
-" <menu action='FileMenu'>\n"
-" <menuitem action='Connect'/>\n"
-" <menu action='FileRecentMenu'/>\n"
-" <separator/>\n"
-" <menuitem action='Close'/>\n"
-" </menu>\n"
-" <menu action='EditMenu'>\n"
-" <menuitem action='CopyToGuest'/>\n"
-" <menuitem action='PasteFromGuest'/>\n"
-" </menu>\n"
-" <menu action='ViewMenu'>\n"
-" <menuitem action='Fullscreen'/>\n"
-" <menuitem action='Toolbar'/>\n"
-" <menuitem action='Statusbar'/>\n"
-" </menu>\n"
-" <menu action='InputMenu'>\n"
-#ifdef USE_SMARTCARD
-" <menuitem action='InsertSmartcard'/>\n"
-" <menuitem action='RemoveSmartcard'/>\n"
-#endif
-#ifdef USE_USBREDIR
-" <menuitem action='SelectUsbDevices'/>\n"
-#endif
-" </menu>\n"
-" <menu action='OptionMenu'>\n"
-" <menuitem action='grab-keyboard'/>\n"
-" <menuitem action='grab-mouse'/>\n"
-" <menuitem action='MouseMode'/>\n"
-" <menuitem action='resize-guest'/>\n"
-" <menuitem action='scaling'/>\n"
-" <menuitem action='disable-inputs'/>\n"
-" <menuitem action='sync-modifiers'/>\n"
-" <menuitem action='auto-clipboard'/>\n"
-" <menuitem action='auto-usbredir'/>\n"
-" <menu action='CompressionMenu'>\n"
-" <menuitem action='auto-glz'/>\n"
-" <menuitem action='auto-lz'/>\n"
-" <menuitem action='quic'/>\n"
-" <menuitem action='glz'/>\n"
-" <menuitem action='lz'/>\n"
-#ifdef USE_LZ4
-" <menuitem action='lz4'/>\n"
-#endif
-" <menuitem action='off'/>\n"
-" </menu>\n"
-" </menu>\n"
-" <menu action='HelpMenu'>\n"
-" <menuitem action='About'/>\n"
-" </menu>\n"
-" </menubar>\n"
-" <toolbar action='ToolBar'>\n"
-" <toolitem action='Close'/>\n"
-" <separator/>\n"
-" <toolitem action='CopyToGuest'/>\n"
-" <toolitem action='PasteFromGuest'/>\n"
-" <separator/>\n"
-" <toolitem action='Fullscreen'/>\n"
-" </toolbar>\n"
-"</ui>\n";
-
-static gboolean is_gtk_session_property(const gchar *property)
-{
- int i;
-
- for (i = 0; i < G_N_ELEMENTS(spice_gtk_session_properties); i++) {
- if (!strcmp(spice_gtk_session_properties[i], property)) {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static void recent_item_activated_cb(GtkRecentChooser *chooser, gpointer data)
-{
- GtkRecentInfo *info;
- struct spice_connection *conn;
- const char *uri;
-
- info = gtk_recent_chooser_get_current_item(chooser);
-
- uri = gtk_recent_info_get_uri(info);
- g_return_if_fail(uri != NULL);
-
- conn = connection_new();
- g_object_set(conn->session, "uri", uri, NULL);
- gtk_recent_info_unref(info);
- connection_connect(conn);
-}
-
-static void compression_cb(GtkRadioAction *action G_GNUC_UNUSED,
- GtkRadioAction *current,
- gpointer user_data)
-{
- spice_display_change_preferred_compression(SPICE_CHANNEL(user_data),
- gtk_radio_action_get_current_value(current));
-}
-
-static void
-spice_window_class_init (SpiceWindowClass *klass)
-{
-}
-
-static void
-spice_window_init (SpiceWindow *self)
-{
-}
-
-static SpiceWindow *create_spice_window(spice_connection *conn, SpiceChannel *channel, int id, gint monitor_id)
-{
- char title[32];
- SpiceWindow *win;
- GtkAction *toggle;
- gboolean state;
- GtkWidget *vbox, *frame;
- GError *err = NULL;
- int i;
- SpiceGrabSequence *seq;
-
- win = g_object_new(SPICE_TYPE_WINDOW, NULL);
- win->id = id;
- win->monitor_id = monitor_id;
- win->conn = conn;
- win->display_channel = channel;
-
- /* toplevel */
- win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- if (spicy_title == NULL) {
- snprintf(title, sizeof(title), "spice display %d:%d", id, monitor_id);
- } else {
- snprintf(title, sizeof(title), "%s", spicy_title);
- }
-
- gtk_window_set_title(GTK_WINDOW(win->toplevel), title);
- g_signal_connect(G_OBJECT(win->toplevel), "window-state-event",
- G_CALLBACK(window_state_cb), win);
- g_signal_connect(G_OBJECT(win->toplevel), "delete-event",
- G_CALLBACK(delete_cb), win);
-
- /* menu + toolbar */
- win->ui = gtk_ui_manager_new();
- win->ag = gtk_action_group_new("MenuActions");
- gtk_action_group_add_actions(win->ag, entries, G_N_ELEMENTS(entries), win);
- gtk_action_group_add_toggle_actions(win->ag, tentries,
- G_N_ELEMENTS(tentries), win);
- gtk_action_group_add_radio_actions(win->ag, compression_entries,
- G_N_ELEMENTS(compression_entries), -1,
- G_CALLBACK(compression_cb), win->display_channel);
- if (!spice_channel_test_capability(win->display_channel, SPICE_DISPLAY_CAP_PREF_COMPRESSION)) {
- GtkAction *compression_menu_action = gtk_action_group_get_action(win->ag, "CompressionMenu");
- gtk_action_set_sensitive(compression_menu_action, FALSE);
- }
- gtk_ui_manager_insert_action_group(win->ui, win->ag, 0);
- gtk_window_add_accel_group(GTK_WINDOW(win->toplevel),
- gtk_ui_manager_get_accel_group(win->ui));
-
- err = NULL;
- if (!gtk_ui_manager_add_ui_from_string(win->ui, ui_xml, -1, &err)) {
- g_warning("building menus failed: %s", err->message);
- g_error_free(err);
- exit(1);
- }
- win->menubar = gtk_ui_manager_get_widget(win->ui, "/MainMenu");
- win->toolbar = gtk_ui_manager_get_widget(win->ui, "/ToolBar");
-
- /* recent menu */
- win->ritem = gtk_ui_manager_get_widget
- (win->ui, "/MainMenu/FileMenu/FileRecentMenu");
-
- GtkRecentFilter *rfilter;
-
- win->rmenu = gtk_recent_chooser_menu_new();
- gtk_recent_chooser_set_show_icons(GTK_RECENT_CHOOSER(win->rmenu), FALSE);
- rfilter = gtk_recent_filter_new();
- gtk_recent_filter_add_mime_type(rfilter, "application/x-spice");
- gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(win->rmenu), rfilter);
- gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(win->rmenu), FALSE);
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->ritem), win->rmenu);
- g_signal_connect(win->rmenu, "item-activated",
- G_CALLBACK(recent_item_activated_cb), win);
-
- /* spice display */
- win->spice = GTK_WIDGET(spice_display_new_with_monitor(conn->session, id, monitor_id));
- seq = spice_grab_sequence_new_from_string("Shift_L+F12");
- spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
- spice_grab_sequence_free(seq);
-
- g_signal_connect(G_OBJECT(win->spice), "mouse-grab",
- G_CALLBACK(mouse_grab_cb), win);
- g_signal_connect(G_OBJECT(win->spice), "keyboard-grab",
- G_CALLBACK(keyboard_grab_cb), win);
- g_signal_connect(G_OBJECT(win->spice), "grab-keys-pressed",
- G_CALLBACK(grab_keys_pressed_cb), win);
-
- /* status line */
- win->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
-
- win->status = gtk_label_new("status line");
- gtk_misc_set_alignment(GTK_MISC(win->status), 0, 0.5);
- gtk_misc_set_padding(GTK_MISC(win->status), 3, 1);
- update_status_window(win);
-
- frame = gtk_frame_new(NULL);
- gtk_box_pack_start(GTK_BOX(win->statusbar), frame, TRUE, TRUE, 0);
- gtk_container_add(GTK_CONTAINER(frame), win->status);
-
- for (i = 0; i < STATE_MAX; i++) {
- win->st[i] = gtk_label_new("?");
- gtk_label_set_width_chars(GTK_LABEL(win->st[i]), 5);
- frame = gtk_frame_new(NULL);
- gtk_box_pack_end(GTK_BOX(win->statusbar), frame, FALSE, FALSE, 0);
- gtk_container_add(GTK_CONTAINER(frame), win->st[i]);
- }
-
- /* Make a vbox and put stuff in */
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
- gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
- gtk_container_add(GTK_CONTAINER(win->toplevel), vbox);
- gtk_box_pack_start(GTK_BOX(vbox), win->menubar, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(vbox), win->toolbar, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(vbox), win->spice, TRUE, TRUE, 0);
- gtk_box_pack_end(GTK_BOX(vbox), win->statusbar, FALSE, TRUE, 0);
-
- /* show window */
- if (fullscreen)
- gtk_window_fullscreen(GTK_WINDOW(win->toplevel));
-
- gtk_widget_show_all(vbox);
- restore_configuration(win);
-
- /* init toggle actions */
- for (i = 0; i < G_N_ELEMENTS(spice_display_properties); i++) {
- toggle = gtk_action_group_get_action(win->ag,
- spice_display_properties[i]);
- g_object_get(win->spice, spice_display_properties[i], &state, NULL);
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
- }
-
- for (i = 0; i < G_N_ELEMENTS(spice_gtk_session_properties); i++) {
- char notify[64];
-
- toggle = gtk_action_group_get_action(win->ag,
- spice_gtk_session_properties[i]);
- g_object_get(win->conn->gtk_session, spice_gtk_session_properties[i],
- &state, NULL);
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
-
- snprintf(notify, sizeof(notify), "notify::%s",
- spice_gtk_session_properties[i]);
- spice_g_signal_connect_object(win->conn->gtk_session, notify,
- G_CALLBACK(menu_cb_conn_bool_prop_changed),
- win, 0);
- }
-
- update_edit_menu_window(win);
-
- toggle = gtk_action_group_get_action(win->ag, "Toolbar");
- state = gtk_widget_get_visible(win->toolbar);
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
-
- toggle = gtk_action_group_get_action(win->ag, "Statusbar");
- state = gtk_widget_get_visible(win->statusbar);
- gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
-
-#ifdef USE_SMARTCARD
- gboolean smartcard;
-
- enable_smartcard_actions(win, NULL, FALSE, FALSE);
- g_object_get(G_OBJECT(conn->session),
- "enable-smartcard", &smartcard,
- NULL);
- if (smartcard) {
- g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-added",
- (GCallback)reader_added_cb, win);
- g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-removed",
- (GCallback)reader_removed_cb, win);
- g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-inserted",
- (GCallback)card_inserted_cb, win);
- g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-removed",
- (GCallback)card_removed_cb, win);
- }
-#endif
-
-#ifndef USE_USBREDIR
- GtkAction *usbredir = gtk_action_group_get_action(win->ag, "auto-usbredir");
- gtk_action_set_visible(usbredir, FALSE);
-#endif
-
- gtk_widget_grab_focus(win->spice);
-
- return win;
-}
-
-static void destroy_spice_window(SpiceWindow *win)
-{
- if (win == NULL)
- return;
-
- SPICE_DEBUG("destroy window (#%d:%d)", win->id, win->monitor_id);
- g_object_unref(win->ag);
- g_object_unref(win->ui);
- gtk_widget_destroy(win->toplevel);
- g_object_unref(win);
-}
-
-/* ------------------------------------------------------------------ */
-
-static void recent_add(SpiceSession *session)
-{
- GtkRecentManager *recent;
- GtkRecentData meta = {
- .mime_type = (char*)"application/x-spice",
- .app_name = (char*)"spicy",
- .app_exec = (char*)"spicy --uri=%u",
- };
- char *uri;
-
- g_object_get(session, "uri", &uri, NULL);
- SPICE_DEBUG("%s: %s", __FUNCTION__, uri);
-
- recent = gtk_recent_manager_get_default();
- if (g_str_has_prefix(uri, "spice://"))
- meta.display_name = uri + 8;
- else if (g_str_has_prefix(uri, "spice+unix://"))
- meta.display_name = uri + 13;
- else
- g_return_if_reached();
-
- if (!gtk_recent_manager_add_full(recent, uri, &meta))
- g_warning("Recent item couldn't be added successfully");
-
- g_free(uri);
-}
-
-static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
- gpointer data)
-{
- const GError *error = NULL;
- spice_connection *conn = data;
- char password[64];
- int rc;
-
- switch (event) {
- case SPICE_CHANNEL_OPENED:
- g_message("main channel: opened");
- recent_add(conn->session);
- break;
- case SPICE_CHANNEL_SWITCHING:
- g_message("main channel: switching host");
- break;
- case SPICE_CHANNEL_CLOSED:
- /* this event is only sent if the channel was succesfully opened before */
- g_message("main channel: closed");
- connection_disconnect(conn);
- break;
- case SPICE_CHANNEL_ERROR_IO:
- connection_disconnect(conn);
- break;
- case SPICE_CHANNEL_ERROR_TLS:
- case SPICE_CHANNEL_ERROR_LINK:
- case SPICE_CHANNEL_ERROR_CONNECT:
- error = spice_channel_get_error(channel);
- g_message("main channel: failed to connect");
- if (error) {
- g_message("channel error: %s", error->message);
- }
-
- if (spicy_connect_dialog(conn->session)) {
- connection_connect(conn);
- } else {
- connection_disconnect(conn);
- }
- break;
- case SPICE_CHANNEL_ERROR_AUTH:
- g_warning("main channel: auth failure (wrong password?)");
- strcpy(password, "");
- /* FIXME i18 */
- rc = ask_user(NULL, "Authentication",
- "Please enter the spice server password",
- password, sizeof(password), true);
- if (rc == 0) {
- g_object_set(conn->session, "password", password, NULL);
- connection_connect(conn);
- } else {
- connection_disconnect(conn);
- }
- break;
- default:
- /* TODO: more sophisticated error handling */
- g_warning("unknown main channel event: %u", event);
- /* connection_disconnect(conn); */
- break;
- }
-}
-
-static void main_mouse_update(SpiceChannel *channel, gpointer data)
-{
- spice_connection *conn = data;
- gint mode;
-
- g_object_get(channel, "mouse-mode", &mode, NULL);
- switch (mode) {
- case SPICE_MOUSE_MODE_SERVER:
- conn->mouse_state = "server";
- break;
- case SPICE_MOUSE_MODE_CLIENT:
- conn->mouse_state = "client";
- break;
- default:
- conn->mouse_state = "?";
- break;
- }
- update_status(conn);
-}
-
-static void main_agent_update(SpiceChannel *channel, gpointer data)
-{
- spice_connection *conn = data;
-
- g_object_get(channel, "agent-connected", &conn->agent_connected, NULL);
- conn->agent_state = conn->agent_connected ? "yes" : "no";
- update_status(conn);
- update_edit_menu(conn);
-}
-
-static void inputs_modifiers(SpiceChannel *channel, gpointer data)
-{
- spice_connection *conn = data;
- int m, i;
-
- g_object_get(channel, "key-modifiers", &m, NULL);
- for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
- if (conn->wins[i] == NULL)
- continue;
-
- gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_SCROLL_LOCK]),
- m & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK ? "SCROLL" : "");
- gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_CAPS_LOCK]),
- m & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK ? "CAPS" : "");
- gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_NUM_LOCK]),
- m & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK ? "NUM" : "");
- }
-}
-
-static void display_mark(SpiceChannel *channel, gint mark, SpiceWindow *win)
-{
- g_return_if_fail(win != NULL);
- g_return_if_fail(win->toplevel != NULL);
-
- if (mark == TRUE) {
- gtk_widget_show(win->toplevel);
- } else {
- gtk_widget_hide(win->toplevel);
- }
-}
-
-static void update_auto_usbredir_sensitive(spice_connection *conn)
-{
-#ifdef USE_USBREDIR
- int i;
- GtkAction *ac;
- gboolean sensitive;
-
- sensitive = spice_session_has_channel_type(conn->session,
- SPICE_CHANNEL_USBREDIR);
- for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
- if (conn->wins[i] == NULL)
- continue;
- ac = gtk_action_group_get_action(conn->wins[i]->ag, "auto-usbredir");
- gtk_action_set_sensitive(ac, sensitive);
- }
-#endif
-}
-
-static SpiceWindow* get_window(spice_connection *conn, int channel_id, int monitor_id)
-{
- g_return_val_if_fail(channel_id < CHANNELID_MAX, NULL);
- g_return_val_if_fail(monitor_id < MONITORID_MAX, NULL);
-
- return conn->wins[channel_id * CHANNELID_MAX + monitor_id];
-}
-
-static void add_window(spice_connection *conn, SpiceWindow *win)
-{
- g_return_if_fail(win != NULL);
- g_return_if_fail(win->id < CHANNELID_MAX);
- g_return_if_fail(win->monitor_id < MONITORID_MAX);
- g_return_if_fail(conn->wins[win->id * CHANNELID_MAX + win->monitor_id] == NULL);
-
- SPICE_DEBUG("add display monitor %d:%d", win->id, win->monitor_id);
- conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = win;
-}
-
-static void del_window(spice_connection *conn, SpiceWindow *win)
-{
- if (win == NULL)
- return;
-
- g_return_if_fail(win->id < CHANNELID_MAX);
- g_return_if_fail(win->monitor_id < MONITORID_MAX);
-
- g_debug("del display monitor %d:%d", win->id, win->monitor_id);
- conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
- if (win->id > 0)
- spice_main_set_display_enabled(conn->main, win->id, FALSE);
- else
- spice_main_set_display_enabled(conn->main, win->monitor_id, FALSE);
- spice_main_send_monitor_config(conn->main);
-
- destroy_spice_window(win);
-}
-
-static void display_monitors(SpiceChannel *display, GParamSpec *pspec,
- spice_connection *conn)
-{
- GArray *monitors = NULL;
- int id;
- guint i;
-
- g_object_get(display,
- "channel-id", &id,
- "monitors", &monitors,
- NULL);
- g_return_if_fail(monitors != NULL);
-
- for (i = 0; i < monitors->len; i++) {
- SpiceWindow *w;
-
- if (!get_window(conn, id, i)) {
- w = create_spice_window(conn, display, id, i);
- add_window(conn, w);
- spice_g_signal_connect_object(display, "display-mark",
- G_CALLBACK(display_mark), w, 0);
- gtk_widget_show(w->toplevel);
- update_auto_usbredir_sensitive(conn);
- }
- }
-
- for (; i < MONITORID_MAX; i++)
- del_window(conn, get_window(conn, id, i));
-
- g_clear_pointer(&monitors, g_array_unref);
-}
-
-static void port_write_cb(GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- SpicePortChannel *port = SPICE_PORT_CHANNEL(source_object);
- GError *error = NULL;
-
- spice_port_write_finish(port, res, &error);
- if (error != NULL)
- g_warning("%s", error->message);
- g_clear_error(&error);
-}
-
-static void port_flushed_cb(GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- SpiceChannel *channel = SPICE_CHANNEL(source_object);
- GError *error = NULL;
-
- spice_channel_flush_finish(channel, res, &error);
- if (error != NULL)
- g_warning("%s", error->message);
- g_clear_error(&error);
-
- spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED);
-}
-
-static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
-{
- char buf[4096];
- gsize bytes_read;
- GIOStatus status;
-
- if (!(condition & G_IO_IN))
- return FALSE;
-
- status = g_io_channel_read_chars(gin, buf, sizeof(buf), &bytes_read, NULL);
- if (status != G_IO_STATUS_NORMAL)
- return FALSE;
-
- if (stdin_port != NULL)
- spice_port_write_async(stdin_port, buf, bytes_read, NULL, port_write_cb, NULL);
-
- return TRUE;
-}
-
-static void watch_stdin(void);
-
-static void port_opened(SpiceChannel *channel, GParamSpec *pspec,
- spice_connection *conn)
-{
- SpicePortChannel *port = SPICE_PORT_CHANNEL(channel);
- gchar *name = NULL;
- gboolean opened = FALSE;
-
- g_object_get(channel,
- "port-name", &name,
- "port-opened", &opened,
- NULL);
-
- g_printerr("port %p %s: %s\n", channel, name, opened ? "opened" : "closed");
-
- if (opened) {
- /* only send a break event and disconnect */
- if (g_strcmp0(name, "org.spice.spicy.break") == 0) {
- spice_port_event(port, SPICE_PORT_EVENT_BREAK);
- spice_channel_flush_async(channel, NULL, port_flushed_cb, conn);
- }
-
- /* handle the first spicy port and connect it to stdin/out */
- if (g_strcmp0(name, "org.spice.spicy") == 0 && stdin_port == NULL) {
- watch_stdin();
- stdin_port = port;
- }
- } else {
- if (port == stdin_port)
- stdin_port = NULL;
- }
-
- g_free(name);
-}
-
-static void port_data(SpicePortChannel *port,
- gpointer data, int size, spice_connection *conn)
-{
- int r;
-
- if (port != stdin_port)
- return;
-
- r = write(fileno(stdout), data, size);
- if (r != size) {
- g_warning("port write failed result %d/%d errno %d", r, size, errno);
- }
-}
-
-typedef struct {
- GtkWidget *vbox;
- GtkWidget *hbox;
- GtkWidget *progress;
- GtkWidget *label;
- GtkWidget *cancel;
-} TransferTaskWidgets;
-
-static void transfer_update_progress(GObject *object,
- GParamSpec *pspec,
- gpointer user_data)
-{
- spice_connection *conn = user_data;
- TransferTaskWidgets *widgets = g_hash_table_lookup(conn->transfers, object);
- g_return_if_fail(widgets);
- gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(widgets->progress),
- spice_file_transfer_task_get_progress(SPICE_FILE_TRANSFER_TASK(object)));
-}
-
-static void transfer_task_finished(SpiceFileTransferTask *task, GError *error, spice_connection *conn)
-{
- if (error)
- g_warning("%s", error->message);
- g_hash_table_remove(conn->transfers, task);
- if (!g_hash_table_size(conn->transfers))
- gtk_widget_hide(conn->transfer_dialog);
-}
-
-static void dialog_response_cb(GtkDialog *dialog,
- gint response_id,
- gpointer user_data)
-{
- spice_connection *conn = user_data;
- g_print("Reponse: %i\n", response_id);
-
- if (response_id == GTK_RESPONSE_CANCEL) {
- GHashTableIter iter;
- gpointer key, value;
-
- g_hash_table_iter_init(&iter, conn->transfers);
- while (g_hash_table_iter_next(&iter, &key, &value)) {
- SpiceFileTransferTask *task = key;
- spice_file_transfer_task_cancel(task);
- }
- }
-}
-
-static void
-task_cancel_cb(GtkButton *button,
- gpointer user_data)
-{
- SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(user_data);
- spice_file_transfer_task_cancel(task);
-}
-
-static TransferTaskWidgets *
-transfer_task_widgets_new(SpiceFileTransferTask *task)
-{
- char *filename;
- TransferTaskWidgets *widgets = g_new0(TransferTaskWidgets, 1);
-
- widgets->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- widgets->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
- widgets->cancel = gtk_button_new_with_label("Cancel");
-
- widgets->progress = gtk_progress_bar_new();
- filename = spice_file_transfer_task_get_filename(task);
- widgets->label = gtk_label_new(filename);
- g_free(filename);
-
- gtk_widget_set_halign(widgets->label, GTK_ALIGN_START);
- gtk_widget_set_valign(widgets->label, GTK_ALIGN_BASELINE);
- gtk_widget_set_valign(widgets->progress, GTK_ALIGN_CENTER);
- gtk_widget_set_hexpand(widgets->progress, TRUE);
- gtk_widget_set_valign(widgets->cancel, GTK_ALIGN_CENTER);
- gtk_widget_set_hexpand(widgets->progress, FALSE);
-
- gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->progress,
- TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->cancel,
- FALSE, TRUE, 0);
-
- gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->label,
- TRUE, TRUE, 0);
- gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->hbox,
- TRUE, TRUE, 0);
-
- g_signal_connect(widgets->cancel, "clicked",
- G_CALLBACK(task_cancel_cb), task);
-
- gtk_widget_show_all(widgets->vbox);
-
- return widgets;
-}
-
-static void
-transfer_task_widgets_free(TransferTaskWidgets *widgets)
-{
- /* child widgets will be destroyed automatically */
- gtk_widget_destroy(widgets->vbox);
- g_free(widgets);
-}
-
-static void spice_connection_add_task(spice_connection *conn, SpiceFileTransferTask *task)
-{
- TransferTaskWidgets *widgets;
- GtkWidget *content = NULL;
-
- g_signal_connect(task, "notify::progress",
- G_CALLBACK(transfer_update_progress), conn);
- g_signal_connect(task, "finished",
- G_CALLBACK(transfer_task_finished), conn);
- if (!conn->transfer_dialog) {
- conn->transfer_dialog = gtk_dialog_new_with_buttons("File Transfers",
- GTK_WINDOW(conn->wins[0]->toplevel), 0,
- "Cancel", GTK_RESPONSE_CANCEL, NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(conn->transfer_dialog),
- GTK_RESPONSE_CANCEL);
- gtk_window_set_resizable(GTK_WINDOW(conn->transfer_dialog), FALSE);
- g_signal_connect(conn->transfer_dialog, "response",
- G_CALLBACK(dialog_response_cb), conn);
- }
- gtk_widget_show(conn->transfer_dialog);
- content = gtk_dialog_get_content_area(GTK_DIALOG(conn->transfer_dialog));
- gtk_container_set_border_width(GTK_CONTAINER(content), 12);
-
- widgets = transfer_task_widgets_new(task);
- g_hash_table_insert(conn->transfers, g_object_ref(task), widgets);
- gtk_box_pack_start(GTK_BOX(content),
- widgets->vbox, TRUE, TRUE, 6);
-}
-
-static void new_file_transfer(SpiceMainChannel *main, SpiceFileTransferTask *task, gpointer user_data)
-{
- spice_connection *conn = user_data;
- g_debug("new file transfer task");
- spice_connection_add_task(conn, task);
-}
-
-static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
-{
- spice_connection *conn = data;
- int id;
-
- g_object_get(channel, "channel-id", &id, NULL);
- conn->channels++;
- SPICE_DEBUG("new channel (#%d)", id);
-
- if (SPICE_IS_MAIN_CHANNEL(channel)) {
- SPICE_DEBUG("new main channel");
- conn->main = SPICE_MAIN_CHANNEL(channel);
- g_signal_connect(channel, "channel-event",
- G_CALLBACK(main_channel_event), conn);
- g_signal_connect(channel, "main-mouse-update",
- G_CALLBACK(main_mouse_update), conn);
- g_signal_connect(channel, "main-agent-update",
- G_CALLBACK(main_agent_update), conn);
- g_signal_connect(channel, "new-file-transfer",
- G_CALLBACK(new_file_transfer), conn);
- main_mouse_update(channel, conn);
- main_agent_update(channel, conn);
- }
-
- if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
- if (id >= SPICE_N_ELEMENTS(conn->wins))
- return;
- if (conn->wins[id] != NULL)
- return;
- SPICE_DEBUG("new display channel (#%d)", id);
- g_signal_connect(channel, "notify::monitors",
- G_CALLBACK(display_monitors), conn);
- spice_channel_connect(channel);
- }
-
- if (SPICE_IS_INPUTS_CHANNEL(channel)) {
- SPICE_DEBUG("new inputs channel");
- g_signal_connect(channel, "inputs-modifiers",
- G_CALLBACK(inputs_modifiers), conn);
- }
-
- if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
- SPICE_DEBUG("new audio channel");
- conn->audio = spice_audio_get(s, NULL);
- }
-
- if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
- update_auto_usbredir_sensitive(conn);
- }
-
- if (SPICE_IS_PORT_CHANNEL(channel)) {
- g_signal_connect(channel, "notify::port-opened",
- G_CALLBACK(port_opened), conn);
- g_signal_connect(channel, "port-data",
- G_CALLBACK(port_data), conn);
- spice_channel_connect(channel);
- }
-}
-
-static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data)
-{
- spice_connection *conn = data;
- int id;
-
- g_object_get(channel, "channel-id", &id, NULL);
- if (SPICE_IS_MAIN_CHANNEL(channel)) {
- SPICE_DEBUG("zap main channel");
- conn->main = NULL;
- }
-
- if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
- if (id >= SPICE_N_ELEMENTS(conn->wins))
- return;
- SPICE_DEBUG("zap display channel (#%d)", id);
- /* FIXME destroy widget only */
- }
-
- if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
- SPICE_DEBUG("zap audio channel");
- }
-
- if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
- update_auto_usbredir_sensitive(conn);
- }
-
- if (SPICE_IS_PORT_CHANNEL(channel)) {
- if (SPICE_PORT_CHANNEL(channel) == stdin_port)
- stdin_port = NULL;
- }
-
- conn->channels--;
- if (conn->channels > 0) {
- return;
- }
-
- connection_destroy(conn);
-}
-
-static void migration_state(GObject *session,
- GParamSpec *pspec, gpointer data)
-{
- SpiceSessionMigration mig;
-
- g_object_get(session, "migration-state", &mig, NULL);
- if (mig == SPICE_SESSION_MIGRATION_SWITCHING)
- g_message("migrating session");
-}
-
-static spice_connection *connection_new(void)
-{
- spice_connection *conn;
- SpiceUsbDeviceManager *manager;
-
- conn = g_new0(spice_connection, 1);
- conn->session = spice_session_new();
- conn->gtk_session = spice_gtk_session_get(conn->session);
- g_signal_connect(conn->session, "channel-new",
- G_CALLBACK(channel_new), conn);
- g_signal_connect(conn->session, "channel-destroy",
- G_CALLBACK(channel_destroy), conn);
- g_signal_connect(conn->session, "notify::migration-state",
- G_CALLBACK(migration_state), conn);
-
- manager = spice_usb_device_manager_get(conn->session, NULL);
- if (manager) {
- g_signal_connect(manager, "auto-connect-failed",
- G_CALLBACK(usb_connect_failed), NULL);
- g_signal_connect(manager, "device-error",
- G_CALLBACK(usb_connect_failed), NULL);
- }
-
- conn->transfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
- g_object_unref,
- (GDestroyNotify)transfer_task_widgets_free);
- connections++;
- SPICE_DEBUG("%s (%d)", __FUNCTION__, connections);
- return conn;
-}
-
-static void connection_connect(spice_connection *conn)
-{
- conn->disconnecting = false;
- spice_session_connect(conn->session);
-}
-
-static void connection_disconnect(spice_connection *conn)
-{
- if (conn->disconnecting)
- return;
- conn->disconnecting = true;
- spice_session_disconnect(conn->session);
-}
-
-static void connection_destroy(spice_connection *conn)
-{
- g_object_unref(conn->session);
- g_hash_table_unref(conn->transfers);
- free(conn);
-
- connections--;
- SPICE_DEBUG("%s (%d)", __FUNCTION__, connections);
- if (connections > 0) {
- return;
- }
-
- g_main_loop_quit(mainloop);
-}
-
-/* ------------------------------------------------------------------ */
-
-static GOptionEntry cmd_entries[] = {
- {
- .long_name = "full-screen",
- .short_name = 'f',
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &fullscreen,
- .description = "Open in full screen mode",
- },{
- .long_name = "version",
- .arg = G_OPTION_ARG_NONE,
- .arg_data = &version,
- .description = "Display version and quit",
- },{
- .long_name = "title",
- .arg = G_OPTION_ARG_STRING,
- .arg_data = &spicy_title,
- .description = "Set the window title",
- .arg_description = "<title>",
- },{
- /* end of list */
- }
-};
-
-static void usb_connect_failed(GObject *object,
- SpiceUsbDevice *device,
- GError *error,
- gpointer data)
-{
- GtkWidget *dialog;
-
- if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
- return;
-
- dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- "USB redirection error");
- gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
- "%s", error->message);
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
-}
-
-static void setup_terminal(gboolean reset)
-{
- int stdinfd = fileno(stdin);
-
- if (!isatty(stdinfd))
- return;
-
-#ifdef HAVE_TERMIOS_H
- struct termios tios;
- static struct termios saved_tios;
- static bool saved = false;
-
- if (reset) {
- if (!saved)
- return;
- tios = saved_tios;
- } else {
- tcgetattr(stdinfd, &tios);
- saved_tios = tios;
- saved = true;
- tios.c_lflag &= ~(ICANON | ECHO);
- }
-
- tcsetattr(stdinfd, TCSANOW, &tios);
-#endif
-}
-
-static void watch_stdin(void)
-{
- int stdinfd = fileno(stdin);
- GIOChannel *gin;
-
- setup_terminal(false);
- gin = g_io_channel_unix_new(stdinfd);
- g_io_channel_set_flags(gin, G_IO_FLAG_NONBLOCK, NULL);
- g_io_add_watch(gin, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, input_cb, NULL);
-}
-
-int main(int argc, char *argv[])
-{
- GError *error = NULL;
- GOptionContext *context;
- spice_connection *conn;
- gchar *conf_file, *conf;
- char *host = NULL, *port = NULL, *tls_port = NULL, *unix_path = NULL;
-
- keyfile = g_key_file_new();
-
- int mode = S_IRWXU;
- conf_file = g_build_filename(g_get_user_config_dir(), "spicy", NULL);
- if (g_mkdir_with_parents(conf_file, mode) == -1)
- SPICE_DEBUG("failed to create config directory");
- g_free(conf_file);
-
- conf_file = g_build_filename(g_get_user_config_dir(), "spicy", "settings", NULL);
- if (!g_key_file_load_from_file(keyfile, conf_file,
- G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
- SPICE_DEBUG("Couldn't load configuration: %s", error->message);
- g_clear_error(&error);
- }
-
- /* parse opts */
- gtk_init(&argc, &argv);
- context = g_option_context_new("- spice client test application");
- g_option_context_set_summary(context, "Gtk+ test client to connect to Spice servers.");
- g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
- g_option_context_add_group(context, spice_get_option_group());
- g_option_context_set_main_group(context, spice_cmdline_get_option_group());
- g_option_context_add_main_entries(context, cmd_entries, NULL);
- g_option_context_add_group(context, gtk_get_option_group(TRUE));
- if (!g_option_context_parse (context, &argc, &argv, &error)) {
- g_print("option parsing failed: %s\n", error->message);
- exit(1);
- }
- g_option_context_free(context);
-
- if (version) {
- g_print("spicy " PACKAGE_VERSION "\n");
- exit(0);
- }
-
- mainloop = g_main_loop_new(NULL, false);
-
- conn = connection_new();
- spice_set_session_option(conn->session);
- spice_cmdline_session_setup(conn->session);
-
- g_object_get(conn->session,
- "unix-path", &unix_path,
- "host", &host,
- "port", &port,
- "tls-port", &tls_port,
- NULL);
- /* If user doesn't provide hostname and port, show the dialog window
- instead of connecting to server automatically */
- if ((host == NULL || (port == NULL && tls_port == NULL)) && unix_path == NULL) {
- if (!spicy_connect_dialog(conn->session)) {
- exit(0);
- }
- }
- g_free(host);
- g_free(port);
- g_free(tls_port);
- g_free(unix_path);
-
- connection_connect(conn);
- if (connections > 0)
- g_main_loop_run(mainloop);
- g_main_loop_unref(mainloop);
-
- if ((conf = g_key_file_to_data(keyfile, NULL, &error)) == NULL ||
- !g_file_set_contents(conf_file, conf, -1, &error)) {
- SPICE_DEBUG("Couldn't save configuration: %s", error->message);
- g_error_free(error);
- error = NULL;
- }
-
- g_free(conf_file);
- g_free(conf);
- g_key_file_free(keyfile);
-
- g_free(spicy_title);
-
- setup_terminal(true);
- return 0;
-}
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..0bdb3c5
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,69 @@
+bin_PROGRAMS = spicy-stats spicy-screenshot
+
+TOOLS_CPPFLAGS = \
+ -DSPICE_COMPILATION \
+ -I$(top_builddir)/src \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ $(COMMON_CFLAGS) \
+ $(GLIB2_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(SMARTCARD_CFLAGS) \
+ $(SPICE_CFLAGS) \
+ $(NULL)
+
+if WITH_GTK
+bin_PROGRAMS += spicy
+TOOLS_CPPFLAGS += $(GTK_CFLAGS)
+endif
+
+spicy_SOURCES = \
+ spicy.c \
+ spicy-connect.h \
+ spicy-connect.c \
+ spice-cmdline.h \
+ spice-cmdline.c \
+ $(NULL)
+
+spicy_LDADD = \
+ $(top_builddir)/src/libspice-client-gtk-3.0.la \
+ $(top_builddir)/src/libspice-client-glib-2.0.la \
+ $(NULL)
+
+# FIXME: GtkAction and lots of GtkUIManager APIs are deprecated
+spicy_CPPFLAGS = \
+ $(TOOLS_CPPFLAGS) \
+ -DSPICE_DISABLE_DEPRECATED \
+ -Wno-deprecated-declarations \
+ $(NULL)
+
+spicy_screenshot_SOURCES = \
+ spicy-screenshot.c \
+ spice-cmdline.h \
+ spice-cmdline.c \
+ $(NULL)
+
+spicy_screenshot_LDADD = \
+ $(top_builddir)/src/libspice-client-glib-2.0.la \
+ $(GOBJECT2_LIBS) \
+ $(NULL)
+
+spicy_screenshot_CPPFLAGS = \
+ $(TOOLS_CPPFLAGS) \
+ $(NULL)
+
+spicy_stats_SOURCES = \
+ spicy-stats.c \
+ spice-cmdline.h \
+ spice-cmdline.c \
+ $(NULL)
+
+spicy_stats_LDADD = \
+ $(top_builddir)/src/libspice-client-glib-2.0.la \
+ $(NULL)
+
+spicy_stats_CPPFLAGS = \
+ $(TOOLS_CPPFLAGS) \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/tools/spice-cmdline.c b/tools/spice-cmdline.c
new file mode 100644
index 0000000..4b6f4c2
--- /dev/null
+++ b/tools/spice-cmdline.c
@@ -0,0 +1,98 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include "spice-client.h"
+#include "spice-common.h"
+#include "spice-cmdline.h"
+
+static char *host;
+static char *port;
+static char *tls_port;
+static char *password;
+static char *uri;
+
+static GOptionEntry spice_entries[] = {
+ {
+ .long_name = "uri",
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &uri,
+ .description = N_("Spice server uri"),
+ .arg_description = N_("<uri>"),
+ },{
+ .long_name = "host",
+ .short_name = 'h',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &host,
+ .description = N_("Spice server address"),
+ .arg_description = N_("<host>"),
+ },{
+ .long_name = "port",
+ .short_name = 'p',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &port,
+ .description = N_("Spice server port"),
+ .arg_description = N_("<port>"),
+ },{
+ .long_name = "secure-port",
+ .short_name = 's',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &tls_port,
+ .description = N_("Spice server secure port"),
+ .arg_description = N_("<port>"),
+ },{
+ .long_name = "password",
+ .short_name = 'w',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &password,
+ .description = N_("Server password"),
+ .arg_description = N_("<password>"),
+ },{
+ /* end of list */
+ }
+};
+
+GOptionGroup *spice_cmdline_get_option_group(void)
+{
+ GOptionGroup *grp;
+
+ grp = g_option_group_new("spice",
+ _("Spice connection options:"),
+ _("Show Spice options"),
+ NULL, NULL);
+ g_option_group_add_entries(grp, spice_entries);
+
+ return grp;
+}
+
+void spice_cmdline_session_setup(SpiceSession *session)
+{
+ g_return_if_fail(SPICE_IS_SESSION(session));
+
+ if (uri)
+ g_object_set(session, "uri", uri, NULL);
+ if (host)
+ g_object_set(session, "host", host, NULL);
+ if (port)
+ g_object_set(session, "port", port, NULL);
+ if (tls_port)
+ g_object_set(session, "tls-port", tls_port, NULL);
+ if (password)
+ g_object_set(session, "password", password, NULL);
+}
diff --git a/tools/spice-cmdline.h b/tools/spice-cmdline.h
new file mode 100644
index 0000000..11a8086
--- /dev/null
+++ b/tools/spice-cmdline.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SPICE_CMDLINE_H_
+# define SPICE_CMDLINE_H_
+
+G_BEGIN_DECLS
+
+GOptionGroup *spice_cmdline_get_option_group(void);
+void spice_cmdline_session_setup(SpiceSession *session);
+
+G_END_DECLS
+
+#endif // SPICE_CMDLINE_H_
diff --git a/tools/spicy-connect.c b/tools/spicy-connect.c
new file mode 100644
index 0000000..39555a6
--- /dev/null
+++ b/tools/spicy-connect.c
@@ -0,0 +1,248 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010-2015 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "spice-common.h"
+#include "spicy-connect.h"
+
+typedef struct
+{
+ gboolean connecting;
+ GMainLoop *loop;
+ SpiceSession *session;
+} ConnectionInfo;
+
+static struct {
+ const char *text;
+ const char *prop;
+ GtkWidget *entry;
+} connect_entries[] = {
+ { .text = "Hostname", .prop = "host" },
+ { .text = "Port", .prop = "port" },
+ { .text = "TLS Port", .prop = "tls-port" },
+};
+
+static gboolean can_connect(void)
+{
+ if ((gtk_entry_get_text_length(GTK_ENTRY(connect_entries[0].entry)) > 0) &&
+ ((gtk_entry_get_text_length(GTK_ENTRY(connect_entries[1].entry)) > 0) ||
+ (gtk_entry_get_text_length(GTK_ENTRY(connect_entries[2].entry)) > 0)))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void set_connection_info(SpiceSession *session)
+{
+ const gchar *txt;
+ int i;
+
+ for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
+ txt = gtk_entry_get_text(GTK_ENTRY(connect_entries[i].entry));
+ g_object_set(session, connect_entries[i].prop, txt, NULL);
+ }
+}
+
+static gboolean close_cb(gpointer data)
+{
+ ConnectionInfo *info = data;
+ info->connecting = FALSE;
+ if (g_main_loop_is_running(info->loop))
+ g_main_loop_quit(info->loop);
+
+ return TRUE;
+}
+
+static void entry_changed_cb(GtkEditable* entry, gpointer data)
+{
+ GtkButton *connect_button = data;
+ gtk_widget_set_sensitive(GTK_WIDGET(connect_button), can_connect());
+}
+
+static gboolean entry_focus_in_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ GtkRecentChooser *recent = GTK_RECENT_CHOOSER(data);
+ gtk_recent_chooser_unselect_all(recent);
+ return TRUE;
+}
+
+static gboolean key_pressed_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ gboolean tst;
+ if (event->type == GDK_KEY_PRESS) {
+ switch (event->key.keyval) {
+ case GDK_KEY_Escape:
+ g_signal_emit_by_name(GTK_WIDGET(data), "delete-event", NULL, &tst);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void recent_selection_changed_dialog_cb(GtkRecentChooser *chooser, gpointer data)
+{
+ GtkRecentInfo *info;
+ gchar *txt = NULL;
+ const gchar *uri;
+ SpiceSession *session = data;
+ int i;
+
+ info = gtk_recent_chooser_get_current_item(chooser);
+ if (info == NULL)
+ return;
+
+ uri = gtk_recent_info_get_uri(info);
+ g_return_if_fail(uri != NULL);
+
+ g_object_set(session, "uri", uri, NULL);
+
+ for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
+ g_object_get(session, connect_entries[i].prop, &txt, NULL);
+ gtk_entry_set_text(GTK_ENTRY(connect_entries[i].entry), txt ? txt : "");
+ g_free(txt);
+ }
+
+ gtk_recent_info_unref(info);
+}
+
+static void connect_cb(gpointer data)
+{
+ ConnectionInfo *info = data;
+ if (can_connect())
+ {
+ info->connecting = TRUE;
+ set_connection_info(info->session);
+ if (g_main_loop_is_running(info->loop))
+ g_main_loop_quit(info->loop);
+ }
+}
+
+gboolean spicy_connect_dialog(SpiceSession *session)
+{
+ GtkWidget *connect_button, *cancel_button, *label;
+ GtkBox *main_box, *recent_box, *button_box;
+ GtkWindow *window;
+ GtkGrid *grid;
+ int i;
+
+ ConnectionInfo info = {
+ FALSE,
+ NULL,
+ session
+ };
+
+ /* Create the widgets */
+ window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+ gtk_window_set_title(window, "Connect to SPICE");
+ gtk_window_set_resizable(window, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 5);
+
+ main_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(main_box));
+
+ grid = GTK_GRID(gtk_grid_new());
+ gtk_box_pack_start(main_box, GTK_WIDGET(grid), FALSE, TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(grid), 5);
+ gtk_grid_set_row_spacing(grid, 5);
+ gtk_grid_set_column_spacing(grid, 5);
+
+ for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
+ gchar *txt;
+ label = gtk_label_new(connect_entries[i].text);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_grid_attach(grid, label, 0, i, 1, 1);
+ connect_entries[i].entry = GTK_WIDGET(gtk_entry_new());
+ gtk_grid_attach(grid, connect_entries[i].entry, 1, i, 1, 1);
+ g_object_get(session, connect_entries[i].prop, &txt, NULL);
+ SPICE_DEBUG("%s: #%i [%s]: \"%s\"",
+ __FUNCTION__, i, connect_entries[i].prop, txt);
+ if (txt) {
+ gtk_entry_set_text(GTK_ENTRY(connect_entries[i].entry), txt);
+ g_free(txt);
+ }
+ }
+
+ recent_box = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));
+ gtk_box_pack_start(main_box, GTK_WIDGET(recent_box), TRUE, TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(recent_box), 5);
+
+ label = gtk_label_new("Recent connections:");
+ gtk_box_pack_start(recent_box, label, FALSE, TRUE, 0);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+
+ button_box = GTK_BOX(gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL));
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(button_box, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(button_box), 5);
+ connect_button = gtk_button_new_with_label("Connect");
+ cancel_button = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_start(button_box, cancel_button, FALSE, TRUE, 0);
+ gtk_box_pack_start(button_box, connect_button, FALSE, TRUE, 1);
+
+ gtk_box_pack_start(main_box, GTK_WIDGET(button_box), FALSE, TRUE, 0);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(connect_button), can_connect());
+
+ g_signal_connect(window, "key-press-event",
+ G_CALLBACK(key_pressed_cb), window);
+ g_signal_connect_swapped(window, "delete-event",
+ G_CALLBACK(close_cb), &info);
+ g_signal_connect_swapped(connect_button, "clicked",
+ G_CALLBACK(connect_cb), &info);
+ g_signal_connect_swapped(cancel_button, "clicked",
+ G_CALLBACK(close_cb), &info);
+
+ GtkRecentFilter *rfilter;
+ GtkWidget *recent;
+
+ recent = GTK_WIDGET(gtk_recent_chooser_widget_new());
+ gtk_recent_chooser_set_show_icons(GTK_RECENT_CHOOSER(recent), FALSE);
+ gtk_box_pack_start(recent_box, recent, TRUE, TRUE, 0);
+
+ rfilter = gtk_recent_filter_new();
+ gtk_recent_filter_add_mime_type(rfilter, "application/x-spice");
+ gtk_recent_chooser_set_filter(GTK_RECENT_CHOOSER(recent), rfilter);
+ gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(recent), FALSE);
+ g_signal_connect(recent, "selection-changed",
+ G_CALLBACK(recent_selection_changed_dialog_cb), session);
+ g_signal_connect_swapped(recent, "item-activated",
+ G_CALLBACK(connect_cb), &info);
+
+ for (i = 0; i < SPICE_N_ELEMENTS(connect_entries); i++) {
+ g_signal_connect_swapped(connect_entries[i].entry, "activate",
+ G_CALLBACK(connect_cb), &info);
+ g_signal_connect(connect_entries[i].entry, "changed",
+ G_CALLBACK(entry_changed_cb), connect_button);
+ g_signal_connect(connect_entries[i].entry, "focus-in-event",
+ G_CALLBACK(entry_focus_in_cb), recent);
+ }
+
+ /* show and wait for response */
+ gtk_widget_show_all(GTK_WIDGET(window));
+
+ info.loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(info.loop);
+
+ gtk_widget_destroy(GTK_WIDGET(window));
+
+ return info.connecting;
+}
diff --git a/tools/spicy-connect.h b/tools/spicy-connect.h
new file mode 100644
index 0000000..56b2d80
--- /dev/null
+++ b/tools/spicy-connect.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010-2015 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SPICY_CONNECT_H
+#define SPICY_CONNECT_H
+
+#include "spice-widget.h"
+
+gboolean spicy_connect_dialog(SpiceSession *session);
+
+#endif
diff --git a/tools/spicy-screenshot.c b/tools/spicy-screenshot.c
new file mode 100644
index 0000000..68f9335
--- /dev/null
+++ b/tools/spicy-screenshot.c
@@ -0,0 +1,194 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include "config.h"
+
+#include "spice-client.h"
+#include "spice-common.h"
+#include "spice-cmdline.h"
+
+/* config */
+static const char *outf = "spicy-screenshot.ppm";
+static gboolean version = FALSE;
+
+/* state */
+static SpiceSession *session;
+static GMainLoop *mainloop;
+
+enum SpiceSurfaceFmt d_format;
+gint d_width, d_height, d_stride;
+gpointer d_data;
+
+/* ------------------------------------------------------------------ */
+
+static void primary_create(SpiceChannel *channel, gint format,
+ gint width, gint height, gint stride,
+ gint shmid, gpointer imgdata, gpointer data)
+{
+ SPICE_DEBUG("%s: %dx%d, format %d", __FUNCTION__, width, height, format);
+ d_format = format;
+ d_width = width;
+ d_height = height;
+ d_stride = stride;
+ d_data = imgdata;
+}
+
+static int write_ppm_32(void)
+{
+ FILE *fp;
+ uint8_t *p;
+ int n;
+
+ fp = fopen(outf,"w");
+ if (NULL == fp) {
+ fprintf(stderr, "%s: can't open %s: %s\n", g_get_prgname(), outf, strerror(errno));
+ return -1;
+ }
+ fprintf(fp, "P6\n%d %d\n255\n",
+ d_width, d_height);
+ n = d_width * d_height;
+ p = d_data;
+ while (n > 0) {
+#ifdef WORDS_BIGENDIAN
+ fputc(p[1], fp);
+ fputc(p[2], fp);
+ fputc(p[3], fp);
+#else
+ fputc(p[2], fp);
+ fputc(p[1], fp);
+ fputc(p[0], fp);
+#endif
+ p += 4;
+ n--;
+ }
+ fclose(fp);
+ return 0;
+}
+
+static void invalidate(SpiceChannel *channel,
+ gint x, gint y, gint w, gint h, gpointer *data)
+{
+ int rc;
+
+ switch (d_format) {
+ case SPICE_SURFACE_FMT_32_xRGB:
+ rc = write_ppm_32();
+ break;
+ default:
+ fprintf(stderr, "unsupported spice surface format %u\n", d_format);
+ rc = -1;
+ break;
+ }
+ if (rc == 0)
+ fprintf(stderr, "wrote screen shot to %s\n", outf);
+ g_main_loop_quit(mainloop);
+}
+
+static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
+ gpointer data)
+{
+ switch (event) {
+ case SPICE_CHANNEL_OPENED:
+ break;
+ default:
+ g_warning("main channel event: %u", event);
+ g_main_loop_quit(mainloop);
+ }
+}
+
+static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer *data)
+{
+ int id;
+
+ if (SPICE_IS_MAIN_CHANNEL(channel)) {
+ g_signal_connect(channel, "channel-event",
+ G_CALLBACK(main_channel_event), data);
+ return;
+ }
+
+ if (!SPICE_IS_DISPLAY_CHANNEL(channel))
+ return;
+
+ g_object_get(channel, "channel-id", &id, NULL);
+ if (id != 0)
+ return;
+
+ g_signal_connect(channel, "display-primary-create",
+ G_CALLBACK(primary_create), NULL);
+ g_signal_connect(channel, "display-invalidate",
+ G_CALLBACK(invalidate), NULL);
+ spice_channel_connect(channel);
+}
+
+/* ------------------------------------------------------------------ */
+
+static GOptionEntry app_entries[] = {
+ {
+ .long_name = "out-file",
+ .short_name = 'o',
+ .arg = G_OPTION_ARG_FILENAME,
+ .arg_data = &outf,
+ .description = "Output file name (default spicy-screenshot.ppm)",
+ .arg_description = "<filename>",
+ },
+ {
+ .long_name = "version",
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &version,
+ .description = "Display version and quit",
+ },
+ {
+ /* end of list */
+ }
+};
+
+int main(int argc, char *argv[])
+{
+ GError *error = NULL;
+ GOptionContext *context;
+
+ /* parse opts */
+ context = g_option_context_new(" - make screen shots");
+ g_option_context_set_summary(context, "A Spice server client to take screenshots in ppm format.");
+ g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
+ g_option_context_set_main_group(context, spice_cmdline_get_option_group());
+ g_option_context_add_main_entries(context, app_entries, NULL);
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_print("option parsing failed: %s\n", error->message);
+ exit(1);
+ }
+
+ if (version) {
+ g_print("%s " PACKAGE_VERSION "\n", g_get_prgname());
+ exit(0);
+ }
+
+ mainloop = g_main_loop_new(NULL, false);
+
+ session = spice_session_new();
+ g_signal_connect(session, "channel-new",
+ G_CALLBACK(channel_new), NULL);
+ spice_cmdline_session_setup(session);
+
+ if (!spice_session_connect(session)) {
+ fprintf(stderr, "spice_session_connect failed\n");
+ exit(1);
+ }
+
+ g_main_loop_run(mainloop);
+ return 0;
+}
diff --git a/tools/spicy-stats.c b/tools/spicy-stats.c
new file mode 100644
index 0000000..8ca4cc1
--- /dev/null
+++ b/tools/spicy-stats.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include "config.h"
+
+#include "spice-client.h"
+#include "spice-common.h"
+#include "spice-cmdline.h"
+
+/* config */
+static gboolean version = FALSE;
+
+/* state */
+static SpiceSession *session;
+static GMainLoop *mainloop;
+
+/* ------------------------------------------------------------------ */
+static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
+ gpointer data)
+{
+ switch (event) {
+ case SPICE_CHANNEL_OPENED:
+ break;
+ default:
+ g_warning("main channel event: %u", event);
+ g_main_loop_quit(mainloop);
+ }
+}
+
+static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer *data)
+{
+ int id;
+
+ if (SPICE_IS_MAIN_CHANNEL(channel)) {
+ SPICE_DEBUG("new main channel");
+ g_signal_connect(channel, "channel-event",
+ G_CALLBACK(main_channel_event), data);
+ }
+
+ if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
+ g_object_get(channel, "channel-id", &id, NULL);
+ if (id != 0)
+ return;
+ }
+
+ spice_channel_connect(channel);
+}
+
+/* ------------------------------------------------------------------ */
+
+static GOptionEntry app_entries[] = {
+ {
+ .long_name = "version",
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &version,
+ .description = "Display version and quit",
+ },
+ {
+ /* end of list */
+ }
+};
+
+static void
+signal_handler(int signum)
+{
+ g_main_loop_quit(mainloop);
+}
+
+int main(int argc, char *argv[])
+{
+ GError *error = NULL;
+ GOptionContext *context;
+
+ signal(SIGINT, signal_handler);
+
+ /* parse opts */
+ context = g_option_context_new(NULL);
+ g_option_context_set_summary(context, "A Spice client used for testing and measurements.");
+ g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
+ g_option_context_set_main_group(context, spice_cmdline_get_option_group());
+ g_option_context_add_main_entries(context, app_entries, NULL);
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_print("option parsing failed: %s\n", error->message);
+ exit(1);
+ }
+
+ if (version) {
+ g_print("spicy-stats " PACKAGE_VERSION "\n");
+ exit(0);
+ }
+
+ mainloop = g_main_loop_new(NULL, false);
+
+ session = spice_session_new();
+ g_signal_connect(session, "channel-new",
+ G_CALLBACK(channel_new), NULL);
+ spice_cmdline_session_setup(session);
+
+ if (!spice_session_connect(session)) {
+ fprintf(stderr, "spice_session_connect failed\n");
+ exit(1);
+ }
+
+ g_main_loop_run(mainloop);
+ {
+ GList *iter, *list = spice_session_get_channels(session);
+ gulong total_read_bytes;
+ gint channel_type;
+ printf("total bytes read:\n");
+ for (iter = list ; iter ; iter = iter->next) {
+ g_object_get(iter->data,
+ "total-read-bytes", &total_read_bytes,
+ "channel-type", &channel_type,
+ NULL);
+ printf("%s: %lu\n",
+ spice_channel_type_to_string(channel_type),
+ total_read_bytes);
+ }
+ g_list_free(list);
+ }
+ return 0;
+}
diff --git a/tools/spicy.c b/tools/spicy.c
new file mode 100644
index 0000000..c502428
--- /dev/null
+++ b/tools/spicy.c
@@ -0,0 +1,1938 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010-2011 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+#include <glib.h>
+
+#include <sys/stat.h>
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef USE_SMARTCARD_012
+#include <vreader.h>
+#endif
+
+#include "spice-widget.h"
+#include "spice-gtk-session.h"
+#include "spice-audio.h"
+#include "spice-common.h"
+#include "spice-cmdline.h"
+#include "spice-option.h"
+#include "usb-device-widget.h"
+
+#include "spicy-connect.h"
+
+typedef struct spice_connection spice_connection;
+
+enum {
+ STATE_SCROLL_LOCK,
+ STATE_CAPS_LOCK,
+ STATE_NUM_LOCK,
+ STATE_MAX,
+};
+
+#define SPICE_TYPE_WINDOW (spice_window_get_type ())
+#define SPICE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_WINDOW, SpiceWindow))
+#define SPICE_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_WINDOW))
+#define SPICE_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_WINDOW, SpiceWindowClass))
+#define SPICE_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_WINDOW))
+#define SPICE_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_WINDOW, SpiceWindowClass))
+
+typedef struct _SpiceWindow SpiceWindow;
+typedef struct _SpiceWindowClass SpiceWindowClass;
+
+struct _SpiceWindow {
+ GObject object;
+ spice_connection *conn;
+ gint id;
+ gint monitor_id;
+ GtkWidget *toplevel, *spice;
+ GtkWidget *menubar, *toolbar;
+ GtkWidget *ritem, *rmenu;
+ GtkWidget *statusbar, *status, *st[STATE_MAX];
+ GtkActionGroup *ag;
+ GtkUIManager *ui;
+ bool fullscreen;
+ bool mouse_grabbed;
+ SpiceChannel *display_channel;
+#ifdef G_OS_WIN32
+ gint win_x;
+ gint win_y;
+#endif
+ bool enable_accels_save;
+ bool enable_mnemonics_save;
+};
+
+struct _SpiceWindowClass
+{
+ GObjectClass parent_class;
+};
+
+static GType spice_window_get_type(void);
+
+G_DEFINE_TYPE (SpiceWindow, spice_window, G_TYPE_OBJECT);
+
+#define CHANNELID_MAX 4
+#define MONITORID_MAX 4
+
+
+// FIXME: turn this into an object, get rid of fixed wins array, use
+// signals to replace the various callback that iterate over wins array
+struct spice_connection {
+ SpiceSession *session;
+ SpiceGtkSession *gtk_session;
+ SpiceMainChannel *main;
+ SpiceWindow *wins[CHANNELID_MAX * MONITORID_MAX];
+ SpiceAudio *audio;
+ const char *mouse_state;
+ const char *agent_state;
+ gboolean agent_connected;
+ int channels;
+ int disconnecting;
+
+ /* key: SpiceFileTransferTask, value: TransferTaskWidgets */
+ GHashTable *transfers;
+ GtkWidget *transfer_dialog;
+};
+
+static spice_connection *connection_new(void);
+static void connection_connect(spice_connection *conn);
+static void connection_disconnect(spice_connection *conn);
+static void connection_destroy(spice_connection *conn);
+static void usb_connect_failed(GObject *object,
+ SpiceUsbDevice *device,
+ GError *error,
+ gpointer data);
+static gboolean is_gtk_session_property(const gchar *property);
+static void del_window(spice_connection *conn, SpiceWindow *win);
+
+/* options */
+static gboolean fullscreen = false;
+static gboolean version = false;
+static char *spicy_title = NULL;
+/* globals */
+static GMainLoop *mainloop = NULL;
+static int connections = 0;
+static GKeyFile *keyfile = NULL;
+static SpicePortChannel*stdin_port = NULL;
+
+/* ------------------------------------------------------------------ */
+
+static int ask_user(GtkWidget *parent, char *title, char *message,
+ char *dest, int dlen, int hide)
+{
+ GtkWidget *dialog, *area, *label, *entry;
+ const char *txt;
+ int retval;
+
+ /* Create the widgets */
+ dialog = gtk_dialog_new_with_buttons(title,
+ parent ? GTK_WINDOW(parent) : NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ "_OK",
+ GTK_RESPONSE_ACCEPT,
+ "_Cancel",
+ GTK_RESPONSE_REJECT,
+ NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+ area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+ label = gtk_label_new(message);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_box_pack_start(GTK_BOX(area), label, FALSE, FALSE, 5);
+
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), dest);
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+ if (hide)
+ gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+ gtk_box_pack_start(GTK_BOX(area), entry, FALSE, FALSE, 5);
+
+ /* show and wait for response */
+ gtk_widget_show_all(dialog);
+ switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
+ case GTK_RESPONSE_ACCEPT:
+ txt = gtk_entry_get_text(GTK_ENTRY(entry));
+ snprintf(dest, dlen, "%s", txt);
+ retval = 0;
+ break;
+ default:
+ retval = -1;
+ break;
+ }
+ gtk_widget_destroy(dialog);
+ return retval;
+}
+
+static void update_status_window(SpiceWindow *win)
+{
+ gchar *status;
+
+ if (win == NULL)
+ return;
+
+ if (win->mouse_grabbed) {
+ SpiceGrabSequence *sequence = spice_display_get_grab_keys(SPICE_DISPLAY(win->spice));
+ gchar *seq = spice_grab_sequence_as_string(sequence);
+ status = g_strdup_printf("Use %s to ungrab mouse.", seq);
+ g_free(seq);
+ } else {
+ status = g_strdup_printf("mouse: %s, agent: %s",
+ win->conn->mouse_state, win->conn->agent_state);
+ }
+
+ gtk_label_set_text(GTK_LABEL(win->status), status);
+ g_free(status);
+}
+
+static void update_status(struct spice_connection *conn)
+{
+ int i;
+
+ for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
+ if (conn->wins[i] == NULL)
+ continue;
+ update_status_window(conn->wins[i]);
+ }
+}
+
+static const char *spice_edit_properties[] = {
+ "CopyToGuest",
+ "PasteFromGuest",
+};
+
+static void update_edit_menu_window(SpiceWindow *win)
+{
+ int i;
+ GtkAction *toggle;
+
+ if (win == NULL) {
+ return;
+ }
+
+ /* Make "CopyToGuest" and "PasteFromGuest" insensitive if spice
+ * agent is not connected */
+ for (i = 0; i < G_N_ELEMENTS(spice_edit_properties); i++) {
+ toggle = gtk_action_group_get_action(win->ag, spice_edit_properties[i]);
+ if (toggle) {
+ gtk_action_set_sensitive(toggle, win->conn->agent_connected);
+ }
+ }
+}
+
+static void update_edit_menu(struct spice_connection *conn)
+{
+ int i;
+
+ for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
+ if (conn->wins[i]) {
+ update_edit_menu_window(conn->wins[i]);
+ }
+ }
+}
+
+static void menu_cb_connect(GtkAction *action, void *data)
+{
+ struct spice_connection *conn;
+
+ conn = connection_new();
+ connection_connect(conn);
+}
+
+static void menu_cb_close(GtkAction *action, void *data)
+{
+ SpiceWindow *win = data;
+
+ connection_disconnect(win->conn);
+}
+
+static void menu_cb_copy(GtkAction *action, void *data)
+{
+ SpiceWindow *win = data;
+
+ spice_gtk_session_copy_to_guest(win->conn->gtk_session);
+}
+
+static void menu_cb_paste(GtkAction *action, void *data)
+{
+ SpiceWindow *win = data;
+
+ spice_gtk_session_paste_from_guest(win->conn->gtk_session);
+}
+
+static void window_set_fullscreen(SpiceWindow *win, gboolean fs)
+{
+ if (fs) {
+#ifdef G_OS_WIN32
+ gtk_window_get_position(GTK_WINDOW(win->toplevel), &win->win_x, &win->win_y);
+#endif
+ gtk_window_fullscreen(GTK_WINDOW(win->toplevel));
+ } else {
+ gtk_window_unfullscreen(GTK_WINDOW(win->toplevel));
+#ifdef G_OS_WIN32
+ gtk_window_move(GTK_WINDOW(win->toplevel), win->win_x, win->win_y);
+#endif
+ }
+}
+
+static void menu_cb_fullscreen(GtkAction *action, void *data)
+{
+ SpiceWindow *win = data;
+
+ window_set_fullscreen(win, !win->fullscreen);
+}
+
+#ifdef USE_SMARTCARD
+static void enable_smartcard_actions(SpiceWindow *win, VReader *reader,
+ gboolean can_insert, gboolean can_remove)
+{
+ GtkAction *action;
+
+ if ((reader != NULL) && (!spice_smartcard_reader_is_software((SpiceSmartcardReader*)reader)))
+ {
+ /* Having menu actions to insert/remove smartcards only makes sense
+ * for software smartcard readers, don't do anything when the event
+ * we received was for a "real" smartcard reader.
+ */
+ return;
+ }
+ action = gtk_action_group_get_action(win->ag, "InsertSmartcard");
+ g_return_if_fail(action != NULL);
+ gtk_action_set_sensitive(action, can_insert);
+ action = gtk_action_group_get_action(win->ag, "RemoveSmartcard");
+ g_return_if_fail(action != NULL);
+ gtk_action_set_sensitive(action, can_remove);
+}
+
+
+static void reader_added_cb(SpiceSmartcardManager *manager, VReader *reader,
+ gpointer user_data)
+{
+ enable_smartcard_actions(user_data, reader, TRUE, FALSE);
+}
+
+static void reader_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
+ gpointer user_data)
+{
+ enable_smartcard_actions(user_data, reader, FALSE, FALSE);
+}
+
+static void card_inserted_cb(SpiceSmartcardManager *manager, VReader *reader,
+ gpointer user_data)
+{
+ enable_smartcard_actions(user_data, reader, FALSE, TRUE);
+}
+
+static void card_removed_cb(SpiceSmartcardManager *manager, VReader *reader,
+ gpointer user_data)
+{
+ enable_smartcard_actions(user_data, reader, TRUE, FALSE);
+}
+
+static void menu_cb_insert_smartcard(GtkAction *action, void *data)
+{
+ spice_smartcard_manager_insert_card(spice_smartcard_manager_get());
+}
+
+static void menu_cb_remove_smartcard(GtkAction *action, void *data)
+{
+ spice_smartcard_manager_remove_card(spice_smartcard_manager_get());
+}
+#endif
+
+static void menu_cb_mouse_mode(GtkAction *action, void *data)
+{
+ SpiceWindow *win = data;
+ SpiceMainChannel *cmain = win->conn->main;
+ int mode;
+
+ g_object_get(cmain, "mouse-mode", &mode, NULL);
+ if (mode == SPICE_MOUSE_MODE_CLIENT)
+ mode = SPICE_MOUSE_MODE_SERVER;
+ else
+ mode = SPICE_MOUSE_MODE_CLIENT;
+
+ spice_main_request_mouse_mode(cmain, mode);
+}
+
+#ifdef USE_USBREDIR
+static void remove_cb(GtkContainer *container, GtkWidget *widget, void *data)
+{
+ gtk_window_resize(GTK_WINDOW(data), 1, 1);
+}
+
+static void menu_cb_select_usb_devices(GtkAction *action, void *data)
+{
+ GtkWidget *dialog, *area, *usb_device_widget;
+ SpiceWindow *win = data;
+
+ /* Create the widgets */
+ dialog = gtk_dialog_new_with_buttons(
+ "Select USB devices for redirection",
+ GTK_WINDOW(win->toplevel),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ "_Close", GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+ gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
+ gtk_box_set_spacing(GTK_BOX(gtk_bin_get_child(GTK_BIN(dialog))), 12);
+
+ area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+
+ usb_device_widget = spice_usb_device_widget_new(win->conn->session,
+ NULL); /* default format */
+ g_signal_connect(usb_device_widget, "connect-failed",
+ G_CALLBACK(usb_connect_failed), NULL);
+ gtk_box_pack_start(GTK_BOX(area), usb_device_widget, TRUE, TRUE, 0);
+
+ /* This shrinks the dialog when USB devices are unplugged */
+ g_signal_connect(usb_device_widget, "remove",
+ G_CALLBACK(remove_cb), dialog);
+
+ /* show and run */
+ gtk_widget_show_all(dialog);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+#endif
+
+static void menu_cb_bool_prop(GtkToggleAction *action, gpointer data)
+{
+ SpiceWindow *win = data;
+ gboolean state = gtk_toggle_action_get_active(action);
+ const char *name;
+ gpointer object;
+
+ name = gtk_action_get_name(GTK_ACTION(action));
+ SPICE_DEBUG("%s: %s = %s", __FUNCTION__, name, state ? "yes" : "no");
+
+ g_key_file_set_boolean(keyfile, "general", name, state);
+
+ if (is_gtk_session_property(name)) {
+ object = win->conn->gtk_session;
+ } else {
+ object = win->spice;
+ }
+ g_object_set(object, name, state, NULL);
+}
+
+static void menu_cb_conn_bool_prop_changed(GObject *gobject,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ SpiceWindow *win = user_data;
+ const gchar *property = g_param_spec_get_name(pspec);
+ GtkAction *toggle;
+ gboolean state;
+
+ toggle = gtk_action_group_get_action(win->ag, property);
+ g_object_get(win->conn->gtk_session, property, &state, NULL);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
+}
+
+static void menu_cb_toolbar(GtkToggleAction *action, gpointer data)
+{
+ SpiceWindow *win = data;
+ gboolean state = gtk_toggle_action_get_active(action);
+
+ gtk_widget_set_visible(win->toolbar, state);
+ g_key_file_set_boolean(keyfile, "ui", "toolbar", state);
+}
+
+static void menu_cb_statusbar(GtkToggleAction *action, gpointer data)
+{
+ SpiceWindow *win = data;
+ gboolean state = gtk_toggle_action_get_active(action);
+
+ gtk_widget_set_visible(win->statusbar, state);
+ g_key_file_set_boolean(keyfile, "ui", "statusbar", state);
+}
+
+static void menu_cb_about(GtkAction *action, void *data)
+{
+ char *comments = "gtk test client app for the\n"
+ "spice remote desktop protocol";
+ static const char *copyright = "(c) 2010 Red Hat";
+ static const char *website = "http://www.spice-space.org";
+ static const char *authors[] = { "Gerd Hoffmann <kraxel at redhat.com>",
+ "Marc-André Lureau <marcandre.lureau at redhat.com>",
+ NULL };
+ SpiceWindow *win = data;
+
+ gtk_show_about_dialog(GTK_WINDOW(win->toplevel),
+ "authors", authors,
+ "comments", comments,
+ "copyright", copyright,
+ "logo-icon-name", "help-about",
+ "website", website,
+ "version", PACKAGE_VERSION,
+ "license", "LGPLv2.1",
+ NULL);
+}
+
+static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ SpiceWindow *win = data;
+
+ if (win->monitor_id == 0)
+ connection_disconnect(win->conn);
+ else
+ del_window(win->conn, win);
+
+ return true;
+}
+
+static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event,
+ gpointer data)
+{
+ SpiceWindow *win = data;
+ if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
+ win->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
+ if (win->fullscreen) {
+ gtk_widget_hide(win->menubar);
+ gtk_widget_hide(win->toolbar);
+ gtk_widget_hide(win->statusbar);
+ gtk_widget_grab_focus(win->spice);
+ } else {
+ gboolean state;
+ GtkAction *toggle;
+
+ gtk_widget_show(win->menubar);
+ toggle = gtk_action_group_get_action(win->ag, "Toolbar");
+ state = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(toggle));
+ gtk_widget_set_visible(win->toolbar, state);
+ toggle = gtk_action_group_get_action(win->ag, "Statusbar");
+ state = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(toggle));
+ gtk_widget_set_visible(win->statusbar, state);
+ }
+ }
+ return TRUE;
+}
+
+static void grab_keys_pressed_cb(GtkWidget *widget, gpointer data)
+{
+ SpiceWindow *win = data;
+
+ /* since mnemonics are disabled, we leave fullscreen when
+ ungrabbing mouse. Perhaps we should have a different handling
+ of fullscreen key, or simply use a UI, like vinagre */
+ window_set_fullscreen(win, FALSE);
+}
+
+static void mouse_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
+{
+ SpiceWindow *win = data;
+
+ win->mouse_grabbed = grabbed;
+ update_status(win->conn);
+}
+
+static void keyboard_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
+{
+ SpiceWindow *win = data;
+ GtkSettings *settings = gtk_widget_get_settings (widget);
+
+ if (grabbed) {
+ /* disable mnemonics & accels */
+ g_object_get(settings,
+ "gtk-enable-accels", &win->enable_accels_save,
+ "gtk-enable-mnemonics", &win->enable_mnemonics_save,
+ NULL);
+ g_object_set(settings,
+ "gtk-enable-accels", FALSE,
+ "gtk-enable-mnemonics", FALSE,
+ NULL);
+ } else {
+ g_object_set(settings,
+ "gtk-enable-accels", win->enable_accels_save,
+ "gtk-enable-mnemonics", win->enable_mnemonics_save,
+ NULL);
+ }
+}
+
+static void restore_configuration(SpiceWindow *win)
+{
+ gboolean state;
+ gchar *str;
+ gchar **keys = NULL;
+ gsize nkeys, i;
+ GError *error = NULL;
+ gpointer object;
+
+ keys = g_key_file_get_keys(keyfile, "general", &nkeys, &error);
+ if (error != NULL) {
+ if (error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND)
+ g_warning("Failed to read configuration file keys: %s", error->message);
+ g_clear_error(&error);
+ return;
+ }
+
+ if (nkeys > 0)
+ g_return_if_fail(keys != NULL);
+
+ for (i = 0; i < nkeys; ++i) {
+ if (g_str_equal(keys[i], "grab-sequence"))
+ continue;
+ state = g_key_file_get_boolean(keyfile, "general", keys[i], &error);
+ if (error != NULL) {
+ g_clear_error(&error);
+ continue;
+ }
+
+ if (is_gtk_session_property(keys[i])) {
+ object = win->conn->gtk_session;
+ } else {
+ object = win->spice;
+ }
+ g_object_set(object, keys[i], state, NULL);
+ }
+
+ g_strfreev(keys);
+
+ str = g_key_file_get_string(keyfile, "general", "grab-sequence", &error);
+ if (error == NULL) {
+ SpiceGrabSequence *seq = spice_grab_sequence_new_from_string(str);
+ spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
+ spice_grab_sequence_free(seq);
+ g_free(str);
+ }
+ g_clear_error(&error);
+
+
+ state = g_key_file_get_boolean(keyfile, "ui", "toolbar", &error);
+ if (error == NULL)
+ gtk_widget_set_visible(win->toolbar, state);
+ g_clear_error(&error);
+
+ state = g_key_file_get_boolean(keyfile, "ui", "statusbar", &error);
+ if (error == NULL)
+ gtk_widget_set_visible(win->statusbar, state);
+ g_clear_error(&error);
+}
+
+/* ------------------------------------------------------------------ */
+
+static const GtkActionEntry entries[] = {
+ {
+ .name = "FileMenu",
+ .label = "_File",
+ },{
+ .name = "FileRecentMenu",
+ .label = "_Recent",
+ },{
+ .name = "EditMenu",
+ .label = "_Edit",
+ },{
+ .name = "ViewMenu",
+ .label = "_View",
+ },{
+ .name = "InputMenu",
+ .label = "_Input",
+ },{
+ .name = "OptionMenu",
+ .label = "_Options",
+ },{
+ .name = "CompressionMenu",
+ .label = "_Preferred image compression",
+ },{
+ .name = "HelpMenu",
+ .label = "_Help",
+ },{
+
+ /* File menu */
+ .name = "Connect",
+ .stock_id = "_Connect",
+ .label = "_Connect ...",
+ .callback = G_CALLBACK(menu_cb_connect),
+ },{
+ .name = "Close",
+ .stock_id = "window-close",
+ .label = "_Close",
+ .callback = G_CALLBACK(menu_cb_close),
+ .accelerator = "", /* none (disable default "<control>W") */
+ },{
+
+ /* Edit menu */
+ .name = "CopyToGuest",
+ .stock_id = "edit-copy",
+ .label = "_Copy to guest",
+ .callback = G_CALLBACK(menu_cb_copy),
+ .accelerator = "", /* none (disable default "<control>C") */
+ },{
+ .name = "PasteFromGuest",
+ .stock_id = "edit-paste",
+ .label = "_Paste from guest",
+ .callback = G_CALLBACK(menu_cb_paste),
+ .accelerator = "", /* none (disable default "<control>V") */
+ },{
+
+ /* View menu */
+ .name = "Fullscreen",
+ .stock_id = "view-fullscreen",
+ .label = "_Fullscreen",
+ .callback = G_CALLBACK(menu_cb_fullscreen),
+ .accelerator = "<shift>F11",
+ },{
+#ifdef USE_SMARTCARD
+ .name = "InsertSmartcard",
+ .label = "_Insert Smartcard",
+ .callback = G_CALLBACK(menu_cb_insert_smartcard),
+ .accelerator = "<shift>F8",
+ },{
+ .name = "RemoveSmartcard",
+ .label = "_Remove Smartcard",
+ .callback = G_CALLBACK(menu_cb_remove_smartcard),
+ .accelerator = "<shift>F9",
+ },{
+#endif
+
+#ifdef USE_USBREDIR
+ .name = "SelectUsbDevices",
+ .label = "_Select USB Devices for redirection",
+ .callback = G_CALLBACK(menu_cb_select_usb_devices),
+ .accelerator = "<shift>F10",
+ },{
+#endif
+
+ .name = "MouseMode",
+ .label = "Toggle _mouse mode",
+ .callback = G_CALLBACK(menu_cb_mouse_mode),
+ .accelerator = "<shift>F7",
+
+ },{
+ /* Help menu */
+ .name = "About",
+ .stock_id = "help-about",
+ .label = "_About ...",
+ .callback = G_CALLBACK(menu_cb_about),
+ }
+};
+
+static const char *spice_display_properties[] = {
+ "grab-keyboard",
+ "grab-mouse",
+ "resize-guest",
+ "scaling",
+ "disable-inputs",
+};
+
+static const char *spice_gtk_session_properties[] = {
+ "auto-clipboard",
+ "auto-usbredir",
+ "sync-modifiers",
+};
+
+static const GtkToggleActionEntry tentries[] = {
+ {
+ .name = "grab-keyboard",
+ .label = "Grab keyboard when active and focused",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "grab-mouse",
+ .label = "Grab mouse in server mode (no tablet/vdagent)",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "resize-guest",
+ .label = "Resize guest to match window size",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "scaling",
+ .label = "Scale display",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "disable-inputs",
+ .label = "Disable inputs",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "sync-modifiers",
+ .label = "Sync modifiers",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "auto-clipboard",
+ .label = "Automatic clipboard sharing between host and guest",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "auto-usbredir",
+ .label = "Auto redirect newly plugged in USB devices",
+ .callback = G_CALLBACK(menu_cb_bool_prop),
+ },{
+ .name = "Statusbar",
+ .label = "Statusbar",
+ .callback = G_CALLBACK(menu_cb_statusbar),
+ },{
+ .name = "Toolbar",
+ .label = "Toolbar",
+ .callback = G_CALLBACK(menu_cb_toolbar),
+ }
+};
+
+static const GtkRadioActionEntry compression_entries[] = {
+ {
+ .name = "auto-glz",
+ .label = "auto-glz",
+ .value = SPICE_IMAGE_COMPRESSION_AUTO_GLZ,
+ },{
+ .name = "auto-lz",
+ .label = "auto-lz",
+ .value = SPICE_IMAGE_COMPRESSION_AUTO_LZ,
+ },{
+ .name = "quic",
+ .label = "quic",
+ .value = SPICE_IMAGE_COMPRESSION_QUIC,
+ },{
+ .name = "glz",
+ .label = "glz",
+ .value = SPICE_IMAGE_COMPRESSION_GLZ,
+ },{
+ .name = "lz",
+ .label = "lz",
+ .value = SPICE_IMAGE_COMPRESSION_LZ,
+ },{
+#ifdef USE_LZ4
+ .name = "lz4",
+ .label = "lz4",
+ .value = SPICE_IMAGE_COMPRESSION_LZ4,
+ },{
+#endif
+ .name = "off",
+ .label = "off",
+ .value = SPICE_IMAGE_COMPRESSION_OFF,
+ }
+};
+
+static char ui_xml[] =
+"<ui>\n"
+" <menubar action='MainMenu'>\n"
+" <menu action='FileMenu'>\n"
+" <menuitem action='Connect'/>\n"
+" <menu action='FileRecentMenu'/>\n"
+" <separator/>\n"
+" <menuitem action='Close'/>\n"
+" </menu>\n"
+" <menu action='EditMenu'>\n"
+" <menuitem action='CopyToGuest'/>\n"
+" <menuitem action='PasteFromGuest'/>\n"
+" </menu>\n"
+" <menu action='ViewMenu'>\n"
+" <menuitem action='Fullscreen'/>\n"
+" <menuitem action='Toolbar'/>\n"
+" <menuitem action='Statusbar'/>\n"
+" </menu>\n"
+" <menu action='InputMenu'>\n"
+#ifdef USE_SMARTCARD
+" <menuitem action='InsertSmartcard'/>\n"
+" <menuitem action='RemoveSmartcard'/>\n"
+#endif
+#ifdef USE_USBREDIR
+" <menuitem action='SelectUsbDevices'/>\n"
+#endif
+" </menu>\n"
+" <menu action='OptionMenu'>\n"
+" <menuitem action='grab-keyboard'/>\n"
+" <menuitem action='grab-mouse'/>\n"
+" <menuitem action='MouseMode'/>\n"
+" <menuitem action='resize-guest'/>\n"
+" <menuitem action='scaling'/>\n"
+" <menuitem action='disable-inputs'/>\n"
+" <menuitem action='sync-modifiers'/>\n"
+" <menuitem action='auto-clipboard'/>\n"
+" <menuitem action='auto-usbredir'/>\n"
+" <menu action='CompressionMenu'>\n"
+" <menuitem action='auto-glz'/>\n"
+" <menuitem action='auto-lz'/>\n"
+" <menuitem action='quic'/>\n"
+" <menuitem action='glz'/>\n"
+" <menuitem action='lz'/>\n"
+#ifdef USE_LZ4
+" <menuitem action='lz4'/>\n"
+#endif
+" <menuitem action='off'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu action='HelpMenu'>\n"
+" <menuitem action='About'/>\n"
+" </menu>\n"
+" </menubar>\n"
+" <toolbar action='ToolBar'>\n"
+" <toolitem action='Close'/>\n"
+" <separator/>\n"
+" <toolitem action='CopyToGuest'/>\n"
+" <toolitem action='PasteFromGuest'/>\n"
+" <separator/>\n"
+" <toolitem action='Fullscreen'/>\n"
+" </toolbar>\n"
+"</ui>\n";
+
+static gboolean is_gtk_session_property(const gchar *property)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS(spice_gtk_session_properties); i++) {
+ if (!strcmp(spice_gtk_session_properties[i], property)) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void recent_item_activated_cb(GtkRecentChooser *chooser, gpointer data)
+{
+ GtkRecentInfo *info;
+ struct spice_connection *conn;
+ const char *uri;
+
+ info = gtk_recent_chooser_get_current_item(chooser);
+
+ uri = gtk_recent_info_get_uri(info);
+ g_return_if_fail(uri != NULL);
+
+ conn = connection_new();
+ g_object_set(conn->session, "uri", uri, NULL);
+ gtk_recent_info_unref(info);
+ connection_connect(conn);
+}
+
+static void compression_cb(GtkRadioAction *action G_GNUC_UNUSED,
+ GtkRadioAction *current,
+ gpointer user_data)
+{
+ spice_display_change_preferred_compression(SPICE_CHANNEL(user_data),
+ gtk_radio_action_get_current_value(current));
+}
+
+static void
+spice_window_class_init (SpiceWindowClass *klass)
+{
+}
+
+static void
+spice_window_init (SpiceWindow *self)
+{
+}
+
+static SpiceWindow *create_spice_window(spice_connection *conn, SpiceChannel *channel, int id, gint monitor_id)
+{
+ char title[32];
+ SpiceWindow *win;
+ GtkAction *toggle;
+ gboolean state;
+ GtkWidget *vbox, *frame;
+ GError *err = NULL;
+ int i;
+ SpiceGrabSequence *seq;
+
+ win = g_object_new(SPICE_TYPE_WINDOW, NULL);
+ win->id = id;
+ win->monitor_id = monitor_id;
+ win->conn = conn;
+ win->display_channel = channel;
+
+ /* toplevel */
+ win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ if (spicy_title == NULL) {
+ snprintf(title, sizeof(title), "spice display %d:%d", id, monitor_id);
+ } else {
+ snprintf(title, sizeof(title), "%s", spicy_title);
+ }
+
+ gtk_window_set_title(GTK_WINDOW(win->toplevel), title);
+ g_signal_connect(G_OBJECT(win->toplevel), "window-state-event",
+ G_CALLBACK(window_state_cb), win);
+ g_signal_connect(G_OBJECT(win->toplevel), "delete-event",
+ G_CALLBACK(delete_cb), win);
+
+ /* menu + toolbar */
+ win->ui = gtk_ui_manager_new();
+ win->ag = gtk_action_group_new("MenuActions");
+ gtk_action_group_add_actions(win->ag, entries, G_N_ELEMENTS(entries), win);
+ gtk_action_group_add_toggle_actions(win->ag, tentries,
+ G_N_ELEMENTS(tentries), win);
+ gtk_action_group_add_radio_actions(win->ag, compression_entries,
+ G_N_ELEMENTS(compression_entries), -1,
+ G_CALLBACK(compression_cb), win->display_channel);
+ if (!spice_channel_test_capability(win->display_channel, SPICE_DISPLAY_CAP_PREF_COMPRESSION)) {
+ GtkAction *compression_menu_action = gtk_action_group_get_action(win->ag, "CompressionMenu");
+ gtk_action_set_sensitive(compression_menu_action, FALSE);
+ }
+ gtk_ui_manager_insert_action_group(win->ui, win->ag, 0);
+ gtk_window_add_accel_group(GTK_WINDOW(win->toplevel),
+ gtk_ui_manager_get_accel_group(win->ui));
+
+ err = NULL;
+ if (!gtk_ui_manager_add_ui_from_string(win->ui, ui_xml, -1, &err)) {
+ g_warning("building menus failed: %s", err->message);
+ g_error_free(err);
+ exit(1);
+ }
+ win->menubar = gtk_ui_manager_get_widget(win->ui, "/MainMenu");
+ win->toolbar = gtk_ui_manager_get_widget(win->ui, "/ToolBar");
+
+ /* recent menu */
+ win->ritem = gtk_ui_manager_get_widget
+ (win->ui, "/MainMenu/FileMenu/FileRecentMenu");
+
+ GtkRecentFilter *rfilter;
+
+ win->rmenu = gtk_recent_chooser_menu_new();
+ gtk_recent_chooser_set_show_icons(GTK_RECENT_CHOOSER(win->rmenu), FALSE);
+ rfilter = gtk_recent_filter_new();
+ gtk_recent_filter_add_mime_type(rfilter, "application/x-spice");
+ gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(win->rmenu), rfilter);
+ gtk_recent_chooser_set_local_only(GTK_RECENT_CHOOSER(win->rmenu), FALSE);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->ritem), win->rmenu);
+ g_signal_connect(win->rmenu, "item-activated",
+ G_CALLBACK(recent_item_activated_cb), win);
+
+ /* spice display */
+ win->spice = GTK_WIDGET(spice_display_new_with_monitor(conn->session, id, monitor_id));
+ seq = spice_grab_sequence_new_from_string("Shift_L+F12");
+ spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
+ spice_grab_sequence_free(seq);
+
+ g_signal_connect(G_OBJECT(win->spice), "mouse-grab",
+ G_CALLBACK(mouse_grab_cb), win);
+ g_signal_connect(G_OBJECT(win->spice), "keyboard-grab",
+ G_CALLBACK(keyboard_grab_cb), win);
+ g_signal_connect(G_OBJECT(win->spice), "grab-keys-pressed",
+ G_CALLBACK(grab_keys_pressed_cb), win);
+
+ /* status line */
+ win->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
+
+ win->status = gtk_label_new("status line");
+ gtk_misc_set_alignment(GTK_MISC(win->status), 0, 0.5);
+ gtk_misc_set_padding(GTK_MISC(win->status), 3, 1);
+ update_status_window(win);
+
+ frame = gtk_frame_new(NULL);
+ gtk_box_pack_start(GTK_BOX(win->statusbar), frame, TRUE, TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), win->status);
+
+ for (i = 0; i < STATE_MAX; i++) {
+ win->st[i] = gtk_label_new("?");
+ gtk_label_set_width_chars(GTK_LABEL(win->st[i]), 5);
+ frame = gtk_frame_new(NULL);
+ gtk_box_pack_end(GTK_BOX(win->statusbar), frame, FALSE, FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), win->st[i]);
+ }
+
+ /* Make a vbox and put stuff in */
+ vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
+ gtk_container_add(GTK_CONTAINER(win->toplevel), vbox);
+ gtk_box_pack_start(GTK_BOX(vbox), win->menubar, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), win->toolbar, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), win->spice, TRUE, TRUE, 0);
+ gtk_box_pack_end(GTK_BOX(vbox), win->statusbar, FALSE, TRUE, 0);
+
+ /* show window */
+ if (fullscreen)
+ gtk_window_fullscreen(GTK_WINDOW(win->toplevel));
+
+ gtk_widget_show_all(vbox);
+ restore_configuration(win);
+
+ /* init toggle actions */
+ for (i = 0; i < G_N_ELEMENTS(spice_display_properties); i++) {
+ toggle = gtk_action_group_get_action(win->ag,
+ spice_display_properties[i]);
+ g_object_get(win->spice, spice_display_properties[i], &state, NULL);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(spice_gtk_session_properties); i++) {
+ char notify[64];
+
+ toggle = gtk_action_group_get_action(win->ag,
+ spice_gtk_session_properties[i]);
+ g_object_get(win->conn->gtk_session, spice_gtk_session_properties[i],
+ &state, NULL);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
+
+ snprintf(notify, sizeof(notify), "notify::%s",
+ spice_gtk_session_properties[i]);
+ spice_g_signal_connect_object(win->conn->gtk_session, notify,
+ G_CALLBACK(menu_cb_conn_bool_prop_changed),
+ win, 0);
+ }
+
+ update_edit_menu_window(win);
+
+ toggle = gtk_action_group_get_action(win->ag, "Toolbar");
+ state = gtk_widget_get_visible(win->toolbar);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
+
+ toggle = gtk_action_group_get_action(win->ag, "Statusbar");
+ state = gtk_widget_get_visible(win->statusbar);
+ gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(toggle), state);
+
+#ifdef USE_SMARTCARD
+ gboolean smartcard;
+
+ enable_smartcard_actions(win, NULL, FALSE, FALSE);
+ g_object_get(G_OBJECT(conn->session),
+ "enable-smartcard", &smartcard,
+ NULL);
+ if (smartcard) {
+ g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-added",
+ (GCallback)reader_added_cb, win);
+ g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-removed",
+ (GCallback)reader_removed_cb, win);
+ g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-inserted",
+ (GCallback)card_inserted_cb, win);
+ g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-removed",
+ (GCallback)card_removed_cb, win);
+ }
+#endif
+
+#ifndef USE_USBREDIR
+ GtkAction *usbredir = gtk_action_group_get_action(win->ag, "auto-usbredir");
+ gtk_action_set_visible(usbredir, FALSE);
+#endif
+
+ gtk_widget_grab_focus(win->spice);
+
+ return win;
+}
+
+static void destroy_spice_window(SpiceWindow *win)
+{
+ if (win == NULL)
+ return;
+
+ SPICE_DEBUG("destroy window (#%d:%d)", win->id, win->monitor_id);
+ g_object_unref(win->ag);
+ g_object_unref(win->ui);
+ gtk_widget_destroy(win->toplevel);
+ g_object_unref(win);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void recent_add(SpiceSession *session)
+{
+ GtkRecentManager *recent;
+ GtkRecentData meta = {
+ .mime_type = (char*)"application/x-spice",
+ .app_name = (char*)"spicy",
+ .app_exec = (char*)"spicy --uri=%u",
+ };
+ char *uri;
+
+ g_object_get(session, "uri", &uri, NULL);
+ SPICE_DEBUG("%s: %s", __FUNCTION__, uri);
+
+ recent = gtk_recent_manager_get_default();
+ if (g_str_has_prefix(uri, "spice://"))
+ meta.display_name = uri + 8;
+ else if (g_str_has_prefix(uri, "spice+unix://"))
+ meta.display_name = uri + 13;
+ else
+ g_return_if_reached();
+
+ if (!gtk_recent_manager_add_full(recent, uri, &meta))
+ g_warning("Recent item couldn't be added successfully");
+
+ g_free(uri);
+}
+
+static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
+ gpointer data)
+{
+ const GError *error = NULL;
+ spice_connection *conn = data;
+ char password[64];
+ int rc;
+
+ switch (event) {
+ case SPICE_CHANNEL_OPENED:
+ g_message("main channel: opened");
+ recent_add(conn->session);
+ break;
+ case SPICE_CHANNEL_SWITCHING:
+ g_message("main channel: switching host");
+ break;
+ case SPICE_CHANNEL_CLOSED:
+ /* this event is only sent if the channel was succesfully opened before */
+ g_message("main channel: closed");
+ connection_disconnect(conn);
+ break;
+ case SPICE_CHANNEL_ERROR_IO:
+ connection_disconnect(conn);
+ break;
+ case SPICE_CHANNEL_ERROR_TLS:
+ case SPICE_CHANNEL_ERROR_LINK:
+ case SPICE_CHANNEL_ERROR_CONNECT:
+ error = spice_channel_get_error(channel);
+ g_message("main channel: failed to connect");
+ if (error) {
+ g_message("channel error: %s", error->message);
+ }
+
+ if (spicy_connect_dialog(conn->session)) {
+ connection_connect(conn);
+ } else {
+ connection_disconnect(conn);
+ }
+ break;
+ case SPICE_CHANNEL_ERROR_AUTH:
+ g_warning("main channel: auth failure (wrong password?)");
+ strcpy(password, "");
+ /* FIXME i18 */
+ rc = ask_user(NULL, "Authentication",
+ "Please enter the spice server password",
+ password, sizeof(password), true);
+ if (rc == 0) {
+ g_object_set(conn->session, "password", password, NULL);
+ connection_connect(conn);
+ } else {
+ connection_disconnect(conn);
+ }
+ break;
+ default:
+ /* TODO: more sophisticated error handling */
+ g_warning("unknown main channel event: %u", event);
+ /* connection_disconnect(conn); */
+ break;
+ }
+}
+
+static void main_mouse_update(SpiceChannel *channel, gpointer data)
+{
+ spice_connection *conn = data;
+ gint mode;
+
+ g_object_get(channel, "mouse-mode", &mode, NULL);
+ switch (mode) {
+ case SPICE_MOUSE_MODE_SERVER:
+ conn->mouse_state = "server";
+ break;
+ case SPICE_MOUSE_MODE_CLIENT:
+ conn->mouse_state = "client";
+ break;
+ default:
+ conn->mouse_state = "?";
+ break;
+ }
+ update_status(conn);
+}
+
+static void main_agent_update(SpiceChannel *channel, gpointer data)
+{
+ spice_connection *conn = data;
+
+ g_object_get(channel, "agent-connected", &conn->agent_connected, NULL);
+ conn->agent_state = conn->agent_connected ? "yes" : "no";
+ update_status(conn);
+ update_edit_menu(conn);
+}
+
+static void inputs_modifiers(SpiceChannel *channel, gpointer data)
+{
+ spice_connection *conn = data;
+ int m, i;
+
+ g_object_get(channel, "key-modifiers", &m, NULL);
+ for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
+ if (conn->wins[i] == NULL)
+ continue;
+
+ gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_SCROLL_LOCK]),
+ m & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK ? "SCROLL" : "");
+ gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_CAPS_LOCK]),
+ m & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK ? "CAPS" : "");
+ gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_NUM_LOCK]),
+ m & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK ? "NUM" : "");
+ }
+}
+
+static void display_mark(SpiceChannel *channel, gint mark, SpiceWindow *win)
+{
+ g_return_if_fail(win != NULL);
+ g_return_if_fail(win->toplevel != NULL);
+
+ if (mark == TRUE) {
+ gtk_widget_show(win->toplevel);
+ } else {
+ gtk_widget_hide(win->toplevel);
+ }
+}
+
+static void update_auto_usbredir_sensitive(spice_connection *conn)
+{
+#ifdef USE_USBREDIR
+ int i;
+ GtkAction *ac;
+ gboolean sensitive;
+
+ sensitive = spice_session_has_channel_type(conn->session,
+ SPICE_CHANNEL_USBREDIR);
+ for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
+ if (conn->wins[i] == NULL)
+ continue;
+ ac = gtk_action_group_get_action(conn->wins[i]->ag, "auto-usbredir");
+ gtk_action_set_sensitive(ac, sensitive);
+ }
+#endif
+}
+
+static SpiceWindow* get_window(spice_connection *conn, int channel_id, int monitor_id)
+{
+ g_return_val_if_fail(channel_id < CHANNELID_MAX, NULL);
+ g_return_val_if_fail(monitor_id < MONITORID_MAX, NULL);
+
+ return conn->wins[channel_id * CHANNELID_MAX + monitor_id];
+}
+
+static void add_window(spice_connection *conn, SpiceWindow *win)
+{
+ g_return_if_fail(win != NULL);
+ g_return_if_fail(win->id < CHANNELID_MAX);
+ g_return_if_fail(win->monitor_id < MONITORID_MAX);
+ g_return_if_fail(conn->wins[win->id * CHANNELID_MAX + win->monitor_id] == NULL);
+
+ SPICE_DEBUG("add display monitor %d:%d", win->id, win->monitor_id);
+ conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = win;
+}
+
+static void del_window(spice_connection *conn, SpiceWindow *win)
+{
+ if (win == NULL)
+ return;
+
+ g_return_if_fail(win->id < CHANNELID_MAX);
+ g_return_if_fail(win->monitor_id < MONITORID_MAX);
+
+ g_debug("del display monitor %d:%d", win->id, win->monitor_id);
+ conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
+ if (win->id > 0)
+ spice_main_set_display_enabled(conn->main, win->id, FALSE);
+ else
+ spice_main_set_display_enabled(conn->main, win->monitor_id, FALSE);
+ spice_main_send_monitor_config(conn->main);
+
+ destroy_spice_window(win);
+}
+
+static void display_monitors(SpiceChannel *display, GParamSpec *pspec,
+ spice_connection *conn)
+{
+ GArray *monitors = NULL;
+ int id;
+ guint i;
+
+ g_object_get(display,
+ "channel-id", &id,
+ "monitors", &monitors,
+ NULL);
+ g_return_if_fail(monitors != NULL);
+
+ for (i = 0; i < monitors->len; i++) {
+ SpiceWindow *w;
+
+ if (!get_window(conn, id, i)) {
+ w = create_spice_window(conn, display, id, i);
+ add_window(conn, w);
+ spice_g_signal_connect_object(display, "display-mark",
+ G_CALLBACK(display_mark), w, 0);
+ gtk_widget_show(w->toplevel);
+ update_auto_usbredir_sensitive(conn);
+ }
+ }
+
+ for (; i < MONITORID_MAX; i++)
+ del_window(conn, get_window(conn, id, i));
+
+ g_clear_pointer(&monitors, g_array_unref);
+}
+
+static void port_write_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ SpicePortChannel *port = SPICE_PORT_CHANNEL(source_object);
+ GError *error = NULL;
+
+ spice_port_write_finish(port, res, &error);
+ if (error != NULL)
+ g_warning("%s", error->message);
+ g_clear_error(&error);
+}
+
+static void port_flushed_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ SpiceChannel *channel = SPICE_CHANNEL(source_object);
+ GError *error = NULL;
+
+ spice_channel_flush_finish(channel, res, &error);
+ if (error != NULL)
+ g_warning("%s", error->message);
+ g_clear_error(&error);
+
+ spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED);
+}
+
+static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
+{
+ char buf[4096];
+ gsize bytes_read;
+ GIOStatus status;
+
+ if (!(condition & G_IO_IN))
+ return FALSE;
+
+ status = g_io_channel_read_chars(gin, buf, sizeof(buf), &bytes_read, NULL);
+ if (status != G_IO_STATUS_NORMAL)
+ return FALSE;
+
+ if (stdin_port != NULL)
+ spice_port_write_async(stdin_port, buf, bytes_read, NULL, port_write_cb, NULL);
+
+ return TRUE;
+}
+
+static void watch_stdin(void);
+
+static void port_opened(SpiceChannel *channel, GParamSpec *pspec,
+ spice_connection *conn)
+{
+ SpicePortChannel *port = SPICE_PORT_CHANNEL(channel);
+ gchar *name = NULL;
+ gboolean opened = FALSE;
+
+ g_object_get(channel,
+ "port-name", &name,
+ "port-opened", &opened,
+ NULL);
+
+ g_printerr("port %p %s: %s\n", channel, name, opened ? "opened" : "closed");
+
+ if (opened) {
+ /* only send a break event and disconnect */
+ if (g_strcmp0(name, "org.spice.spicy.break") == 0) {
+ spice_port_event(port, SPICE_PORT_EVENT_BREAK);
+ spice_channel_flush_async(channel, NULL, port_flushed_cb, conn);
+ }
+
+ /* handle the first spicy port and connect it to stdin/out */
+ if (g_strcmp0(name, "org.spice.spicy") == 0 && stdin_port == NULL) {
+ watch_stdin();
+ stdin_port = port;
+ }
+ } else {
+ if (port == stdin_port)
+ stdin_port = NULL;
+ }
+
+ g_free(name);
+}
+
+static void port_data(SpicePortChannel *port,
+ gpointer data, int size, spice_connection *conn)
+{
+ int r;
+
+ if (port != stdin_port)
+ return;
+
+ r = write(fileno(stdout), data, size);
+ if (r != size) {
+ g_warning("port write failed result %d/%d errno %d", r, size, errno);
+ }
+}
+
+typedef struct {
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *progress;
+ GtkWidget *label;
+ GtkWidget *cancel;
+} TransferTaskWidgets;
+
+static void transfer_update_progress(GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ spice_connection *conn = user_data;
+ TransferTaskWidgets *widgets = g_hash_table_lookup(conn->transfers, object);
+ g_return_if_fail(widgets);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(widgets->progress),
+ spice_file_transfer_task_get_progress(SPICE_FILE_TRANSFER_TASK(object)));
+}
+
+static void transfer_task_finished(SpiceFileTransferTask *task, GError *error, spice_connection *conn)
+{
+ if (error)
+ g_warning("%s", error->message);
+ g_hash_table_remove(conn->transfers, task);
+ if (!g_hash_table_size(conn->transfers))
+ gtk_widget_hide(conn->transfer_dialog);
+}
+
+static void dialog_response_cb(GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ spice_connection *conn = user_data;
+ g_print("Reponse: %i\n", response_id);
+
+ if (response_id == GTK_RESPONSE_CANCEL) {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init(&iter, conn->transfers);
+ while (g_hash_table_iter_next(&iter, &key, &value)) {
+ SpiceFileTransferTask *task = key;
+ spice_file_transfer_task_cancel(task);
+ }
+ }
+}
+
+static void
+task_cancel_cb(GtkButton *button,
+ gpointer user_data)
+{
+ SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(user_data);
+ spice_file_transfer_task_cancel(task);
+}
+
+static TransferTaskWidgets *
+transfer_task_widgets_new(SpiceFileTransferTask *task)
+{
+ char *filename;
+ TransferTaskWidgets *widgets = g_new0(TransferTaskWidgets, 1);
+
+ widgets->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ widgets->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
+ widgets->cancel = gtk_button_new_with_label("Cancel");
+
+ widgets->progress = gtk_progress_bar_new();
+ filename = spice_file_transfer_task_get_filename(task);
+ widgets->label = gtk_label_new(filename);
+ g_free(filename);
+
+ gtk_widget_set_halign(widgets->label, GTK_ALIGN_START);
+ gtk_widget_set_valign(widgets->label, GTK_ALIGN_BASELINE);
+ gtk_widget_set_valign(widgets->progress, GTK_ALIGN_CENTER);
+ gtk_widget_set_hexpand(widgets->progress, TRUE);
+ gtk_widget_set_valign(widgets->cancel, GTK_ALIGN_CENTER);
+ gtk_widget_set_hexpand(widgets->progress, FALSE);
+
+ gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->progress,
+ TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(widgets->hbox), widgets->cancel,
+ FALSE, TRUE, 0);
+
+ gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->label,
+ TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(widgets->vbox), widgets->hbox,
+ TRUE, TRUE, 0);
+
+ g_signal_connect(widgets->cancel, "clicked",
+ G_CALLBACK(task_cancel_cb), task);
+
+ gtk_widget_show_all(widgets->vbox);
+
+ return widgets;
+}
+
+static void
+transfer_task_widgets_free(TransferTaskWidgets *widgets)
+{
+ /* child widgets will be destroyed automatically */
+ gtk_widget_destroy(widgets->vbox);
+ g_free(widgets);
+}
+
+static void spice_connection_add_task(spice_connection *conn, SpiceFileTransferTask *task)
+{
+ TransferTaskWidgets *widgets;
+ GtkWidget *content = NULL;
+
+ g_signal_connect(task, "notify::progress",
+ G_CALLBACK(transfer_update_progress), conn);
+ g_signal_connect(task, "finished",
+ G_CALLBACK(transfer_task_finished), conn);
+ if (!conn->transfer_dialog) {
+ conn->transfer_dialog = gtk_dialog_new_with_buttons("File Transfers",
+ GTK_WINDOW(conn->wins[0]->toplevel), 0,
+ "Cancel", GTK_RESPONSE_CANCEL, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(conn->transfer_dialog),
+ GTK_RESPONSE_CANCEL);
+ gtk_window_set_resizable(GTK_WINDOW(conn->transfer_dialog), FALSE);
+ g_signal_connect(conn->transfer_dialog, "response",
+ G_CALLBACK(dialog_response_cb), conn);
+ }
+ gtk_widget_show(conn->transfer_dialog);
+ content = gtk_dialog_get_content_area(GTK_DIALOG(conn->transfer_dialog));
+ gtk_container_set_border_width(GTK_CONTAINER(content), 12);
+
+ widgets = transfer_task_widgets_new(task);
+ g_hash_table_insert(conn->transfers, g_object_ref(task), widgets);
+ gtk_box_pack_start(GTK_BOX(content),
+ widgets->vbox, TRUE, TRUE, 6);
+}
+
+static void new_file_transfer(SpiceMainChannel *main, SpiceFileTransferTask *task, gpointer user_data)
+{
+ spice_connection *conn = user_data;
+ g_debug("new file transfer task");
+ spice_connection_add_task(conn, task);
+}
+
+static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
+{
+ spice_connection *conn = data;
+ int id;
+
+ g_object_get(channel, "channel-id", &id, NULL);
+ conn->channels++;
+ SPICE_DEBUG("new channel (#%d)", id);
+
+ if (SPICE_IS_MAIN_CHANNEL(channel)) {
+ SPICE_DEBUG("new main channel");
+ conn->main = SPICE_MAIN_CHANNEL(channel);
+ g_signal_connect(channel, "channel-event",
+ G_CALLBACK(main_channel_event), conn);
+ g_signal_connect(channel, "main-mouse-update",
+ G_CALLBACK(main_mouse_update), conn);
+ g_signal_connect(channel, "main-agent-update",
+ G_CALLBACK(main_agent_update), conn);
+ g_signal_connect(channel, "new-file-transfer",
+ G_CALLBACK(new_file_transfer), conn);
+ main_mouse_update(channel, conn);
+ main_agent_update(channel, conn);
+ }
+
+ if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
+ if (id >= SPICE_N_ELEMENTS(conn->wins))
+ return;
+ if (conn->wins[id] != NULL)
+ return;
+ SPICE_DEBUG("new display channel (#%d)", id);
+ g_signal_connect(channel, "notify::monitors",
+ G_CALLBACK(display_monitors), conn);
+ spice_channel_connect(channel);
+ }
+
+ if (SPICE_IS_INPUTS_CHANNEL(channel)) {
+ SPICE_DEBUG("new inputs channel");
+ g_signal_connect(channel, "inputs-modifiers",
+ G_CALLBACK(inputs_modifiers), conn);
+ }
+
+ if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
+ SPICE_DEBUG("new audio channel");
+ conn->audio = spice_audio_get(s, NULL);
+ }
+
+ if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
+ update_auto_usbredir_sensitive(conn);
+ }
+
+ if (SPICE_IS_PORT_CHANNEL(channel)) {
+ g_signal_connect(channel, "notify::port-opened",
+ G_CALLBACK(port_opened), conn);
+ g_signal_connect(channel, "port-data",
+ G_CALLBACK(port_data), conn);
+ spice_channel_connect(channel);
+ }
+}
+
+static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data)
+{
+ spice_connection *conn = data;
+ int id;
+
+ g_object_get(channel, "channel-id", &id, NULL);
+ if (SPICE_IS_MAIN_CHANNEL(channel)) {
+ SPICE_DEBUG("zap main channel");
+ conn->main = NULL;
+ }
+
+ if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
+ if (id >= SPICE_N_ELEMENTS(conn->wins))
+ return;
+ SPICE_DEBUG("zap display channel (#%d)", id);
+ /* FIXME destroy widget only */
+ }
+
+ if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
+ SPICE_DEBUG("zap audio channel");
+ }
+
+ if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
+ update_auto_usbredir_sensitive(conn);
+ }
+
+ if (SPICE_IS_PORT_CHANNEL(channel)) {
+ if (SPICE_PORT_CHANNEL(channel) == stdin_port)
+ stdin_port = NULL;
+ }
+
+ conn->channels--;
+ if (conn->channels > 0) {
+ return;
+ }
+
+ connection_destroy(conn);
+}
+
+static void migration_state(GObject *session,
+ GParamSpec *pspec, gpointer data)
+{
+ SpiceSessionMigration mig;
+
+ g_object_get(session, "migration-state", &mig, NULL);
+ if (mig == SPICE_SESSION_MIGRATION_SWITCHING)
+ g_message("migrating session");
+}
+
+static spice_connection *connection_new(void)
+{
+ spice_connection *conn;
+ SpiceUsbDeviceManager *manager;
+
+ conn = g_new0(spice_connection, 1);
+ conn->session = spice_session_new();
+ conn->gtk_session = spice_gtk_session_get(conn->session);
+ g_signal_connect(conn->session, "channel-new",
+ G_CALLBACK(channel_new), conn);
+ g_signal_connect(conn->session, "channel-destroy",
+ G_CALLBACK(channel_destroy), conn);
+ g_signal_connect(conn->session, "notify::migration-state",
+ G_CALLBACK(migration_state), conn);
+
+ manager = spice_usb_device_manager_get(conn->session, NULL);
+ if (manager) {
+ g_signal_connect(manager, "auto-connect-failed",
+ G_CALLBACK(usb_connect_failed), NULL);
+ g_signal_connect(manager, "device-error",
+ G_CALLBACK(usb_connect_failed), NULL);
+ }
+
+ conn->transfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ g_object_unref,
+ (GDestroyNotify)transfer_task_widgets_free);
+ connections++;
+ SPICE_DEBUG("%s (%d)", __FUNCTION__, connections);
+ return conn;
+}
+
+static void connection_connect(spice_connection *conn)
+{
+ conn->disconnecting = false;
+ spice_session_connect(conn->session);
+}
+
+static void connection_disconnect(spice_connection *conn)
+{
+ if (conn->disconnecting)
+ return;
+ conn->disconnecting = true;
+ spice_session_disconnect(conn->session);
+}
+
+static void connection_destroy(spice_connection *conn)
+{
+ g_object_unref(conn->session);
+ g_hash_table_unref(conn->transfers);
+ free(conn);
+
+ connections--;
+ SPICE_DEBUG("%s (%d)", __FUNCTION__, connections);
+ if (connections > 0) {
+ return;
+ }
+
+ g_main_loop_quit(mainloop);
+}
+
+/* ------------------------------------------------------------------ */
+
+static GOptionEntry cmd_entries[] = {
+ {
+ .long_name = "full-screen",
+ .short_name = 'f',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &fullscreen,
+ .description = "Open in full screen mode",
+ },{
+ .long_name = "version",
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &version,
+ .description = "Display version and quit",
+ },{
+ .long_name = "title",
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &spicy_title,
+ .description = "Set the window title",
+ .arg_description = "<title>",
+ },{
+ /* end of list */
+ }
+};
+
+static void usb_connect_failed(GObject *object,
+ SpiceUsbDevice *device,
+ GError *error,
+ gpointer data)
+{
+ GtkWidget *dialog;
+
+ if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
+ return;
+
+ dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "USB redirection error");
+ gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
+ "%s", error->message);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+static void setup_terminal(gboolean reset)
+{
+ int stdinfd = fileno(stdin);
+
+ if (!isatty(stdinfd))
+ return;
+
+#ifdef HAVE_TERMIOS_H
+ struct termios tios;
+ static struct termios saved_tios;
+ static bool saved = false;
+
+ if (reset) {
+ if (!saved)
+ return;
+ tios = saved_tios;
+ } else {
+ tcgetattr(stdinfd, &tios);
+ saved_tios = tios;
+ saved = true;
+ tios.c_lflag &= ~(ICANON | ECHO);
+ }
+
+ tcsetattr(stdinfd, TCSANOW, &tios);
+#endif
+}
+
+static void watch_stdin(void)
+{
+ int stdinfd = fileno(stdin);
+ GIOChannel *gin;
+
+ setup_terminal(false);
+ gin = g_io_channel_unix_new(stdinfd);
+ g_io_channel_set_flags(gin, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_add_watch(gin, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, input_cb, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ GError *error = NULL;
+ GOptionContext *context;
+ spice_connection *conn;
+ gchar *conf_file, *conf;
+ char *host = NULL, *port = NULL, *tls_port = NULL, *unix_path = NULL;
+
+ keyfile = g_key_file_new();
+
+ int mode = S_IRWXU;
+ conf_file = g_build_filename(g_get_user_config_dir(), "spicy", NULL);
+ if (g_mkdir_with_parents(conf_file, mode) == -1)
+ SPICE_DEBUG("failed to create config directory");
+ g_free(conf_file);
+
+ conf_file = g_build_filename(g_get_user_config_dir(), "spicy", "settings", NULL);
+ if (!g_key_file_load_from_file(keyfile, conf_file,
+ G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
+ SPICE_DEBUG("Couldn't load configuration: %s", error->message);
+ g_clear_error(&error);
+ }
+
+ /* parse opts */
+ gtk_init(&argc, &argv);
+ context = g_option_context_new("- spice client test application");
+ g_option_context_set_summary(context, "Gtk+ test client to connect to Spice servers.");
+ g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT ".");
+ g_option_context_add_group(context, spice_get_option_group());
+ g_option_context_set_main_group(context, spice_cmdline_get_option_group());
+ g_option_context_add_main_entries(context, cmd_entries, NULL);
+ g_option_context_add_group(context, gtk_get_option_group(TRUE));
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_print("option parsing failed: %s\n", error->message);
+ exit(1);
+ }
+ g_option_context_free(context);
+
+ if (version) {
+ g_print("spicy " PACKAGE_VERSION "\n");
+ exit(0);
+ }
+
+ mainloop = g_main_loop_new(NULL, false);
+
+ conn = connection_new();
+ spice_set_session_option(conn->session);
+ spice_cmdline_session_setup(conn->session);
+
+ g_object_get(conn->session,
+ "unix-path", &unix_path,
+ "host", &host,
+ "port", &port,
+ "tls-port", &tls_port,
+ NULL);
+ /* If user doesn't provide hostname and port, show the dialog window
+ instead of connecting to server automatically */
+ if ((host == NULL || (port == NULL && tls_port == NULL)) && unix_path == NULL) {
+ if (!spicy_connect_dialog(conn->session)) {
+ exit(0);
+ }
+ }
+ g_free(host);
+ g_free(port);
+ g_free(tls_port);
+ g_free(unix_path);
+
+ connection_connect(conn);
+ if (connections > 0)
+ g_main_loop_run(mainloop);
+ g_main_loop_unref(mainloop);
+
+ if ((conf = g_key_file_to_data(keyfile, NULL, &error)) == NULL ||
+ !g_file_set_contents(conf_file, conf, -1, &error)) {
+ SPICE_DEBUG("Couldn't save configuration: %s", error->message);
+ g_error_free(error);
+ error = NULL;
+ }
+
+ g_free(conf_file);
+ g_free(conf);
+ g_key_file_free(keyfile);
+
+ g_free(spicy_title);
+
+ setup_terminal(true);
+ return 0;
+}
More information about the Spice-commits
mailing list