[virt-tools-list] [PATCH virt-viewer v2 1/2] Add file transfer dialog

Jonathon Jongsma jjongsma at redhat.com
Fri Apr 8 15:26:32 UTC 2016


This dialog will show the progress of files being transferred from the
client to the guest and allows the user to cancel ongoing file transfer
tasks.  The user can cancel each transfer individually, or cancel all
ongoing transfers at once.
---
 src/Makefile.am                        |   2 +
 src/virt-viewer-file-transfer-dialog.c | 213 +++++++++++++++++++++++++++++++++
 src/virt-viewer-file-transfer-dialog.h |  61 ++++++++++
 src/virt-viewer-session-spice.c        |  23 +++-
 4 files changed, 298 insertions(+), 1 deletion(-)
 create mode 100644 src/virt-viewer-file-transfer-dialog.c
 create mode 100644 src/virt-viewer-file-transfer-dialog.h

diff --git a/src/Makefile.am b/src/Makefile.am
index ff487ba..115d73d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -91,6 +91,8 @@ libvirt_viewer_la_SOURCES += \
 	virt-viewer-session-spice.c \
 	virt-viewer-display-spice.h \
 	virt-viewer-display-spice.c \
+	virt-viewer-file-transfer-dialog.h \
+	virt-viewer-file-transfer-dialog.c \
 	$(NULL)
 endif
 
