[mutter] color: Create color profile for assigned profile



commit 0a0fb2fda951a301e2ee3c688dd9934cf761dd87
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Wed Dec 1 18:57:52 2021 +0100

    color: Create color profile for assigned profile
    
    We created device profiles, that we manage the lifetime of in colord,
    but color devices can be assigned profiles other than the ones it was
    created for. For example, this can include the standard sRGB profile
    provided by colord.
    
    To achieve this, keep track of the default profile of the CdDevice as
    the "assigned" color profile of the device. Given this profile
    (CdProfile), construct a MetaColorProfile that can then be interacted
    with as if it was generated by ourself.
    
    The assigned profile (default profile in colord terms) does nothing
    special so far, but will later be used to determine how to apply CRTC
    gamma ramps etc.
    
    The sRGB.icc file used in the tests was copied from colord. It was
    stated in the repository that it has no known copyright restrictions.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2164>

 src/backends/meta-color-device.c       |  92 +++++++++++++++++
 src/backends/meta-color-device.h       |   4 +
 src/backends/meta-color-profile.c      |  89 ++++++++++++++---
 src/backends/meta-color-profile.h      |  10 ++
 src/backends/meta-color-store.c        | 178 +++++++++++++++++++++++++++++++++
 src/backends/meta-color-store.h        |  11 ++
 src/tests/color-management-tests.c     | 146 +++++++++++++++++++++++++++
 src/tests/dbusmock-templates/colord.py |  33 +++++-
 src/tests/icc-profiles/sRGB.icc        | Bin 0 -> 15400 bytes
 9 files changed, 547 insertions(+), 16 deletions(-)
---
diff --git a/src/backends/meta-color-device.c b/src/backends/meta-color-device.c
index e86d96ef86..8ec1571c4e 100644
--- a/src/backends/meta-color-device.c
+++ b/src/backends/meta-color-device.c
@@ -62,6 +62,10 @@ struct _MetaColorDevice
   MetaColorProfile *device_profile;
   gulong device_profile_ready_handler_id;
 
+  MetaColorProfile *assigned_profile;
+  gulong assigned_profile_ready_handler_id;
+  GCancellable *assigned_profile_cancellable;
+
   GCancellable *cancellable;
 
   PendingState pending_state;
@@ -121,6 +125,77 @@ out:
   return g_string_free (device_id, FALSE);
 }
 
+static void
+ensure_default_profile_cb (GObject      *source_object,
+                           GAsyncResult *res,
+                           gpointer      user_data)
+{
+  MetaColorStore *color_store = META_COLOR_STORE (source_object);
+  MetaColorDevice *color_device;
+  g_autoptr (MetaColorProfile) color_profile = NULL;
+  g_autoptr (GError) error = NULL;
+
+  color_profile = meta_color_store_ensure_colord_profile_finish (color_store,
+                                                                 res,
+                                                                 &error);
+  if (!color_profile)
+    {
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        return;
+
+      g_warning ("Failed to create color profile from colord profile: %s",
+                 error->message);
+    }
+
+  color_device = META_COLOR_DEVICE (user_data);
+  g_set_object (&color_device->assigned_profile, color_profile);
+}
+
+static void
+update_assigned_profile (MetaColorDevice *color_device)
+{
+  MetaColorManager *color_manager = color_device->color_manager;
+  MetaColorStore *color_store =
+    meta_color_manager_get_color_store (color_manager);
+  CdProfile *default_profile;
+  GCancellable *cancellable;
+
+  default_profile = cd_device_get_default_profile (color_device->cd_device);
+
+  if (color_device->assigned_profile &&
+      meta_color_profile_get_cd_profile (color_device->assigned_profile) ==
+      default_profile)
+    return;
+
+  if (color_device->assigned_profile_cancellable)
+    {
+      g_cancellable_cancel (color_device->assigned_profile_cancellable);
+      g_clear_object (&color_device->assigned_profile_cancellable);
+    }
+
+  if (!default_profile)
+    {
+      g_clear_object (&color_device->assigned_profile);
+      return;
+    }
+
+  cancellable = g_cancellable_new ();
+  color_device->assigned_profile_cancellable = cancellable;
+
+  meta_color_store_ensure_colord_profile (color_store,
+                                          default_profile,
+                                          cancellable,
+                                          ensure_default_profile_cb,
+                                          color_device);
+}
+
+static void
+on_cd_device_changed (CdDevice        *cd_device,
+                      MetaColorDevice *color_device)
+{
+  update_assigned_profile (color_device);
+}
+
 typedef struct
 {
   GMainLoop *loop;
@@ -180,11 +255,18 @@ meta_color_device_dispose (GObject *object)
   meta_topic (META_DEBUG_COLOR,
               "Removing color device '%s'", color_device->cd_device_id);
 
+  if (color_device->assigned_profile_cancellable)
+    {
+      g_cancellable_cancel (color_device->assigned_profile_cancellable);
+      g_clear_object (&color_device->assigned_profile_cancellable);
+    }
+
   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->assigned_profile);
   g_clear_object (&color_device->device_profile);
 
   cd_device = color_device->cd_device;
