/* * Copyright (C) 2024 SUSE Software Solutions Germany GmbH * Copyright (C) 2024 Red Hat * * 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 . * * Written by: * Joan Torres * Sebastian Wick */ #include "config.h" #include "meta-wayland-color-management.h" #include "backends/meta-color-device.h" #include "backends/meta-color-manager.h" #include "compositor/meta-surface-actor-wayland.h" #include "core/meta-debug-control-private.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-outputs.h" #include "color-management-v1-server-protocol.h" struct _MetaWaylandColorManager { GObject parent; MetaWaylandCompositor *compositor; struct wl_global *global; gulong color_state_changed_handler_id; /* struct wl_resource */ GList *resources; /* Key: MetaMonitor * Value: MetaWaylandColorManagementOutput */ GHashTable *outputs; /* Key: MetaWaylandSurface * Value: MetaWaylandColorManagementSurface */ GHashTable *surfaces; }; #define META_TYPE_WAYLAND_COLOR_MANAGER (meta_wayland_color_manager_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandColorManager, meta_wayland_color_manager, META, WAYLAND_COLOR_MANAGER, GObject) G_DEFINE_TYPE (MetaWaylandColorManager, meta_wayland_color_manager, G_TYPE_OBJECT) typedef struct _MetaWaylandColorManagementOutput { MetaWaylandColorManager *color_manager; /* struct wl_resource */ GList *resources; MetaWaylandOutput *output; gulong output_destroyed_handler_id; } MetaWaylandColorManagementOutput; typedef struct _MetaWaylandColorManagementSurface { MetaWaylandColorManager *color_manager; struct wl_resource *resource; /* struct wl_resource */ GList *feedback_resources; MetaWaylandSurface *surface; gulong surface_destroyed_handler_id; gulong surface_main_monitor_handler_id; ClutterColorState *preferred_color_state; } MetaWaylandColorManagementSurface; typedef enum _MetaWaylandImageDescriptionState { META_WAYLAND_IMAGE_DESCRIPTION_STATE_PENDING, META_WAYLAND_IMAGE_DESCRIPTION_STATE_READY, META_WAYLAND_IMAGE_DESCRIPTION_STATE_FAILED, } MetaWaylandImageDescriptionState; typedef enum _MetaWaylandImageDescriptionFlags { META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_DEFAULT = 0, META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_ALLOW_INFO = (1 << 0), } MetaWaylandImageDescriptionFlags; typedef struct _MetaWaylandImageDescription { MetaWaylandColorManager *color_manager; struct wl_resource *resource; MetaWaylandImageDescriptionState state; gboolean has_info; ClutterColorState *color_state; } MetaWaylandImageDescription; typedef struct _MetaWaylandCreatorParams { MetaWaylandColorManager *color_manager; struct wl_resource *resource; ClutterColorspace colorspace; ClutterTransferFunction transfer_function; float min_lum, max_lum, ref_lum; } MetaWaylandCreatorParams; static void meta_wayland_color_management_surface_free (MetaWaylandColorManagementSurface *cm_surface); static void meta_wayland_color_management_output_free (MetaWaylandColorManagementOutput *cm_output); static MetaMonitorManager * get_monitor_manager (MetaWaylandColorManager *color_manager) { MetaWaylandCompositor *compositor = color_manager->compositor; MetaBackend *backend = meta_context_get_backend (compositor->context); return meta_backend_get_monitor_manager (backend); } static ClutterContext * get_clutter_context (MetaWaylandColorManager *color_manager) { MetaWaylandCompositor *compositor = color_manager->compositor; MetaBackend *backend = meta_context_get_backend (compositor->context); return meta_backend_get_clutter_context (backend); } static MetaColorManager * get_meta_color_manager (MetaWaylandColorManager *color_manager) { MetaWaylandCompositor *compositor = color_manager->compositor; MetaBackend *backend = meta_context_get_backend (compositor->context); return meta_backend_get_color_manager (backend); } static ClutterColorManager * get_clutter_color_manager (MetaWaylandColorManager *color_manager) { ClutterContext *clutter_context = get_clutter_context (color_manager); return clutter_context_get_color_manager (clutter_context); } static float scaled_uint32_to_float (uint32_t value) { return value * 0.0001f; } static uint32_t float_to_scaled_uint32 (float value) { return (uint32_t) (value * 10000); } static gboolean wayland_tf_to_clutter (enum xx_color_manager_v4_transfer_function tf, ClutterTransferFunction *tf_out) { switch (tf) { case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB: *tf_out = CLUTTER_TRANSFER_FUNCTION_SRGB; return TRUE; case XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ: *tf_out = CLUTTER_TRANSFER_FUNCTION_PQ; return TRUE; default: return FALSE; } } static enum xx_color_manager_v4_transfer_function clutter_tf_to_wayland (ClutterTransferFunction tf) { switch (tf) { case CLUTTER_TRANSFER_FUNCTION_DEFAULT: case CLUTTER_TRANSFER_FUNCTION_SRGB: return XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB; case CLUTTER_TRANSFER_FUNCTION_PQ: return XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ; case CLUTTER_TRANSFER_FUNCTION_LINEAR: return XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR; } g_assert_not_reached (); } static gboolean wayland_primaries_to_clutter (enum xx_color_manager_v4_primaries primaries, ClutterColorspace *primaries_out) { switch (primaries) { case XX_COLOR_MANAGER_V4_PRIMARIES_SRGB: *primaries_out = CLUTTER_COLORSPACE_SRGB; return TRUE; case XX_COLOR_MANAGER_V4_PRIMARIES_BT2020: *primaries_out = CLUTTER_COLORSPACE_BT2020; return TRUE; default: return FALSE; } } static enum xx_color_manager_v4_primaries clutter_primaries_to_wayland (ClutterColorspace primaries) { switch (primaries) { case CLUTTER_COLORSPACE_DEFAULT: case CLUTTER_COLORSPACE_SRGB: return XX_COLOR_MANAGER_V4_PRIMARIES_SRGB; case CLUTTER_COLORSPACE_BT2020: return XX_COLOR_MANAGER_V4_PRIMARIES_BT2020; } g_assert_not_reached (); } static ClutterColorState * get_default_color_state (MetaWaylandColorManager *color_manager) { ClutterColorManager *clutter_color_manager = get_clutter_color_manager (color_manager); return clutter_color_manager_get_default_color_state (clutter_color_manager); } static ClutterColorState * get_output_color_state (MetaWaylandColorManager *color_manager, MetaMonitor *monitor) { MetaColorManager *meta_color_manager = get_meta_color_manager (color_manager); MetaColorDevice *color_device = meta_color_manager_get_color_device (meta_color_manager, monitor); ClutterColorState *color_state = NULL; if (color_device) color_state = meta_color_device_get_color_state (color_device); if (!color_state) color_state = get_default_color_state (color_manager); return color_state; } static MetaWaylandImageDescription * meta_wayland_image_description_new (MetaWaylandColorManager *color_manager, struct wl_resource *resource) { MetaWaylandImageDescription *image_desc; image_desc = g_new0 (MetaWaylandImageDescription, 1); image_desc->color_manager = color_manager; image_desc->resource = resource; return image_desc; } static MetaWaylandImageDescription * meta_wayland_image_description_new_failed (MetaWaylandColorManager *color_manager, struct wl_resource *resource, enum xx_image_description_v4_cause cause, const char *message) { MetaWaylandImageDescription *image_desc; image_desc = meta_wayland_image_description_new (color_manager, resource); image_desc->state = META_WAYLAND_IMAGE_DESCRIPTION_STATE_FAILED; image_desc->has_info = FALSE; xx_image_description_v4_send_failed (resource, cause, message); return image_desc; } static MetaWaylandImageDescription * meta_wayland_image_description_new_color_state (MetaWaylandColorManager *color_manager, struct wl_resource *resource, ClutterColorState *color_state, MetaWaylandImageDescriptionFlags flags) { MetaWaylandImageDescription *image_desc; image_desc = meta_wayland_image_description_new (color_manager, resource); image_desc->state = META_WAYLAND_IMAGE_DESCRIPTION_STATE_READY; image_desc->has_info = !!(flags & META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_ALLOW_INFO); image_desc->color_state = g_object_ref (color_state); xx_image_description_v4_send_ready (resource, clutter_color_state_get_id (color_state)); return image_desc; } static void meta_wayland_image_description_free (MetaWaylandImageDescription *image_desc) { g_clear_object (&image_desc->color_state); free (image_desc); } static void image_description_destructor (struct wl_resource *resource) { MetaWaylandImageDescription *image_desc = wl_resource_get_user_data (resource); meta_wayland_image_description_free (image_desc); } static void image_description_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void send_information (struct wl_resource *info_resource, ClutterColorState *color_state) { ClutterColorspace clutter_colorspace; enum xx_color_manager_v4_primaries primaries; ClutterTransferFunction clutter_tf; enum xx_color_manager_v4_transfer_function tf; float min_lum, max_lum, ref_lum; clutter_colorspace = clutter_color_state_get_colorspace (color_state); primaries = clutter_primaries_to_wayland (clutter_colorspace); xx_image_description_info_v4_send_primaries_named (info_resource, primaries); clutter_tf = clutter_color_state_get_transfer_function (color_state); tf = clutter_tf_to_wayland (clutter_tf); xx_image_description_info_v4_send_tf_named (info_resource, tf); clutter_color_state_get_luminances (color_state, &min_lum, &max_lum, &ref_lum); xx_image_description_info_v4_send_luminances (info_resource, float_to_scaled_uint32 (min_lum), (uint32_t) max_lum, (uint32_t) ref_lum); } static void image_description_get_information (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandImageDescription *image_desc = wl_resource_get_user_data (resource); struct wl_resource *info_resource; if (image_desc->state != META_WAYLAND_IMAGE_DESCRIPTION_STATE_READY) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_V4_ERROR_NOT_READY, "The image description is not ready"); return; } if (!image_desc->has_info) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_V4_ERROR_NO_INFORMATION, "The image description has no information"); return; } g_return_if_fail (image_desc->color_state); info_resource = wl_resource_create (client, &xx_image_description_info_v4_interface, wl_resource_get_version (resource), id); send_information (info_resource, image_desc->color_state); xx_image_description_info_v4_send_done (info_resource); wl_resource_destroy (info_resource); } static const struct xx_image_description_v4_interface meta_wayland_image_description_interface = { image_description_destroy, image_description_get_information, }; static void update_preferred_color_state (MetaWaylandColorManagementSurface *cm_surface) { MetaWaylandColorManager *color_manager = cm_surface->color_manager; MetaMonitorManager *monitor_manager = get_monitor_manager (color_manager); MetaWaylandSurface *surface = cm_surface->surface; MetaLogicalMonitor *logical_monitor; ClutterColorState *color_state = NULL; GList *l; gboolean initial = !cm_surface->preferred_color_state; g_return_if_fail (surface != NULL); logical_monitor = meta_wayland_surface_get_main_monitor (surface); if (!logical_monitor) { logical_monitor = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); } if (logical_monitor) { GList *monitors = meta_logical_monitor_get_monitors (logical_monitor); g_return_if_fail (monitors != NULL); color_state = get_output_color_state (color_manager, monitors->data); } if (!color_state) color_state = get_default_color_state (color_manager); if (cm_surface->preferred_color_state && clutter_color_state_equals (color_state, cm_surface->preferred_color_state)) return; g_set_object (&cm_surface->preferred_color_state, color_state); if (initial) return; for (l = cm_surface->feedback_resources; l; l = l->next) { struct wl_resource *resource = l->data; xx_color_management_feedback_surface_v4_send_preferred_changed (resource); } } static void on_surface_destroy (MetaWaylandSurface *surface, gpointer user_data) { MetaWaylandColorManagementSurface *cm_surface = user_data; meta_wayland_color_management_surface_free (cm_surface); } static void on_main_monitor_changed (MetaWaylandSurface *surface, GParamSpec *pspec, gpointer user_data) { MetaWaylandColorManagementSurface *cm_surface = user_data; update_preferred_color_state (cm_surface); } static MetaWaylandColorManagementSurface * meta_wayland_color_management_surface_new (MetaWaylandColorManager *color_manager, MetaWaylandSurface *surface) { MetaWaylandColorManagementSurface *cm_surface; cm_surface = g_new0 (MetaWaylandColorManagementSurface, 1); cm_surface->color_manager = color_manager; cm_surface->surface = surface; cm_surface->surface_destroyed_handler_id = g_signal_connect (surface, "destroy", G_CALLBACK (on_surface_destroy), cm_surface); cm_surface->surface_main_monitor_handler_id = g_signal_connect (surface, "notify::main-monitor", G_CALLBACK (on_main_monitor_changed), cm_surface); g_hash_table_insert (color_manager->surfaces, surface, cm_surface); return cm_surface; } static void meta_wayland_color_management_surface_free (MetaWaylandColorManagementSurface *cm_surface) { GList *l; for (l = cm_surface->feedback_resources; l; l = l->next) { struct wl_resource *resource = l->data; wl_resource_set_user_data (resource, NULL); } g_clear_list (&cm_surface->feedback_resources, NULL); if (cm_surface->resource) wl_resource_set_user_data (cm_surface->resource, NULL); g_clear_signal_handler (&cm_surface->surface_destroyed_handler_id, cm_surface->surface); g_clear_signal_handler (&cm_surface->surface_main_monitor_handler_id, cm_surface->surface); g_hash_table_remove (cm_surface->color_manager->surfaces, cm_surface->surface); free (cm_surface); } static MetaWaylandColorManagementSurface * ensure_color_management_surface (MetaWaylandColorManager *color_manager, MetaWaylandSurface *surface) { MetaWaylandColorManagementSurface *cm_surface; cm_surface = g_hash_table_lookup (color_manager->surfaces, surface); if (cm_surface) return cm_surface; return meta_wayland_color_management_surface_new (color_manager, surface); } static void set_image_description (MetaWaylandColorManagementSurface *cm_surface, ClutterColorState *color_state) { MetaWaylandColorManager *color_manager = cm_surface->color_manager; MetaWaylandSurface *surface = cm_surface->surface; MetaWaylandSurfaceState *pending = meta_wayland_surface_get_pending_state (surface); ClutterColorState *new_color_state; if (color_state) new_color_state = color_state; else new_color_state = get_default_color_state (color_manager); g_assert (new_color_state); pending->has_new_color_state = TRUE; g_set_object (&pending->color_state, new_color_state); } static void color_management_surface_destructor (struct wl_resource *resource) { MetaWaylandColorManagementSurface *cm_surface = wl_resource_get_user_data (resource); if (!cm_surface) return; set_image_description (cm_surface, NULL); cm_surface->resource = NULL; } static void color_management_surface_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void color_management_surface_set_image_description (struct wl_client *client, struct wl_resource *resource, struct wl_resource *image_desc_resource, uint32_t render_intent) { MetaWaylandColorManagementSurface *cm_surface = wl_resource_get_user_data (resource); MetaWaylandImageDescription *image_desc = wl_resource_get_user_data (image_desc_resource); if (!cm_surface) { /* FIXME: the next version will have an ERROR_INERT */ wl_resource_post_error (resource, XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Underlying surface object has been destroyed"); return; } if (!image_desc->color_state || image_desc->state != META_WAYLAND_IMAGE_DESCRIPTION_STATE_READY) { wl_resource_post_error (resource, XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Trying to set an image description which is not ready"); return; } switch (render_intent) { case XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL: break; default: wl_resource_post_error (resource, XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_RENDER_INTENT, "Trying to use an unsupported rendering intent"); return; } set_image_description (cm_surface, image_desc->color_state); } static void color_management_surface_unset_image_description (struct wl_client *client, struct wl_resource *resource) { MetaWaylandColorManagementSurface *cm_surface = wl_resource_get_user_data (resource); if (!cm_surface) { /* FIXME: the next version will have an ERROR_INERT */ wl_resource_post_error (resource, XX_COLOR_MANAGEMENT_SURFACE_V4_ERROR_IMAGE_DESCRIPTION, "Underlying surface object has been destroyed"); return; } set_image_description (cm_surface, NULL); } static const struct xx_color_management_surface_v4_interface meta_wayland_color_management_surface_interface = { color_management_surface_destroy, color_management_surface_set_image_description, color_management_surface_unset_image_description, }; static void on_output_destroyed (MetaWaylandOutput *wayland_output, gpointer user_data) { MetaWaylandColorManagementOutput *cm_output = user_data; meta_wayland_color_management_output_free (cm_output); } static MetaWaylandColorManagementOutput * meta_wayland_color_management_output_new (MetaWaylandColorManager *color_manager, MetaWaylandOutput *output) { MetaWaylandColorManagementOutput *cm_output; cm_output = g_new0 (MetaWaylandColorManagementOutput, 1); cm_output->color_manager = color_manager; cm_output->output = output; cm_output->output_destroyed_handler_id = g_signal_connect (output, "output-destroyed", G_CALLBACK (on_output_destroyed), cm_output); g_hash_table_insert (color_manager->outputs, meta_wayland_output_get_monitor (output), cm_output); return cm_output; } static void meta_wayland_color_management_output_free (MetaWaylandColorManagementOutput *cm_output) { GList *l; for (l = cm_output->resources; l; l = l->next) { struct wl_resource *resource = l->data; wl_resource_set_user_data (resource, NULL); } g_clear_list (&cm_output->resources, NULL); g_clear_signal_handler (&cm_output->output_destroyed_handler_id, cm_output->output); g_hash_table_remove (cm_output->color_manager->outputs, meta_wayland_output_get_monitor (cm_output->output)); free (cm_output); } static MetaWaylandColorManagementOutput * ensure_color_management_output (MetaWaylandColorManager *color_manager, MetaWaylandOutput *output) { MetaWaylandColorManagementOutput *cm_output; cm_output = g_hash_table_lookup (color_manager->outputs, meta_wayland_output_get_monitor (output)); if (cm_output) return cm_output; return meta_wayland_color_management_output_new (color_manager, output); } static void color_management_output_destructor (struct wl_resource *resource) { MetaWaylandColorManagementOutput *cm_output = wl_resource_get_user_data (resource); if (!cm_output) return; cm_output->resources = g_list_remove (cm_output->resources, resource); } static void color_management_output_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void color_management_output_get_image_description (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandColorManagementOutput *cm_output = wl_resource_get_user_data (resource); MetaWaylandCompositor *compositor = wl_client_get_user_data (client); MetaWaylandColorManager *color_manager = g_object_get_data (G_OBJECT (compositor), "-meta-wayland-color-manager"); struct wl_resource *image_desc_resource; MetaWaylandImageDescription *image_desc; image_desc_resource = wl_resource_create (client, &xx_image_description_v4_interface, wl_resource_get_version (resource), id); if (cm_output) { MetaMonitor *monitor = meta_wayland_output_get_monitor (cm_output->output); ClutterColorState *color_state = get_output_color_state (color_manager, monitor); image_desc = meta_wayland_image_description_new_color_state (color_manager, image_desc_resource, color_state, META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_DEFAULT | META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_ALLOW_INFO); } else { image_desc = meta_wayland_image_description_new_failed (color_manager, image_desc_resource, XX_IMAGE_DESCRIPTION_V4_CAUSE_NO_OUTPUT, "Underlying output object has been destroyed"); } wl_resource_set_implementation (image_desc_resource, &meta_wayland_image_description_interface, image_desc, image_description_destructor); } static const struct xx_color_management_output_v4_interface meta_wayland_color_management_output_interface = { color_management_output_destroy, color_management_output_get_image_description, }; static MetaWaylandCreatorParams * meta_wayland_creator_params_new (MetaWaylandColorManager *color_manager, struct wl_resource *resource) { MetaWaylandCreatorParams *creator_params; creator_params = g_new0 (MetaWaylandCreatorParams, 1); creator_params->color_manager = color_manager; creator_params->resource = resource; creator_params->colorspace = CLUTTER_COLORSPACE_DEFAULT; creator_params->transfer_function = CLUTTER_TRANSFER_FUNCTION_DEFAULT; creator_params->min_lum = -1.0f; creator_params->max_lum = -1.0f; creator_params->ref_lum = -1.0f; return creator_params; } static void meta_wayland_creator_params_free (MetaWaylandCreatorParams *creator_params) { free (creator_params); } static void creator_params_destructor (struct wl_resource *resource) { MetaWaylandCreatorParams *creator_params = wl_resource_get_user_data (resource); meta_wayland_creator_params_free (creator_params); } static void creator_params_create (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandCreatorParams *creator_params = wl_resource_get_user_data (resource); MetaWaylandColorManager *color_manager = creator_params->color_manager; ClutterContext *clutter_context = get_clutter_context (color_manager); struct wl_resource *image_desc_resource; g_autoptr (ClutterColorState) color_state = NULL; MetaWaylandImageDescription *image_desc; if (creator_params->colorspace == CLUTTER_COLORSPACE_DEFAULT || creator_params->transfer_function == CLUTTER_TRANSFER_FUNCTION_DEFAULT) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INCOMPLETE_SET, "Not all required parameters were set"); return; } image_desc_resource = wl_resource_create (client, &xx_image_description_v4_interface, wl_resource_get_version (resource), id); color_state = clutter_color_state_new_full (clutter_context, creator_params->colorspace, creator_params->transfer_function, creator_params->min_lum, creator_params->max_lum, creator_params->ref_lum); image_desc = meta_wayland_image_description_new_color_state (color_manager, image_desc_resource, color_state, META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_DEFAULT); wl_resource_set_implementation (image_desc_resource, &meta_wayland_image_description_interface, image_desc, image_description_destructor); wl_resource_destroy (resource); } static void creator_params_set_tf_named (struct wl_client *client, struct wl_resource *resource, uint32_t tf) { MetaWaylandCreatorParams *creator_params = wl_resource_get_user_data (resource); ClutterTransferFunction clutter_tf; if (creator_params->transfer_function != CLUTTER_TRANSFER_FUNCTION_DEFAULT) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "The transfer characteristics were already set"); return; } if (!wayland_tf_to_clutter (tf, &clutter_tf)) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "The named transfer function is not supported"); return; } creator_params->transfer_function = clutter_tf; } static void creator_params_set_tf_power (struct wl_client *client, struct wl_resource *resource, uint32_t eexp) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_TF, "Setting power based transfer characteristics is not supported"); } static void creator_params_set_primaries_named (struct wl_client *client, struct wl_resource *resource, uint32_t primaries) { MetaWaylandCreatorParams *creator_params = wl_resource_get_user_data (resource); ClutterColorspace colorspace; if (creator_params->colorspace != CLUTTER_COLORSPACE_DEFAULT) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "The primaries were already set"); return; } if (!wayland_primaries_to_clutter (primaries, &colorspace)) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "The named primaries are not supported"); return; } creator_params->colorspace = colorspace; } static void creator_params_set_primaries (struct wl_client *client, struct wl_resource *resource, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_PRIMARIES, "Setting arbitrary primaries is not supported"); } static void creator_params_set_luminance (struct wl_client *client, struct wl_resource *resource, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) { MetaWaylandCreatorParams *creator_params = wl_resource_get_user_data (resource); float min, max, ref; if (creator_params->min_lum >= 0.0f || creator_params->max_lum >= 0.0f || creator_params->ref_lum >= 0.0f) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_ALREADY_SET, "The luminance was already set"); return; } min = scaled_uint32_to_float (min_lum); max = (float) max_lum; ref = (float) reference_lum; if (max < ref) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "The maximum luminance is smaller than the reference luminance"); return; } if (ref <= min) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_LUMINANCE, "The reference luminance is less or equal to the minimum luminance"); return; } creator_params->min_lum = min; creator_params->max_lum = max; creator_params->ref_lum = ref; } static void creator_params_set_mastering_display_primaries (struct wl_client *client, struct wl_resource *resource, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_MASTERING, "Setting mastering display primaries is not supported"); } static void creator_params_set_mastering_luminance (struct wl_client *client, struct wl_resource *resource, uint32_t min_lum, uint32_t max_lum) { wl_resource_post_error (resource, XX_IMAGE_DESCRIPTION_CREATOR_PARAMS_V4_ERROR_INVALID_MASTERING, "Setting mastering display luminances is not supported"); } static void creator_params_set_max_cll (struct wl_client *client, struct wl_resource *resource, uint32_t max_cll) { /* ignoring for now */ /* FIXME: technically we must send errors in some cases */ } static void creator_params_set_max_fall (struct wl_client *client, struct wl_resource *resource, uint32_t max_fall) { /* ignoring for now */ /* FIXME: technically we must send errors in some cases */ } static const struct xx_image_description_creator_params_v4_interface meta_wayland_image_description_creator_params_interface = { creator_params_create, creator_params_set_tf_named, creator_params_set_tf_power, creator_params_set_primaries_named, creator_params_set_primaries, creator_params_set_luminance, creator_params_set_mastering_display_primaries, creator_params_set_mastering_luminance, creator_params_set_max_cll, creator_params_set_max_fall, }; static void color_manager_destructor (struct wl_resource *resource) { MetaWaylandColorManager *color_manager = wl_resource_get_user_data (resource); color_manager->resources = g_list_remove (color_manager->resources, resource); } static void color_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void color_manager_get_output (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output_resource) { MetaWaylandColorManager *color_manager = wl_resource_get_user_data (resource); MetaWaylandOutput *output = wl_resource_get_user_data (output_resource); MetaWaylandColorManagementOutput *cm_output = NULL; struct wl_resource *cm_output_resource; cm_output_resource = wl_resource_create (client, &xx_color_management_output_v4_interface, wl_resource_get_version (resource), id); if (output) { cm_output = ensure_color_management_output (color_manager, output); cm_output->resources = g_list_prepend (cm_output->resources, cm_output_resource); } wl_resource_set_implementation (cm_output_resource, &meta_wayland_color_management_output_interface, cm_output, color_management_output_destructor); } static void color_manager_get_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { MetaWaylandColorManager *color_manager = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandColorManagementSurface *cm_surface; cm_surface = ensure_color_management_surface (color_manager, surface); if (cm_surface->resource) { wl_resource_post_error (resource, XX_COLOR_MANAGER_V4_ERROR_SURFACE_EXISTS, "surface already requested"); return; } cm_surface->resource = wl_resource_create (client, &xx_color_management_surface_v4_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (cm_surface->resource, &meta_wayland_color_management_surface_interface, cm_surface, color_management_surface_destructor); } static void color_management_feedback_surface_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void color_management_feedback_surface_get_preferred (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandColorManagementSurface *cm_surface = wl_resource_get_user_data (resource); MetaWaylandColorManager *color_manager = cm_surface->color_manager; MetaWaylandSurface *surface = cm_surface->surface; struct wl_resource *image_desc_resource; MetaWaylandImageDescription *image_desc; if (!surface) { wl_resource_post_error (resource, XX_COLOR_MANAGEMENT_FEEDBACK_SURFACE_V4_ERROR_INERT, "Underlying surface object has been destroyed"); return; } image_desc_resource = wl_resource_create (client, &xx_image_description_v4_interface, wl_resource_get_version (resource), id); image_desc = meta_wayland_image_description_new_color_state (color_manager, image_desc_resource, cm_surface->preferred_color_state, META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_DEFAULT | META_WAYLAND_IMAGE_DESCRIPTION_FLAGS_ALLOW_INFO); wl_resource_set_implementation (image_desc_resource, &meta_wayland_image_description_interface, image_desc, image_description_destructor); } static const struct xx_color_management_feedback_surface_v4_interface meta_wayland_color_management_feedback_surface_interface = { color_management_feedback_surface_destroy, color_management_feedback_surface_get_preferred, }; static void color_management_feedback_surface_destructor (struct wl_resource *resource) { MetaWaylandColorManagementSurface *cm_surface = wl_resource_get_user_data (resource); if (!cm_surface) return; cm_surface->feedback_resources = g_list_remove (cm_surface->feedback_resources, resource); } static void color_manager_get_feedback_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { MetaWaylandColorManager *color_manager = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandColorManagementSurface *cm_surface; struct wl_resource *cm_feedback_surface_resource; cm_surface = ensure_color_management_surface (color_manager, surface); cm_feedback_surface_resource = wl_resource_create (client, &xx_color_management_feedback_surface_v4_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (cm_feedback_surface_resource, &meta_wayland_color_management_feedback_surface_interface, cm_surface, color_management_feedback_surface_destructor); cm_surface->feedback_resources = g_list_prepend (cm_surface->feedback_resources, cm_feedback_surface_resource); update_preferred_color_state (cm_surface); } static void color_manager_new_icc_creator (struct wl_client *client, struct wl_resource *resource, uint32_t id) { wl_resource_post_error (resource, XX_COLOR_MANAGER_V4_ERROR_UNSUPPORTED_FEATURE, "ICC-based image description creator is unsupported"); } static void color_manager_new_parametric_creator (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandColorManager *color_manager = wl_resource_get_user_data (resource); MetaWaylandCreatorParams *creator_params; struct wl_resource *creator_resource; creator_resource = wl_resource_create (client, &xx_image_description_creator_params_v4_interface, wl_resource_get_version (resource), id); creator_params = meta_wayland_creator_params_new (color_manager, creator_resource); wl_resource_set_implementation (creator_resource, &meta_wayland_image_description_creator_params_interface, creator_params, creator_params_destructor); } static void color_manager_send_supported_events (struct wl_resource *resource) { xx_color_manager_v4_send_supported_intent (resource, XX_COLOR_MANAGER_V4_RENDER_INTENT_PERCEPTUAL); xx_color_manager_v4_send_supported_feature (resource, XX_COLOR_MANAGER_V4_FEATURE_PARAMETRIC); xx_color_manager_v4_send_supported_feature (resource, XX_COLOR_MANAGER_V4_FEATURE_SET_LUMINANCES); xx_color_manager_v4_send_supported_tf_named (resource, XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB); xx_color_manager_v4_send_supported_tf_named (resource, XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ); xx_color_manager_v4_send_supported_primaries_named (resource, XX_COLOR_MANAGER_V4_PRIMARIES_SRGB); xx_color_manager_v4_send_supported_primaries_named (resource, XX_COLOR_MANAGER_V4_PRIMARIES_BT2020); } static const struct xx_color_manager_v4_interface meta_wayland_color_manager_interface = { color_manager_destroy, color_manager_get_output, color_manager_get_surface, color_manager_get_feedback_surface, color_manager_new_icc_creator, color_manager_new_parametric_creator, }; static void color_management_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandColorManager *color_manager = data; struct wl_resource *resource; resource = wl_resource_create (client, &xx_color_manager_v4_interface, version, id); wl_resource_set_implementation (resource, &meta_wayland_color_manager_interface, color_manager, color_manager_destructor); color_manager->resources = g_list_prepend (color_manager->resources, resource); color_manager_send_supported_events (resource); } static void update_output_color_state (MetaWaylandColorManager *color_manager, MetaMonitor *monitor) { MetaWaylandColorManagementOutput *cm_output; GHashTableIter iter_surfaces; MetaWaylandColorManagementSurface *cm_surface; MetaWaylandOutput *wayland_output; cm_output = g_hash_table_lookup (color_manager->outputs, monitor); if (cm_output) { GList *l; for (l = cm_output->resources; l; l = l->next) { struct wl_resource *resource = l->data; xx_color_management_output_v4_send_image_description_changed (resource); } } wayland_output = g_hash_table_lookup (color_manager->compositor->outputs, meta_monitor_get_spec (monitor)); g_hash_table_iter_init (&iter_surfaces, color_manager->surfaces); while (g_hash_table_iter_next (&iter_surfaces, NULL, (gpointer *)&cm_surface)) { MetaWaylandSurface *surface = cm_surface->surface; if (g_hash_table_contains (surface->outputs, wayland_output)) update_preferred_color_state (cm_surface); } } static void on_device_color_state_changed (MetaColorManager *meta_color_manager, MetaColorDevice *color_device, MetaWaylandColorManager *color_manager) { MetaMonitor *monitor = meta_color_device_get_monitor (color_device); update_output_color_state (color_manager, monitor); } static void meta_wayland_color_manager_dispose (GObject *object) { MetaWaylandColorManager *color_manager = META_WAYLAND_COLOR_MANAGER (object); MetaColorManager *meta_color_manager = get_meta_color_manager (color_manager); g_clear_signal_handler (&color_manager->color_state_changed_handler_id, meta_color_manager); g_clear_pointer (&color_manager->outputs, g_hash_table_destroy); g_clear_pointer (&color_manager->surfaces, g_hash_table_destroy); } static void meta_wayland_color_manager_init (MetaWaylandColorManager *color_manager) { color_manager->outputs = g_hash_table_new (NULL, NULL); color_manager->surfaces = g_hash_table_new (NULL, NULL); } static void meta_wayland_color_manager_class_init (MetaWaylandColorManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_wayland_color_manager_dispose; } static MetaWaylandColorManager * meta_wayland_color_manager_new (MetaWaylandCompositor *compositor) { MetaWaylandColorManager *color_manager; MetaColorManager *meta_color_manager; color_manager = g_object_new (META_TYPE_WAYLAND_COLOR_MANAGER, NULL); color_manager->compositor = compositor; meta_color_manager = get_meta_color_manager (color_manager); color_manager->color_state_changed_handler_id = g_signal_connect_object (meta_color_manager, "device-color-state-changed", G_CALLBACK (on_device_color_state_changed), color_manager, 0); return color_manager; } static void update_enabled (MetaWaylandColorManager *color_manager) { MetaWaylandCompositor *compositor = color_manager->compositor; MetaDebugControl *debug_control = meta_context_get_debug_control (compositor->context); gboolean is_enabled = meta_debug_control_is_color_management_protocol_enabled (debug_control); if (is_enabled && color_manager->global == NULL) { color_manager->global = wl_global_create (compositor->wayland_display, &xx_color_manager_v4_interface, META_XX_COLOR_MANAGEMENT_VERSION, color_manager, color_management_bind); if (color_manager->global == NULL) g_error ("Failed to register a global wp_color_management object"); } else if (!is_enabled) { g_clear_pointer (&color_manager->global, wl_global_destroy); } } void meta_wayland_init_color_management (MetaWaylandCompositor *compositor) { MetaDebugControl *debug_control = meta_context_get_debug_control (compositor->context); g_autoptr (MetaWaylandColorManager) color_manager = NULL; color_manager = meta_wayland_color_manager_new (compositor); g_signal_connect_data (debug_control, "notify::color-management-protocol", G_CALLBACK (update_enabled), color_manager, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); update_enabled (color_manager); g_object_set_data_full (G_OBJECT (compositor), "-meta-wayland-color-manager", g_steal_pointer (&color_manager), g_object_unref); }