mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-20 15:48:54 +02:00
Merge branch 'copy-move-async-with-closures' into 'main'
Add g_file_copy_async_with_closures() and g_file_move_async_with_closures() See merge request GNOME/glib!3939
This commit is contained in:
153
gio/gfile.c
153
gio/gfile.c
@@ -3818,6 +3818,123 @@ g_file_copy_async (GFile *source,
|
|||||||
user_data);
|
user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _CopyAsyncClosuresData
|
||||||
|
{
|
||||||
|
GClosure *progress_callback_closure;
|
||||||
|
GClosure *ready_callback_closure;
|
||||||
|
} CopyAsyncClosuresData;
|
||||||
|
|
||||||
|
static CopyAsyncClosuresData *
|
||||||
|
copy_async_closures_data_new (GClosure *progress_callback_closure,
|
||||||
|
GClosure *ready_callback_closure)
|
||||||
|
{
|
||||||
|
CopyAsyncClosuresData *data;
|
||||||
|
|
||||||
|
data = g_new0 (CopyAsyncClosuresData, 1);
|
||||||
|
|
||||||
|
if (progress_callback_closure != NULL)
|
||||||
|
{
|
||||||
|
data->progress_callback_closure = g_closure_ref (progress_callback_closure);
|
||||||
|
g_closure_sink (progress_callback_closure);
|
||||||
|
if (G_CLOSURE_NEEDS_MARSHAL (progress_callback_closure))
|
||||||
|
g_closure_set_marshal (progress_callback_closure, g_cclosure_marshal_generic);
|
||||||
|
}
|
||||||
|
|
||||||
|
data->ready_callback_closure = g_closure_ref (ready_callback_closure);
|
||||||
|
g_closure_sink (ready_callback_closure);
|
||||||
|
if (G_CLOSURE_NEEDS_MARSHAL (ready_callback_closure))
|
||||||
|
g_closure_set_marshal (ready_callback_closure, g_cclosure_marshal_generic);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
copy_async_closures_data_free (CopyAsyncClosuresData *data)
|
||||||
|
{
|
||||||
|
if (data->progress_callback_closure != NULL)
|
||||||
|
g_closure_unref (data->progress_callback_closure);
|
||||||
|
|
||||||
|
g_closure_unref (data->ready_callback_closure);
|
||||||
|
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
copy_async_invoke_progress (goffset current_num_bytes,
|
||||||
|
goffset total_num_bytes,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
CopyAsyncClosuresData *data = (CopyAsyncClosuresData *) user_data;
|
||||||
|
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
|
||||||
|
|
||||||
|
/* goffset is 64-bits even on 32-bits platforms */
|
||||||
|
g_value_init (¶ms[0], G_TYPE_INT64);
|
||||||
|
g_value_set_int64 (¶ms[0], current_num_bytes);
|
||||||
|
g_value_init (¶ms[1], G_TYPE_INT64);
|
||||||
|
g_value_set_int64 (¶ms[1], total_num_bytes);
|
||||||
|
|
||||||
|
g_closure_invoke (data->progress_callback_closure, /* result = */ NULL, 2, params, /* hint = */ NULL);
|
||||||
|
|
||||||
|
g_value_unset (¶ms[0]);
|
||||||
|
g_value_unset (¶ms[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
copy_async_invoke_ready (GObject *file,
|
||||||
|
GAsyncResult *result,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
CopyAsyncClosuresData *data = (CopyAsyncClosuresData *) user_data;
|
||||||
|
GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
|
||||||
|
|
||||||
|
g_value_init (¶ms[0], G_TYPE_FILE);
|
||||||
|
g_value_set_object (¶ms[0], file);
|
||||||
|
g_value_init (¶ms[1], G_TYPE_ASYNC_RESULT);
|
||||||
|
g_value_set_object (¶ms[1], result);
|
||||||
|
|
||||||
|
g_closure_invoke (data->ready_callback_closure, /* result = */ NULL, 2, params, /* hint = */ NULL);
|
||||||
|
|
||||||
|
copy_async_closures_data_free (data);
|
||||||
|
g_value_unset (¶ms[0]);
|
||||||
|
g_value_unset (¶ms[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_file_copy_async_with_closures: (rename-to g_file_copy_async) (finish-func g_file_copy_finish):
|
||||||
|
* @source: input [type@Gio.File]
|
||||||
|
* @destination: destination [type@Gio.File]
|
||||||
|
* @flags: set of [flags@Gio.FileCopyFlags]
|
||||||
|
* @io_priority: the [I/O priority](iface.AsyncResult.html#io-priority) of the request
|
||||||
|
* @cancellable: (nullable): optional [class@Gio.Cancellable] object,
|
||||||
|
* `NULL` to ignore
|
||||||
|
* @progress_callback_closure: (nullable): [type@GObject.Closure] to invoke with progress
|
||||||
|
* information, or `NULL` if progress information is not needed
|
||||||
|
* @ready_callback_closure: (not nullable): [type@GObject.Closure] to invoke when the request is satisfied
|
||||||
|
*
|
||||||
|
* Version of [method@Gio.File.copy_async] using closures instead of callbacks for
|
||||||
|
* easier binding in other languages.
|
||||||
|
*
|
||||||
|
* Since: 2.82
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
g_file_copy_async_with_closures (GFile *source,
|
||||||
|
GFile *destination,
|
||||||
|
GFileCopyFlags flags,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GClosure *progress_callback_closure,
|
||||||
|
GClosure *ready_callback_closure)
|
||||||
|
{
|
||||||
|
CopyAsyncClosuresData *data;
|
||||||
|
|
||||||
|
/* freed in copy_async_invoke_ready */
|
||||||
|
data = copy_async_closures_data_new (progress_callback_closure, ready_callback_closure);
|
||||||
|
|
||||||
|
g_file_copy_async (source, destination, flags, io_priority, cancellable,
|
||||||
|
progress_callback_closure == NULL ? NULL : copy_async_invoke_progress, data,
|
||||||
|
copy_async_invoke_ready, data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_file_copy_finish:
|
* g_file_copy_finish:
|
||||||
* @file: input #GFile
|
* @file: input #GFile
|
||||||
@@ -4035,6 +4152,42 @@ g_file_move_async (GFile *source,
|
|||||||
user_data);
|
user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* g_file_move_async_with_closures: (rename-to g_file_move_async) (finish-func g_file_move_finish):
|
||||||
|
* @source: input [type@Gio.File]
|
||||||
|
* @destination: destination [type@Gio.File]
|
||||||
|
* @flags: set of [flags@Gio.FileCopyFlags]
|
||||||
|
* @io_priority: the [I/O priority](iface.AsyncResult.html#io-priority) of the request
|
||||||
|
* @cancellable: (nullable): optional [class@Gio.Cancellable] object,
|
||||||
|
* `NULL` to ignore
|
||||||
|
* @progress_callback_closure: (nullable): [type@GObject.Closure] to invoke with progress
|
||||||
|
* information, or `NULL` if progress information is not needed
|
||||||
|
* @ready_callback_closure: (not nullable): [type@GObject.Closure] to invoke when the request is satisfied
|
||||||
|
*
|
||||||
|
* Version of [method@Gio.File.move_async] using closures instead of callbacks for
|
||||||
|
* easier binding in other languages.
|
||||||
|
*
|
||||||
|
* Since: 2.82
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
g_file_move_async_with_closures (GFile *source,
|
||||||
|
GFile *destination,
|
||||||
|
GFileCopyFlags flags,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GClosure *progress_callback_closure,
|
||||||
|
GClosure *ready_callback_closure)
|
||||||
|
{
|
||||||
|
CopyAsyncClosuresData *data;
|
||||||
|
|
||||||
|
/* freed in copy_async_invoke_ready */
|
||||||
|
data = copy_async_closures_data_new (progress_callback_closure, ready_callback_closure);
|
||||||
|
|
||||||
|
g_file_move_async (source, destination, flags, io_priority, cancellable,
|
||||||
|
progress_callback_closure == NULL ? NULL : copy_async_invoke_progress, data,
|
||||||
|
copy_async_invoke_ready, data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* g_file_move_finish:
|
* g_file_move_finish:
|
||||||
* @file: input source #GFile
|
* @file: input source #GFile
|
||||||
|
16
gio/gfile.h
16
gio/gfile.h
@@ -944,6 +944,14 @@ void g_file_copy_async (GFile
|
|||||||
gpointer progress_callback_data,
|
gpointer progress_callback_data,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
GIO_AVAILABLE_IN_2_82
|
||||||
|
void g_file_copy_async_with_closures (GFile *source,
|
||||||
|
GFile *destination,
|
||||||
|
GFileCopyFlags flags,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GClosure *progress_callback_closure,
|
||||||
|
GClosure *ready_callback_closure);
|
||||||
GIO_AVAILABLE_IN_ALL
|
GIO_AVAILABLE_IN_ALL
|
||||||
gboolean g_file_copy_finish (GFile *file,
|
gboolean g_file_copy_finish (GFile *file,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
@@ -966,6 +974,14 @@ void g_file_move_async (GFile
|
|||||||
gpointer progress_callback_data,
|
gpointer progress_callback_data,
|
||||||
GAsyncReadyCallback callback,
|
GAsyncReadyCallback callback,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
|
GIO_AVAILABLE_IN_2_82
|
||||||
|
void g_file_move_async_with_closures (GFile *source,
|
||||||
|
GFile *destination,
|
||||||
|
GFileCopyFlags flags,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GClosure *progress_callback_closure,
|
||||||
|
GClosure *ready_callback_closure);
|
||||||
GIO_AVAILABLE_IN_2_72
|
GIO_AVAILABLE_IN_2_72
|
||||||
gboolean g_file_move_finish (GFile *file,
|
gboolean g_file_move_finish (GFile *file,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
|
189
gio/tests/file.c
189
gio/tests/file.c
@@ -2606,6 +2606,119 @@ test_copy_progress (void)
|
|||||||
g_clear_object (&dest_tmpfile);
|
g_clear_object (&dest_tmpfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GError *error;
|
||||||
|
gboolean done;
|
||||||
|
gboolean res;
|
||||||
|
} CopyAsyncData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_copy_async_cb (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
GFile *file = G_FILE (object);
|
||||||
|
CopyAsyncData *data = user_data;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
data->res = g_file_move_finish (file, result, &error);
|
||||||
|
data->error = g_steal_pointer (&error);
|
||||||
|
data->done = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
goffset total_num_bytes;
|
||||||
|
} CopyAsyncProgressData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_copy_async_progress_cb (goffset current_num_bytes,
|
||||||
|
goffset total_num_bytes,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
CopyAsyncProgressData *data = user_data;
|
||||||
|
data->total_num_bytes = total_num_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exercise copy_async_with_closures() */
|
||||||
|
static void
|
||||||
|
test_copy_async_with_closures (void)
|
||||||
|
{
|
||||||
|
CopyAsyncData data = { 0 };
|
||||||
|
CopyAsyncProgressData progress_data = { 0 };
|
||||||
|
GFile *source;
|
||||||
|
GFileIOStream *iostream;
|
||||||
|
GOutputStream *ostream;
|
||||||
|
GFile *destination;
|
||||||
|
gchar *destination_path;
|
||||||
|
GError *error = NULL;
|
||||||
|
gboolean res;
|
||||||
|
const guint8 buffer[] = { 1, 2, 3, 4, 5 };
|
||||||
|
GClosure *progress_closure;
|
||||||
|
GClosure *ready_closure;
|
||||||
|
|
||||||
|
source = g_file_new_tmp ("g_file_copy_async_with_closures_XXXXXX", &iostream, NULL);
|
||||||
|
|
||||||
|
destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_copy_async_with_closures_target", NULL);
|
||||||
|
destination = g_file_new_for_path (destination_path);
|
||||||
|
|
||||||
|
g_assert_nonnull (source);
|
||||||
|
g_assert_nonnull (iostream);
|
||||||
|
|
||||||
|
res = g_file_query_exists (source, NULL);
|
||||||
|
g_assert_true (res);
|
||||||
|
res = g_file_query_exists (destination, NULL);
|
||||||
|
g_assert_false (res);
|
||||||
|
|
||||||
|
/* Write a known number of bytes to the file, so we can test the progress
|
||||||
|
* callback against it */
|
||||||
|
ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
|
||||||
|
g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
progress_closure = g_cclosure_new (G_CALLBACK (test_copy_async_progress_cb), &progress_data, NULL);
|
||||||
|
ready_closure = g_cclosure_new (G_CALLBACK (test_copy_async_cb), &data, NULL);
|
||||||
|
|
||||||
|
g_file_copy_async_with_closures (source,
|
||||||
|
destination,
|
||||||
|
G_FILE_COPY_NONE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
progress_closure,
|
||||||
|
ready_closure);
|
||||||
|
|
||||||
|
while (!data.done)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
g_assert_no_error (data.error);
|
||||||
|
g_assert_true (data.res);
|
||||||
|
g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
|
||||||
|
|
||||||
|
res = g_file_query_exists (source, NULL);
|
||||||
|
g_assert_true (res);
|
||||||
|
res = g_file_query_exists (destination, NULL);
|
||||||
|
g_assert_true (res);
|
||||||
|
|
||||||
|
res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (res);
|
||||||
|
g_object_unref (iostream);
|
||||||
|
|
||||||
|
res = g_file_delete (source, NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (res);
|
||||||
|
|
||||||
|
res = g_file_delete (destination, NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (res);
|
||||||
|
|
||||||
|
g_object_unref (source);
|
||||||
|
g_object_unref (destination);
|
||||||
|
|
||||||
|
g_free (destination_path);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_measure (void)
|
test_measure (void)
|
||||||
{
|
{
|
||||||
@@ -3577,6 +3690,80 @@ test_move_async (void)
|
|||||||
g_free (destination_path);
|
g_free (destination_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Same test as for move_async(), but for move_async_with_closures() */
|
||||||
|
static void
|
||||||
|
test_move_async_with_closures (void)
|
||||||
|
{
|
||||||
|
MoveAsyncData data = { 0 };
|
||||||
|
MoveAsyncProgressData progress_data = { 0 };
|
||||||
|
GFile *source;
|
||||||
|
GFileIOStream *iostream;
|
||||||
|
GOutputStream *ostream;
|
||||||
|
GFile *destination;
|
||||||
|
gchar *destination_path;
|
||||||
|
GError *error = NULL;
|
||||||
|
gboolean res;
|
||||||
|
const guint8 buffer[] = { 1, 2, 3, 4, 5 };
|
||||||
|
GClosure *progress_closure;
|
||||||
|
GClosure *ready_closure;
|
||||||
|
|
||||||
|
source = g_file_new_tmp ("g_file_move_async_with_closures_XXXXXX", &iostream, NULL);
|
||||||
|
|
||||||
|
destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_move_async_with_closures_target", NULL);
|
||||||
|
destination = g_file_new_for_path (destination_path);
|
||||||
|
|
||||||
|
g_assert_nonnull (source);
|
||||||
|
g_assert_nonnull (iostream);
|
||||||
|
|
||||||
|
res = g_file_query_exists (source, NULL);
|
||||||
|
g_assert_true (res);
|
||||||
|
res = g_file_query_exists (destination, NULL);
|
||||||
|
g_assert_false (res);
|
||||||
|
|
||||||
|
/* Write a known number of bytes to the file, so we can test the progress
|
||||||
|
* callback against it */
|
||||||
|
ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
|
||||||
|
g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
progress_closure = g_cclosure_new (G_CALLBACK (test_move_async_progress_cb), &progress_data, NULL);
|
||||||
|
ready_closure = g_cclosure_new (G_CALLBACK (test_move_async_cb), &data, NULL);
|
||||||
|
|
||||||
|
g_file_move_async_with_closures (source,
|
||||||
|
destination,
|
||||||
|
G_FILE_COPY_NONE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
progress_closure,
|
||||||
|
ready_closure);
|
||||||
|
|
||||||
|
while (!data.done)
|
||||||
|
g_main_context_iteration (NULL, TRUE);
|
||||||
|
|
||||||
|
g_assert_no_error (data.error);
|
||||||
|
g_assert_true (data.res);
|
||||||
|
g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
|
||||||
|
|
||||||
|
res = g_file_query_exists (source, NULL);
|
||||||
|
g_assert_false (res);
|
||||||
|
res = g_file_query_exists (destination, NULL);
|
||||||
|
g_assert_true (res);
|
||||||
|
|
||||||
|
res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (res);
|
||||||
|
g_object_unref (iostream);
|
||||||
|
|
||||||
|
res = g_file_delete (destination, NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_true (res);
|
||||||
|
|
||||||
|
g_object_unref (source);
|
||||||
|
g_object_unref (destination);
|
||||||
|
|
||||||
|
g_free (destination_path);
|
||||||
|
}
|
||||||
|
|
||||||
static GAppInfo *
|
static GAppInfo *
|
||||||
create_command_line_app_info (const char *name,
|
create_command_line_app_info (const char *name,
|
||||||
const char *command_line,
|
const char *command_line,
|
||||||
@@ -4028,6 +4215,7 @@ main (int argc, char *argv[])
|
|||||||
g_test_add_func ("/file/async-make-symlink", test_async_make_symlink);
|
g_test_add_func ("/file/async-make-symlink", test_async_make_symlink);
|
||||||
g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
|
g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
|
||||||
g_test_add_func ("/file/copy/progress", test_copy_progress);
|
g_test_add_func ("/file/copy/progress", test_copy_progress);
|
||||||
|
g_test_add_func ("/file/copy-async-with-closurse", test_copy_async_with_closures);
|
||||||
g_test_add_func ("/file/measure", test_measure);
|
g_test_add_func ("/file/measure", test_measure);
|
||||||
g_test_add_func ("/file/measure-async", test_measure_async);
|
g_test_add_func ("/file/measure-async", test_measure_async);
|
||||||
g_test_add_func ("/file/load-bytes", test_load_bytes);
|
g_test_add_func ("/file/load-bytes", test_load_bytes);
|
||||||
@@ -4045,6 +4233,7 @@ main (int argc, char *argv[])
|
|||||||
g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation);
|
g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation);
|
||||||
g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy);
|
g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy);
|
||||||
g_test_add_func ("/file/move_async", test_move_async);
|
g_test_add_func ("/file/move_async", test_move_async);
|
||||||
|
g_test_add_func ("/file/move-async-with-closures", test_move_async_with_closures);
|
||||||
g_test_add_func ("/file/query-zero-length-content-type", test_query_zero_length_content_type);
|
g_test_add_func ("/file/query-zero-length-content-type", test_query_zero_length_content_type);
|
||||||
g_test_add_func ("/file/query-default-handler-file", test_query_default_handler_file);
|
g_test_add_func ("/file/query-default-handler-file", test_query_default_handler_file);
|
||||||
g_test_add_func ("/file/query-default-handler-file-async", test_query_default_handler_file_async);
|
g_test_add_func ("/file/query-default-handler-file-async", test_query_default_handler_file_async);
|
||||||
|
Reference in New Issue
Block a user