@@ -284,6 +366,10 @@ on_cd_device_connected (GObject      *source_object,
 
   color_device->pending_state &= ~PENDING_CONNECTED;
 
+  g_signal_connect (cd_device, "changed",
+                    G_CALLBACK (on_cd_device_changed), color_device);
+  update_assigned_profile (color_device);
+
   maybe_finish_setup (color_device);
 }
 
@@ -981,3 +1067,9 @@ meta_color_device_is_ready (MetaColorDevice *color_device)
 {
   return color_device->is_ready;
 }
+
+MetaColorProfile *
+meta_color_device_get_assigned_profile (MetaColorDevice *color_device)
+{
+  return color_device->assigned_profile;
+}
diff --git a/src/backends/meta-color-device.h b/src/backends/meta-color-device.h
index 6022fc0a8d..6f03750ff7 100644
--- a/src/backends/meta-color-device.h
+++ b/src/backends/meta-color-device.h
@@ -37,6 +37,7 @@ void meta_color_device_destroy (MetaColorDevice *color_device);
 void meta_color_device_update_monitor (MetaColorDevice *color_device,
                                        MetaMonitor     *monitor);
 
+META_EXPORT_TEST
 const char * meta_color_device_get_id (MetaColorDevice *color_device);
 
 META_EXPORT_TEST
@@ -45,6 +46,9 @@ MetaMonitor * meta_color_device_get_monitor (MetaColorDevice *color_device);
 META_EXPORT_TEST
 MetaColorProfile * meta_color_device_get_device_profile (MetaColorDevice *color_device);
 
+META_EXPORT_TEST
+MetaColorProfile * meta_color_device_get_assigned_profile (MetaColorDevice *color_device);
+
 void meta_color_device_generate_profile (MetaColorDevice     *color_device,
                                          const char          *file_path,
                                          GCancellable        *cancellable,
diff --git a/src/backends/meta-color-profile.c b/src/backends/meta-color-profile.c
index cfd038d84b..201d1c57b6 100644
--- a/src/backends/meta-color-profile.c
+++ b/src/backends/meta-color-profile.c
@@ -46,8 +46,10 @@ struct _MetaColorProfile
   GBytes *bytes;
 
   char *cd_profile_id;
+  gboolean is_owner;
   CdProfile *cd_profile;
   GCancellable *cancellable;
+  guint notify_ready_id;
 
   gboolean is_ready;
 };
@@ -109,30 +111,35 @@ 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);
+  g_clear_handle_id (&color_profile->notify_ready_id, g_source_remove);
 
