On Fri, Apr 8, 2016 at 12:06 AM, Jonathon Jongsma <jjongsma@xxxxxxxxxx> wrote: > The file transfer progress dialog depends on new API provided by > spice-gtk to display the progress of each individual file being > transferred between the client and the guest. If we're not building > against a new enough version of spice-gtk, the dialog will simply be > disabled. > > The dialog also allows each transfer to be cancelled individually, or to > cancel all ongoing transfers at once. > --- > src/Makefile.am | 2 + > src/virt-viewer-file-transfer-dialog.c | 231 +++++++++++++++++++++++++++++++++ > src/virt-viewer-file-transfer-dialog.h | 65 ++++++++++ > src/virt-viewer-session-spice.c | 35 +++++ > 4 files changed, 333 insertions(+) > 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..4cf3221 > --- /dev/null > +++ b/src/virt-viewer-file-transfer-dialog.c > @@ -0,0 +1,231 @@ > +/* > + * Virt Viewer: A virtual machine console viewer > + * > + * Copyright (C) 2015 Red Hat, Inc. 2016? > + * > + * 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> > + > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) No need for this check, we already depend on 0.31 > + > +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); > + > + if (self->priv->file_transfers) { > + g_hash_table_unref(self->priv->file_transfers); > + self->priv->file_transfers = NULL; > + } g_clear_object() can be used here, right? > + > + 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); > + > +#if !GTK_CHECK_VERSION(3, 0, 0) > + w->vbox = gtk_vbox_new(FALSE, 6); > + w->hbox = gtk_hbox_new(FALSE, 12); > +#else > + w->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); > + w->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); > +#endif As only GTK3 is supported nowadays, these #if GTK_CHECK_VERSION() can all be removed. > + w->progress = gtk_progress_bar_new(); > + w->label = gtk_label_new(spice_file_transfer_task_get_filename(task)); > +#if !GTK_CHECK_VERSION(3, 0, 0) > + w->cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL); > +#else > + w->cancel = gtk_button_new_from_icon_name("gtk-cancel", GTK_ICON_SIZE_SMALL_TOOLBAR); > +#endif > +#if GTK_CHECK_VERSION(3, 0, 0) > + 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); > +#endif > + > + 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)); > +} > + > +#endif /* SPICE_GTK_CHECK_VERSION(0, 30, 6) */ > diff --git a/src/virt-viewer-file-transfer-dialog.h b/src/virt-viewer-file-transfer-dialog.h > new file mode 100644 > index 0000000..49ced30 > --- /dev/null > +++ b/src/virt-viewer-file-transfer-dialog.h > @@ -0,0 +1,65 @@ > +/* > + * Virt Viewer: A virtual machine console viewer > + * > + * Copyright (C) 2015 Red Hat, Inc. 2016? > + * > + * 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 > + > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) We already depend on 0.31 > + > +#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); > + > +#endif /* SPICE_GTK_CHECK_VERSION(0, 30, 6) */ > + > +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..fc028e5 100644 > --- a/src/virt-viewer-session-spice.c > +++ b/src/virt-viewer-session-spice.c > @@ -36,6 +36,10 @@ > #include "virt-viewer-auth.h" > > > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) > +#include "virt-viewer-file-transfer-dialog.h" > +#endif We already depend on 0.31 ... > + > G_DEFINE_TYPE (VirtViewerSessionSpice, virt_viewer_session_spice, VIRT_VIEWER_TYPE_SESSION) > > > @@ -50,6 +54,10 @@ struct _VirtViewerSessionSpicePrivate { > gboolean has_sw_smartcard_reader; > guint pass_try; > gboolean did_auto_conf; > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) > + VirtViewerFileTransferDialog *file_transfer_dialog; > +#endif > + Same here ... > }; > > #define VIRT_VIEWER_SESSION_SPICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_SESSION_SPICE, VirtViewerSessionSpicePrivate)) > @@ -147,6 +155,12 @@ virt_viewer_session_spice_dispose(GObject *obj) > spice->priv->audio = NULL; > > g_clear_object(&spice->priv->main_window); > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) > + if (spice->priv->file_transfer_dialog) { > + gtk_widget_destroy(GTK_WIDGET(spice->priv->file_transfer_dialog)); > + spice->priv->file_transfer_dialog = NULL; > + } > +#endif > Same here .. > G_OBJECT_CLASS(virt_viewer_session_spice_parent_class)->dispose(obj); > } > @@ -224,6 +238,11 @@ virt_viewer_session_spice_constructed(GObject *obj) > G_CALLBACK(update_share_folder), self, > G_CONNECT_SWAPPED); > > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) > + self->priv->file_transfer_dialog = > + virt_viewer_file_transfer_dialog_new(self->priv->main_window); > +#endif > + Same here ... > G_OBJECT_CLASS(virt_viewer_session_spice_parent_class)->constructed(obj); > } > > @@ -921,6 +940,18 @@ virt_viewer_session_spice_display_monitors(SpiceChannel *channel, > > } > > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) > +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); > +} > +#endif Same here ... > + > static void > virt_viewer_session_spice_channel_new(SpiceSession *s, > SpiceChannel *channel, > @@ -953,6 +984,10 @@ virt_viewer_session_spice_channel_new(SpiceSession *s, > > virt_viewer_signal_connect_object(channel, "notify::agent-connected", > G_CALLBACK(agent_connected_changed), self, 0); > +#if SPICE_GTK_CHECK_VERSION(0, 30, 6) > + virt_viewer_signal_connect_object(channel, "new-file-transfer", > + G_CALLBACK(on_new_file_transfer), self, 0); > +#endif Same here ... > } > > if (SPICE_IS_DISPLAY_CHANNEL(channel)) { > -- > 2.4.11 > > _______________________________________________ > virt-tools-list mailing list > virt-tools-list@xxxxxxxxxx > https://www.redhat.com/mailman/listinfo/virt-tools-list _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list