diff --git a/src/virt-viewer-file-transfer-dialog.c b/src/virt-viewer-file-transfer-dialog.c
new file mode 100644
index 0000000..bd85d82
--- /dev/null
+++ b/src/virt-viewer-file-transfer-dialog.c
@@ -0,0 +1,213 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "virt-viewer-file-transfer-dialog.h"
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE(VirtViewerFileTransferDialog, virt_viewer_file_transfer_dialog, GTK_TYPE_DIALOG)
+
+#define FILE_TRANSFER_DIALOG_PRIVATE(o) \
+        (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialogPrivate))
+
+struct _VirtViewerFileTransferDialogPrivate
+{
+    /* GHashTable<SpiceFileTransferTask, widgets> */
+    GHashTable *file_transfers;
+};
+
+
+static void
+virt_viewer_file_transfer_dialog_dispose(GObject *object)
+{
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(object);
+
+    g_clear_pointer(&self->priv->file_transfers, g_hash_table_unref);
+
+    G_OBJECT_CLASS(virt_viewer_file_transfer_dialog_parent_class)->dispose(object);
+}
+
+static void
+virt_viewer_file_transfer_dialog_class_init(VirtViewerFileTransferDialogClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+    g_type_class_add_private(klass, sizeof(VirtViewerFileTransferDialogPrivate));
+
+    object_class->dispose = virt_viewer_file_transfer_dialog_dispose;
+}
+
+static void
+dialog_response(GtkDialog *dialog,
+                gint response_id,
+                gpointer user_data G_GNUC_UNUSED)
+{
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(dialog);
+    GHashTableIter iter;
+    gpointer key, value;
+
+    switch (response_id) {
+        case GTK_RESPONSE_CANCEL:
+            /* cancel all current tasks */
+            g_hash_table_iter_init(&iter, self->priv->file_transfers);
+
+            while (g_hash_table_iter_next(&iter, &key, &value)) {
+                spice_file_transfer_task_cancel(SPICE_FILE_TRANSFER_TASK(key));
+            }
+            break;
+        case GTK_RESPONSE_DELETE_EVENT:
+            /* silently ignore */
+            break;
+        default:
+            g_warn_if_reached();
+    }
+}
+
+static void task_cancel_clicked(GtkButton *button G_GNUC_UNUSED,
+                                gpointer user_data)
+{
+    SpiceFileTransferTask *task = user_data;
+    spice_file_transfer_task_cancel(task);
+}
+
+typedef struct {
+    GtkWidget *vbox;
+    GtkWidget *hbox;
+    GtkWidget *progress;
+    GtkWidget *label;
+    GtkWidget *cancel;
+} TaskWidgets;
+
+static TaskWidgets *task_widgets_new(SpiceFileTransferTask *task)
+{
+    TaskWidgets *w = g_new0(TaskWidgets, 1);
+
+    w->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
+    w->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
+    w->progress = gtk_progress_bar_new();
+    w->label = gtk_label_new(spice_file_transfer_task_get_filename(task));
+    w->cancel = gtk_button_new_from_icon_name("gtk-cancel", GTK_ICON_SIZE_SMALL_TOOLBAR);
+    gtk_widget_set_hexpand(w->progress, TRUE);
+    gtk_widget_set_valign(w->progress, GTK_ALIGN_CENTER);
+    gtk_widget_set_hexpand(w->label, TRUE);
+    gtk_widget_set_valign(w->label, GTK_ALIGN_END);
+    gtk_widget_set_halign(w->label, GTK_ALIGN_START);
+    gtk_widget_set_hexpand(w->cancel, FALSE);
+    gtk_widget_set_valign(w->cancel, GTK_ALIGN_CENTER);
+
+    g_signal_connect(w->cancel, "clicked",
+                     G_CALLBACK(task_cancel_clicked), task);
+
+    gtk_box_pack_start(GTK_BOX(w->hbox), w->progress, TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(w->hbox), w->cancel, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(w->vbox), w->label, TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(w->vbox), w->hbox, TRUE, TRUE, 0);
+
+    gtk_widget_show_all(w->vbox);
+    return w;
+}
+
+static gboolean delete_event(GtkWidget *widget,
+                             GdkEvent *event G_GNUC_UNUSED,
+                             gpointer user_data G_GNUC_UNUSED)
+{
+    /* don't allow window to be deleted, just process the response signal,
+     * which may result in the window being hidden */
+    gtk_dialog_response(GTK_DIALOG(widget), GTK_RESPONSE_CANCEL);
+    return TRUE;
+}
+
+static void
+virt_viewer_file_transfer_dialog_init(VirtViewerFileTransferDialog *self)
+{
+    GtkBox *content = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self)));
+
+    self->priv = FILE_TRANSFER_DIALOG_PRIVATE(self);
+
+    gtk_widget_set_size_request(GTK_WIDGET(content), 400, -1);
+    gtk_container_set_border_width(GTK_CONTAINER(content), 12);
+    self->priv->file_transfers = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                                       g_object_unref,
+                                                       (GDestroyNotify)g_free);
+    gtk_dialog_add_button(GTK_DIALOG(self), _("Cancel"), GTK_RESPONSE_CANCEL);
+    gtk_dialog_set_default_response(GTK_DIALOG(self),
+                                    GTK_RESPONSE_CANCEL);
+    g_signal_connect(self, "response", G_CALLBACK(dialog_response), NULL);
+    g_signal_connect(self, "delete-event", G_CALLBACK(delete_event), NULL);
+}
+
+VirtViewerFileTransferDialog *
+virt_viewer_file_transfer_dialog_new(GtkWindow *parent)
+{
+    return g_object_new(VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG,
+                        "title", _("File Transfers"),
+                        "transient-for", parent,
+                        "resizable", FALSE,
+                        NULL);
+}
+
+static void task_progress_notify(GObject *object,
+                                 GParamSpec *pspec G_GNUC_UNUSED,
+                                 gpointer user_data)
+{
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data);
+    SpiceFileTransferTask *task = SPICE_FILE_TRANSFER_TASK(object);
+    TaskWidgets *w = g_hash_table_lookup(self->priv->file_transfers, task);
+    g_return_if_fail(w);
+
+    double pct = spice_file_transfer_task_get_progress(task);
+    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(w->progress), pct);
+}
+
+static void task_finished(SpiceFileTransferTask *task,
+                          GError *error,
+                          gpointer user_data)
+{
+    VirtViewerFileTransferDialog *self = VIRT_VIEWER_FILE_TRANSFER_DIALOG(user_data);
+    TaskWidgets *w = g_hash_table_lookup(self->priv->file_transfers, task);
+
+    if (error && !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning("File transfer task %p failed: %s", task, error->message);
+
+    g_return_if_fail(w);
+
+    gtk_widget_destroy(w->vbox);
+
+    g_hash_table_remove(self->priv->file_transfers, task);
+
+    /* if this is the last transfer, close the dialog */
+    if (!g_hash_table_size(self->priv->file_transfers))
+        gtk_widget_hide(GTK_WIDGET(self));
+}
+
+void virt_viewer_file_transfer_dialog_add_task(VirtViewerFileTransferDialog *self,
+                                               SpiceFileTransferTask *task)
+{
+    GtkBox *content = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(self)));
+    TaskWidgets *w = task_widgets_new(task);
+
+    gtk_box_pack_start(content,
+                       w->vbox,
+                       TRUE, TRUE, 12);
+    g_hash_table_insert(self->priv->file_transfers, g_object_ref(task), w);
+    g_signal_connect(task, "notify::progress", G_CALLBACK(task_progress_notify), self);
+    g_signal_connect(task, "finished", G_CALLBACK(task_finished), self);
+
+    gtk_widget_show(GTK_WIDGET(self));
+}
diff --git a/src/virt-viewer-file-transfer-dialog.h b/src/virt-viewer-file-transfer-dialog.h
new file mode 100644
index 0000000..8805b14
--- /dev/null
+++ b/src/virt-viewer-file-transfer-dialog.h
@@ -0,0 +1,61 @@
+/*
+ * Virt Viewer: A virtual machine console viewer
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__
+#define __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include <spice-client.h>
+
+G_BEGIN_DECLS
+
+#define VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG virt_viewer_file_transfer_dialog_get_type()
+
+#define VIRT_VIEWER_FILE_TRANSFER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialog))
+#define VIRT_VIEWER_FILE_TRANSFER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialogClass))
+#define VIRT_VIEWER_IS_FILE_TRANSFER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG))
+#define VIRT_VIEWER_IS_FILE_TRANSFER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG))
+#define VIRT_VIEWER_FILE_TRANSFER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), VIRT_VIEWER_TYPE_FILE_TRANSFER_DIALOG, VirtViewerFileTransferDialogClass))
+
+typedef struct _VirtViewerFileTransferDialog VirtViewerFileTransferDialog;
+typedef struct _VirtViewerFileTransferDialogClass VirtViewerFileTransferDialogClass;
+typedef struct _VirtViewerFileTransferDialogPrivate VirtViewerFileTransferDialogPrivate;
+
+struct _VirtViewerFileTransferDialog
+{
+    GtkDialog parent;
+
+    VirtViewerFileTransferDialogPrivate *priv;
+};
+
+struct _VirtViewerFileTransferDialogClass
+{
+    GtkDialogClass parent_class;
+};
+
+GType virt_viewer_file_transfer_dialog_get_type(void) G_GNUC_CONST;
+
+VirtViewerFileTransferDialog *virt_viewer_file_transfer_dialog_new(GtkWindow *parent);
+void virt_viewer_file_transfer_dialog_add_task(VirtViewerFileTransferDialog *self,
+                                               SpiceFileTransferTask *task);
+
+G_END_DECLS
+
+#endif /* __VIRT_VIEWER_FILE_TRANSFER_DIALOG_H__ */
diff --git a/src/virt-viewer-session-spice.c b/src/virt-viewer-session-spice.c
index f7f2dc9..5f05df7 100644
--- a/src/virt-viewer-session-spice.c
+++ b/src/virt-viewer-session-spice.c
@@ -30,12 +30,12 @@
 
 #include <usb-device-widget.h>
 #include "virt-viewer-file.h"