-  cd_profile = color_profile->cd_profile;
-  if (!cd_profile)
+  if (color_profile->is_owner)
     {
-      g_autoptr (GError) error = NULL;
+      CdProfile *cd_profile;
 
-      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))
+      cd_profile = color_profile->cd_profile;
+      if (!cd_profile)
         {
-          g_warning ("Failed to find colord profile %s: %s",
-                     color_profile->cd_profile_id,
-                     error->message);
+          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);
+      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);
@@ -270,6 +277,7 @@ meta_color_profile_new_from_icc (MetaColorManager *color_manager,
   color_profile->cd_icc = cd_icc;
   color_profile->bytes = raw_bytes;
   color_profile->cancellable = g_cancellable_new ();
+  color_profile->is_owner = TRUE;
 
   color_profile->cd_profile_id = g_strdup_printf ("icc-%s", checksum);
 
@@ -278,6 +286,45 @@ meta_color_profile_new_from_icc (MetaColorManager *color_manager,
   return color_profile;
 }
 
+static gboolean
+notify_ready_idle (gpointer user_data)
+{
+  MetaColorProfile *color_profile = user_data;
+
+  color_profile->notify_ready_id = 0;
+  color_profile->is_ready = TRUE;
+  g_signal_emit (color_profile, signals[READY], 0);
+
+  return G_SOURCE_REMOVE;
+}
+
+MetaColorProfile *
+meta_color_profile_new_from_cd_profile (MetaColorManager *color_manager,
+                                        CdProfile        *cd_profile,
+                                        CdIcc            *cd_icc,
+                                        GBytes           *raw_bytes)
+{
+  MetaColorProfile *color_profile;
+  const char *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->is_owner = FALSE;
+
+  checksum = cd_icc_get_metadata_item (cd_icc,
+                                       CD_PROFILE_METADATA_FILE_CHECKSUM);
+  color_profile->cd_profile_id = g_strdup_printf ("icc-%s", checksum);
+  color_profile->cd_profile = g_object_ref (cd_profile);
+
+  color_profile->notify_ready_id = g_idle_add (notify_ready_idle,
+                                               color_profile);
+
+  return color_profile;
+}
+
 gboolean
 meta_color_profile_equals_bytes (MetaColorProfile *color_profile,
                                  GBytes           *bytes)
@@ -303,8 +350,20 @@ meta_color_profile_get_cd_icc (MetaColorProfile *color_profile)
   return color_profile->cd_icc;
 }
 
+CdProfile *
+meta_color_profile_get_cd_profile (MetaColorProfile *color_profile)
+{
+  return color_profile->cd_profile;
+}
+
 gboolean
 meta_color_profile_is_ready (MetaColorProfile *color_profile)
 {
   return color_profile->is_ready;
 }
+
+const char *
+meta_color_profile_get_id (MetaColorProfile *color_profile)
+{
+  return color_profile->cd_profile_id;
+}
diff --git a/src/backends/meta-color-profile.h b/src/backends/meta-color-profile.h
index 6a80c6452b..0367860323 100644
--- a/src/backends/meta-color-profile.h
+++ b/src/backends/meta-color-profile.h
@@ -34,6 +34,11 @@ MetaColorProfile * meta_color_profile_new_from_icc (MetaColorManager *color_mana
                                                     CdIcc            *icc,
                                                     GBytes           *raw_bytes);
 
+MetaColorProfile * meta_color_profile_new_from_cd_profile (MetaColorManager *color_manager,
+                                                           CdProfile        *cd_profile,
+                                                           CdIcc            *cd_icc,
+                                                           GBytes           *raw_bytes);
+
 gboolean meta_color_profile_equals_bytes (MetaColorProfile *color_profile,
                                           GBytes           *bytes);
 
@@ -44,6 +49,11 @@ size_t meta_color_profile_get_data_size (MetaColorProfile *color_profile);
 META_EXPORT_TEST
 CdIcc * meta_color_profile_get_cd_icc (MetaColorProfile *color_profile);
 
+CdProfile * meta_color_profile_get_cd_profile (MetaColorProfile *color_profile);
+
 gboolean meta_color_profile_is_ready (MetaColorProfile *color_profile);
 
+META_EXPORT_TEST
+const char * meta_color_profile_get_id (MetaColorProfile *color_profile);
+
 #endif /* META_COLOR_PROFILE_H */
