mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2025-12-20 04:40:07 +01:00
It's much more common to have bits defined as enums than having a position, so let's just directly take a bit as argument to the bit asserts. For the few cases where only the position is available, it's easy to get the right bit using a shift. The is_pow2_64() helper functions have been added in order to validate the bit passed as argument and prevent programming errors. Signed-off-by: Loïc Molinari <loic.molinari@collabora.com>
1754 lines
56 KiB
C
1754 lines
56 KiB
C
/*
|
|
* Copyright 2023 Collabora, Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "color.h"
|
|
#include "color-management.h"
|
|
#include <libweston/libweston.h>
|
|
#include "shared/string-helpers.h"
|
|
#include "shared/weston-assert.h"
|
|
#include "shared/xalloc.h"
|
|
#include "shared/helpers.h"
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include "color-management-v1-server-protocol.h"
|
|
|
|
enum supports_get_info {
|
|
NO_GET_INFO = false,
|
|
YES_GET_INFO = true,
|
|
};
|
|
|
|
/**
|
|
* Backs the image description abstraction from the protocol. We may have
|
|
* multiple images descriptions for the same color profile.
|
|
*
|
|
* Image description that we failed to create do not have such backing object.
|
|
*/
|
|
struct cm_image_desc {
|
|
struct wl_resource *owner;
|
|
struct weston_color_manager *cm;
|
|
|
|
/* Reference to the color profile that it is backing up. An image
|
|
* description without a cprof is valid, and that simply means that it
|
|
* isn't ready (i.e. we didn't send the 'ready' event because we are
|
|
* still in the process of creating the color profile). */
|
|
struct weston_color_profile *cprof;
|
|
|
|
/* Depending how the image description is created, the protocol states
|
|
* that get_information() request should be invalid. */
|
|
bool supports_get_info;
|
|
};
|
|
|
|
/**
|
|
* Object created when get_info() is called for an image description object. It
|
|
* gets destroyed when all the info is sent, i.e. with the done() event.
|
|
*/
|
|
struct cm_image_desc_info {
|
|
struct wl_resource *owner;
|
|
struct weston_compositor *compositor;
|
|
};
|
|
|
|
/**
|
|
* Backs protocol objects that are used to create ICC-based image descriptions.
|
|
*/
|
|
struct cm_creator_icc {
|
|
struct wl_resource *owner;
|
|
|
|
struct weston_compositor *compositor;
|
|
|
|
/* ICC profile data given by the client. */
|
|
int32_t icc_profile_fd;
|
|
size_t icc_data_length;
|
|
size_t icc_data_offset;
|
|
};
|
|
|
|
/**
|
|
* Backs protocol objects that are used to create parametric image descriptions.
|
|
*/
|
|
struct cm_creator_params {
|
|
struct wl_resource *owner;
|
|
struct weston_compositor *compositor;
|
|
|
|
/* This accumulates the parameters given by the clients. */
|
|
struct weston_color_profile_param_builder *builder;
|
|
};
|
|
|
|
/**
|
|
* For an ICC-based image description, sends the ICC information to the
|
|
* client.
|
|
*
|
|
* If callers fail to create the fd for the ICC, they can call this function
|
|
* with fd == -1 and it should return the proper error to clients.
|
|
*
|
|
* This is a helper function that should be used by the color plugin
|
|
* that owns the color profile and has information about it.
|
|
*
|
|
* \param cm_image_desc_info The image description info object
|
|
* \param fd The ICC profile file descriptor, or -1 in case of a failure
|
|
* \param len The ICC profile size, in bytes
|
|
*/
|
|
WL_EXPORT void
|
|
weston_cm_send_icc_file(struct cm_image_desc_info *cm_image_desc_info,
|
|
int32_t fd, uint32_t len)
|
|
{
|
|
/* Caller failed to create fd. At this point we already know that the
|
|
* ICC is valid, so let's disconnect the client with OOM. */
|
|
if (fd < 0) {
|
|
wl_resource_post_no_memory(cm_image_desc_info->owner);
|
|
return;
|
|
}
|
|
|
|
wp_image_description_info_v1_send_icc_file(cm_image_desc_info->owner,
|
|
fd, len);
|
|
}
|
|
|
|
/**
|
|
* For a parametric image description, sends its
|
|
* enum wp_color_manager_v1_primaries code to the client.
|
|
*
|
|
* This is a helper function that should be used by the color plugin
|
|
* that owns the color profile and has information about it.
|
|
*
|
|
* \param cm_image_desc_info The image description info object
|
|
* \param primaries_info The primaries_info object
|
|
*/
|
|
WL_EXPORT void
|
|
weston_cm_send_primaries_named(struct cm_image_desc_info *cm_image_desc_info,
|
|
const struct weston_color_primaries_info *primaries_info)
|
|
{
|
|
wp_image_description_info_v1_send_primaries_named(cm_image_desc_info->owner,
|
|
primaries_info->protocol_primaries);
|
|
}
|
|
|
|
/**
|
|
* For a parametric image description, sends the primary color volume primaries
|
|
* and white point using CIE 1931 xy chromaticity coordinates to the client.
|
|
*
|
|
* This is a helper function that should be used by the color plugin
|
|
* that owns the color profile and has information about it.
|
|
*
|
|
* \param cm_image_desc_info The image description info object
|
|
* \param color_gamut The CIE 1931 xy chromaticity coordinates
|
|
*/
|
|
WL_EXPORT void
|
|
weston_cm_send_primaries(struct cm_image_desc_info *cm_image_desc_info,
|
|
const struct weston_color_gamut *color_gamut)
|
|
{
|
|
wp_image_description_info_v1_send_primaries(cm_image_desc_info->owner,
|
|
/* red */
|
|
round(color_gamut->primary[0].x * 1000000),
|
|
round(color_gamut->primary[0].y * 1000000),
|
|
/* green */
|
|
round(color_gamut->primary[1].x * 1000000),
|
|
round(color_gamut->primary[1].y * 1000000),
|
|
/* blue */
|
|
round(color_gamut->primary[2].x * 1000000),
|
|
round(color_gamut->primary[2].y * 1000000),
|
|
/* white point */
|
|
round(color_gamut->white_point.x * 1000000),
|
|
round(color_gamut->white_point.y * 1000000));
|
|
}
|
|
|
|
/**
|
|
* For a parametric image description, sends the target color volume primaries
|
|
* and white point using CIE 1931 xy chromaticity coordinates to the client.
|
|
*
|
|
* This is a helper function that should be used by the color plugin
|
|
* that owns the color profile and has information about it.
|
|
*
|
|
* \param cm_image_desc_info The image description info object
|
|
* \param color_gamut The CIE 1931 xy chromaticity coordinates
|
|
*/
|
|
WL_EXPORT void
|
|
weston_cm_send_target_primaries(struct cm_image_desc_info *cm_image_desc_info,
|
|
const struct weston_color_gamut *color_gamut)
|
|
{
|
|
wp_image_description_info_v1_send_target_primaries(cm_image_desc_info->owner,
|
|
/* red */
|
|
round(color_gamut->primary[0].x * 1000000),
|
|
round(color_gamut->primary[0].y * 1000000),
|
|
/* green */
|
|
round(color_gamut->primary[1].x * 1000000),
|
|
round(color_gamut->primary[1].y * 1000000),
|
|
/* blue */
|
|
round(color_gamut->primary[2].x * 1000000),
|
|
round(color_gamut->primary[2].y * 1000000),
|
|
/* white point */
|
|
round(color_gamut->white_point.x * 1000000),
|
|
round(color_gamut->white_point.y * 1000000));
|
|
}
|
|
|
|
/**
|
|
* For a parametric image description, sends its
|
|
* enum wp_color_manager_v1_transfer_function code to the client.
|
|
*
|
|
* This is a helper function that should be used by the color plugin
|
|
* that owns the color profile and has information about it.
|
|
*
|
|
* \param cm_image_desc_info The image description info object
|
|
* \param tf_info The tf_info object
|
|
*/
|
|
WL_EXPORT void
|
|
weston_cm_send_tf_named(struct cm_image_desc_info *cm_image_desc_info,
|
|
const struct weston_color_tf_info *tf_info)
|
|
{
|
|
wp_image_description_info_v1_send_tf_named(cm_image_desc_info->owner,
|
|
tf_info->protocol_tf);
|
|
}
|
|
|
|
/**
|
|
* For a parametric image description, sends the primary luminances
|
|
* to the client.
|
|
*
|
|
* This is a helper function that should be used by the color plugin
|
|
* that owns the color profile and has information about it.
|
|
*
|
|
* \param cm_image_desc_info The image description info object
|
|
* \param min_lum The minimum luminance (cd/m²)
|
|
* \param max_lum The maximum luminance (cd/m²)
|
|
* \param ref_lum The reference white luminance (cd/m²)
|
|
*/
|
|
WL_EXPORT void
|
|
weston_cm_send_luminances(struct cm_image_desc_info *cm_image_desc_info,
|
|
float min_lum, float max_lum, float ref_lum)
|
|
{
|
|
wp_image_description_info_v1_send_luminances(cm_image_desc_info->owner,
|
|
min_lum * 10000,
|
|
max_lum, ref_lum);
|
|
}
|
|
|
|
/**
|
|
* For a parametric image description, sends the target luminances
|
|
* to the client.
|
|
*
|
|
* This is a helper function that should be used by the color plugin
|
|
* that owns the color profile and has information about it.
|
|
*
|
|
* \param cm_image_desc_info The image description info object
|
|
* \param min_lum The minimum target luminance (cd/m²)
|
|
* \param max_lum The maximum target luminance (cd/m²)
|
|
*/
|
|
WL_EXPORT void
|
|
weston_cm_send_target_luminances(struct cm_image_desc_info *cm_image_desc_info,
|
|
float min_lum, float max_lum)
|
|
{
|
|
wp_image_description_info_v1_send_target_luminance(cm_image_desc_info->owner,
|
|
min_lum * 10000,
|
|
max_lum);
|
|
}
|
|
|
|
/**
|
|
* Destroy an image description info object.
|
|
*/
|
|
static void
|
|
cm_image_desc_info_destroy(struct cm_image_desc_info *cm_image_desc_info)
|
|
{
|
|
free(cm_image_desc_info);
|
|
}
|
|
|
|
/**
|
|
* Resource destruction function for the image description info. Destroys the
|
|
* image description info backing object.
|
|
*/
|
|
static void
|
|
image_description_info_resource_destroy(struct wl_resource *cm_image_desc_info_res)
|
|
{
|
|
struct cm_image_desc_info *cm_image_desc_info =
|
|
wl_resource_get_user_data(cm_image_desc_info_res);
|
|
|
|
cm_image_desc_info_destroy(cm_image_desc_info);
|
|
}
|
|
|
|
/**
|
|
* Creates object to send information of a certain image description.
|
|
*/
|
|
static struct cm_image_desc_info *
|
|
image_description_info_create(struct wl_client *client, uint32_t version,
|
|
struct weston_compositor *compositor,
|
|
uint32_t cm_image_desc_info_id)
|
|
{
|
|
struct cm_image_desc_info *cm_image_desc_info;
|
|
|
|
cm_image_desc_info = xzalloc(sizeof(*cm_image_desc_info));
|
|
|
|
cm_image_desc_info->compositor = compositor;
|
|
|
|
cm_image_desc_info->owner =
|
|
wl_resource_create(client, &wp_image_description_info_v1_interface,
|
|
version, cm_image_desc_info_id);
|
|
if (!cm_image_desc_info->owner) {
|
|
free(cm_image_desc_info);
|
|
return NULL;
|
|
}
|
|
|
|
wl_resource_set_implementation(cm_image_desc_info->owner,
|
|
NULL, cm_image_desc_info,
|
|
image_description_info_resource_destroy);
|
|
|
|
return cm_image_desc_info;
|
|
}
|
|
|
|
/**
|
|
* Client wants the image description information.
|
|
*/
|
|
static void
|
|
image_description_get_information(struct wl_client *client,
|
|
struct wl_resource *cm_image_desc_res,
|
|
uint32_t cm_image_desc_info_id)
|
|
{
|
|
struct cm_image_desc *cm_image_desc =
|
|
wl_resource_get_user_data(cm_image_desc_res);
|
|
uint32_t version = wl_resource_get_version(cm_image_desc_res);
|
|
struct cm_image_desc_info *cm_image_desc_info;
|
|
bool success;
|
|
|
|
if (!cm_image_desc) {
|
|
wl_resource_post_error(cm_image_desc_res,
|
|
WP_IMAGE_DESCRIPTION_V1_ERROR_NOT_READY,
|
|
"we gracefully failed to create this image " \
|
|
"description");
|
|
return;
|
|
}
|
|
|
|
if (!cm_image_desc->cprof) {
|
|
wl_resource_post_error(cm_image_desc_res,
|
|
WP_IMAGE_DESCRIPTION_V1_ERROR_NOT_READY,
|
|
"image description not ready yet");
|
|
return;
|
|
}
|
|
|
|
if (!cm_image_desc->supports_get_info) {
|
|
wl_resource_post_error(cm_image_desc_res,
|
|
WP_IMAGE_DESCRIPTION_V1_ERROR_NO_INFORMATION,
|
|
"get_information is not allowed for this "
|
|
"image description");
|
|
return;
|
|
}
|
|
|
|
cm_image_desc_info =
|
|
image_description_info_create(client, version,
|
|
cm_image_desc->cm->compositor,
|
|
cm_image_desc_info_id);
|
|
if (!cm_image_desc_info) {
|
|
wl_resource_post_no_memory(cm_image_desc_res);
|
|
return;
|
|
}
|
|
|
|
/* The color plugin is the one that has information about the color
|
|
* profile, so we go through it to send the info to clients. */
|
|
success = cm_image_desc->cm->send_image_desc_info(cm_image_desc_info,
|
|
cm_image_desc->cprof);
|
|
if (success)
|
|
wp_image_description_info_v1_send_done(cm_image_desc_info->owner);
|
|
|
|
/* All info sent, so destroy the object. */
|
|
wl_resource_destroy(cm_image_desc_info->owner);
|
|
}
|
|
|
|
/**
|
|
* Client will not use the image description anymore, so we destroy its
|
|
* resource.
|
|
*/
|
|
static void
|
|
image_description_destroy(struct wl_client *client,
|
|
struct wl_resource *cm_image_desc_res)
|
|
{
|
|
wl_resource_destroy(cm_image_desc_res);
|
|
}
|
|
|
|
static void
|
|
cm_image_desc_destroy(struct cm_image_desc *cm_image_desc);
|
|
|
|
/**
|
|
* Resource destruction function for the image description. Destroys the image
|
|
* description backing object.
|
|
*/
|
|
static void
|
|
image_description_resource_destroy(struct wl_resource *cm_image_desc_res)
|
|
{
|
|
struct cm_image_desc *cm_image_desc =
|
|
wl_resource_get_user_data(cm_image_desc_res);
|
|
|
|
/* Image description that we failed to create do not have a backing
|
|
* struct cm_image_desc object. */
|
|
if (!cm_image_desc)
|
|
return;
|
|
|
|
cm_image_desc_destroy(cm_image_desc);
|
|
}
|
|
|
|
static const struct wp_image_description_v1_interface
|
|
image_description_implementation = {
|
|
.destroy = image_description_destroy,
|
|
.get_information = image_description_get_information,
|
|
};
|
|
|
|
/**
|
|
* Creates an image description object for a certain color profile.
|
|
*/
|
|
static struct cm_image_desc *
|
|
cm_image_desc_create(struct weston_color_manager *cm,
|
|
struct weston_color_profile *cprof,
|
|
struct wl_client *client, uint32_t version,
|
|
uint32_t image_description_id,
|
|
enum supports_get_info supports_get_info)
|
|
{
|
|
struct cm_image_desc *cm_image_desc;
|
|
|
|
cm_image_desc = xzalloc(sizeof(*cm_image_desc));
|
|
|
|
cm_image_desc->owner =
|
|
wl_resource_create(client, &wp_image_description_v1_interface,
|
|
version, image_description_id);
|
|
if (!cm_image_desc->owner) {
|
|
free(cm_image_desc);
|
|
return NULL;
|
|
}
|
|
|
|
wl_resource_set_implementation(cm_image_desc->owner,
|
|
&image_description_implementation,
|
|
cm_image_desc,
|
|
image_description_resource_destroy);
|
|
|
|
cm_image_desc->cm = cm;
|
|
cm_image_desc->cprof = weston_color_profile_ref(cprof);
|
|
cm_image_desc->supports_get_info = supports_get_info;
|
|
|
|
return cm_image_desc;
|
|
}
|
|
|
|
/**
|
|
* Destroy an image description object.
|
|
*/
|
|
static void
|
|
cm_image_desc_destroy(struct cm_image_desc *cm_image_desc)
|
|
{
|
|
weston_color_profile_unref(cm_image_desc->cprof);
|
|
free(cm_image_desc);
|
|
}
|
|
|
|
/**
|
|
* Called by clients when they want to get the output's image description.
|
|
*/
|
|
static void
|
|
cm_output_get_image_description(struct wl_client *client,
|
|
struct wl_resource *cm_output_res,
|
|
uint32_t protocol_object_id)
|
|
{
|
|
struct weston_head *head = wl_resource_get_user_data(cm_output_res);
|
|
struct weston_compositor *compositor;
|
|
struct weston_output *output;
|
|
uint32_t version = wl_resource_get_version(cm_output_res);
|
|
struct cm_image_desc *cm_image_desc;
|
|
struct wl_resource *cm_image_desc_res;
|
|
|
|
/* The protocol states that if the wl_output global (which is backed by
|
|
* the weston_head object) no longer exists, we should immediately send
|
|
* a "failed" event for the image desc. After receiving that, clients
|
|
* are not allowed to make requests other than "destroy" for the image
|
|
* description. For such image descriptions that we failed to create, we
|
|
* do not create a backing cm_image_desc (and other functions can tell
|
|
* that they are invalid through that). */
|
|
if (!head) {
|
|
cm_image_desc_res =
|
|
wl_resource_create(client, &wp_image_description_v1_interface,
|
|
version, protocol_object_id);
|
|
if (!cm_image_desc_res) {
|
|
wl_resource_post_no_memory(cm_output_res);
|
|
return;
|
|
}
|
|
|
|
wl_resource_set_implementation(cm_image_desc_res,
|
|
&image_description_implementation,
|
|
NULL, image_description_resource_destroy);
|
|
|
|
wp_image_description_v1_send_failed(cm_image_desc_res,
|
|
WP_IMAGE_DESCRIPTION_V1_CAUSE_NO_OUTPUT,
|
|
"the wl_output global no longer exists");
|
|
return;
|
|
}
|
|
|
|
compositor = head->compositor;
|
|
output = head->output;
|
|
|
|
/* If the head becomes inactive (head->output == NULL), the respective
|
|
* wl_output global gets destroyed. In such case we make the cm_output
|
|
* object inert. We do that in weston_head_remove_global(), and the
|
|
* cm_output_res user data (which was the head itself) is set to NULL.
|
|
* So if we reached here, head is active and head->output != NULL. */
|
|
weston_assert_ptr(compositor, output);
|
|
|
|
cm_image_desc = cm_image_desc_create(compositor->color_manager,
|
|
output->color_profile, client,
|
|
version, protocol_object_id,
|
|
YES_GET_INFO);
|
|
if (!cm_image_desc) {
|
|
wl_resource_post_no_memory(cm_output_res);
|
|
return;
|
|
}
|
|
|
|
wp_image_description_v1_send_ready(cm_image_desc->owner,
|
|
cm_image_desc->cprof->id);
|
|
}
|
|
|
|
/**
|
|
* Client will not use the cm_output anymore, so we destroy its resource.
|
|
*/
|
|
static void
|
|
cm_output_destroy(struct wl_client *client, struct wl_resource *cm_output_res)
|
|
{
|
|
wl_resource_destroy(cm_output_res);
|
|
}
|
|
|
|
/**
|
|
* Resource destruction function for the cm_output.
|
|
*/
|
|
static void
|
|
cm_output_resource_destroy(struct wl_resource *cm_output_res)
|
|
{
|
|
struct weston_head *head = wl_resource_get_user_data(cm_output_res);
|
|
|
|
/* For inert cm_output, we don't have to do anything.
|
|
*
|
|
* If the cm_get_output() was called after we made the head inactive, we
|
|
* created the cm_output with no resource user data and didn't add the
|
|
* resource link to weston_head::cm_output_resource_list.
|
|
*
|
|
* If the cm_output was created with an active head but it became
|
|
* inactive later, we have already done what was necessary when
|
|
* cm_output became inert, in weston_head_remove_global(). */
|
|
if (!head)
|
|
return;
|
|
|
|
/* We are destroying the cm_output_res, so simply remove it from
|
|
* weston_head::cm_output_resource_list. */
|
|
wl_list_remove(wl_resource_get_link(cm_output_res));
|
|
}
|
|
|
|
static const struct wp_color_management_output_v1_interface
|
|
cm_output_implementation = {
|
|
.destroy = cm_output_destroy,
|
|
.get_image_description = cm_output_get_image_description,
|
|
};
|
|
|
|
/**
|
|
* Should be called when the struct weston_output color profile is updated.
|
|
*
|
|
* For each weston_head attached to the weston_output, we need to tell clients
|
|
* that the cm_output image description has changed.
|
|
*
|
|
* If this is called during output initialization, this function is no-op. There
|
|
* will be no client resources in weston_head::cm_output_resource_list.
|
|
*
|
|
* \param output The weston_output that changed the color profile.
|
|
*/
|
|
void
|
|
weston_output_send_image_description_changed(struct weston_output *output)
|
|
{
|
|
struct weston_head *head;
|
|
struct wl_resource *res;
|
|
int ver;
|
|
|
|
/* Send the events for each head attached to this weston_output. */
|
|
wl_list_for_each(head, &output->head_list, output_link) {
|
|
wl_resource_for_each(res, &head->cm_output_resource_list)
|
|
wp_color_management_output_v1_send_image_description_changed(res);
|
|
|
|
/* wl_output.done should be sent after collecting all the
|
|
* changes related to the output. But in Weston we are lacking
|
|
* an atomic output configuration API, so we have no facilities
|
|
* to do that.
|
|
*
|
|
* TODO: enhance this behavior after we add the atomic output
|
|
* configuration API.
|
|
*/
|
|
wl_resource_for_each(res, &head->resource_list) {
|
|
ver = wl_resource_get_version(res);
|
|
if (ver >= WL_OUTPUT_DONE_SINCE_VERSION)
|
|
wl_output_send_done(res);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Client called get_output(). We already have the backing object, so just
|
|
* create a resource for the client.
|
|
*/
|
|
static void
|
|
cm_get_output(struct wl_client *client, struct wl_resource *cm_res,
|
|
uint32_t cm_output_id, struct wl_resource *output_res)
|
|
{
|
|
struct weston_head *head = weston_head_from_resource(output_res);
|
|
uint32_t version = wl_resource_get_version(cm_res);
|
|
struct wl_resource *res;
|
|
|
|
res = wl_resource_create(client, &wp_color_management_output_v1_interface,
|
|
version, cm_output_id);
|
|
if (!res) {
|
|
wl_resource_post_no_memory(cm_res);
|
|
return;
|
|
}
|
|
|
|
/* Client wants the cm_output but we've already made the head inactive,
|
|
* so let's set the implementation data as NULL (and other functions can
|
|
* tell that they are inert through that). */
|
|
if (!head) {
|
|
wl_resource_set_implementation(res, &cm_output_implementation,
|
|
NULL, cm_output_resource_destroy);
|
|
return;
|
|
}
|
|
|
|
wl_resource_set_implementation(res, &cm_output_implementation,
|
|
head, cm_output_resource_destroy);
|
|
|
|
wl_list_insert(&head->cm_output_resource_list,
|
|
wl_resource_get_link(res));
|
|
}
|
|
|
|
/**
|
|
* Called by clients to update the image description of a surface.
|
|
*
|
|
* If the surface state is commited, libweston will update the struct
|
|
* weston_surface color profile and render intent.
|
|
*/
|
|
static void
|
|
cm_surface_set_image_description(struct wl_client *client,
|
|
struct wl_resource *cm_surface_res,
|
|
struct wl_resource *cm_image_desc_res,
|
|
uint32_t protocol_render_intent)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(cm_surface_res);
|
|
struct cm_image_desc *cm_image_desc =
|
|
wl_resource_get_user_data(cm_image_desc_res);
|
|
struct weston_color_manager *cm;
|
|
const struct weston_render_intent_info *render_intent;
|
|
|
|
/* The surface might have been already gone, in such case cm_surface is
|
|
* inert. */
|
|
if (!surface) {
|
|
wl_resource_post_error(cm_surface_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_INERT,
|
|
"the wl_surface has already been destroyed");
|
|
return;
|
|
}
|
|
|
|
/* Invalid image description for this request, as we gracefully failed
|
|
* to create it. */
|
|
if (!cm_image_desc) {
|
|
wl_resource_post_error(cm_surface_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION,
|
|
"we gracefully failed to create this image description");
|
|
return;
|
|
}
|
|
|
|
/* Invalid image description for this request, as it isn't ready yet. */
|
|
if (!cm_image_desc->cprof) {
|
|
wl_resource_post_error(cm_surface_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION,
|
|
"the image description is not ready");
|
|
return;
|
|
}
|
|
|
|
cm = cm_image_desc->cm;
|
|
|
|
render_intent = weston_render_intent_info_from_protocol(surface->compositor,
|
|
protocol_render_intent);
|
|
if (!render_intent) {
|
|
wl_resource_post_error(cm_surface_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_RENDER_INTENT,
|
|
"unknown render intent");
|
|
return;
|
|
}
|
|
|
|
if (!((cm->supported_rendering_intents >> render_intent->intent) & 1)) {
|
|
wl_resource_post_error(cm_surface_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_RENDER_INTENT,
|
|
"unsupported render intent");
|
|
return;
|
|
}
|
|
|
|
weston_color_profile_unref(surface->pending.color_profile);
|
|
surface->pending.color_profile =
|
|
weston_color_profile_ref(cm_image_desc->cprof);
|
|
surface->pending.render_intent = render_intent;
|
|
}
|
|
|
|
/**
|
|
* Called by clients to unset the image description.
|
|
*
|
|
* If the surface state is commited, libweston will update the struct
|
|
* weston_surface color profile and render intent.
|
|
*/
|
|
static void
|
|
cm_surface_unset_image_description(struct wl_client *client,
|
|
struct wl_resource *cm_surface_res)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(cm_surface_res);
|
|
|
|
/* The surface might have been already gone, in such case cm_surface is
|
|
* inert. */
|
|
if (!surface) {
|
|
wl_resource_post_error(cm_surface_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_INERT,
|
|
"the wl_surface has already been destroyed");
|
|
return;
|
|
}
|
|
|
|
weston_color_profile_unref(surface->pending.color_profile);
|
|
surface->pending.color_profile = NULL;
|
|
surface->pending.render_intent = NULL;
|
|
}
|
|
|
|
/**
|
|
* Client will not use the cm_surface anymore, so we destroy its resource.
|
|
*/
|
|
static void
|
|
cm_surface_destroy(struct wl_client *client, struct wl_resource *cm_surface_res)
|
|
{
|
|
wl_resource_destroy(cm_surface_res);
|
|
}
|
|
|
|
/**
|
|
* Resource destruction function for the cm_surface.
|
|
*/
|
|
static void
|
|
cm_surface_resource_destroy(struct wl_resource *cm_surface_res)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(cm_surface_res);
|
|
|
|
/* For inert cm_surface, we don't have to do anything.
|
|
*
|
|
* We already did what was necessary when cm_surface became inert, in
|
|
* the surface destruction process (in weston_surface_unref(), which
|
|
* is the surface destruction function). */
|
|
if (!surface)
|
|
return;
|
|
|
|
surface->cm_surface = NULL;
|
|
|
|
/* Do the same as unset_image_description */
|
|
weston_color_profile_unref(surface->pending.color_profile);
|
|
surface->pending.color_profile = NULL;
|
|
surface->pending.render_intent = NULL;
|
|
}
|
|
|
|
static const struct wp_color_management_surface_v1_interface
|
|
cm_surface_implementation = {
|
|
.destroy = cm_surface_destroy,
|
|
.set_image_description = cm_surface_set_image_description,
|
|
.unset_image_description = cm_surface_unset_image_description,
|
|
};
|
|
|
|
/**
|
|
* Client called get_surface(). We already have the backing object, so just
|
|
* create a resource for the client.
|
|
*/
|
|
static void
|
|
cm_get_surface(struct wl_client *client, struct wl_resource *cm_res,
|
|
uint32_t cm_surface_id, struct wl_resource *surface_res)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(surface_res);
|
|
uint32_t version = wl_resource_get_version(cm_res);
|
|
struct wl_resource *res;
|
|
|
|
if (surface->cm_surface) {
|
|
wl_resource_post_error(cm_res,
|
|
WP_COLOR_MANAGER_V1_ERROR_SURFACE_EXISTS,
|
|
"surface already requested");
|
|
return;
|
|
}
|
|
|
|
res = wl_resource_create(client, &wp_color_management_surface_v1_interface,
|
|
version, cm_surface_id);
|
|
if (!res) {
|
|
wl_resource_post_no_memory(cm_res);
|
|
return;
|
|
}
|
|
|
|
wl_resource_set_implementation(res, &cm_surface_implementation,
|
|
surface, cm_surface_resource_destroy);
|
|
|
|
surface->cm_surface = res;
|
|
}
|
|
|
|
/**
|
|
* Client will not use the cm_surface_feedback anymore, so we destroy its resource.
|
|
*/
|
|
static void
|
|
cm_surface_feedback_destroy(struct wl_client *client,
|
|
struct wl_resource *cm_surface_feedback_res)
|
|
{
|
|
wl_resource_destroy(cm_surface_feedback_res);
|
|
}
|
|
|
|
/**
|
|
* Called by clients when they want to know the preferred image description of
|
|
* the surface.
|
|
*/
|
|
static void
|
|
cm_surface_feedback_get_preferred(struct wl_client *client,
|
|
struct wl_resource *cm_surface_feedback_res,
|
|
uint32_t protocol_object_id)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(cm_surface_feedback_res);
|
|
uint32_t version = wl_resource_get_version(cm_surface_feedback_res);
|
|
struct weston_color_manager *cm;
|
|
struct cm_image_desc *cm_image_desc;
|
|
|
|
/* The surface might have been already gone, in such case cm_surface_feedback is
|
|
* inert. */
|
|
if (!surface) {
|
|
wl_resource_post_error(cm_surface_feedback_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_ERROR_INERT,
|
|
"the wl_surface has already been destroyed");
|
|
return;
|
|
}
|
|
|
|
cm = surface->compositor->color_manager;
|
|
|
|
cm_image_desc = cm_image_desc_create(cm, surface->preferred_color_profile,
|
|
client, version, protocol_object_id,
|
|
YES_GET_INFO);
|
|
if (!cm_image_desc) {
|
|
wl_resource_post_no_memory(cm_surface_feedback_res);
|
|
return;
|
|
}
|
|
|
|
wp_image_description_v1_send_ready(cm_image_desc->owner,
|
|
cm_image_desc->cprof->id);
|
|
}
|
|
|
|
static void
|
|
cm_surface_feedback_get_preferred_parametric(struct wl_client *client,
|
|
struct wl_resource *cm_surface_feedback_res,
|
|
uint32_t protocol_object_id)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(cm_surface_feedback_res);
|
|
uint32_t version = wl_resource_get_version(cm_surface_feedback_res);
|
|
struct weston_color_manager *cm;
|
|
struct cm_image_desc *cm_image_desc;
|
|
char *err_msg;
|
|
|
|
/* The surface might have been already gone, in such case cm_surface_feedback is
|
|
* inert. */
|
|
if (!surface) {
|
|
wl_resource_post_error(cm_surface_feedback_res,
|
|
WP_COLOR_MANAGEMENT_SURFACE_FEEDBACK_V1_ERROR_INERT,
|
|
"the wl_surface has already been destroyed");
|
|
return;
|
|
}
|
|
|
|
cm = surface->compositor->color_manager;
|
|
|
|
/* Create the image description with cprof == NULL. */
|
|
cm_image_desc = cm_image_desc_create(cm, NULL, client, version,
|
|
protocol_object_id, YES_GET_INFO);
|
|
if (!cm_image_desc) {
|
|
wl_resource_post_no_memory(cm_surface_feedback_res);
|
|
return;
|
|
}
|
|
|
|
cm_image_desc->cprof =
|
|
cm->get_parametric_color_profile(surface->preferred_color_profile,
|
|
&err_msg);
|
|
|
|
/* Failed to get a parametric cprof for surface preferred cprof. */
|
|
if (!cm_image_desc->cprof) {
|
|
wp_image_description_v1_send_failed(cm_image_desc->owner,
|
|
WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED,
|
|
err_msg);
|
|
free(err_msg);
|
|
|
|
/* Failed to create the image description, let's set the
|
|
* resource userdata to NULL (and other functions can tell that
|
|
* it is invalid through that). */
|
|
wl_resource_set_user_data(cm_image_desc->owner, NULL);
|
|
cm_image_desc_destroy(cm_image_desc);
|
|
|
|
return;
|
|
}
|
|
|
|
wp_image_description_v1_send_ready(cm_image_desc->owner,
|
|
cm_image_desc->cprof->id);
|
|
}
|
|
|
|
static const struct wp_color_management_surface_feedback_v1_interface
|
|
cm_surface_feedback_implementation = {
|
|
.destroy = cm_surface_feedback_destroy,
|
|
.get_preferred = cm_surface_feedback_get_preferred,
|
|
.get_preferred_parametric = cm_surface_feedback_get_preferred_parametric,
|
|
};
|
|
|
|
/**
|
|
* Resource destruction function for the cm_surface_feedback.
|
|
*/
|
|
static void
|
|
cm_surface_feedback_resource_destroy(struct wl_resource *cm_surface_feedback_res)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(cm_surface_feedback_res);
|
|
|
|
/* For inert cm_surface_feedback, we don't have to do anything.
|
|
*
|
|
* We already did what was necessary when cm_surface_feedback became
|
|
* inert, in the surface destruction process: weston_surface_unref(). */
|
|
if (!surface)
|
|
return;
|
|
|
|
/* We are destroying the cm_surface_feedback_res, so simply remove it from
|
|
* weston_surface::cm_surface_feedback_resource_list. */
|
|
wl_list_remove(wl_resource_get_link(cm_surface_feedback_res));
|
|
}
|
|
|
|
/**
|
|
* Notifies clients that their surface preferred image description changed.
|
|
*
|
|
* \param surface The surface that changed its preferred image description.
|
|
*/
|
|
void
|
|
weston_surface_send_preferred_image_description_changed(struct weston_surface *surface)
|
|
{
|
|
struct wl_resource *res;
|
|
uint32_t id = surface->preferred_color_profile->id;
|
|
|
|
wl_resource_for_each(res, &surface->cm_surface_feedback_resource_list)
|
|
wp_color_management_surface_feedback_v1_send_preferred_changed(res, id);
|
|
}
|
|
|
|
/**
|
|
* Client called get_surface_feedback(). We already have the backing object, so just
|
|
* create a resource for the client.
|
|
*/
|
|
static void
|
|
cm_get_surface_feedback(struct wl_client *client, struct wl_resource *cm_res,
|
|
uint32_t cm_surface_id, struct wl_resource *surface_res)
|
|
{
|
|
struct weston_surface *surface = wl_resource_get_user_data(surface_res);
|
|
uint32_t version = wl_resource_get_version(cm_res);
|
|
struct wl_resource *res;
|
|
|
|
res = wl_resource_create(client, &wp_color_management_surface_feedback_v1_interface,
|
|
version, cm_surface_id);
|
|
if (!res) {
|
|
wl_resource_post_no_memory(cm_res);
|
|
return;
|
|
}
|
|
|
|
wl_resource_set_implementation(res, &cm_surface_feedback_implementation,
|
|
surface, cm_surface_feedback_resource_destroy);
|
|
wl_list_insert(&surface->cm_surface_feedback_resource_list, wl_resource_get_link(res));
|
|
}
|
|
|
|
/**
|
|
* Sets the ICC file for the ICC-based image description creator object.
|
|
*/
|
|
static void
|
|
cm_creator_icc_set_icc_file(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
int32_t icc_profile_fd,
|
|
uint32_t offset, uint32_t length)
|
|
{
|
|
struct cm_creator_icc *cm_creator_icc = wl_resource_get_user_data(resource);
|
|
int flags;
|
|
uint32_t err_code;
|
|
const char *err_msg;
|
|
|
|
if (cm_creator_icc->icc_data_length > 0) {
|
|
err_code = WP_IMAGE_DESCRIPTION_CREATOR_ICC_V1_ERROR_ALREADY_SET;
|
|
err_msg = "ICC file was already set";
|
|
goto err;
|
|
}
|
|
|
|
if (length == 0 || length > (32 * 1024 * 1024)) {
|
|
err_code = WP_IMAGE_DESCRIPTION_CREATOR_ICC_V1_ERROR_BAD_SIZE;
|
|
err_msg = "invalid ICC file size, should be in the " \
|
|
"(0, 32MB] interval";
|
|
goto err;
|
|
}
|
|
|
|
flags = fcntl(icc_profile_fd, F_GETFL);
|
|
if ((flags & O_ACCMODE) == O_WRONLY) {
|
|
err_code = WP_IMAGE_DESCRIPTION_CREATOR_ICC_V1_ERROR_BAD_FD;
|
|
err_msg = "ICC fd is not readable";
|
|
goto err;
|
|
}
|
|
|
|
if (lseek(icc_profile_fd, 0, SEEK_CUR) < 0) {
|
|
err_code = WP_IMAGE_DESCRIPTION_CREATOR_ICC_V1_ERROR_BAD_FD;
|
|
err_msg = "ICC fd is not seekable";
|
|
goto err;
|
|
}
|
|
|
|
cm_creator_icc->icc_profile_fd = icc_profile_fd;
|
|
|
|
/* We save length and offset in size_t variables. This ensures that they
|
|
* fit. We received them as uint32_t from the protocol. */
|
|
static_assert(UINT32_MAX <= SIZE_MAX,
|
|
"won't be able to save uint32_t var into size_t");
|
|
cm_creator_icc->icc_data_length = length;
|
|
cm_creator_icc->icc_data_offset = offset;
|
|
|
|
return;
|
|
|
|
err:
|
|
close(icc_profile_fd);
|
|
wl_resource_post_error(resource, err_code, "%s", err_msg);
|
|
}
|
|
|
|
static bool
|
|
do_length_and_offset_fit(struct cm_creator_icc *cm_creator_icc)
|
|
{
|
|
size_t end;
|
|
off_t end_off;
|
|
|
|
/* Ensure that length + offset doesn't overflow in size_t. If that isn't
|
|
* true, we won't be able to make it fit into off_t. And we may need
|
|
* that to read the ICC file. */
|
|
if (cm_creator_icc->icc_data_length > SIZE_MAX - cm_creator_icc->icc_data_offset)
|
|
return false;
|
|
|
|
/* Ensure that length + offset doesn't overflow in off_t. */
|
|
end = cm_creator_icc->icc_data_offset + cm_creator_icc->icc_data_length;
|
|
end_off = end;
|
|
if (end_off < 0 || end != (size_t)end_off)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int
|
|
create_image_description_color_profile_from_icc_creator(struct cm_image_desc *cm_image_desc,
|
|
struct cm_creator_icc *cm_creator_icc)
|
|
{
|
|
struct weston_compositor *compositor = cm_creator_icc->compositor;
|
|
struct weston_color_manager *cm = compositor->color_manager;
|
|
struct weston_color_profile *cprof;
|
|
char *err_msg;
|
|
void *icc_prof_data;
|
|
size_t bytes_read = 0;
|
|
ssize_t pread_ret;
|
|
bool ret;
|
|
|
|
if (!do_length_and_offset_fit(cm_creator_icc)) {
|
|
wp_image_description_v1_send_failed(cm_image_desc->owner,
|
|
WP_IMAGE_DESCRIPTION_V1_CAUSE_OPERATING_SYSTEM,
|
|
"length + offset does not fit off_t");
|
|
return -1;
|
|
}
|
|
|
|
/* Create buffer to read ICC profile. As they may have up to 32MB, we
|
|
* send OOM if something fails (instead of using xalloc). */
|
|
icc_prof_data = zalloc(cm_creator_icc->icc_data_length);
|
|
if (!icc_prof_data) {
|
|
wl_resource_post_no_memory(cm_creator_icc->owner);
|
|
return -1;
|
|
}
|
|
|
|
/* Read ICC file.
|
|
*
|
|
* TODO: it is not that simple. Clients can abuse that to DoS the
|
|
* compositor. See the discussion in the link below.
|
|
*
|
|
* https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/1356#note_2125102
|
|
*/
|
|
while (bytes_read < cm_creator_icc->icc_data_length) {
|
|
pread_ret = pread(cm_creator_icc->icc_profile_fd,
|
|
icc_prof_data + bytes_read,
|
|
cm_creator_icc->icc_data_length - bytes_read,
|
|
(off_t)cm_creator_icc->icc_data_offset + bytes_read);
|
|
if (pread_ret < 0) {
|
|
/* Interruption, so continue trying to read. */
|
|
if (errno == EINTR)
|
|
continue;
|
|
|
|
/* Reading the ICC failed */
|
|
free(icc_prof_data);
|
|
str_printf(&err_msg, "failed to read ICC file: %s", strerror(errno));
|
|
wp_image_description_v1_send_failed(cm_image_desc->owner,
|
|
WP_IMAGE_DESCRIPTION_V1_CAUSE_OPERATING_SYSTEM,
|
|
err_msg);
|
|
free(err_msg);
|
|
return -1;
|
|
} else if (pread_ret == 0) {
|
|
/* We were expecting to read more than 0 bytes, but we
|
|
* didn't. That means that we've tried to read beyond
|
|
* EOF. This is client's fault, it must make sure that
|
|
* the given ICC file don't simply change. */
|
|
free(icc_prof_data);
|
|
wl_resource_post_error(cm_creator_icc->owner,
|
|
WP_IMAGE_DESCRIPTION_CREATOR_ICC_V1_ERROR_OUT_OF_FILE,
|
|
"tried to read ICC beyond EOF");
|
|
return -1;
|
|
}
|
|
bytes_read += (size_t)pread_ret;
|
|
}
|
|
weston_assert_true(compositor, bytes_read == cm_creator_icc->icc_data_length);
|
|
|
|
ret = cm->get_color_profile_from_icc(cm, icc_prof_data,
|
|
cm_creator_icc->icc_data_length,
|
|
"icc-from-client", &cprof, &err_msg);
|
|
free(icc_prof_data);
|
|
|
|
if (!ret) {
|
|
/* We can't tell if it is client's fault that the ICC profile is
|
|
* invalid, so let's gracefully fail without returning a
|
|
* protocol error.
|
|
*
|
|
* TODO: we need to return proper error codes from the
|
|
* color-manager plugins and decide if we should gracefully fail
|
|
* or return a protocol error.
|
|
*/
|
|
wp_image_description_v1_send_failed(cm_image_desc->owner,
|
|
WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED,
|
|
err_msg);
|
|
free(err_msg);
|
|
return -1;
|
|
}
|
|
|
|
cm_image_desc->cprof = cprof;
|
|
wp_image_description_v1_send_ready(cm_image_desc->owner,
|
|
cm_image_desc->cprof->id);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Creates image description using the ICC-based image description creator
|
|
* object. This is a destructor type request, so the cm_creator_icc resource
|
|
* gets destroyed after this.
|
|
*/
|
|
static void
|
|
cm_creator_icc_create(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t image_description_id)
|
|
{
|
|
struct cm_creator_icc *cm_creator_icc =
|
|
wl_resource_get_user_data(resource);
|
|
struct weston_compositor *compositor = cm_creator_icc->compositor;
|
|
struct weston_color_manager *cm = compositor->color_manager;
|
|
uint32_t version = wl_resource_get_version(cm_creator_icc->owner);
|
|
struct cm_image_desc *cm_image_desc;
|
|
int ret;
|
|
|
|
if (cm_creator_icc->icc_data_length == 0) {
|
|
wl_resource_post_error(resource,
|
|
WP_IMAGE_DESCRIPTION_CREATOR_ICC_V1_ERROR_INCOMPLETE_SET,
|
|
"trying to create image description before " \
|
|
"setting the ICC file");
|
|
return;
|
|
}
|
|
|
|
/* Create the image description with cprof == NULL. */
|
|
cm_image_desc = cm_image_desc_create(cm, NULL, client, version,
|
|
image_description_id, NO_GET_INFO);
|
|
if (!cm_image_desc) {
|
|
wl_resource_post_no_memory(resource);
|
|
return;
|
|
}
|
|
|
|
/* Create the cprof for the image description. */
|
|
ret = create_image_description_color_profile_from_icc_creator(cm_image_desc,
|
|
cm_creator_icc);
|
|
if (ret < 0) {
|
|
/* Failed to create the image description, let's set the
|
|
* resource userdata to NULL (and other functions can tell that
|
|
* it is invalid through that). */
|
|
wl_resource_set_user_data(cm_image_desc->owner, NULL);
|
|
cm_image_desc_destroy(cm_image_desc);
|
|
}
|
|
|
|
/* Destroy the cm_creator_icc resource. This is a destructor request. */
|
|
wl_resource_destroy(cm_creator_icc->owner);
|
|
}
|
|
|
|
/**
|
|
* Resource destruction function for the cm_creator_icc.
|
|
* It should only destroy itself, but not the image description it creates.
|
|
*/
|
|
static void
|
|
cm_creator_icc_destructor(struct wl_resource *resource)
|
|
{
|
|
struct cm_creator_icc *cm_creator_icc =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (cm_creator_icc->icc_profile_fd >= 0)
|
|
close(cm_creator_icc->icc_profile_fd);
|
|
|
|
free(cm_creator_icc);
|
|
}
|
|
|
|
static const struct wp_image_description_creator_icc_v1_interface
|
|
cm_creator_icc_implementation = {
|
|
.create = cm_creator_icc_create,
|
|
.set_icc_file = cm_creator_icc_set_icc_file,
|
|
};
|
|
|
|
/**
|
|
* Creates an ICC-based image description creator for the client.
|
|
*/
|
|
static void
|
|
cm_create_image_description_creator_icc(struct wl_client *client,
|
|
struct wl_resource *cm_res,
|
|
uint32_t cm_creator_icc_id)
|
|
{
|
|
struct cm_creator_icc *cm_creator_icc;
|
|
struct weston_compositor *compositor = wl_resource_get_user_data(cm_res);
|
|
struct weston_color_manager *cm = compositor->color_manager;
|
|
uint32_t version = wl_resource_get_version(cm_res);
|
|
|
|
if (!((cm->supported_color_features >> WESTON_COLOR_FEATURE_ICC) & 1)) {
|
|
wl_resource_post_error(cm_res, WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE,
|
|
"creating ICC image descriptions is not supported");
|
|
return;
|
|
}
|
|
|
|
cm_creator_icc = xzalloc(sizeof(*cm_creator_icc));
|
|
|
|
cm_creator_icc->compositor = compositor;
|
|
cm_creator_icc->icc_profile_fd = -1;
|
|
|
|
cm_creator_icc->owner =
|
|
wl_resource_create(client, &wp_image_description_creator_icc_v1_interface,
|
|
version, cm_creator_icc_id);
|
|
if (!cm_creator_icc->owner)
|
|
goto err;
|
|
|
|
wl_resource_set_implementation(cm_creator_icc->owner, &cm_creator_icc_implementation,
|
|
cm_creator_icc, cm_creator_icc_destructor);
|
|
|
|
return;
|
|
|
|
err:
|
|
free(cm_creator_icc);
|
|
wl_resource_post_no_memory(cm_res);
|
|
}
|
|
|
|
/**
|
|
* Convert from param builder error to protocol error.
|
|
*
|
|
* If the error does not have a protocol counterpart, this returns -1.
|
|
*/
|
|
static int32_t
|
|
cm_creator_params_error_to_protocol(struct weston_compositor *compositor,
|
|
enum weston_color_profile_param_builder_error err)
|
|
{
|
|
switch(err) {
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_INVALID_TF:
|
|
return WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF;
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_INVALID_PRIMARIES_NAMED:
|
|
return WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED;
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_INVALID_LUMINANCE:
|
|
return WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_LUMINANCE;
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_INCOMPLETE_SET:
|
|
return WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INCOMPLETE_SET;
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_ALREADY_SET:
|
|
return WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_ALREADY_SET;
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_UNSUPPORTED:
|
|
return WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_UNSUPPORTED_FEATURE;
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_CREATE_FAILED:
|
|
case WESTON_COLOR_PROFILE_PARAM_BUILDER_ERROR_CIE_XY_OUT_OF_RANGE:
|
|
/* These are not protocol errors, but should result in graceful
|
|
* failures when creating the image description. */
|
|
return -1;
|
|
}
|
|
|
|
weston_assert_not_reached(compositor, "unknown params profile builder error");
|
|
}
|
|
|
|
/**
|
|
* Used by cm_creator_params setters to post protocol errors.
|
|
*
|
|
* Errors that should not result in a protocol error are not posted. These are
|
|
* graceful failures that we handle in cm_creator_params_create().
|
|
*/
|
|
static void
|
|
cm_creator_params_post_protocol_error(struct cm_creator_params *cm_creator_params)
|
|
{
|
|
struct weston_compositor *compositor = cm_creator_params->compositor;
|
|
enum weston_color_profile_param_builder_error err;
|
|
int32_t protocol_err;
|
|
char *err_msg;
|
|
|
|
if (!weston_color_profile_param_builder_get_error(cm_creator_params->builder,
|
|
&err, &err_msg))
|
|
return;
|
|
|
|
protocol_err = cm_creator_params_error_to_protocol(compositor, err);
|
|
if (protocol_err >= 0)
|
|
wl_resource_post_error(cm_creator_params->owner,
|
|
protocol_err, "%s", err_msg);
|
|
|
|
free(err_msg);
|
|
}
|
|
|
|
/**
|
|
* Set named primaries for parametric-based image description creator object.
|
|
*/
|
|
static void
|
|
cm_creator_params_set_primaries_named(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t primaries_named)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
const struct weston_color_primaries_info *primaries_info;
|
|
|
|
primaries_info = weston_color_primaries_info_from_protocol(primaries_named);
|
|
if (!primaries_info) {
|
|
wl_resource_post_error(resource,
|
|
WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_PRIMARIES_NAMED,
|
|
"invalid primaries named: %u", primaries_named);
|
|
return;
|
|
}
|
|
|
|
if (!weston_color_profile_param_builder_set_primaries_named(cm_creator_params->builder,
|
|
primaries_info->primaries))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set primaries for parametric-based image description creator object.
|
|
*
|
|
* The primaries we receive from clients are multiplied by 1000000.
|
|
*/
|
|
static void
|
|
cm_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)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
struct weston_color_gamut primaries;
|
|
|
|
primaries.primary[0].x = r_x / 1000000.0f;
|
|
primaries.primary[0].y = r_y / 1000000.0f;
|
|
primaries.primary[1].x = g_x / 1000000.0f;
|
|
primaries.primary[1].y = g_y / 1000000.0f;
|
|
primaries.primary[2].x = b_x / 1000000.0f;
|
|
primaries.primary[2].y = b_y / 1000000.0f;
|
|
primaries.white_point.x = w_x / 1000000.0f;
|
|
primaries.white_point.y = w_y / 1000000.0f;
|
|
|
|
if (!weston_color_profile_param_builder_set_primaries(cm_creator_params->builder,
|
|
&primaries))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set tf named for parametric-based image description creator object.
|
|
*/
|
|
static void
|
|
cm_creator_params_set_tf_named(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t tf_named)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
const struct weston_color_tf_info *tf_info;
|
|
|
|
tf_info = weston_color_tf_info_from_protocol(tf_named);
|
|
if (!tf_info) {
|
|
wl_resource_post_error(resource,
|
|
WP_IMAGE_DESCRIPTION_CREATOR_PARAMS_V1_ERROR_INVALID_TF,
|
|
"invalid tf named: %u", tf_named);
|
|
return;
|
|
}
|
|
|
|
if (!weston_color_profile_param_builder_set_tf_named(cm_creator_params->builder,
|
|
tf_info->tf))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set tf power for parametric-based image description creator object.
|
|
*
|
|
* The exponent we receive from clients is multiplied by 10000.
|
|
*/
|
|
static void
|
|
cm_creator_params_set_tf_power(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t exp)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (!weston_color_profile_param_builder_set_tf_power_exponent(cm_creator_params->builder,
|
|
exp / 10000.0f))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set primary luminance for parametric-based image description creator object.
|
|
*
|
|
* The min luminance we receive from clients is multiplied by 10000.
|
|
*/
|
|
static void
|
|
cm_creator_params_set_luminances(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t min_lum, uint32_t max_lum,
|
|
uint32_t reference_lum)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (!weston_color_profile_param_builder_set_primary_luminance(cm_creator_params->builder,
|
|
reference_lum,
|
|
min_lum / 10000.f, max_lum))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set mastering display primaries for parametric-based image description creator object.
|
|
*
|
|
* The primaries we receive from clients are multiplied by 1000000.
|
|
*/
|
|
static void
|
|
cm_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)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
struct weston_color_gamut primaries;
|
|
|
|
primaries.primary[0].x = r_x / 1000000.0f;
|
|
primaries.primary[0].y = r_y / 1000000.0f;
|
|
primaries.primary[1].x = g_x / 1000000.0f;
|
|
primaries.primary[1].y = g_y / 1000000.0f;
|
|
primaries.primary[2].x = b_x / 1000000.0f;
|
|
primaries.primary[2].y = b_y / 1000000.0f;
|
|
primaries.white_point.x = w_x / 1000000.0f;
|
|
primaries.white_point.y = w_y / 1000000.0f;
|
|
|
|
if (!weston_color_profile_param_builder_set_target_primaries(cm_creator_params->builder,
|
|
&primaries))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set mastering display luminance for parametric-based image description creator object.
|
|
*
|
|
* The min luminance we receive from clients is multiplied by 10000.
|
|
*/
|
|
static void
|
|
cm_creator_params_set_mastering_luminance(struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t min_lum, uint32_t max_lum)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (!weston_color_profile_param_builder_set_target_luminance(cm_creator_params->builder,
|
|
min_lum / 10000.0f, max_lum))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set max cll for parametric-based image description creator object.
|
|
*/
|
|
static void
|
|
cm_creator_params_set_max_cll(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t max_cll)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (!weston_color_profile_param_builder_set_maxCLL(cm_creator_params->builder,
|
|
max_cll))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Set max fall for parametric-based image description creator object.
|
|
*/
|
|
static void
|
|
cm_creator_params_set_max_fall(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t max_fall)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (!weston_color_profile_param_builder_set_maxFALL(cm_creator_params->builder,
|
|
max_fall))
|
|
cm_creator_params_post_protocol_error(cm_creator_params);
|
|
}
|
|
|
|
/**
|
|
* Creates image description using the parametric-based image description
|
|
* creator object. This is a destructor type request, so the cm_creator_params
|
|
* resource gets destroyed after this.
|
|
*/
|
|
static void
|
|
cm_creator_params_create(struct wl_client *client, struct wl_resource *resource,
|
|
uint32_t protocol_object_id)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
struct weston_compositor *compositor = cm_creator_params->compositor;
|
|
struct weston_color_manager *cm = compositor->color_manager;
|
|
uint32_t version = wl_resource_get_version(cm_creator_params->owner);
|
|
struct cm_image_desc *cm_image_desc;
|
|
enum weston_color_profile_param_builder_error err;
|
|
int32_t protocol_err;
|
|
char *err_msg;
|
|
|
|
/* Create the image description with cprof == NULL. */
|
|
cm_image_desc = cm_image_desc_create(cm, NULL, client, version,
|
|
protocol_object_id, NO_GET_INFO);
|
|
if (!cm_image_desc) {
|
|
wl_resource_post_no_memory(resource);
|
|
return;
|
|
}
|
|
|
|
/* Create the color profile through the param builder. This will destroy
|
|
* the builder object. */
|
|
cm_image_desc->cprof =
|
|
weston_color_profile_param_builder_create_color_profile(cm_creator_params->builder,
|
|
"client",
|
|
&err, &err_msg);
|
|
cm_creator_params->builder = NULL;
|
|
|
|
if (cm_image_desc->cprof) {
|
|
wp_image_description_v1_send_ready(cm_image_desc->owner,
|
|
cm_image_desc->cprof->id);
|
|
} else {
|
|
protocol_err = cm_creator_params_error_to_protocol(compositor, err);
|
|
if (protocol_err >= 0)
|
|
wl_resource_post_error(cm_creator_params->owner,
|
|
protocol_err, "%s", err_msg);
|
|
else
|
|
wp_image_description_v1_send_failed(cm_image_desc->owner,
|
|
WP_IMAGE_DESCRIPTION_V1_CAUSE_UNSUPPORTED,
|
|
err_msg);
|
|
free(err_msg);
|
|
|
|
/* Failed to create the cprof (and so the image description).
|
|
* Let's set the image description resource userdata to NULL
|
|
* (and other functions can tell that it is invalid through
|
|
* that). */
|
|
wl_resource_set_user_data(cm_image_desc->owner, NULL);
|
|
cm_image_desc_destroy(cm_image_desc);
|
|
}
|
|
|
|
/* Destroy the cm_creator_params resource. This is a destructor request. */
|
|
wl_resource_destroy(cm_creator_params->owner);
|
|
}
|
|
|
|
/**
|
|
* Resource destruction function for the cm_creator_params.
|
|
* It should only destroy itself, but not the image description it creates.
|
|
*/
|
|
static void
|
|
cm_creator_params_destructor(struct wl_resource *resource)
|
|
{
|
|
struct cm_creator_params *cm_creator_params =
|
|
wl_resource_get_user_data(resource);
|
|
|
|
if (cm_creator_params->builder)
|
|
weston_color_profile_param_builder_destroy(cm_creator_params->builder);
|
|
|
|
free(cm_creator_params);
|
|
}
|
|
|
|
static const struct wp_image_description_creator_params_v1_interface
|
|
cm_creator_params_implementation = {
|
|
.set_primaries_named = cm_creator_params_set_primaries_named,
|
|
.set_primaries = cm_creator_params_set_primaries,
|
|
.set_tf_named = cm_creator_params_set_tf_named,
|
|
.set_tf_power = cm_creator_params_set_tf_power,
|
|
.set_luminances = cm_creator_params_set_luminances,
|
|
.set_mastering_display_primaries = cm_creator_params_set_mastering_display_primaries,
|
|
.set_mastering_luminance = cm_creator_params_set_mastering_luminance,
|
|
.set_max_cll = cm_creator_params_set_max_cll,
|
|
.set_max_fall = cm_creator_params_set_max_fall,
|
|
.create = cm_creator_params_create,
|
|
};
|
|
|
|
/**
|
|
* Creates a parametric image description creator for the client.
|
|
*/
|
|
static void
|
|
cm_create_image_description_creator_params(struct wl_client *client,
|
|
struct wl_resource *cm_res,
|
|
uint32_t cm_creator_params_id)
|
|
{
|
|
struct cm_creator_params *cm_creator_params;
|
|
struct weston_compositor *compositor = wl_resource_get_user_data(cm_res);
|
|
struct weston_color_manager *cm = compositor->color_manager;
|
|
uint32_t version = wl_resource_get_version(cm_res);
|
|
|
|
if (!((cm->supported_color_features >> WESTON_COLOR_FEATURE_PARAMETRIC) & 1)) {
|
|
wl_resource_post_error(cm_res, WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE,
|
|
"creating parametric image descriptions " \
|
|
"is not supported");
|
|
return;
|
|
}
|
|
|
|
cm_creator_params = xzalloc(sizeof(*cm_creator_params));
|
|
|
|
cm_creator_params->compositor = compositor;
|
|
|
|
cm_creator_params->builder =
|
|
weston_color_profile_param_builder_create(compositor);
|
|
|
|
cm_creator_params->owner =
|
|
wl_resource_create(client, &wp_image_description_creator_params_v1_interface,
|
|
version, cm_creator_params_id);
|
|
if (!cm_creator_params->owner)
|
|
goto err;
|
|
|
|
wl_resource_set_implementation(cm_creator_params->owner, &cm_creator_params_implementation,
|
|
cm_creator_params, cm_creator_params_destructor);
|
|
|
|
return;
|
|
|
|
err:
|
|
weston_color_profile_param_builder_destroy(cm_creator_params->builder);
|
|
free(cm_creator_params);
|
|
wl_resource_post_no_memory(cm_res);
|
|
}
|
|
|
|
static void
|
|
cm_create_windows_scrgb(struct wl_client *client, struct wl_resource *cm_res,
|
|
uint32_t image_description)
|
|
{
|
|
wl_resource_post_error(cm_res,
|
|
WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE,
|
|
"creating windows scrgb is not supported");
|
|
}
|
|
|
|
/**
|
|
* Client will not use the color management object anymore, so we destroy its
|
|
* resource. That should not affect the other objects in any way.
|
|
*/
|
|
static void
|
|
cm_destroy(struct wl_client *client, struct wl_resource *cm_res)
|
|
{
|
|
wl_resource_destroy(cm_res);
|
|
}
|
|
|
|
static const struct wp_color_manager_v1_interface
|
|
color_manager_implementation = {
|
|
.destroy = cm_destroy,
|
|
.get_output = cm_get_output,
|
|
.get_surface = cm_get_surface,
|
|
.get_surface_feedback = cm_get_surface_feedback,
|
|
.create_icc_creator = cm_create_image_description_creator_icc,
|
|
.create_parametric_creator = cm_create_image_description_creator_params,
|
|
.create_windows_scrgb = cm_create_windows_scrgb,
|
|
};
|
|
|
|
/**
|
|
* Called when clients bind to the color-management protocol.
|
|
*/
|
|
static void
|
|
bind_color_management(struct wl_client *client, void *data, uint32_t version,
|
|
uint32_t id)
|
|
{
|
|
struct wl_resource *resource;
|
|
struct weston_compositor *compositor = data;
|
|
struct weston_color_manager *cm = compositor->color_manager;
|
|
const struct weston_color_feature_info *feature_info;
|
|
const struct weston_render_intent_info *render_intent;
|
|
const struct weston_color_primaries_info *primaries;
|
|
const struct weston_color_tf_info *tf;
|
|
unsigned int i;
|
|
|
|
resource = wl_resource_create(client, &wp_color_manager_v1_interface,
|
|
version, id);
|
|
if (!resource) {
|
|
wl_client_post_no_memory(client);
|
|
return;
|
|
}
|
|
|
|
wl_resource_set_implementation(resource, &color_manager_implementation,
|
|
compositor, NULL);
|
|
|
|
/* Expose the supported color features to the client. */
|
|
for (i = 0; i < 32; i++) {
|
|
if (!((cm->supported_color_features >> i) & 1))
|
|
continue;
|
|
feature_info = weston_color_feature_info_from(compositor, i);
|
|
wp_color_manager_v1_send_supported_feature(resource,
|
|
feature_info->protocol_feature);
|
|
}
|
|
|
|
/* Expose the supported rendering intents to the client. */
|
|
for (i = 0; i < 32; i++) {
|
|
if (!((cm->supported_rendering_intents >> i) & 1))
|
|
continue;
|
|
render_intent = weston_render_intent_info_from(compositor, i);
|
|
wp_color_manager_v1_send_supported_intent(resource,
|
|
render_intent->protocol_intent);
|
|
}
|
|
|
|
/* Expose the supported primaries named to the client. */
|
|
for (i = 0; i < 32; i++) {
|
|
if (!((cm->supported_primaries_named >> i) & 1))
|
|
continue;
|
|
primaries = weston_color_primaries_info_from(compositor, i);
|
|
wp_color_manager_v1_send_supported_primaries_named(resource,
|
|
primaries->protocol_primaries);
|
|
}
|
|
|
|
/* Expose the supported tf named to the client. */
|
|
for (i = 0; i < 32; i++) {
|
|
if (!((cm->supported_tf_named >> i) & 1))
|
|
continue;
|
|
tf = weston_color_tf_info_from(compositor, i);
|
|
wp_color_manager_v1_send_supported_tf_named(resource,
|
|
tf->protocol_tf);
|
|
}
|
|
|
|
wp_color_manager_v1_send_done(resource);
|
|
}
|
|
|
|
/** Advertise color-management support
|
|
*
|
|
* Calling this initializes the color-management protocol support, so that
|
|
* wp_color_manager_v1_interface will be advertised to clients. Essentially it
|
|
* creates a global. Do not call this function multiple times in the
|
|
* compositor's lifetime. There is no way to deinit explicitly, globals will be
|
|
* reaped when the wl_display gets destroyed.
|
|
*
|
|
* \param compositor The compositor to init for.
|
|
* \return Zero on success, -1 on failure.
|
|
*/
|
|
int
|
|
weston_compositor_enable_color_management_protocol(struct weston_compositor *compositor)
|
|
{
|
|
uint32_t version = 1;
|
|
|
|
weston_assert_bit_is_set(compositor,
|
|
compositor->color_manager->supported_rendering_intents,
|
|
1ull << WESTON_RENDER_INTENT_PERCEPTUAL);
|
|
|
|
if (!wl_global_create(compositor->wl_display,
|
|
&wp_color_manager_v1_interface,
|
|
version, compositor, bind_color_management))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|