color-representation-v1: new protocol

Add a minimal implementation of the color-representation-v1 protocol
(not including anything to actually use the new properties in
rendering/scanout)
This commit is contained in:
David Turner 2025-02-04 17:44:58 +00:00
parent f5dc6416f0
commit eff620770c
4 changed files with 427 additions and 0 deletions

View file

@ -0,0 +1,85 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_USE_UNSTABLE
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
#endif
#ifndef WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H
#define WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H
#include <wayland-server-core.h>
#include <wlr/render/color.h>
#include "color-representation-v1-protocol.h"
struct wlr_surface;
// Supported coefficients and range are always paired together
struct wlr_color_representation_v1_coeffs_and_range {
enum wp_color_representation_surface_v1_coefficients coeffs;
enum wp_color_representation_surface_v1_range range;
};
struct wlr_color_representation_manager_v1 {
struct wl_global *global;
struct {
// Manager is being destroyed
struct wl_signal destroy;
} events;
struct {
enum wp_color_representation_surface_v1_alpha_mode
*supported_alpha_modes;
size_t supported_alpha_modes_len;
struct wlr_color_representation_v1_coeffs_and_range
*supported_coeffs_and_ranges;
size_t supported_coeffs_and_ranges_len;
struct wl_listener display_destroy;
} WLR_PRIVATE;
};
// Options used when initialising a wlr_color_representation_manager_v1
struct wlr_color_representation_v1_options {
enum wp_color_representation_surface_v1_alpha_mode
*supported_alpha_modes;
size_t supported_alpha_modes_len;
const struct wlr_color_representation_v1_coeffs_and_range
*supported_coeffs_and_ranges;
size_t supported_coeffs_and_ranges_len;
};
struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_create(
struct wl_display *display, uint32_t version,
const struct wlr_color_representation_v1_options *options);
// This is all the color-representation state which can be attached to a
// surface, double-buffered and made current on commit
struct wlr_color_representation_v1_surface_state {
// The enum premultiplied_electrical has value zero and is defined
// to be the default if unspecified.
enum wp_color_representation_surface_v1_alpha_mode alpha_mode;
// If zero then indicates unset, otherwise values correspond to
// enum wp_color_representation_surface_v1_coefficients
uint32_t coefficients;
// If zero then indicates unset, otherwise values correspond to
// enum wp_color_representation_surface_v1_range
uint32_t range;
// If zero then indicates unset, otherwise values correspond to
// enum wp_color_representation_surface_v1_chroma_location
uint32_t chroma_location;
};
// Get the current color representation state committed to a surface
const struct wlr_color_representation_v1_surface_state *wlr_color_representation_v1_get_surface_state(
struct wlr_surface *surface);
#endif // WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H

View file

