From cc84a45b567e0deadcf87d244cfa9d9110dff721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 30 Nov 2021 11:56:31 +0100 Subject: [PATCH] color-profile: Create colord profiles This works similiarly to how MetaColorDevice works, by creating them asynchronously then signalling the 'ready' signal when done. Also similarly to MetaColorDevice, the on-demand sync cleanup on finalize is added, to avoid race conditions when hotplugs happens very rapidly, e.g. in tests. Part-of: --- src/backends/meta-color-device.c | 28 +++- src/backends/meta-color-profile.c | 207 ++++++++++++++++++++++++++++++ src/backends/meta-color-profile.h | 2 + 3 files changed, 235 insertions(+), 2 deletions(-) diff --git a/src/backends/meta-color-device.c b/src/backends/meta-color-device.c index 57f606414..e86d96ef8 100644 --- a/src/backends/meta-color-device.c +++ b/src/backends/meta-color-device.c @@ -45,7 +45,8 @@ static guint signals[N_SIGNALS]; typedef enum { PENDING_EDID_PROFILE = 1 << 0, - PENDING_CONNECTED = 1 << 1, + PENDING_PROFILE_READY = 1 << 1, + PENDING_CONNECTED = 1 << 2, } PendingState; struct _MetaColorDevice @@ -59,6 +60,7 @@ struct _MetaColorDevice CdDevice *cd_device; MetaColorProfile *device_profile; + gulong device_profile_ready_handler_id; GCancellable *cancellable; @@ -180,6 +182,8 @@ meta_color_device_dispose (GObject *object) g_cancellable_cancel (color_device->cancellable); g_clear_object (&color_device->cancellable); + g_clear_signal_handler (&color_device->device_profile_ready_handler_id, + color_device->device_profile); g_clear_object (&color_device->device_profile); @@ -283,6 +287,14 @@ on_cd_device_connected (GObject *source_object, maybe_finish_setup (color_device); } +static void +on_profile_ready (MetaColorProfile *color_profile, + MetaColorDevice *color_device) +{ + color_device->pending_state &= ~PENDING_PROFILE_READY; + maybe_finish_setup (color_device); +} + static void ensure_device_profile_cb (GObject *source_object, GAsyncResult *res, @@ -314,7 +326,18 @@ ensure_device_profile_cb (GObject *source_object, color_device->pending_state &= ~PENDING_EDID_PROFILE; g_set_object (&color_device->device_profile, color_profile); - maybe_finish_setup (color_device); + if (!meta_color_profile_is_ready (color_profile)) + { + color_device->device_profile_ready_handler_id = + g_signal_connect (color_profile, "ready", + G_CALLBACK (on_profile_ready), + color_device); + color_device->pending_state |= PENDING_PROFILE_READY; + } + else + { + maybe_finish_setup (color_device); + } } static void @@ -863,6 +886,7 @@ on_efi_panel_color_info_loaded (GObject *source_object, /* Set metadata needed by colord */ cd_icc_add_metadata (cd_icc, CD_PROFILE_PROPERTY_FILENAME, file_path); + file_md5_checksum = g_compute_checksum_for_bytes (G_CHECKSUM_MD5, bytes); cd_icc_add_metadata (cd_icc, CD_PROFILE_METADATA_FILE_CHECKSUM, diff --git a/src/backends/meta-color-profile.c b/src/backends/meta-color-profile.c index 48b39fa09..cfd038d84 100644 --- a/src/backends/meta-color-profile.c +++ b/src/backends/meta-color-profile.c @@ -25,6 +25,17 @@ #include #include +#include "backends/meta-color-manager-private.h" + +enum +{ + READY, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + struct _MetaColorProfile { GObject parent; @@ -33,18 +44,100 @@ struct _MetaColorProfile CdIcc *cd_icc; GBytes *bytes; + + char *cd_profile_id; + CdProfile *cd_profile; + GCancellable *cancellable; + + gboolean is_ready; }; G_DEFINE_TYPE (MetaColorProfile, meta_color_profile, G_TYPE_OBJECT) +typedef struct +{ + GMainLoop *loop; + CdProfile *cd_profile; + GError *error; +} FindProfileData; + +static void +on_find_profile (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CdClient *cd_client = CD_CLIENT (source_object); + FindProfileData *data = user_data; + + data->cd_profile = cd_client_find_profile_finish (cd_client, res, + &data->error); + g_main_loop_quit (data->loop); +} + +static CdProfile * +find_profile_sync (CdClient *cd_client, + const char *cd_profile_id, + GError **error) +{ + g_autoptr (GMainContext) main_context = NULL; + g_autoptr (GMainLoop) main_loop = NULL; + FindProfileData data = {}; + + main_context = g_main_context_new (); + main_loop = g_main_loop_new (main_context, FALSE); + g_main_context_push_thread_default (main_context); + + data = (FindProfileData) { + .loop = main_loop, + }; + cd_client_find_profile (cd_client, cd_profile_id, NULL, + on_find_profile, + &data); + g_main_loop_run (main_loop); + + g_main_context_pop_thread_default (main_context); + + if (data.error) + g_propagate_error (error, data.error); + return data.cd_profile; +} + static void meta_color_profile_finalize (GObject *object) { MetaColorProfile *color_profile = META_COLOR_PROFILE (object); + MetaColorManager *color_manager = color_profile->color_manager; + CdClient *cd_client = meta_color_manager_get_cd_client (color_manager); + CdProfile *cd_profile; + g_cancellable_cancel (color_profile->cancellable); + g_clear_object (&color_profile->cancellable); + + cd_profile = color_profile->cd_profile; + if (!cd_profile) + { + g_autoptr (GError) error = NULL; + + cd_profile = find_profile_sync (cd_client, + color_profile->cd_profile_id, + &error); + if (!cd_profile && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_warning ("Failed to find colord profile %s: %s", + color_profile->cd_profile_id, + error->message); + } + } + + if (cd_profile) + cd_client_delete_profile (cd_client, cd_profile, NULL, NULL, NULL); + + g_clear_pointer (&color_profile->cd_profile_id, g_free); g_clear_object (&color_profile->cd_icc); g_clear_pointer (&color_profile->bytes, g_bytes_unref); + g_clear_object (&color_profile->cd_profile); G_OBJECT_CLASS (meta_color_profile_parent_class)->finalize (object); } @@ -55,6 +148,13 @@ meta_color_profile_class_init (MetaColorProfileClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_color_profile_finalize; + + signals[READY] = + g_signal_new ("ready", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } static void @@ -62,17 +162,118 @@ meta_color_profile_init (MetaColorProfile *color_profile) { } +static void +on_cd_profile_connected (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CdProfile *cd_profile = CD_PROFILE (source_object); + MetaColorProfile *color_profile = user_data; + g_autoptr (GError) error = NULL; + + if (!cd_profile_connect_finish (cd_profile, res, &error)) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_warning ("Failed to connect to colord profile %s: %s", + color_profile->cd_profile_id, + error->message); + } + else + { + g_warn_if_fail (g_strcmp0 (cd_profile_get_id (cd_profile), + color_profile->cd_profile_id) == 0); + + meta_topic (META_DEBUG_COLOR, "Color profile '%s' connected", + color_profile->cd_profile_id); + } + + color_profile->is_ready = TRUE; + g_signal_emit (color_profile, signals[READY], 0); +} + +static void +on_cd_profile_created (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CdClient *cd_client = CD_CLIENT (source_object); + MetaColorProfile *color_profile = META_COLOR_PROFILE (user_data); + g_autoptr (GError) error = NULL; + CdProfile *cd_profile; + + cd_profile = cd_client_create_profile_finish (cd_client, res, &error); + if (!cd_profile) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_warning ("Failed to create colord color profile: %s", error->message); + + color_profile->is_ready = TRUE; + g_signal_emit (color_profile, signals[READY], 0); + return; + } + + meta_topic (META_DEBUG_COLOR, "Created colord color profile '%s'", + color_profile->cd_profile_id); + + color_profile->cd_profile = cd_profile; + + cd_profile_connect (cd_profile, color_profile->cancellable, + on_cd_profile_connected, color_profile); +} + +static void +create_cd_profile (MetaColorProfile *color_profile, + const char *checksum) +{ + MetaColorManager *color_manager = color_profile->color_manager; + CdClient *cd_client = meta_color_manager_get_cd_client (color_manager); + const char *filename; + g_autoptr (GHashTable) profile_props = NULL; + + filename = cd_icc_get_metadata_item (color_profile->cd_icc, + CD_PROFILE_PROPERTY_FILENAME); + + profile_props = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, NULL); + g_hash_table_insert (profile_props, + (gpointer) CD_PROFILE_PROPERTY_FILENAME, + (gpointer) filename); + g_hash_table_insert (profile_props, + (gpointer) CD_PROFILE_METADATA_FILE_CHECKSUM, + (gpointer) checksum); + cd_client_create_profile (cd_client, + color_profile->cd_profile_id, + CD_OBJECT_SCOPE_TEMP, + profile_props, + color_profile->cancellable, + on_cd_profile_created, + color_profile); +} + MetaColorProfile * meta_color_profile_new_from_icc (MetaColorManager *color_manager, CdIcc *cd_icc, GBytes *raw_bytes) { MetaColorProfile *color_profile; + const char *checksum; + + checksum = cd_icc_get_metadata_item (cd_icc, + CD_PROFILE_METADATA_FILE_CHECKSUM); color_profile = g_object_new (META_TYPE_COLOR_PROFILE, NULL); color_profile->color_manager = color_manager; color_profile->cd_icc = cd_icc; color_profile->bytes = raw_bytes; + color_profile->cancellable = g_cancellable_new (); + + color_profile->cd_profile_id = g_strdup_printf ("icc-%s", checksum); + + create_cd_profile (color_profile, checksum); return color_profile; } @@ -101,3 +302,9 @@ meta_color_profile_get_cd_icc (MetaColorProfile *color_profile) { return color_profile->cd_icc; } + +gboolean +meta_color_profile_is_ready (MetaColorProfile *color_profile) +{ + return color_profile->is_ready; +} diff --git a/src/backends/meta-color-profile.h b/src/backends/meta-color-profile.h index 7dae53083..6a80c6452 100644 --- a/src/backends/meta-color-profile.h +++ b/src/backends/meta-color-profile.h @@ -44,4 +44,6 @@ size_t meta_color_profile_get_data_size (MetaColorProfile *color_profile); META_EXPORT_TEST CdIcc * meta_color_profile_get_cd_icc (MetaColorProfile *color_profile); +gboolean meta_color_profile_is_ready (MetaColorProfile *color_profile); + #endif /* META_COLOR_PROFILE_H */