/* * Copyright (C) 2007 William Jon McCann * Copyright (C) 2011-2013 Richard Hughes * Copyright (C) 2020 NVIDIA CORPORATION * Copyright (C) 2021 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "backends/meta-color-device.h" #include #include "backends/meta-color-manager-private.h" #include "backends/meta-monitor.h" struct _MetaColorDevice { GObject parent; MetaColorManager *color_manager; char *cd_device_id; MetaMonitor *monitor; CdDevice *cd_device; GCancellable *cancellable; }; G_DEFINE_TYPE (MetaColorDevice, meta_color_device, G_TYPE_OBJECT) /* * Generate a colord DeviceId according to * `device-and-profiling-naming-spec.txt`. * * A rough summary is that it should use the following format: * * xrandr[-{%edid_vendor_name}][-{%edid_product][-{%edid_serial}] * */ static char * generate_cd_device_id (MetaMonitor *monitor) { GString *device_id; const char *vendor; const char *product; const char *serial; vendor = meta_monitor_get_vendor (monitor); product = meta_monitor_get_product (monitor); serial = meta_monitor_get_serial (monitor); device_id = g_string_new ("xrandr"); if (!vendor && !product && !serial) { g_string_append_printf (device_id, "-%s", meta_monitor_get_connector (monitor)); goto out; } if (vendor) { MetaBackend *backend = meta_monitor_get_backend (monitor); g_autofree char *vendor_name = NULL; vendor_name = meta_backend_get_vendor_name (backend, vendor); g_string_append_printf (device_id, "-%s", vendor_name ? vendor_name : vendor); } if (product) g_string_append_printf (device_id, "-%s", product); if (serial) g_string_append_printf (device_id, "-%s", serial); out: return g_string_free (device_id, FALSE); } typedef struct { GMainLoop *loop; CdDevice *cd_device; GError *error; } FindDeviceData; static void on_find_device (GObject *source_object, GAsyncResult *res, gpointer user_data) { CdClient *cd_client = CD_CLIENT (source_object); FindDeviceData *data = user_data; data->cd_device = cd_client_find_device_finish (cd_client, res, &data->error); g_main_loop_quit (data->loop); } static CdDevice * find_device_sync (CdClient *cd_client, const char *cd_device_id, GError **error) { g_autoptr (GMainContext) main_context = NULL; g_autoptr (GMainLoop) main_loop = NULL; FindDeviceData 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 = (FindDeviceData) { .loop = main_loop, }; cd_client_find_device (cd_client, cd_device_id, NULL, on_find_device, &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_device; } static void meta_color_device_dispose (GObject *object) { MetaColorDevice *color_device = META_COLOR_DEVICE (object); MetaColorManager *color_manager = color_device->color_manager; CdClient *cd_client = meta_color_manager_get_cd_client (color_manager); CdDevice *cd_device; const char *cd_device_id; meta_topic (META_DEBUG_COLOR, "Removing color device '%s'", color_device->cd_device_id); g_cancellable_cancel (color_device->cancellable); g_clear_object (&color_device->cancellable); cd_device = color_device->cd_device; cd_device_id = color_device->cd_device_id; if (!cd_device && cd_device_id) { g_autoptr (GError) error = NULL; cd_device = find_device_sync (cd_client, cd_device_id, &error); if (!cd_device && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_warning ("Failed to find colord device %s: %s", cd_device_id, error->message); } } if (cd_device) cd_client_delete_device (cd_client, cd_device, NULL, NULL, NULL); g_clear_pointer (&color_device->cd_device_id, g_free); g_clear_object (&color_device->cd_device); g_clear_object (&color_device->monitor); G_OBJECT_CLASS (meta_color_device_parent_class)->dispose (object); } static void meta_color_device_class_init (MetaColorDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_color_device_dispose; } static void meta_color_device_init (MetaColorDevice *color_device) { } static void on_cd_device_connected (GObject *source_object, GAsyncResult *res, gpointer user_data) { CdDevice *cd_device = CD_DEVICE (source_object); MetaColorDevice *color_device = user_data; g_autoptr (GError) error = NULL; if (!cd_device_connect_finish (cd_device, res, &error)) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; g_warning ("Failed to connect to colord device %s: %s", color_device->cd_device_id, error->message); return; } } static void on_cd_device_created (GObject *object, GAsyncResult *res, gpointer user_data) { CdClient *cd_client = CD_CLIENT (object); MetaColorDevice *color_device = user_data; CdDevice *cd_device; g_autoptr (GError) error = NULL; cd_device = cd_client_create_device_finish (cd_client, res, &error); if (!cd_device) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; g_warning ("Failed to create colord device for '%s': %s", color_device->cd_device_id, error->message); return; } color_device->cd_device = cd_device; cd_device_connect (cd_device, color_device->cancellable, on_cd_device_connected, color_device); } static void add_device_property (GHashTable *device_props, const char *key, const char *value) { g_hash_table_insert (device_props, (gpointer) key, g_strdup (value)); } static GHashTable * generate_color_device_props (MetaMonitor *monitor) { MetaBackend *backend = meta_monitor_get_backend (monitor); GHashTable *device_props; g_autofree char *vendor_name = NULL; const char *edid_checksum_md5; device_props = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); add_device_property (device_props, CD_DEVICE_PROPERTY_KIND, cd_device_kind_to_string (CD_DEVICE_KIND_DISPLAY)); add_device_property (device_props, CD_DEVICE_PROPERTY_MODE, meta_monitor_is_virtual (monitor) ? cd_device_mode_to_string (CD_DEVICE_MODE_VIRTUAL) : cd_device_mode_to_string (CD_DEVICE_MODE_PHYSICAL)); add_device_property (device_props, CD_DEVICE_PROPERTY_COLORSPACE, cd_colorspace_to_string (CD_COLORSPACE_RGB)); vendor_name = meta_backend_get_vendor_name (backend, meta_monitor_get_vendor (monitor)); add_device_property (device_props, CD_DEVICE_PROPERTY_VENDOR, vendor_name); add_device_property (device_props, CD_DEVICE_PROPERTY_MODEL, meta_monitor_get_product (monitor)); add_device_property (device_props, CD_DEVICE_PROPERTY_SERIAL, meta_monitor_get_serial (monitor)); add_device_property (device_props, CD_DEVICE_METADATA_XRANDR_NAME, meta_monitor_get_connector (monitor)); add_device_property (device_props, CD_DEVICE_METADATA_OUTPUT_PRIORITY, meta_monitor_is_primary (monitor) ? CD_DEVICE_METADATA_OUTPUT_PRIORITY_PRIMARY : CD_DEVICE_METADATA_OUTPUT_PRIORITY_SECONDARY); edid_checksum_md5 = meta_monitor_get_edid_checksum_md5 (monitor); if (edid_checksum_md5) { add_device_property (device_props, CD_DEVICE_METADATA_OUTPUT_EDID_MD5, edid_checksum_md5); } if (meta_monitor_is_laptop_panel (monitor)) { add_device_property (device_props, CD_DEVICE_PROPERTY_EMBEDDED, NULL); } return device_props; } MetaColorDevice * meta_color_device_new (MetaColorManager *color_manager, MetaMonitor *monitor) { MetaColorDevice *color_device; g_autoptr (GHashTable) device_props = NULL; device_props = generate_color_device_props (monitor); color_device = g_object_new (META_TYPE_COLOR_DEVICE, NULL); color_device->cd_device_id = generate_cd_device_id (monitor); color_device->monitor = g_object_ref (monitor); color_device->cancellable = g_cancellable_new (); color_device->color_manager = color_manager; cd_client_create_device (meta_color_manager_get_cd_client (color_manager), color_device->cd_device_id, CD_OBJECT_SCOPE_TEMP, device_props, color_device->cancellable, on_cd_device_created, color_device); return color_device; } void meta_color_device_destroy (MetaColorDevice *color_device) { g_object_run_dispose (G_OBJECT (color_device)); g_object_unref (color_device); } void meta_color_device_update_monitor (MetaColorDevice *color_device, MetaMonitor *monitor) { g_warn_if_fail (meta_monitor_is_same_as (monitor, color_device->monitor)); g_set_object (&color_device->monitor, monitor); } const char * meta_color_device_get_id (MetaColorDevice *color_device) { return color_device->cd_device_id; } MetaMonitor * meta_color_device_get_monitor (MetaColorDevice *color_device) { return color_device->monitor; }