diff --git a/src/backends/meta-color-store.c b/src/backends/meta-color-store.c
index 860d81a898..d267890175 100644
--- a/src/backends/meta-color-store.c
+++ b/src/backends/meta-color-store.c
@@ -54,6 +54,7 @@ meta_color_store_finalize (GObject *object)
 {
   MetaColorStore *color_store = META_COLOR_STORE (object);
 
+  g_clear_pointer (&color_store->profiles, g_hash_table_unref);
   g_clear_pointer (&color_store->device_profiles, g_hash_table_unref);
   g_clear_pointer (&color_store->pending_device_profiles, g_hash_table_unref);
 
@@ -80,6 +81,8 @@ meta_color_store_new (MetaColorManager *color_manager)
 
   color_store = g_object_new (META_TYPE_COLOR_STORE, NULL);
   color_store->color_manager = color_manager;
+  color_store->profiles =
+    g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
   color_store->device_profiles =
     g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
   color_store->pending_device_profiles =
@@ -205,5 +208,180 @@ meta_color_store_ensure_device_profile_finish (MetaColorStore  *color_store,
   g_hash_table_insert (color_store->device_profiles,
                        g_steal_pointer (&data->key),
                        g_object_ref (color_profile));
+  g_hash_table_insert (color_store->profiles,
+                       g_strdup (meta_color_profile_get_id (color_profile)),
+                       g_object_ref (color_profile));
   return color_profile;
 }
+
+typedef struct
+{
+  MetaColorStore *color_store;
+  CdProfile *cd_profile;
+
+  MetaColorProfile *created_profile;
+} EnsureColordProfileData;
+
+static void
+ensure_colord_profile_data_free (EnsureColordProfileData *data)
+{
+  g_clear_object (&data->cd_profile);
+  g_free (data);
+}
+
+static void
+on_cd_profile_contents_loaded (GObject      *source_object,
+                               GAsyncResult *res,
+                               gpointer      user_data)
+{
+  GFile *file = G_FILE (source_object);
+  g_autoptr (GTask) task = G_TASK (user_data);
+  EnsureColordProfileData *data = g_task_get_task_data (task);
+  MetaColorStore *color_store = data->color_store;
+  MetaColorManager *color_manager = color_store->color_manager;
+  CdProfile *cd_profile = data->cd_profile;
+  g_autoptr (GError) error = NULL;
+  g_autofree char *contents = NULL;
+  size_t length;
+  g_autoptr (CdIcc) cd_icc = NULL;
+  g_autofree char *file_md5_checksum = NULL;
+  GBytes *bytes;
+  MetaColorProfile *color_profile;
+
+  if (!g_file_load_contents_finish (file, res,
+                                    &contents,
+                                    &length,
+                                    NULL,
+                                    &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  cd_icc = cd_icc_new ();
+  if (!cd_icc_load_data (cd_icc,
+                         (uint8_t *) contents,
+                         length,
+                         CD_ICC_LOAD_FLAGS_METADATA,
+                         &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  cd_icc_add_metadata (cd_icc, CD_PROFILE_PROPERTY_FILENAME,
+                       g_file_peek_path (file));
+
+  file_md5_checksum = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+                                                   (uint8_t *) contents,
+                                                   length);
+  cd_icc_add_metadata (cd_icc, CD_PROFILE_METADATA_FILE_CHECKSUM,
+                       file_md5_checksum);
+
+  bytes = g_bytes_new_take (g_steal_pointer (&contents), length);
+  color_profile =
+    meta_color_profile_new_from_cd_profile (color_manager,
+                                            cd_profile,
+                                            g_steal_pointer (&cd_icc),
+                                            bytes);
+
+  g_hash_table_insert (color_store->profiles,
+                       g_strdup (meta_color_profile_get_id (color_profile)),
+                       color_profile);
+
+  meta_topic (META_DEBUG_COLOR, "Created colord profile '%s' from '%s'",
+              cd_profile_get_id (cd_profile),
+              cd_profile_get_filename (cd_profile));
+
+  g_task_return_pointer (task, g_object_ref (color_profile), g_object_unref);
+}
+
+static void
+on_cd_profile_connected (GObject      *source_object,
+                         GAsyncResult *res,
+                         gpointer      user_data)
+{
+  CdProfile *cd_profile = CD_PROFILE (source_object);
+  g_autoptr (GTask) task = G_TASK (user_data);
+  EnsureColordProfileData *data = g_task_get_task_data (task);
+  MetaColorStore *color_store = data->color_store;
+  g_autoptr (GError) error = NULL;
+  const char *file_path;
+  g_autoptr (GFile) file = NULL;
+  MetaColorProfile *color_profile;
+  GCancellable *cancellable;
+
+  if (!cd_profile_connect_finish (cd_profile, res, &error))
+    {
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        return;
+
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  color_profile = g_hash_table_lookup (color_store->profiles,
+                                       cd_profile_get_id (cd_profile));
+  if (color_profile)
+    {
+      meta_topic (META_DEBUG_COLOR, "Found existing colord profile '%s'",
+                  cd_profile_get_id (cd_profile));
+      g_task_return_pointer (task,
+                             g_object_ref (color_profile),
+                             g_object_unref);
+      return;
+    }
+
+  file_path = cd_profile_get_filename (cd_profile);
+  if (!file_path)
+    {
+      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               "Tried to assign non-local profile");
+      return;
+    }
+
+  file = g_file_new_for_path (file_path);
+  cancellable = g_task_get_cancellable (task);
+  g_file_load_contents_async (file,
+                              cancellable,
+                              on_cd_profile_contents_loaded,
+                              g_steal_pointer (&task));
+}
+
+void
+meta_color_store_ensure_colord_profile (MetaColorStore      *color_store,
+                                        CdProfile           *cd_profile,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  GTask *task;
+  EnsureColordProfileData *data;
+
+  task = g_task_new (G_OBJECT (color_store), cancellable, callback, user_data);
+  g_task_set_source_tag (task, meta_color_store_ensure_colord_profile);
+
+  data = g_new0 (EnsureColordProfileData, 1);
+  data->color_store = color_store;
+  data->cd_profile = g_object_ref (cd_profile);
+  g_task_set_task_data (task, data,
+                        (GDestroyNotify) ensure_colord_profile_data_free);
+
+  cd_profile_connect (cd_profile,
+                      cancellable,
+                      on_cd_profile_connected,
+                      task);
+}
+
+MetaColorProfile *
+meta_color_store_ensure_colord_profile_finish (MetaColorStore  *color_store,
+                                               GAsyncResult    *res,
+                                               GError         **error)
+{
+  GTask *task = G_TASK (res);
+
+  g_assert (g_task_get_source_tag (task) ==
+            meta_color_store_ensure_colord_profile);
+
+  return g_task_propagate_pointer (G_TASK (res), error);
+}
diff --git a/src/backends/meta-color-store.h b/src/backends/meta-color-store.h
index 74bac9688a..5634dae762 100644
--- a/src/backends/meta-color-store.h
+++ b/src/backends/meta-color-store.h
@@ -18,6 +18,7 @@
 #ifndef META_COLOR_STORE_H
 #define META_COLOR_STORE_H
 
+#include <colord.h>
 #include <gio/gio.h>
 #include <glib-object.h>
 
@@ -40,4 +41,14 @@ MetaColorProfile * meta_color_store_ensure_device_profile_finish (MetaColorStore
                                                                   GAsyncResult    *res,
                                                                   GError         **error);
 
+void meta_color_store_ensure_colord_profile (MetaColorStore      *color_store,
+                                             CdProfile           *cd_profile,
+                                             GCancellable        *cancellable,
+                                             GAsyncReadyCallback  callback,
+                                             gpointer             user_data);
+
+MetaColorProfile * meta_color_store_ensure_colord_profile_finish (MetaColorStore  *color_store,
+                                                                  GAsyncResult    *res,
+                                                                  GError         **error);
+
 #endif /* META_COLOR_STORE_H */
diff --git a/src/tests/color-management-tests.c b/src/tests/color-management-tests.c
index 7fefd3314a..754326faf7 100644
--- a/src/tests/color-management-tests.c
+++ b/src/tests/color-management-tests.c
@@ -28,6 +28,9 @@
 
 static MetaContext *test_context;
 
+/* Profile ID is 'icc-$(md5sum sRGB.icc)' */
+#define SRGB_ICC_PROFILE_ID "icc-112034c661b5e0c91c51f109684612a0";
+
 #define PRIMARY_EPSILON 0.000015
 
 static MonitorTestCaseSetup base_monitor_setup = {
@@ -101,6 +104,82 @@ static MonitorTestCaseSetup base_monitor_setup = {
     .white_y = 0.329102, \
   })
 
+static GDBusProxy *
+get_colord_mock_proxy (void)
+{
+  GDBusProxy *proxy;
+  g_autoptr (GError) error = NULL;
+  g_autoptr (GVariant) ret = NULL;
+
+  proxy =
+    g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                   G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+                                   G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                   NULL,
+                                   "org.freedesktop.ColorManager",
+                                   "/org/freedesktop/ColorManager",
+                                   "org.freedesktop.DBus.Mock",
+                                   NULL, &error);
+  if (!proxy)
+    {
+      g_error ("Failed to find mocked color manager system service, %s",
+               error->message);
+    }
+
+  return proxy;
+}
+
+static void
+set_colord_device_profiles (const char  *cd_device_id,
+                            const char **cd_profile_ids,
+                            int          n_cd_profile_ids)
+{
+  GDBusProxy *proxy;
+  g_autoptr (GError) error = NULL;
+  GVariantBuilder params_builder;
+  GVariantBuilder profiles_builder;
+  int i;
+
+  proxy = get_colord_mock_proxy ();
+
+  g_variant_builder_init (&params_builder, G_VARIANT_TYPE ("(sas)"));
+  g_variant_builder_add (&params_builder, "s", cd_device_id);
+
+  g_variant_builder_init (&profiles_builder, G_VARIANT_TYPE ("as"));
+  for (i = 0; i < n_cd_profile_ids; i++)
+    g_variant_builder_add (&profiles_builder, "s", cd_profile_ids[i]);
+  g_variant_builder_add (&params_builder, "as", &profiles_builder);
+
+  if (!g_dbus_proxy_call_sync (proxy,
+                               "SetDeviceProfiles",
+                               g_variant_builder_end (&params_builder),
+                               G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL,
+                               &error))
+    g_error ("Failed to set device profile: %s", error->message);
+}
+
+static void
+add_colord_system_profile (const char *cd_profile_id,
+                           const char *file_path)
+{
+  GDBusProxy *proxy;
+  g_autoptr (GError) error = NULL;
+  GVariantBuilder params_builder;
+
+  proxy = get_colord_mock_proxy ();
+
+  g_variant_builder_init (&params_builder, G_VARIANT_TYPE ("(ss)"));
+  g_variant_builder_add (&params_builder, "s", cd_profile_id);
+  g_variant_builder_add (&params_builder, "s", file_path);
+
+  if (!g_dbus_proxy_call_sync (proxy,
+                               "AddSystemProfile",
+                               g_variant_builder_end (&params_builder),
+                               G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL,
+                               &error))
+    g_error ("Failed to add system profile: %s", error->message);
+}
+
 static void
 meta_test_color_management_device_basic (void)
 {
@@ -269,6 +348,63 @@ meta_test_color_management_profile_device (void)
   g_assert_cmpfloat_with_epsilon (white->Z, 1.10479736, PRIMARY_EPSILON);
 }
 
+static void
+meta_test_color_management_profile_system (void)
+{
+  MetaBackend *backend = meta_context_get_backend (test_context);
+  MetaMonitorManager *monitor_manager =
+    meta_backend_get_monitor_manager (backend);
+  MetaMonitorManagerTest *monitor_manager_test =
+    META_MONITOR_MANAGER_TEST (monitor_manager);
+  MetaColorManager *color_manager =
+    meta_backend_get_color_manager (backend);
+  MetaEdidInfo edid_info;
+  MonitorTestCaseSetup test_case_setup = base_monitor_setup;
+  MetaMonitorTestSetup *test_setup;
+  MetaMonitor *monitor;
+  MetaColorDevice *color_device;
+  const char *path;
+  const char *color_profiles[1];
+  MetaColorProfile *color_profile;
+  const char *srgb_profile_id = SRGB_ICC_PROFILE_ID;
+
+  edid_info = CALTECH_MONITOR_EDID;
+  test_case_setup.outputs[0].edid_info = edid_info;
+  test_case_setup.outputs[0].has_edid_info = TRUE;
+  test_setup = meta_create_monitor_test_setup (backend, &test_case_setup,
+                                               MONITOR_TEST_FLAG_NO_STORED);
+  meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup);
+
+  monitor = meta_monitor_manager_get_monitors (monitor_manager)->data;
+  color_device = meta_color_manager_get_color_device (color_manager, monitor);
+  g_assert_nonnull (color_device);
+
+  while (!meta_color_device_is_ready (color_device))
+    g_main_context_iteration (NULL, TRUE);
+
+  g_assert_null (meta_color_device_get_assigned_profile (color_device));
+
+  path = g_test_get_filename (G_TEST_DIST, "tests", "icc-profiles", "sRGB.icc",
+                              NULL);
+  add_colord_system_profile (srgb_profile_id, path);
+  color_profiles[0] = srgb_profile_id;
+  set_colord_device_profiles (meta_color_device_get_id (color_device),
+                              color_profiles, G_N_ELEMENTS (color_profiles));
+
+  while (TRUE)
+    {
+      color_profile = meta_color_device_get_assigned_profile (color_device);
+      if (color_profile)
+        break;
+
+      g_main_context_iteration (NULL, TRUE);
+    }
+
+  g_assert_cmpstr (meta_color_profile_get_id (color_profile),
+                   ==,
+                   srgb_profile_id);
+}
+
 static MetaMonitorTestSetup *
 create_stage_view_test_setup (MetaBackend *backend)
 {
@@ -296,18 +432,28 @@ init_tests (void)
                    meta_test_color_management_device_basic);
   g_test_add_func ("/color-management/profile/device",
                    meta_test_color_management_profile_device);
+  g_test_add_func ("/color-management/profile/system",
+                   meta_test_color_management_profile_system);
 }
 
 int
 main (int argc, char **argv)
 {
   g_autoptr (MetaContext) context = NULL;
+  char *path;
 
   context = meta_create_test_context (META_CONTEXT_TEST_TYPE_NESTED,
                                       META_CONTEXT_TEST_FLAG_NONE);
 
   g_assert (meta_context_configure (context, &argc, &argv, NULL));
 
+  path = g_test_build_filename (G_TEST_BUILT,
+                                "tests",
+                                "share",
+                                NULL);
+  g_setenv ("XDG_DATA_HOME", path, TRUE);
+  g_free (path);
+
   test_context = context;
 
   init_tests ();
diff --git a/src/tests/dbusmock-templates/colord.py b/src/tests/dbusmock-templates/colord.py
index d1912af445..f51d065792 100644
--- a/src/tests/dbusmock-templates/colord.py
+++ b/src/tests/dbusmock-templates/colord.py
@@ -13,7 +13,7 @@ __copyright__ = '(c) 2021 Red Hat Inc.'
 import dbus
 import os
 import pwd
-from dbusmock import MOCK_IFACE
+from dbusmock import MOCK_IFACE, mockobject
 
 
 BUS_PREFIX = 'org.freedesktop.ColorManager'
@@ -65,7 +65,9 @@ def CreateDevice(self, device_id, scope, props):
                    DEVICE_IFACE,
                    {
                      'DeviceId': device_id,
+                     'Profiles': dbus.types.Array(signature='o'),
                      'Enabled': True,
+                     'ProfilingInhibitors': dbus.types.Array(signature='s'),
                    },
                    [])
     self.EmitSignal(MAIN_IFACE, 'DeviceAdded', 'o', [device_path])
@@ -119,3 +121,32 @@ def Reset(self):
     for profile_path in self.profiles.values():
         self.RemoveObject(profile_path)
     self.profiles = {}
+
+@dbus.service.method(MOCK_IFACE, in_signature='ss')
+def AddSystemProfile(self, profile_id, file_path):
+    uid = os.getuid()
+    username = get_username(uid)
+    profile_path = PATH_PREFIX + '/profiles/' + \
+        escape_unit_name(profile_id) + \
+        '_' + username + '_' + str(uid)
+    self.profiles[profile_id] = profile_path
+    self.AddObject(profile_path,
+                   PROFILE_IFACE,
+                   {
+                     'ProfileId': profile_id,
+                     'Filename': file_path,
+                     'Enabled': True,
+                   },
+                   [])
+    self.EmitSignal(MAIN_IFACE, 'ProfileAdded', 'o', [profile_path])
+
+@dbus.service.method(MOCK_IFACE, in_signature='sas')
+def SetDeviceProfiles(self, device_id, profile_ids):
+    device_path = self.devices[device_id]
+    device = mockobject.objects[device_path]
+    profile_paths = [
+        dbus.types.ObjectPath(self.profiles[profile_id])
+        for profile_id in profile_ids
+    ]
+    device.UpdateProperties(DEVICE_IFACE, {'Profiles': dbus.types.Array(profile_paths)})
+    device.EmitSignal(DEVICE_IFACE, 'Changed', '', [])
diff --git a/src/tests/icc-profiles/sRGB.icc b/src/tests/icc-profiles/sRGB.icc
new file mode 100644
index 0000000000..9844b8c337
Binary files /dev/null and b/src/tests/icc-profiles/sRGB.icc differ


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]