From 17c99edfe0774ef2ce567d7e66b357e91003984f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 6 Dec 2021 11:47:00 +0100 Subject: [PATCH] x11: Add X11 color manager subclass This is used as part of the X11 backend to updated X11 root window properties according to the ICC Profiles in X Specification. Part-of: --- src/backends/meta-color-manager.h | 1 + src/backends/x11/meta-backend-x11.c | 3 +- src/backends/x11/meta-color-manager-x11.c | 207 ++++++++++++++++++++++ src/backends/x11/meta-color-manager-x11.h | 30 ++++ src/meson.build | 2 + src/tests/meta-backend-test.c | 10 ++ 6 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 src/backends/x11/meta-color-manager-x11.c create mode 100644 src/backends/x11/meta-color-manager-x11.h diff --git a/src/backends/meta-color-manager.h b/src/backends/meta-color-manager.h index 90369b8f7..246922264 100644 --- a/src/backends/meta-color-manager.h +++ b/src/backends/meta-color-manager.h @@ -24,6 +24,7 @@ #include "core/util-private.h" #define META_TYPE_COLOR_MANAGER (meta_color_manager_get_type ()) +META_EXPORT_TEST G_DECLARE_DERIVABLE_TYPE (MetaColorManager, meta_color_manager, META, COLOR_MANAGER, GObject) diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c index 4d4a5da9c..8d4430525 100644 --- a/src/backends/x11/meta-backend-x11.c +++ b/src/backends/x11/meta-backend-x11.c @@ -48,6 +48,7 @@ #include "backends/meta-stage-private.h" #include "backends/x11/meta-barrier-x11.h" #include "backends/x11/meta-clutter-backend-x11.h" +#include "backends/x11/meta-color-manager-x11.h" #include "backends/x11/meta-event-x11.h" #include "backends/x11/meta-seat-x11.h" #include "backends/x11/meta-stage-x11.h" @@ -599,7 +600,7 @@ meta_backend_x11_create_clutter_backend (MetaBackend *backend) static MetaColorManager * meta_backend_x11_create_color_manager (MetaBackend *backend) { - return g_object_new (META_TYPE_COLOR_MANAGER, + return g_object_new (META_TYPE_COLOR_MANAGER_X11, "backend", backend, NULL); } diff --git a/src/backends/x11/meta-color-manager-x11.c b/src/backends/x11/meta-color-manager-x11.c new file mode 100644 index 000000000..8f1e6370b --- /dev/null +++ b/src/backends/x11/meta-color-manager-x11.c @@ -0,0 +1,207 @@ +/* + * 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/x11/meta-color-manager-x11.h" + +#include +#include + +#include "backends/meta-color-device.h" +#include "backends/meta-color-profile.h" +#include "backends/meta-monitor.h" +#include "backends/x11/meta-backend-x11.h" +#include "backends/x11/meta-output-xrandr.h" + +struct _MetaColorManagerX11 +{ + MetaColorManager parent; + + CdIcc *srgb_cd_icc; + GBytes *srgb_icc_bytes; +}; + +G_DEFINE_TYPE (MetaColorManagerX11, meta_color_manager_x11, + META_TYPE_COLOR_MANAGER) + +/* see http://www.oyranos.org/wiki/index.php?title=ICC_Profiles_in_X_Specification_0.3 */ +#define ICC_PROFILE_IN_X_VERSION_MAJOR 0 +#define ICC_PROFILE_IN_X_VERSION_MINOR 3 + +static CdIcc * +ensure_srgb_profile (MetaColorManagerX11 *color_manager_x11) +{ + CdIcc *srgb_cd_icc; + g_autoptr (GError) error = NULL; + + if (color_manager_x11->srgb_cd_icc) + return color_manager_x11->srgb_cd_icc; + + srgb_cd_icc = cd_icc_new (); + if (!cd_icc_create_default_full (srgb_cd_icc, + CD_ICC_LOAD_FLAGS_PRIMARIES, + &error)) + { + g_warning_once ("Failed to create sRGB ICC profile: %s", error->message); + return NULL; + } + + color_manager_x11->srgb_cd_icc = srgb_cd_icc; + return srgb_cd_icc; +} + +static GBytes * +ensure_srgb_profile_bytes (MetaColorManagerX11 *color_manager_x11) +{ + CdIcc *srgb_cd_icc; + g_autoptr (GError) error = NULL; + GBytes *bytes; + + srgb_cd_icc = ensure_srgb_profile (color_manager_x11); + if (!srgb_cd_icc) + return NULL; + + bytes = cd_icc_save_data (srgb_cd_icc, + CD_ICC_SAVE_FLAGS_NONE, + &error); + if (!bytes) + { + g_warning_once ("Failed to export sRGB ICC profile: %s", error->message); + return NULL; + } + + color_manager_x11->srgb_icc_bytes = bytes; + return bytes; +} + +static void +on_color_device_updated (MetaColorManager *color_manager, + MetaColorDevice *color_device) +{ + MetaColorManagerX11 *color_manager_x11 = + META_COLOR_MANAGER_X11 (color_manager); + MetaBackend *backend = meta_color_manager_get_backend (color_manager); + MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); + Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); + Window xroot = meta_backend_x11_get_root_xwindow (backend_x11); + Atom icc_profile_atom; + Atom icc_profile_version_atom; + MetaMonitor *monitor; + const uint8_t *profile_contents = NULL; + size_t profile_size; + + monitor = meta_color_device_get_monitor (color_device); + if (!meta_monitor_is_primary (monitor)) + return; + + if (meta_monitor_supports_color_transform (monitor)) + { + GBytes *profile_bytes; + + /* + * If the output supports color transforms, then + * applications should use the standard sRGB color profile and + * the window system will take care of converting colors to + * match the output device's measured color profile. + */ + profile_bytes = ensure_srgb_profile_bytes (color_manager_x11); + if (!profile_bytes) + g_warning_once ("Failed to generate sRGB ICC profile"); + else + profile_contents = g_bytes_get_data (profile_bytes, &profile_size); + } + else + { + MetaColorProfile *color_profile; + + color_profile = meta_color_device_get_assigned_profile (color_device); + if (color_profile) + { + profile_contents = meta_color_profile_get_data (color_profile); + profile_size = meta_color_profile_get_data_size (color_profile); + } + } + + icc_profile_atom = XInternAtom (xdisplay, "_ICC_PROFILE", False); + icc_profile_version_atom = XInternAtom (xdisplay, + "_ICC_PROFILE_IN_X_VERSION", False); + if (profile_contents) + { + unsigned int version_data; + + XChangeProperty (xdisplay, xroot, + icc_profile_atom, + XA_CARDINAL, 8, + PropModeReplace, + profile_contents, profile_size); + + version_data = + ICC_PROFILE_IN_X_VERSION_MAJOR * 100 + + ICC_PROFILE_IN_X_VERSION_MINOR * 1; + + XChangeProperty (xdisplay, xroot, + icc_profile_version_atom, + XA_CARDINAL, 8, + PropModeReplace, + (uint8_t *) &version_data, 1); + } + else + { + XDeleteProperty (xdisplay, xroot, icc_profile_atom); + XDeleteProperty (xdisplay, xroot, icc_profile_version_atom); + } +} + +static void +meta_color_manager_x11_constructed (GObject *object) +{ + MetaColorManager *color_manager = META_COLOR_MANAGER (object); + + g_signal_connect (color_manager, "device-updated", + G_CALLBACK (on_color_device_updated), NULL); + + G_OBJECT_CLASS (meta_color_manager_x11_parent_class)->constructed (object); +} + +static void +meta_color_manager_x11_finalize (GObject *object) +{ + MetaColorManagerX11 *color_manager_x11 = META_COLOR_MANAGER_X11 (object); + + g_clear_pointer (&color_manager_x11->srgb_icc_bytes, g_bytes_unref); + g_clear_object (&color_manager_x11->srgb_cd_icc); + + G_OBJECT_CLASS (meta_color_manager_x11_parent_class)->finalize (object); +} + +static void +meta_color_manager_x11_class_init (MetaColorManagerX11Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = meta_color_manager_x11_constructed; + object_class->finalize = meta_color_manager_x11_finalize; +} + +static void +meta_color_manager_x11_init (MetaColorManagerX11 *color_manager_x11) +{ +} diff --git a/src/backends/x11/meta-color-manager-x11.h b/src/backends/x11/meta-color-manager-x11.h new file mode 100644 index 000000000..cb178c0bc --- /dev/null +++ b/src/backends/x11/meta-color-manager-x11.h @@ -0,0 +1,30 @@ +/* + * 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 . + */ + +#ifndef META_COLOR_MANAGER_X11_H +#define META_COLOR_MANAGER_X11_H + +#include + +#include "backends/meta-color-manager-private.h" + +#define META_TYPE_COLOR_MANAGER_X11 (meta_color_manager_x11_get_type ()) +G_DECLARE_FINAL_TYPE (MetaColorManagerX11, meta_color_manager_x11, + META, COLOR_MANAGER_X11, + MetaColorManager) + +#endif /* META_COLOR_MANAGER_X11_H */ diff --git a/src/meson.build b/src/meson.build index 4bc295057..7b6237968 100644 --- a/src/meson.build +++ b/src/meson.build @@ -387,6 +387,8 @@ if have_x11 'backends/x11/meta-barrier-x11.h', 'backends/x11/meta-clutter-backend-x11.c', 'backends/x11/meta-clutter-backend-x11.h', + 'backends/x11/meta-color-manager-x11.c', + 'backends/x11/meta-color-manager-x11.h', 'backends/x11/meta-crtc-xrandr.c', 'backends/x11/meta-crtc-xrandr.h', 'backends/x11/meta-cursor-renderer-x11.c', diff --git a/src/tests/meta-backend-test.c b/src/tests/meta-backend-test.c index 6916994e4..9101d8874 100644 --- a/src/tests/meta-backend-test.c +++ b/src/tests/meta-backend-test.c @@ -21,6 +21,7 @@ #include "tests/meta-backend-test.h" +#include "backends/meta-color-manager.h" #include "tests/meta-gpu-test.h" #include "tests/meta-monitor-manager-test.h" @@ -76,6 +77,14 @@ meta_backend_test_create_monitor_manager (MetaBackend *backend, NULL); } +static MetaColorManager * +meta_backend_test_create_color_manager (MetaBackend *backend) +{ + return g_object_new (META_TYPE_COLOR_MANAGER, + "backend", backend, + NULL); +} + ClutterInputDevice * meta_backend_test_add_test_device (MetaBackendTest *backend_test, const char *name, @@ -183,6 +192,7 @@ meta_backend_test_class_init (MetaBackendTestClass *klass) META_BACKEND_X11_NESTED_CLASS (klass); backend_class->create_monitor_manager = meta_backend_test_create_monitor_manager; + backend_class->create_color_manager = meta_backend_test_create_color_manager; backend_class->is_lid_closed = meta_backend_test_is_lid_closed; backend_x11_nested_class->init_gpus = meta_backend_test_init_gpus;