[Spice-devel] [spice-gtk v1 7/9] file-xfer: move to spice-file-transfer-task.c
Victor Toso
victortoso at redhat.com
Thu May 19 11:21:47 UTC 2016
Previous six patches are related to this change. This patch moves:
* GObject boilerplate
* External API related to SpiceFileTransferTask
* Internal API needed by channel-main
* Helpers that belong to this object
---
src/Makefile.am | 2 +
src/channel-main.c | 682 +----------------------------------
src/spice-file-transfer-task-priv.h | 56 +++
src/spice-file-transfer-task.c | 697 ++++++++++++++++++++++++++++++++++++
4 files changed, 756 insertions(+), 681 deletions(-)
create mode 100644 src/spice-file-transfer-task-priv.h
create mode 100644 src/spice-file-transfer-task.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 73bb39c..35ac906 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -233,6 +233,8 @@ libspice_client_glib_2_0_la_SOURCES = \
spice-channel.c \
spice-channel-cache.h \
spice-channel-priv.h \
+ spice-file-transfer-task.c \
+ spice-file-transfer-task-priv.h \
coroutine.h \
gio-coroutine.c \
gio-coroutine.h \
diff --git a/src/channel-main.c b/src/channel-main.c
index efe559c..2fcc382 100644
--- a/src/channel-main.c
+++ b/src/channel-main.c
@@ -29,7 +29,7 @@
#include "spice-channel-priv.h"
#include "spice-session-priv.h"
#include "spice-audio-priv.h"
-#include "spice-file-transfer-task.h"
+#include "spice-file-transfer-task-priv.h"
/**
* SECTION:channel-main
@@ -54,87 +54,6 @@
typedef struct spice_migrate spice_migrate;
-/**
- * SECTION:file-transfer-task
- * @short_description: Monitoring file transfers
- * @title: File Transfer Task
- * @section_id:
- * @see_also: #SpiceMainChannel
- * @stability: Stable
- * @include: spice-client.h
- *
- * SpiceFileTransferTask is an object that represents a particular file
- * transfer between the client and the guest. The properties and signals of the
- * object can be used to monitor the status and result of the transfer. The
- * Main Channel's #SpiceMainChannel::new-file-transfer signal will be emitted
- * whenever a new file transfer task is initiated.
- *
- * Since: 0.31
- */
-G_DEFINE_TYPE(SpiceFileTransferTask, spice_file_transfer_task, G_TYPE_OBJECT)
-
-#define FILE_TRANSFER_TASK_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_FILE_TRANSFER_TASK, SpiceFileTransferTaskPrivate))
-
-#define FILE_XFER_CHUNK_SIZE (VD_AGENT_MAX_DATA_SIZE * 32)
-
-typedef void (*SpiceFileTransferTaskFlushCb)(SpiceFileTransferTask *xfer_task,
- void *buffer,
- gssize count,
- gpointer user_data);
-
-static SpiceMainChannel *spice_file_transfer_task_get_channel(SpiceFileTransferTask *self);
-static GCancellable *spice_file_transfer_task_get_cancellable(SpiceFileTransferTask *self);
-static void spice_file_transfer_task_flush_done(SpiceFileTransferTask *self, GError *error);
-static GList *spice_file_transfer_task_create_tasks(SpiceMainChannel *channel,
- GFile **files,
- GFileCopyFlags flags,
- GCancellable *cancellable,
- SpiceFileTransferTaskFlushCb flush_callback,
- gpointer flush_callback_data,
- GAsyncReadyCallback callback,
- gpointer user_data);
-static void spice_file_transfer_task_start_task(SpiceFileTransferTask *self);
-
-struct _SpiceFileTransferTaskPrivate
-
-/* private */
-{
- uint32_t id;
- gboolean pending;
- GFile *file;
- SpiceMainChannel *channel;
- GFileInputStream *file_stream;
- GFileCopyFlags flags;
- GCancellable *cancellable;
- SpiceFileTransferTaskFlushCb flush_callback;
- gpointer flush_callback_data;
- GAsyncReadyCallback callback;
- gpointer user_data;
- char *buffer;
- uint64_t read_bytes;
- uint64_t file_size;
- gint64 start_time;
- gint64 last_update;
- GError *error;
-};
-
-enum {
- PROP_TASK_ID = 1,
- PROP_TASK_CHANNEL,
- PROP_TASK_CANCELLABLE,
- PROP_TASK_FILE,
- PROP_TASK_PROGRESS,
-};
-
-enum {
- SIGNAL_FINISHED,
- SIGNAL_FILE_INFO,
- LAST_TASK_SIGNAL
-};
-
-static guint task_signals[LAST_TASK_SIGNAL];
-
typedef enum {
DISPLAY_UNDEFINED,
DISPLAY_DISABLED,
@@ -253,8 +172,6 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev
gpointer data);
static gboolean main_migrate_handshake_done(gpointer data);
static void spice_main_channel_send_migration_handshake(SpiceChannel *channel);
-static void file_xfer_continue_read(SpiceFileTransferTask *task);
-static void spice_file_transfer_task_completed(SpiceFileTransferTask *self, GError *error);
static void file_xfer_flushed(SpiceMainChannel *channel, gboolean success);
static void spice_main_set_max_clipboard(SpiceMainChannel *self, gint max);
static void set_agent_connected(SpiceMainChannel *channel, gboolean connected);
@@ -1814,59 +1731,6 @@ static void main_handle_agent_disconnected(SpiceChannel *channel, SpiceMsgIn *in
agent_stopped(SPICE_MAIN_CHANNEL(channel));
}
-/* main context */
-static void file_xfer_close_cb(GObject *object,
- GAsyncResult *close_res,
- gpointer user_data)
-{
- GTask *task;
- SpiceFileTransferTask *self;
- GError *error = NULL;
-
- self = user_data;
-
- if (object) {
- GInputStream *stream = G_INPUT_STREAM(object);
- g_input_stream_close_finish(stream, close_res, &error);
- if (error) {
- /* This error dont need to report to user, just print a log */
- SPICE_DEBUG("close file error: %s", error->message);
- g_clear_error(&error);
- }
- }
-
- /* Notify to user that files have been transferred or something error
- happened. */
- task = g_task_new(self->priv->channel,
- self->priv->cancellable,
- self->priv->callback,
- self->priv->user_data);
-
- if (self->priv->error) {
- g_task_return_error(task, self->priv->error);
- } else {
- g_task_return_boolean(task, TRUE);
- if (spice_util_get_debug()) {
- gint64 now = g_get_monotonic_time();
- gchar *basename = g_file_get_basename(self->priv->file);
- double seconds = (double) (now - self->priv->start_time) / G_TIME_SPAN_SECOND;
- gchar *file_size_str = g_format_size(self->priv->file_size);
- gchar *transfer_speed_str = g_format_size(self->priv->file_size / seconds);
-
- g_warn_if_fail(self->priv->read_bytes == self->priv->file_size);
- SPICE_DEBUG("transferred file %s of %s size in %.1f seconds (%s/s)",
- basename, file_size_str, seconds, transfer_speed_str);
-
- g_free(basename);
- g_free(file_size_str);
- g_free(transfer_speed_str);
- }
- }
- g_object_unref(task);
-
- g_object_unref(self);
-}
-
static void file_xfer_data_flushed_cb(GObject *source_object,
GAsyncResult *res,
gpointer user_data)
@@ -1913,93 +1777,6 @@ static void file_xfer_flush_callback(SpiceFileTransferTask *xfer_task,
file_xfer_flush_async(main_channel, cancellable, file_xfer_data_flushed_cb, xfer_task);
}
-/* main context */
-static void file_xfer_read_cb(GObject *source_object,
- GAsyncResult *res,
- gpointer user_data)
-{
- SpiceFileTransferTask *self = user_data;
- gssize count;
- GError *error = NULL;
-
- self->priv->pending = FALSE;
- count = g_input_stream_read_finish(G_INPUT_STREAM(self->priv->file_stream),
- res, &error);
- /* Check for pending earlier errors */
- if (self->priv->error) {
- spice_file_transfer_task_completed(self, error);
- return;
- }
-
- if (count > 0 || self->priv->file_size == 0) {
- self->priv->read_bytes += count;
- g_object_notify(G_OBJECT(self), "progress");
-
- if (self->priv->flush_callback) {
- self->priv->pending = TRUE;
- self->priv->flush_callback(self, self->priv->buffer, count, self->priv->flush_callback_data);
- }
- } else if (error) {
- spice_channel_wakeup(SPICE_CHANNEL(self->priv->channel), FALSE);
- spice_file_transfer_task_completed(self, error);
- }
- /* else EOF, do nothing (wait for VD_AGENT_FILE_XFER_STATUS from agent) */
-}
-
-/* coroutine context */
-static void file_xfer_continue_read(SpiceFileTransferTask *self)
-{
- g_input_stream_read_async(G_INPUT_STREAM(self->priv->file_stream),
- self->priv->buffer,
- FILE_XFER_CHUNK_SIZE,
- G_PRIORITY_DEFAULT,
- self->priv->cancellable,
- file_xfer_read_cb,
- self);
- self->priv->pending = TRUE;
-}
-
-/* coroutine context */
-static void spice_file_transfer_task_handle_status(SpiceFileTransferTask *task,
- VDAgentFileXferStatusMessage *msg)
-{
- GError *error = NULL;
- g_return_if_fail(task != NULL);
-
- SPICE_DEBUG("task %d received response %d", msg->id, msg->result);
-
- switch (msg->result) {
- case VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA:
- if (task->priv->pending) {
- error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
- "transfer received CAN_SEND_DATA in pending state");
- break;
- }
- file_xfer_continue_read(task);
- return;
- case VD_AGENT_FILE_XFER_STATUS_CANCELLED:
- error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
- "transfer is cancelled by spice agent");
- break;
- case VD_AGENT_FILE_XFER_STATUS_ERROR:
- error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
- "some errors occurred in the spice agent");
- break;
- case VD_AGENT_FILE_XFER_STATUS_SUCCESS:
- if (task->priv->pending)
- error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
- "transfer received success in pending state");
- break;
- default:
- g_warn_if_reached();
- error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
- "unhandled status type: %u", msg->result);
- break;
- }
-
- spice_file_transfer_task_completed(task, error);
-}
-
/* any context: the message is not flushed immediately,
you can wakeup() the channel coroutine or send_msg_queue() */
static void agent_max_clipboard(SpiceMainChannel *self)
@@ -2953,88 +2730,6 @@ void spice_main_set_display_enabled(SpiceMainChannel *channel, int id, gboolean
spice_main_update_display_enabled(channel, id, enabled, TRUE);
}
-static void spice_file_transfer_task_completed(SpiceFileTransferTask *self,
- GError *error)
-{
- /* In case of multiple errors we only report the first error */
- if (self->priv->error)
- g_clear_error(&error);
- if (error) {
- gchar *path = g_file_get_path(self->priv->file);
- SPICE_DEBUG("File %s xfer failed: %s",
- path, error->message);
- g_free(path);
- self->priv->error = error;
- }
-
- if (self->priv->pending)
- return;
-
- if (!self->priv->file_stream) {
- file_xfer_close_cb(NULL, NULL, self);
- goto signal;
- }
-
- g_input_stream_close_async(G_INPUT_STREAM(self->priv->file_stream),
- G_PRIORITY_DEFAULT,
- self->priv->cancellable,
- file_xfer_close_cb,
- self);
- self->priv->pending = TRUE;
-signal:
- g_signal_emit(self, task_signals[SIGNAL_FINISHED], 0, self->priv->error);
-}
-
-
-static void file_xfer_info_async_cb(GObject *obj, GAsyncResult *res, gpointer data)
-{
- GFileInfo *info;
- GFile *file = G_FILE(obj);
- GError *error = NULL;
- SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(data);
-
- self->priv->pending = FALSE;
- info = g_file_query_info_finish(file, res, &error);
- if (error || self->priv->error)
- goto failed;
-
- self->priv->file_size =
- g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
- g_signal_emit(self, task_signals[SIGNAL_FILE_INFO], 0, info);
- g_object_notify(G_OBJECT(self), "progress");
-
- return;
-
-failed:
- spice_file_transfer_task_completed(self, error);
-}
-
-static void file_xfer_read_async_cb(GObject *obj, GAsyncResult *res, gpointer data)
-{
- GFile *file = G_FILE(obj);
- SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(data);
- GError *error = NULL;
-
- self->priv->pending = FALSE;
- self->priv->file_stream = g_file_read_finish(file, res, &error);
- if (error || self->priv->error) {
- spice_file_transfer_task_completed(self, error);
- return;
- }
-
- g_file_query_info_async(self->priv->file,
- "standard::*",
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_DEFAULT,
- self->priv->cancellable,
- file_xfer_info_async_cb,
- self);
- self->priv->pending = TRUE;
-}
-
-static SpiceFileTransferTask *spice_file_transfer_task_new(SpiceMainChannel *channel,
- GFile *file,
- GCancellable *cancellable);
static void file_xfer_on_file_info(SpiceFileTransferTask *xfer_task,
GFileInfo *info,
gpointer data)
@@ -3299,378 +2994,3 @@ gboolean spice_main_file_copy_finish(SpiceMainChannel *channel,
return g_task_propagate_boolean(task, error);
}
-
-static SpiceMainChannel *spice_file_transfer_task_get_channel(SpiceFileTransferTask *self)
-{
- g_assert_nonnull(self);
- return self->priv->channel;
-}
-
-static GCancellable *spice_file_transfer_task_get_cancellable(SpiceFileTransferTask *self)
-{
- g_assert_nonnull(self);
- return self->priv->cancellable;
-}
-
-static void spice_file_transfer_task_flush_done(SpiceFileTransferTask *self, GError *error)
-{
- g_assert_nonnull(self);
- g_return_if_fail(self->priv->pending);
-
- self->priv->pending = FALSE;
-
- if (error || self->priv->error) {
- spice_file_transfer_task_completed(self, error);
- return;
- }
-
- if (spice_util_get_debug()) {
- const GTimeSpan interval = 20 * G_TIME_SPAN_SECOND;
- gint64 now = g_get_monotonic_time();
-
- if (interval < now - self->priv->last_update) {
- gchar *basename = g_file_get_basename(self->priv->file);
- self->priv->last_update = now;
- SPICE_DEBUG("transferred %.2f%% of the file %s",
- 100.0 * self->priv->read_bytes / self->priv->file_size, basename);
- g_free(basename);
- }
- }
-
- /* Read more data */
- file_xfer_continue_read(self);
-}
-
-static GList *spice_file_transfer_task_create_tasks(SpiceMainChannel *channel,
- GFile **files,
- GFileCopyFlags flags,
- GCancellable *cancellable,
- SpiceFileTransferTaskFlushCb flush_callback,
- gpointer flush_callback_data,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SpiceFileTransferTask *task;
- gint i;
- GList *tasks = NULL;
-
- for (i = 0; files[i] != NULL && !g_cancellable_is_cancelled(cancellable); i++) {
- GCancellable *task_cancellable = cancellable;
- /* if a cancellable object was not provided for the overall operation,
- * create a separate object for each file so that they can be cancelled
- * separately */
- if (!task_cancellable)
- task_cancellable = g_cancellable_new();
-
- task = spice_file_transfer_task_new(channel, files[i], task_cancellable);
- task->priv->flags = flags;
- task->priv->flush_callback = flush_callback;
- task->priv->flush_callback_data = flush_callback_data;
- task->priv->callback = callback;
- task->priv->user_data = user_data;
-
- CHANNEL_DEBUG(channel, "Insert a xfer task:%d to task list",
- task->priv->id);
- tasks = g_list_prepend(tasks, task);
-
- /* if we created a per-task cancellable above, free it */
- if (!cancellable)
- g_object_unref(task_cancellable);
- }
-
- return g_list_reverse(tasks);
-}
-
-static void spice_file_transfer_task_start_task(SpiceFileTransferTask *self)
-{
- g_assert_nonnull(self);
- self->priv->pending = TRUE;
- g_file_read_async(self->priv->file,
- G_PRIORITY_DEFAULT,
- self->priv->cancellable,
- file_xfer_read_async_cb,
- g_object_ref(self));
-}
-
-static void
-spice_file_transfer_task_get_property(GObject *object,
- guint property_id,
- GValue *value,
- GParamSpec *pspec)
-{
- SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
-
- switch (property_id)
- {
- case PROP_TASK_ID:
- g_value_set_uint(value, self->priv->id);
- break;
- case PROP_TASK_FILE:
- g_value_set_object(value, self->priv->file);
- break;
- case PROP_TASK_PROGRESS:
- g_value_set_double(value, spice_file_transfer_task_get_progress(self));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
- }
-}
-
-static void
-spice_file_transfer_task_set_property(GObject *object,
- guint property_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
-
- switch (property_id)
- {
- case PROP_TASK_ID:
- self->priv->id = g_value_get_uint(value);
- break;
- case PROP_TASK_FILE:
- self->priv->file = g_value_dup_object(value);
- break;
- case PROP_TASK_CHANNEL:
- self->priv->channel = g_value_dup_object(value);
- break;
- case PROP_TASK_CANCELLABLE:
- self->priv->cancellable = g_value_dup_object(value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
- }
-}
-
-static void
-spice_file_transfer_task_dispose(GObject *object)
-{
- SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
-
- g_clear_object(&self->priv->file);
-
- G_OBJECT_CLASS(spice_file_transfer_task_parent_class)->dispose(object);
-}
-
-static void
-spice_file_transfer_task_finalize(GObject *object)
-{
- SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
-
- g_free(self->priv->buffer);
-
- G_OBJECT_CLASS(spice_file_transfer_task_parent_class)->finalize(object);
-}
-
-static void
-spice_file_transfer_task_constructed(GObject *object)
-{
- SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
-
- if (spice_util_get_debug()) {
- gchar *basename = g_file_get_basename(self->priv->file);
- self->priv->start_time = g_get_monotonic_time();
- self->priv->last_update = self->priv->start_time;
-
- SPICE_DEBUG("transfer of file %s has started", basename);
- g_free(basename);
- }
-}
-
-static void
-spice_file_transfer_task_class_init(SpiceFileTransferTaskClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS(klass);
-
- g_type_class_add_private(klass, sizeof(SpiceFileTransferTaskPrivate));
-
- object_class->get_property = spice_file_transfer_task_get_property;
- object_class->set_property = spice_file_transfer_task_set_property;
- object_class->finalize = spice_file_transfer_task_finalize;
- object_class->dispose = spice_file_transfer_task_dispose;
- object_class->constructed = spice_file_transfer_task_constructed;
-
- /**
- * SpiceFileTransferTask:id:
- *
- * The ID of the file transfer task
- *
- * Since: 0.31
- **/
- g_object_class_install_property(object_class, PROP_TASK_ID,
- g_param_spec_uint("id",
- "id",
- "The id of the task",
- 0, G_MAXUINT, 0,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- /**
- * SpiceFileTransferTask:channel:
- *
- * The main channel that owns the file transfer task
- *
- * Since: 0.31
- **/
- g_object_class_install_property(object_class, PROP_TASK_CHANNEL,
- g_param_spec_object("channel",
- "channel",
- "The channel transferring the file",
- SPICE_TYPE_MAIN_CHANNEL,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- /**
- * SpiceFileTransferTask:cancellable:
- *
- * A cancellable object used to cancel the file transfer
- *
- * Since: 0.31
- **/
- g_object_class_install_property(object_class, PROP_TASK_CANCELLABLE,
- g_param_spec_object("cancellable",
- "cancellable",
- "The object used to cancel the task",
- G_TYPE_CANCELLABLE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- /**
- * SpiceFileTransferTask:file:
- *
- * The file that is being transferred in this file transfer task
- *
- * Since: 0.31
- **/
- g_object_class_install_property(object_class, PROP_TASK_FILE,
- g_param_spec_object("file",
- "File",
- "The file being transferred",
- G_TYPE_FILE,
- G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- /**
- * SpiceFileTransferTask:progress:
- *
- * The current state of the file transfer. This value indicates a
- * percentage, and ranges from 0 to 100. Listen for change notifications on
- * this property to be updated whenever the file transfer progress changes.
- *
- * Since: 0.31
- **/
- g_object_class_install_property(object_class, PROP_TASK_PROGRESS,
- g_param_spec_double("progress",
- "Progress",
- "The percentage of the file transferred",
- 0.0, 100.0, 0.0,
- G_PARAM_READABLE |
- G_PARAM_STATIC_STRINGS));
-
- /**
- * SpiceFileTransferTask::file-info
- * @task: the file transfer task that emitted the signal
- * @file_info: (transfer none): A GFileInfo object to retrieve file
- * information. Only keys from standard namespace are supported.
- *
- * The #SpiceFileTransferTask::file-info signal is emitted just before the file
- * transfer effectively starts.
- *
- * Since: 0.32
- **/
- task_signals[SIGNAL_FILE_INFO] = g_signal_new("file-info", SPICE_TYPE_FILE_TRANSFER_TASK,
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__BOXED,
- G_TYPE_NONE, 1,
- G_TYPE_FILE_INFO);
-
- /**
- * SpiceFileTransferTask::finished:
- * @task: the file transfer task that emitted the signal
- * @error: (transfer none): the error state of the transfer. Will be %NULL
- * if the file transfer was successful.
- *
- * The #SpiceFileTransferTask::finished signal is emitted when the file
- * transfer has completed transferring to the guest.
- *
- * Since: 0.31
- **/
- task_signals[SIGNAL_FINISHED] = g_signal_new("finished", SPICE_TYPE_FILE_TRANSFER_TASK,
- G_SIGNAL_RUN_FIRST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__BOXED,
- G_TYPE_NONE, 1,
- G_TYPE_ERROR);
-}
-
-static void
-spice_file_transfer_task_init(SpiceFileTransferTask *self)
-{
- self->priv = FILE_TRANSFER_TASK_PRIVATE(self);
- self->priv->buffer = g_malloc0(FILE_XFER_CHUNK_SIZE);
-}
-
-static SpiceFileTransferTask *
-spice_file_transfer_task_new(SpiceMainChannel *channel, GFile *file, GCancellable *cancellable)
-{
- static uint32_t xfer_id = 0; /* Used to identify task id */
-
- return g_object_new(SPICE_TYPE_FILE_TRANSFER_TASK,
- "id", xfer_id++,
- "file", file,
- "channel", channel,
- "cancellable", cancellable,
- NULL);
-}
-
-/**
- * spice_file_transfer_task_get_progress:
- * @self: a file transfer task
- *
- * Convenience function for retrieving the current progress of this file
- * transfer task.
- *
- * Returns: A percentage value between 0 and 100
- *
- * Since: 0.31
- **/
-double spice_file_transfer_task_get_progress(SpiceFileTransferTask *self)
-{
- if (self->priv->file_size == 0)
- return 0.0;
-
- return (double)self->priv->read_bytes / self->priv->file_size;
-}
-
-/**
- * spice_file_transfer_task_cancel:
- * @self: a file transfer task
- *
- * Cancels the file transfer task. Note that depending on how the file transfer
- * was initiated, multiple file transfer tasks may share a single
- * #SpiceFileTransferTask::cancellable object, so canceling one task may result
- * in the cancellation of other tasks.
- *
- * Since: 0.31
- **/
-void spice_file_transfer_task_cancel(SpiceFileTransferTask *self)
-{
- g_cancellable_cancel(self->priv->cancellable);
-}
-
-/**
- * spice_file_transfer_task_get_filename:
- * @self: a file transfer task
- *
- * Gets the name of the file being transferred in this task
- *
- * Returns: (transfer none): The basename of the file
- *
- * Since: 0.31
- **/
-char* spice_file_transfer_task_get_filename(SpiceFileTransferTask *self)
-{
- return g_file_get_basename(self->priv->file);
-}
diff --git a/src/spice-file-transfer-task-priv.h b/src/spice-file-transfer-task-priv.h
new file mode 100644
index 0000000..fa28d00
--- /dev/null
+++ b/src/spice-file-transfer-task-priv.h
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2016 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_FILE_TRANSFER_TASK_PRIV_H__
+#define __SPICE_FILE_TRANSFER_TASK_PRIV_H__
+
+#include "config.h"
+
+#include <spice/vd_agent.h>
+
+#include "spice-client.h"
+#include "channel-main.h"
+#include "spice-file-transfer-task.h"
+#include "spice-channel-priv.h"
+
+G_BEGIN_DECLS
+
+typedef void (*SpiceFileTransferTaskFlushCb)(SpiceFileTransferTask *xfer_task,
+ void *buffer,
+ gssize count,
+ gpointer user_data);
+
+SpiceMainChannel *spice_file_transfer_task_get_channel(SpiceFileTransferTask *self);
+GCancellable *spice_file_transfer_task_get_cancellable(SpiceFileTransferTask *self);
+void spice_file_transfer_task_flush_done(SpiceFileTransferTask *self, GError *error);
+GList *spice_file_transfer_task_create_tasks(SpiceMainChannel *channel,
+ GFile **files,
+ GFileCopyFlags flags,
+ GCancellable *cancellable,
+ SpiceFileTransferTaskFlushCb flush_callback,
+ gpointer flush_callback_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+void spice_file_transfer_task_start_task(SpiceFileTransferTask *self);
+void spice_file_transfer_task_completed(SpiceFileTransferTask *self, GError *error);
+void spice_file_transfer_task_handle_status(SpiceFileTransferTask *task,
+ VDAgentFileXferStatusMessage *msg);
+
+
+G_END_DECLS
+
+#endif /* __SPICE_FILE_TRANSFER_TASK_PRIV_H__ */
diff --git a/src/spice-file-transfer-task.c b/src/spice-file-transfer-task.c
new file mode 100644
index 0000000..8d006b5
--- /dev/null
+++ b/src/spice-file-transfer-task.c
@@ -0,0 +1,697 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2010-2016 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-file-transfer-task-priv.h"
+
+/**
+ * SECTION:file-transfer-task
+ * @short_description: Monitoring file transfers
+ * @title: File Transfer Task
+ * @section_id:
+ * @see_also: #SpiceMainChannel
+ * @stability: Stable
+ * @include: spice-client.h
+ *
+ * SpiceFileTransferTask is an object that represents a particular file
+ * transfer between the client and the guest. The properties and signals of the
+ * object can be used to monitor the status and result of the transfer. The
+ * Main Channel's #SpiceMainChannel::new-file-transfer signal will be emitted
+ * whenever a new file transfer task is initiated.
+ *
+ * Since: 0.31
+ */
+G_DEFINE_TYPE(SpiceFileTransferTask, spice_file_transfer_task, G_TYPE_OBJECT)
+
+#define FILE_TRANSFER_TASK_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((o), SPICE_TYPE_FILE_TRANSFER_TASK, SpiceFileTransferTaskPrivate))
+
+#define FILE_XFER_CHUNK_SIZE (VD_AGENT_MAX_DATA_SIZE * 32)
+
+struct _SpiceFileTransferTaskPrivate
+{
+ uint32_t id;
+ gboolean pending;
+ GFile *file;
+ SpiceMainChannel *channel;
+ GFileInputStream *file_stream;
+ GFileCopyFlags flags;
+ GCancellable *cancellable;
+ SpiceFileTransferTaskFlushCb flush_callback;
+ gpointer flush_callback_data;
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ char *buffer;
+ uint64_t read_bytes;
+ uint64_t file_size;
+ gint64 start_time;
+ gint64 last_update;
+ GError *error;
+};
+
+enum {
+ PROP_TASK_ID = 1,
+ PROP_TASK_CHANNEL,
+ PROP_TASK_CANCELLABLE,
+ PROP_TASK_FILE,
+ PROP_TASK_PROGRESS,
+};
+
+enum {
+ SIGNAL_FINISHED,
+ SIGNAL_FILE_INFO,
+ LAST_TASK_SIGNAL
+};
+
+static guint task_signals[LAST_TASK_SIGNAL];
+
+/*******************************************************************************
+ * Helpers
+ ******************************************************************************/
+static SpiceFileTransferTask *
+spice_file_transfer_task_new(SpiceMainChannel *channel, GFile *file, GCancellable *cancellable)
+{
+ static uint32_t xfer_id = 0; /* Used to identify task id */
+
+ return g_object_new(SPICE_TYPE_FILE_TRANSFER_TASK,
+ "id", xfer_id++,
+ "file", file,
+ "channel", channel,
+ "cancellable", cancellable,
+ NULL);
+}
+
+/* main context */
+static void file_xfer_close_cb(GObject *object,
+ GAsyncResult *close_res,
+ gpointer user_data)
+{
+ GTask *task;
+ SpiceFileTransferTask *self;
+ GError *error = NULL;
+
+ self = user_data;
+
+ if (object) {
+ GInputStream *stream = G_INPUT_STREAM(object);
+ g_input_stream_close_finish(stream, close_res, &error);
+ if (error) {
+ /* This error dont need to report to user, just print a log */
+ SPICE_DEBUG("close file error: %s", error->message);
+ g_clear_error(&error);
+ }
+ }
+
+ /* Notify to user that files have been transferred or something error
+ happened. */
+ task = g_task_new(self->priv->channel,
+ self->priv->cancellable,
+ self->priv->callback,
+ self->priv->user_data);
+
+ if (self->priv->error) {
+ g_task_return_error(task, self->priv->error);
+ } else {
+ g_task_return_boolean(task, TRUE);
+ if (spice_util_get_debug()) {
+ gint64 now = g_get_monotonic_time();
+ gchar *basename = g_file_get_basename(self->priv->file);
+ double seconds = (double) (now - self->priv->start_time) / G_TIME_SPAN_SECOND;
+ gchar *file_size_str = g_format_size(self->priv->file_size);
+ gchar *transfer_speed_str = g_format_size(self->priv->file_size / seconds);
+
+ g_warn_if_fail(self->priv->read_bytes == self->priv->file_size);
+ SPICE_DEBUG("transferred file %s of %s size in %.1f seconds (%s/s)",
+ basename, file_size_str, seconds, transfer_speed_str);
+
+ g_free(basename);
+ g_free(file_size_str);
+ g_free(transfer_speed_str);
+ }
+ }
+ g_object_unref(task);
+
+ g_object_unref(self);
+}
+
+/* main context */
+static void file_xfer_read_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ SpiceFileTransferTask *self = user_data;
+ gssize count;
+ GError *error = NULL;
+
+ self->priv->pending = FALSE;
+ count = g_input_stream_read_finish(G_INPUT_STREAM(self->priv->file_stream),
+ res, &error);
+ /* Check for pending earlier errors */
+ if (self->priv->error) {
+ spice_file_transfer_task_completed(self, error);
+ return;
+ }
+
+ if (count > 0 || self->priv->file_size == 0) {
+ self->priv->read_bytes += count;
+ g_object_notify(G_OBJECT(self), "progress");
+
+ if (self->priv->flush_callback) {
+ self->priv->pending = TRUE;
+ self->priv->flush_callback(self, self->priv->buffer, count, self->priv->flush_callback_data);
+ }
+ } else if (error) {
+ spice_channel_wakeup(SPICE_CHANNEL(self->priv->channel), FALSE);
+ spice_file_transfer_task_completed(self, error);
+ }
+ /* else EOF, do nothing (wait for VD_AGENT_FILE_XFER_STATUS from agent) */
+}
+
+/* coroutine context */
+static void file_xfer_continue_read(SpiceFileTransferTask *self)
+{
+ g_input_stream_read_async(G_INPUT_STREAM(self->priv->file_stream),
+ self->priv->buffer,
+ FILE_XFER_CHUNK_SIZE,
+ G_PRIORITY_DEFAULT,
+ self->priv->cancellable,
+ file_xfer_read_cb,
+ self);
+ self->priv->pending = TRUE;
+}
+
+static void file_xfer_info_async_cb(GObject *obj, GAsyncResult *res, gpointer data)
+{
+ GFileInfo *info;
+ GFile *file = G_FILE(obj);
+ GError *error = NULL;
+ SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(data);
+
+ self->priv->pending = FALSE;
+ info = g_file_query_info_finish(file, res, &error);
+ if (error || self->priv->error)
+ goto failed;
+
+ self->priv->file_size =
+ g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
+ g_signal_emit(self, task_signals[SIGNAL_FILE_INFO], 0, info);
+ g_object_notify(G_OBJECT(self), "progress");
+
+ return;
+
+failed:
+ spice_file_transfer_task_completed(self, error);
+}
+
+static void file_xfer_read_async_cb(GObject *obj, GAsyncResult *res, gpointer data)
+{
+ GFile *file = G_FILE(obj);
+ SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(data);
+ GError *error = NULL;
+
+ self->priv->pending = FALSE;
+ self->priv->file_stream = g_file_read_finish(file, res, &error);
+ if (error || self->priv->error) {
+ spice_file_transfer_task_completed(self, error);
+ return;
+ }
+
+ g_file_query_info_async(self->priv->file,
+ "standard::*",
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ self->priv->cancellable,
+ file_xfer_info_async_cb,
+ self);
+ self->priv->pending = TRUE;
+}
+
+/*******************************************************************************
+ * Internal API
+ ******************************************************************************/
+
+/* coroutine context */
+G_GNUC_INTERNAL
+void spice_file_transfer_task_handle_status(SpiceFileTransferTask *task,
+ VDAgentFileXferStatusMessage *msg)
+{
+ GError *error = NULL;
+ g_return_if_fail(task != NULL);
+
+ SPICE_DEBUG("task %d received response %d", msg->id, msg->result);
+
+ switch (msg->result) {
+ case VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA:
+ if (task->priv->pending) {
+ error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+ "transfer received CAN_SEND_DATA in pending state");
+ break;
+ }
+ file_xfer_continue_read(task);
+ return;
+ case VD_AGENT_FILE_XFER_STATUS_CANCELLED:
+ error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+ "transfer is cancelled by spice agent");
+ break;
+ case VD_AGENT_FILE_XFER_STATUS_ERROR:
+ error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+ "some errors occurred in the spice agent");
+ break;
+ case VD_AGENT_FILE_XFER_STATUS_SUCCESS:
+ if (task->priv->pending)
+ error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+ "transfer received success in pending state");
+ break;
+ default:
+ g_warn_if_reached();
+ error = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+ "unhandled status type: %u", msg->result);
+ break;
+ }
+
+ spice_file_transfer_task_completed(task, error);
+}
+
+G_GNUC_INTERNAL
+void spice_file_transfer_task_completed(SpiceFileTransferTask *self,
+ GError *error)
+{
+ /* In case of multiple errors we only report the first error */
+ if (self->priv->error)
+ g_clear_error(&error);
+ if (error) {
+ gchar *path = g_file_get_path(self->priv->file);
+ SPICE_DEBUG("File %s xfer failed: %s",
+ path, error->message);
+ g_free(path);
+ self->priv->error = error;
+ }
+
+ if (self->priv->pending)
+ return;
+
+ if (!self->priv->file_stream) {
+ file_xfer_close_cb(NULL, NULL, self);
+ goto signal;
+ }
+
+ g_input_stream_close_async(G_INPUT_STREAM(self->priv->file_stream),
+ G_PRIORITY_DEFAULT,
+ self->priv->cancellable,
+ file_xfer_close_cb,
+ self);
+ self->priv->pending = TRUE;
+signal:
+ g_signal_emit(self, task_signals[SIGNAL_FINISHED], 0, self->priv->error);
+}
+
+G_GNUC_INTERNAL
+SpiceMainChannel *spice_file_transfer_task_get_channel(SpiceFileTransferTask *self)
+{
+ g_assert_nonnull(self);
+ return self->priv->channel;
+}
+
+G_GNUC_INTERNAL
+GCancellable *spice_file_transfer_task_get_cancellable(SpiceFileTransferTask *self)
+{
+ g_assert_nonnull(self);
+ return self->priv->cancellable;
+}
+
+G_GNUC_INTERNAL
+void spice_file_transfer_task_flush_done(SpiceFileTransferTask *self, GError *error)
+{
+ g_assert_nonnull(self);
+ g_return_if_fail(self->priv->pending);
+
+ self->priv->pending = FALSE;
+
+ if (error || self->priv->error) {
+ spice_file_transfer_task_completed(self, error);
+ return;
+ }
+
+ if (spice_util_get_debug()) {
+ const GTimeSpan interval = 20 * G_TIME_SPAN_SECOND;
+ gint64 now = g_get_monotonic_time();
+
+ if (interval < now - self->priv->last_update) {
+ gchar *basename = g_file_get_basename(self->priv->file);
+ self->priv->last_update = now;
+ SPICE_DEBUG("transferred %.2f%% of the file %s",
+ 100.0 * self->priv->read_bytes / self->priv->file_size, basename);
+ g_free(basename);
+ }
+ }
+
+ /* Read more data */
+ file_xfer_continue_read(self);
+}
+
+G_GNUC_INTERNAL
+GList *spice_file_transfer_task_create_tasks(SpiceMainChannel *channel,
+ GFile **files,
+ GFileCopyFlags flags,
+ GCancellable *cancellable,
+ SpiceFileTransferTaskFlushCb flush_callback,
+ gpointer flush_callback_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SpiceFileTransferTask *task;
+ gint i;
+ GList *tasks = NULL;
+
+ for (i = 0; files[i] != NULL && !g_cancellable_is_cancelled(cancellable); i++) {
+ GCancellable *task_cancellable = cancellable;
+ /* if a cancellable object was not provided for the overall operation,
+ * create a separate object for each file so that they can be cancelled
+ * separately */
+ if (!task_cancellable)
+ task_cancellable = g_cancellable_new();
+
+ task = spice_file_transfer_task_new(channel, files[i], task_cancellable);
+ task->priv->flags = flags;
+ task->priv->flush_callback = flush_callback;
+ task->priv->flush_callback_data = flush_callback_data;
+ task->priv->callback = callback;
+ task->priv->user_data = user_data;
+
+ CHANNEL_DEBUG(channel, "Insert a xfer task:%d to task list",
+ task->priv->id);
+ tasks = g_list_prepend(tasks, task);
+
+ /* if we created a per-task cancellable above, free it */
+ if (!cancellable)
+ g_object_unref(task_cancellable);
+ }
+
+ return g_list_reverse(tasks);
+}
+
+G_GNUC_INTERNAL
+void spice_file_transfer_task_start_task(SpiceFileTransferTask *self)
+{
+ g_assert_nonnull(self);
+ self->priv->pending = TRUE;
+ g_file_read_async(self->priv->file,
+ G_PRIORITY_DEFAULT,
+ self->priv->cancellable,
+ file_xfer_read_async_cb,
+ g_object_ref(self));
+}
+
+/*******************************************************************************
+ * External API
+ ******************************************************************************/
+
+/**
+ * spice_file_transfer_task_get_progress:
+ * @self: a file transfer task
+ *
+ * Convenience function for retrieving the current progress of this file
+ * transfer task.
+ *
+ * Returns: A percentage value between 0 and 100
+ *
+ * Since: 0.31
+ **/
+double spice_file_transfer_task_get_progress(SpiceFileTransferTask *self)
+{
+ if (self->priv->file_size == 0)
+ return 0.0;
+
+ return (double)self->priv->read_bytes / self->priv->file_size;
+}
+
+/**
+ * spice_file_transfer_task_cancel:
+ * @self: a file transfer task
+ *
+ * Cancels the file transfer task. Note that depending on how the file transfer
+ * was initiated, multiple file transfer tasks may share a single
+ * #SpiceFileTransferTask::cancellable object, so canceling one task may result
+ * in the cancellation of other tasks.
+ *
+ * Since: 0.31
+ **/
+void spice_file_transfer_task_cancel(SpiceFileTransferTask *self)
+{
+ g_cancellable_cancel(self->priv->cancellable);
+}
+
+/**
+ * spice_file_transfer_task_get_filename:
+ * @self: a file transfer task
+ *
+ * Gets the name of the file being transferred in this task
+ *
+ * Returns: (transfer none): The basename of the file
+ *
+ * Since: 0.31
+ **/
+char* spice_file_transfer_task_get_filename(SpiceFileTransferTask *self)
+{
+ return g_file_get_basename(self->priv->file);
+}
+
+/*******************************************************************************
+ * GObject
+ ******************************************************************************/
+
+static void
+spice_file_transfer_task_get_property(GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
+
+ switch (property_id)
+ {
+ case PROP_TASK_ID:
+ g_value_set_uint(value, self->priv->id);
+ break;
+ case PROP_TASK_FILE:
+ g_value_set_object(value, self->priv->file);
+ break;
+ case PROP_TASK_PROGRESS:
+ g_value_set_double(value, spice_file_transfer_task_get_progress(self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+spice_file_transfer_task_set_property(GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
+
+ switch (property_id)
+ {
+ case PROP_TASK_ID:
+ self->priv->id = g_value_get_uint(value);
+ break;
+ case PROP_TASK_FILE:
+ self->priv->file = g_value_dup_object(value);
+ break;
+ case PROP_TASK_CHANNEL:
+ self->priv->channel = g_value_dup_object(value);
+ break;
+ case PROP_TASK_CANCELLABLE:
+ self->priv->cancellable = g_value_dup_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+spice_file_transfer_task_dispose(GObject *object)
+{
+ SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
+
+ g_clear_object(&self->priv->file);
+
+ G_OBJECT_CLASS(spice_file_transfer_task_parent_class)->dispose(object);
+}
+
+static void
+spice_file_transfer_task_finalize(GObject *object)
+{
+ SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
+
+ g_free(self->priv->buffer);
+
+ G_OBJECT_CLASS(spice_file_transfer_task_parent_class)->finalize(object);
+}
+
+static void
+spice_file_transfer_task_constructed(GObject *object)
+{
+ SpiceFileTransferTask *self = SPICE_FILE_TRANSFER_TASK(object);
+
+ if (spice_util_get_debug()) {
+ gchar *basename = g_file_get_basename(self->priv->file);
+ self->priv->start_time = g_get_monotonic_time();
+ self->priv->last_update = self->priv->start_time;
+
+ SPICE_DEBUG("transfer of file %s has started", basename);
+ g_free(basename);
+ }
+}
+
+static void
+spice_file_transfer_task_class_init(SpiceFileTransferTaskClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(SpiceFileTransferTaskPrivate));
+
+ object_class->get_property = spice_file_transfer_task_get_property;
+ object_class->set_property = spice_file_transfer_task_set_property;
+ object_class->finalize = spice_file_transfer_task_finalize;
+ object_class->dispose = spice_file_transfer_task_dispose;
+ object_class->constructed = spice_file_transfer_task_constructed;
+
+ /**
+ * SpiceFileTransferTask:id:
+ *
+ * The ID of the file transfer task
+ *
+ * Since: 0.31
+ **/
+ g_object_class_install_property(object_class, PROP_TASK_ID,
+ g_param_spec_uint("id",
+ "id",
+ "The id of the task",
+ 0, G_MAXUINT, 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * SpiceFileTransferTask:channel:
+ *
+ * The main channel that owns the file transfer task
+ *
+ * Since: 0.31
+ **/
+ g_object_class_install_property(object_class, PROP_TASK_CHANNEL,
+ g_param_spec_object("channel",
+ "channel",
+ "The channel transferring the file",
+ SPICE_TYPE_MAIN_CHANNEL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * SpiceFileTransferTask:cancellable:
+ *
+ * A cancellable object used to cancel the file transfer
+ *
+ * Since: 0.31
+ **/
+ g_object_class_install_property(object_class, PROP_TASK_CANCELLABLE,
+ g_param_spec_object("cancellable",
+ "cancellable",
+ "The object used to cancel the task",
+ G_TYPE_CANCELLABLE,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * SpiceFileTransferTask:file:
+ *
+ * The file that is being transferred in this file transfer task
+ *
+ * Since: 0.31
+ **/
+ g_object_class_install_property(object_class, PROP_TASK_FILE,
+ g_param_spec_object("file",
+ "File",
+ "The file being transferred",
+ G_TYPE_FILE,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * SpiceFileTransferTask:progress:
+ *
+ * The current state of the file transfer. This value indicates a
+ * percentage, and ranges from 0 to 100. Listen for change notifications on
+ * this property to be updated whenever the file transfer progress changes.
+ *
+ * Since: 0.31
+ **/
+ g_object_class_install_property(object_class, PROP_TASK_PROGRESS,
+ g_param_spec_double("progress",
+ "Progress",
+ "The percentage of the file transferred",
+ 0.0, 100.0, 0.0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * SpiceFileTransferTask::file-info
+ * @task: the file transfer task that emitted the signal
+ * @file_info: (transfer none): A GFileInfo object to retrieve file
+ * information. Only keys from standard namespace are supported.
+ *
+ * The #SpiceFileTransferTask::file-info signal is emitted just before the file
+ * transfer effectively starts.
+ *
+ * Since: 0.32
+ **/
+ task_signals[SIGNAL_FILE_INFO] = g_signal_new("file-info", SPICE_TYPE_FILE_TRANSFER_TASK,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ G_TYPE_FILE_INFO);
+
+ /**
+ * SpiceFileTransferTask::finished:
+ * @task: the file transfer task that emitted the signal
+ * @error: (transfer none): the error state of the transfer. Will be %NULL
+ * if the file transfer was successful.
+ *
+ * The #SpiceFileTransferTask::finished signal is emitted when the file
+ * transfer has completed transferring to the guest.
+ *
+ * Since: 0.31
+ **/
+ task_signals[SIGNAL_FINISHED] = g_signal_new("finished", SPICE_TYPE_FILE_TRANSFER_TASK,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ G_TYPE_ERROR);
+}
+
+static void
+spice_file_transfer_task_init(SpiceFileTransferTask *self)
+{
+ self->priv = FILE_TRANSFER_TASK_PRIVATE(self);
+ self->priv->buffer = g_malloc0(FILE_XFER_CHUNK_SIZE);
+}
--
2.5.5
More information about the Spice-devel
mailing list