+#include "virt-viewer-file-transfer-dialog.h"
 #include "virt-viewer-util.h"
 #include "virt-viewer-session-spice.h"
 #include "virt-viewer-display-spice.h"
 #include "virt-viewer-auth.h"
 
-
 G_DEFINE_TYPE (VirtViewerSessionSpice, virt_viewer_session_spice, VIRT_VIEWER_TYPE_SESSION)
 
 
@@ -50,6 +50,8 @@ struct _VirtViewerSessionSpicePrivate {
     gboolean has_sw_smartcard_reader;
     guint pass_try;
     gboolean did_auto_conf;
+    VirtViewerFileTransferDialog *file_transfer_dialog;
+
 };
 
 #define VIRT_VIEWER_SESSION_SPICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_SESSION_SPICE, VirtViewerSessionSpicePrivate))
@@ -147,6 +149,10 @@ virt_viewer_session_spice_dispose(GObject *obj)
     spice->priv->audio = NULL;
 
     g_clear_object(&spice->priv->main_window);
+    if (spice->priv->file_transfer_dialog) {
+        gtk_widget_destroy(GTK_WIDGET(spice->priv->file_transfer_dialog));
+        spice->priv->file_transfer_dialog = NULL;
+    }
 
     G_OBJECT_CLASS(virt_viewer_session_spice_parent_class)->dispose(obj);
 }
@@ -224,6 +230,9 @@ virt_viewer_session_spice_constructed(GObject *obj)
                                       G_CALLBACK(update_share_folder), self,
                                       G_CONNECT_SWAPPED);
 
+    self->priv->file_transfer_dialog =
+        virt_viewer_file_transfer_dialog_new(self->priv->main_window);
+
     G_OBJECT_CLASS(virt_viewer_session_spice_parent_class)->constructed(obj);
 }
 
@@ -922,6 +931,16 @@ virt_viewer_session_spice_display_monitors(SpiceChannel *channel,
 }
 
 static void
+on_new_file_transfer(SpiceMainChannel *channel G_GNUC_UNUSED,
+                     SpiceFileTransferTask *task,
+                     gpointer user_data)
+{
+    VirtViewerSessionSpice *self = VIRT_VIEWER_SESSION_SPICE(user_data);
+    virt_viewer_file_transfer_dialog_add_task(self->priv->file_transfer_dialog,
+                                              task);
+}
+
+static void
 virt_viewer_session_spice_channel_new(SpiceSession *s,
                                       SpiceChannel *channel,
                                       VirtViewerSession *session)
@@ -953,6 +972,8 @@ virt_viewer_session_spice_channel_new(SpiceSession *s,
 
         virt_viewer_signal_connect_object(channel, "notify::agent-connected",
                                           G_CALLBACK(agent_connected_changed), self, 0);
+        virt_viewer_signal_connect_object(channel, "new-file-transfer",
+                                          G_CALLBACK(on_new_file_transfer), self, 0);
     }
 
     if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
-- 
2.4.11




More information about the virt-tools-list mailing list