From 6e416fc25a01b309a4549c4c5d899e5c3d6e0e34 Mon Sep 17 00:00:00 2001 From: Julian Sparber Date: Wed, 27 Nov 2024 15:01:23 +0100 Subject: [PATCH] GPortalNotificationBackend: Serialize sound property The notification portal v2 supports setting a sound for a notification, use the property. --- gio/gportalnotificationbackend.c | 200 +++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/gio/gportalnotificationbackend.c b/gio/gportalnotificationbackend.c index ff60513a5..5dbcb10cd 100644 --- a/gio/gportalnotificationbackend.c +++ b/gio/gportalnotificationbackend.c @@ -517,6 +517,195 @@ serialize_icon_cb (GObject *source_object, g_object_unref (task); } +static void +file_read_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + GOutputStream *stream_out = G_OUTPUT_STREAM (g_task_get_task_data (task)); + GError *error = NULL; + GFileInputStream *stream_in = NULL; + + stream_in = g_file_read_finish (G_FILE (source_object), res, &error); + if (!stream_in) + { + g_task_return_error (task, g_steal_pointer (&error)); + g_object_unref (task); + return; + } + + g_output_stream_splice_async (stream_out, + G_INPUT_STREAM (stream_in), + G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE, + g_task_get_priority (task), + g_task_get_cancellable (task), + splice_cb, + g_object_ref (task)); + + g_object_unref (stream_in); + g_object_unref (task); +} + + +static void +serialize_sound (GNotificationSound *sound, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task = NULL; + GBytes *bytes = NULL; + GFile *file = NULL; + + task = g_task_new (NULL, NULL, callback, user_data); + g_task_set_source_tag (task, serialize_sound); + + if (sound == NULL) + { + g_task_set_task_data (task, + g_variant_take_ref (g_variant_new_string ("silent")), + (GDestroyNotify) g_variant_unref); + g_task_return_boolean (task, TRUE); + } + else if (g_notification_sound_is_default (sound)) + { + g_task_set_task_data (task, + g_variant_take_ref (g_variant_new_string ("default")), + (GDestroyNotify) g_variant_unref); + g_task_return_boolean (task, TRUE); + } + else if ((bytes = g_notification_sound_get_bytes (sound))) + { + GOutputStream *stream_out = NULL; + GError *error = NULL; + int fd = -1; + + fd = bytes_to_memfd ("notification-media", + bytes, + &error); + if (fd == -1) + { + g_task_return_error (task, g_steal_pointer (&error)); + g_object_unref (task); + return; + } + + stream_out = g_unix_output_stream_new (g_steal_fd (&fd), TRUE); + g_task_set_task_data (task, g_steal_pointer (&stream_out), g_object_unref); + g_task_return_boolean (task, TRUE); + } + else if ((file = g_notification_sound_get_file (sound))) + { + GOutputStream *stream_out = NULL; + int fd = -1; + + fd = memfd_create ("notification-sound", MFD_ALLOW_SEALING); + if (fd == -1) + { + int saved_errno; + + saved_errno = errno; + g_task_return_new_error (task, + G_IO_ERROR, + g_io_error_from_errno (saved_errno), + "memfd_create: %s", g_strerror (saved_errno)); + + g_object_unref (task); + return; + } + + stream_out = g_unix_output_stream_new (g_steal_fd (&fd), TRUE); + g_task_set_task_data (task, g_steal_pointer (&stream_out), g_object_unref); + + g_file_read_async (file, + g_task_get_priority (task), + NULL, + file_read_cb, + g_object_ref (task)); + } + else + { + g_assert_not_reached (); + } + + g_object_unref (task); +} + +static GVariant* +serialize_sound_finish (GAsyncResult *result, + GUnixFDList *fd_list, + GError **error) +{ + GTask *task = G_TASK (result); + gpointer data; + + g_return_val_if_fail (g_task_get_source_tag (task) == serialize_sound, NULL); + + if (!g_task_propagate_boolean (G_TASK (result), error)) + return NULL; + + data = g_task_get_task_data (task); + g_assert (data != NULL); + + if (G_IS_UNIX_OUTPUT_STREAM (data)) + { + int fd; + int fd_in; + + fd = g_unix_output_stream_get_fd (G_UNIX_OUTPUT_STREAM (data)); + if (lseek (fd, 0, SEEK_SET) == -1) + { + int saved_errno = errno; + + g_task_return_new_error (task, + G_IO_ERROR, + g_io_error_from_errno (saved_errno), + "lseek: %s", g_strerror (saved_errno)); + return NULL; + } + + fd_in = g_unix_fd_list_append (fd_list, fd, error); + if (fd_in == -1) + return NULL; + + return g_variant_ref_sink (g_variant_new ("(sv)", "file-descriptor", g_variant_new_handle (fd_in))); + } + else + { + g_assert (data != NULL); + return g_variant_ref (data); + } +} + +static void +serialize_sound_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GTask *task = G_TASK (user_data); + ParserData *data = g_task_get_task_data (task); + GError *error = NULL; + GVariant *sound_out = NULL; + + sound_out = serialize_sound_finish (res, data->fd_list, &error); + + if (!sound_out) + { + g_prefix_error_literal (&error, "Failed to serialize sound: "); + g_task_return_error (task, g_steal_pointer (&error)); + g_object_unref (task); + return; + } + + g_variant_builder_add (data->builder, "{sv}", "sound", sound_out); + + if (parser_data_release (data)) + g_task_return_boolean (task, TRUE); + + g_clear_pointer (&sound_out, g_variant_unref); + g_object_unref (task); +} + static GVariant * serialize_buttons (GNotification *notification) { @@ -586,6 +775,7 @@ serialize_notification (const char *id, gchar *default_action = NULL; GVariant *default_action_target = NULL; GVariant *buttons = NULL; + GNotificationSound *sound = NULL; task = g_task_new (NULL, NULL, callback, user_data); g_task_set_source_tag (task, serialize_notification); @@ -617,6 +807,16 @@ serialize_notification (const char *id, } } + sound = g_notification_get_sound (notification); + /* For the portal a custom sound is considered a button */ + if (version > 1 && !(sound && g_notification_sound_get_custom (sound, NULL, NULL))) + { + parser_data_hold (data); + serialize_sound (sound, + serialize_sound_cb, + g_object_ref (task)); + } + g_variant_builder_add (data->builder, "{sv}", "priority", serialize_priority (notification)); if (g_notification_get_default_action (notification, &default_action, &default_action_target))