@ -25,6 +25,7 @@ protocols = {
# Staging upstream protocols
'alpha-modifier-v1': wl_protocol_dir / 'staging/alpha-modifier/alpha-modifier-v1.xml',
'color-management-v1': wl_protocol_dir / 'staging/color-management/color-management-v1.xml',
'color-representation-v1': wl_protocol_dir / 'staging/color-representation/color-representation-v1.xml',
'content-type-v1': wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',

View file

@ -48,6 +48,7 @@ wlr_files += files(
'wlr_drm.c',
'wlr_export_dmabuf_v1.c',
'wlr_foreign_toplevel_management_v1.c',
'wlr_color_representation_v1.c',
'wlr_ext_image_copy_capture_v1.c',
'wlr_ext_foreign_toplevel_list_v1.c',
'wlr_ext_data_control_v1.c',

View file

@ -0,0 +1,340 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_color_representation_v1.h>
#include <wlr/util/addon.h>
#include <wlr/util/log.h>
#include "color-representation-v1-protocol.h"
#include "util/mem.h"
#define WP_COLOR_REPRESENTATION_VERSION 1
struct wlr_color_representation_v1 {
struct wl_resource *resource;
struct wlr_surface *surface;
struct wlr_color_representation_manager_v1 *manager;
// Associate the wlr_color_representation_v1 with a wlr_surface
struct wlr_addon addon;
struct wlr_surface_synced synced;
struct wlr_color_representation_v1_surface_state pending, current;
};
static const struct wp_color_representation_surface_v1_interface color_repr_impl;
static struct wlr_color_representation_v1 *color_repr_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&wp_color_representation_surface_v1_interface,
&color_repr_impl));
return wl_resource_get_user_data(resource);
}
static void color_repr_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
// Actual destroying is done by the resource-destroy handler
wl_resource_destroy(resource);
}
static void color_repr_handle_set_alpha_mode(struct wl_client *client,
struct wl_resource *resource, uint32_t alpha_mode) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
if (color_repr == NULL) {
wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"Associated surface has been destroyed, object is inert");
return;
}
bool found = false;
for (size_t i = 0; i < color_repr->manager->supported_alpha_modes_len; i++) {
if (color_repr->manager->supported_alpha_modes[i] == alpha_mode) {
found = true;
break;
}
}
if (!found) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_ALPHA_MODE,
"Unsupported alpha mode");
return;
}
color_repr->pending.alpha_mode = alpha_mode;
}
static void color_repr_handle_set_coefficients_and_range(struct wl_client *client,
struct wl_resource *resource, uint32_t coefficients,
uint32_t range) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
if (color_repr == NULL) {
wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"Associated surface has been destroyed, object is inert");
return;
}
bool found = false;
for (size_t i = 0; i < color_repr->manager->supported_coeffs_and_ranges_len; i++) {
struct wlr_color_representation_v1_coeffs_and_range *supported =
&color_repr->manager->supported_coeffs_and_ranges[i];
if (supported->coeffs == coefficients && supported->range == range) {
found = true;
break;
}
}
if (!found) {
wl_resource_post_error(resource,
WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_COEFFICIENTS,
"Unsupported coefficients/range pair");
return;
}
color_repr->pending.coefficients = coefficients;
color_repr->pending.range = range;
}
static void color_repr_handle_set_chroma_location(struct wl_client *client,
struct wl_resource *resource, uint32_t chroma_location) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
if (color_repr == NULL) {
wl_resource_post_error(resource, WP_COLOR_REPRESENTATION_SURFACE_V1_ERROR_INERT,
"Associated surface has been destroyed, object is inert");
return;
}
uint32_t version = wl_resource_get_version(resource);
if (!wp_color_representation_surface_v1_chroma_location_is_valid(
version, chroma_location)) {
wlr_log(WLR_ERROR, "Client sent chroma location which isn't a valid enum value");
// TODO: Post actual error once
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/429
// is merged and wlroots depends on a new enough wayland-protocols.
wl_client_post_implementation_error(resource->client,
"Chroma location is not a valid enum value");
return;
}
// In this protocol there's no concept of supported chroma locations
// from a client point-of-view. The compositor should just ignore any
// chroma locations it doesn't know what to do with.
color_repr->pending.chroma_location = chroma_location;
}
static const struct wp_color_representation_surface_v1_interface color_repr_impl = {
.destroy = color_repr_handle_destroy,
.set_alpha_mode = color_repr_handle_set_alpha_mode,
.set_coefficients_and_range = color_repr_handle_set_coefficients_and_range,
.set_chroma_location = color_repr_handle_set_chroma_location,
};
static void color_repr_destroy(struct wlr_color_representation_v1 *color_repr) {
if (color_repr == NULL) {
return;
}
wlr_surface_synced_finish(&color_repr->synced);
wlr_addon_finish(&color_repr->addon);
wl_resource_set_user_data(color_repr->resource, NULL);
free(color_repr);
}
static void color_repr_addon_destroy(struct wlr_addon *addon) {
struct wlr_color_representation_v1 *color_repr =
wl_container_of(addon, color_repr, addon);
color_repr_destroy(color_repr);
}
static const struct wlr_addon_interface surface_addon_impl = {
.name = "wlr_color_representation_v1",
.destroy = color_repr_addon_destroy,
};
static void color_repr_handle_resource_destroy(struct wl_resource *resource) {
struct wlr_color_representation_v1 *color_repr =
color_repr_from_resource(resource);
color_repr_destroy(color_repr);
}
static void color_repr_manager_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
// Actual destroying is done by the resource-destroy handler
wl_resource_destroy(resource);
}
static const struct wlr_surface_synced_impl surface_synced_impl = {
.state_size = sizeof(struct wlr_color_representation_v1_surface_state),
};
static struct wlr_color_representation_v1 *color_repr_from_surface(
struct wlr_surface *surface) {
struct wlr_addon *addon = wlr_addon_find(&surface->addons, NULL, &surface_addon_impl);
if (addon == NULL) {
return NULL;
}
struct wlr_color_representation_v1 *color_repr = wl_container_of(addon, color_repr, addon);
return color_repr;
}
static const struct wp_color_representation_manager_v1_interface color_repr_manager_impl;
static struct wlr_color_representation_manager_v1 *manager_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&wp_color_representation_manager_v1_interface,
&color_repr_manager_impl));
return wl_resource_get_user_data(resource);
}
static void color_repr_manager_handle_get_surface(struct wl_client *client,
struct wl_resource *manager_resource,
uint32_t color_repr_id,
struct wl_resource *surface_resource) {
struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
// Check if there's already a color-representation attached to
// this surface
if (color_repr_from_surface(surface) != NULL) {
wl_resource_post_error(manager_resource,
WP_COLOR_REPRESENTATION_MANAGER_V1_ERROR_SURFACE_EXISTS,
"wp_color_representation_surface_v1 already exists for this surface");
return;
}
struct wlr_color_representation_v1 *color_repr = calloc(1, sizeof(*color_repr));
if (!color_repr) {
wl_resource_post_no_memory(manager_resource);
return;
}
color_repr->manager = manager_from_resource(manager_resource);
if (!wlr_surface_synced_init(&color_repr->synced, surface,
&surface_synced_impl, &color_repr->pending, &color_repr->current)) {
free(color_repr);
wl_resource_post_no_memory(manager_resource);
return;
}
uint32_t version = wl_resource_get_version(manager_resource);
color_repr->resource = wl_resource_create(client,
&wp_color_representation_surface_v1_interface, version, color_repr_id);
if (color_repr->resource == NULL) {
wlr_surface_synced_finish(&color_repr->synced);
free(color_repr);
wl_resource_post_no_memory(manager_resource);
return;
}
wl_resource_set_implementation(color_repr->resource,
&color_repr_impl, color_repr, color_repr_handle_resource_destroy);
wlr_addon_init(&color_repr->addon, &surface->addons, NULL, &surface_addon_impl);
}
static const struct wp_color_representation_manager_v1_interface color_repr_manager_impl = {
.destroy = color_repr_manager_handle_destroy,
.get_surface = color_repr_manager_handle_get_surface,
};
static void send_supported(struct wlr_color_representation_manager_v1 *manager,
struct wl_resource *resource) {
for (size_t i = 0; i < manager->supported_alpha_modes_len; i++) {
wp_color_representation_manager_v1_send_supported_alpha_mode(
resource, manager->supported_alpha_modes[i]);
}
for (size_t i = 0; i < manager->supported_coeffs_and_ranges_len; i++) {
struct wlr_color_representation_v1_coeffs_and_range *supported =
&manager->supported_coeffs_and_ranges[i];
wp_color_representation_manager_v1_send_supported_coefficients_and_ranges(
resource, supported->coeffs, supported->range);
}
// Note that there is no event for supported chroma locations in the
// v1 protocol.
wp_color_representation_manager_v1_send_done(resource);
}
static void manager_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct wlr_color_representation_manager_v1 *manager = data;
struct wl_resource *resource = wl_resource_create(client,
&wp_color_representation_manager_v1_interface,
version, id);
if (resource == NULL) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &color_repr_manager_impl, manager, NULL);
send_supported(manager, resource);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_color_representation_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wl_signal_emit_mutable(&manager->events.destroy, NULL);
assert(wl_list_empty(&manager->events.destroy.listener_list));
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_create(
struct wl_display *display, uint32_t version,
const struct wlr_color_representation_v1_options *options) {
assert(version <= WP_COLOR_REPRESENTATION_VERSION);
struct wlr_color_representation_manager_v1 *manager = calloc(1, sizeof(*manager));
if (manager == NULL) {
return NULL;
}
manager->global = wl_global_create(display,
&wp_color_representation_manager_v1_interface,
version, manager, manager_bind);
if (manager->global == NULL) {
free(manager);
return NULL;
}
bool ok = true;
ok &= memdup(&manager->supported_alpha_modes,
options->supported_alpha_modes,
sizeof(options->supported_alpha_modes[0]) * options->supported_alpha_modes_len);
ok &= memdup(&manager->supported_coeffs_and_ranges,
options->supported_coeffs_and_ranges,
sizeof(options->supported_coeffs_and_ranges[0]) * options->supported_coeffs_and_ranges_len);
if (!ok) {
goto err_options;
}
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
err_options:
free(manager->supported_alpha_modes);
free(manager->supported_coeffs_and_ranges);
return NULL;
}
const struct wlr_color_representation_v1_surface_state *wlr_color_representation_v1_get_surface_state(
struct wlr_surface *surface) {
struct wlr_color_representation_v1 *color_repr = color_repr_from_surface(surface);
if (color_repr == NULL) {
return NULL;
}
return &color_repr->current;
}