wayland: Redo the image description code

Main changes:

1. Avoid invalid writes by not passing pointers to a GArray that
   realloc()s its data
2. Use a hash table to store image defs, instead of an array. This
   requires a custom hash/equal function
3. Make image desc computation sync, so that setting a cs always
   succeeds or always fails and doesn't depend on timing.
4. Add a few debug messages in failure paths. For lack of a category,
   they ended up in MISC.
This commit is contained in:
Benjamin Otte
2024-07-27 06:30:49 +02:00
parent 45e76c1604
commit 25c812fa87
3 changed files with 157 additions and 73 deletions

View File

@@ -536,7 +536,7 @@ gdk_registry_handle_global (void *data,
}
else if (strcmp (interface, "xx_color_manager_v4") == 0)
{
display_wayland->color = gdk_wayland_color_new (registry, id, version);
display_wayland->color = gdk_wayland_color_new (display_wayland, registry, id, version);
}
else if (strcmp (interface, wp_single_pixel_buffer_manager_v1_interface.name) == 0)
{

View File

@@ -1,11 +1,13 @@
#pragma once
#include "gdkcolorstateprivate.h"
#include "gdkwaylanddisplay.h"
#include <wayland-client.h>
typedef struct _GdkWaylandColor GdkWaylandColor;
GdkWaylandColor * gdk_wayland_color_new (struct wl_registry *registry,
GdkWaylandColor * gdk_wayland_color_new (GdkWaylandDisplay *display,
struct wl_registry *registry,
uint32_t id,
uint32_t version);

View File

@@ -66,14 +66,10 @@ cicp_to_wl_transfer (uint tf)
return 0;
}
typedef struct
{
guint cp, tf;
struct xx_image_description_v4 *desc;
} ImageDescEntry;
struct _GdkWaylandColor
{
GdkWaylandDisplay *display;
struct xx_color_manager_v4 *color_manager;
struct {
unsigned int intents;
@@ -82,9 +78,47 @@ struct _GdkWaylandColor
unsigned int primaries;
} color_manager_supported;
GArray *image_descs;
GHashTable *cs_to_desc; /* GdkColorState => xx_image_description_v4 or NULL */
};
static guint
color_state_hash (gconstpointer data)
{
GdkColorState *cs = (GdkColorState *) data;
const GdkCicp *cicp;
cicp = gdk_color_state_get_cicp (cs);
if (cicp)
{
GdkCicp norm;
gdk_cicp_normalize (cicp, &norm);
return norm.color_primaries << 24 | norm.transfer_function;
}
return 0;
}
static gboolean
color_state_equal (gconstpointer a,
gconstpointer b)
{
GdkColorState *csa = (GdkColorState *) a;
GdkColorState *csb = (GdkColorState *) b;
const GdkCicp *cicpa, *cicpb;
GdkCicp norma, normb;
cicpa = gdk_color_state_get_cicp (csa);
cicpb = gdk_color_state_get_cicp (csb);
if (cicpa == NULL || cicpb == NULL)
return FALSE;
gdk_cicp_normalize (cicpa, &norma);
gdk_cicp_normalize (cicpb, &normb);
return norma.color_primaries == normb.color_primaries &&
norma.transfer_function == normb.transfer_function;
}
static void
xx_color_manager_v4_supported_intent (void *data,
struct xx_color_manager_v4 *xx_color_manager_v4,
@@ -133,7 +167,8 @@ static struct xx_color_manager_v4_listener color_manager_listener = {
};
GdkWaylandColor *
gdk_wayland_color_new (struct wl_registry *registry,
gdk_wayland_color_new (GdkWaylandDisplay *display,
struct wl_registry *registry,
uint32_t id,
uint32_t version)
{
@@ -141,7 +176,11 @@ gdk_wayland_color_new (struct wl_registry *registry,
color = g_new0 (GdkWaylandColor, 1);
color->image_descs = g_array_new (FALSE, FALSE, sizeof (ImageDescEntry));
color->display = display;
color->cs_to_desc = g_hash_table_new_full (color_state_hash,
color_state_equal,
(GDestroyNotify) gdk_color_state_unref,
(GDestroyNotify) xx_image_description_v4_destroy);
color->color_manager = wl_registry_bind (registry,
id,
@@ -160,13 +199,7 @@ gdk_wayland_color_free (GdkWaylandColor *color)
{
g_clear_pointer (&color->color_manager, xx_color_manager_v4_destroy);
for (int i = 0; i < color->image_descs->len; i++)
{
ImageDescEntry *e = &g_array_index (color->image_descs, ImageDescEntry, i);
xx_image_description_v4_destroy (e->desc);
}
g_array_unref (color->image_descs);
g_hash_table_unref (color->cs_to_desc);
g_free (color);
}
@@ -177,60 +210,123 @@ gdk_wayland_color_get_color_manager (GdkWaylandColor *color)
return (struct wl_proxy *) color->color_manager;
}
typedef struct _CsImageDescListenerData {
GdkWaylandColor *color;
GdkColorState *color_state;
gboolean sync;
gboolean done;
} CsImageDescListenerData;
static void
std_image_desc_failed (void *data,
struct xx_image_description_v4 *desc,
uint32_t cause,
const char *msg)
cs_image_listener_data_free (CsImageDescListenerData *csi)
{
csi->done = TRUE;
if (csi->sync)
return;
g_free (csi);
}
static void
cs_image_desc_failed (void *data,
struct xx_image_description_v4 *desc,
uint32_t cause,
const char *msg)
{
CsImageDescListenerData *csi = data;
g_warning ("Failed to get one of the standard image descriptions: %s", msg);
xx_image_description_v4_destroy (desc);
g_hash_table_insert (csi->color->cs_to_desc,
gdk_color_state_ref (csi->color_state),
NULL);
cs_image_listener_data_free (csi);
}
static void
std_image_desc_ready (void *data,
struct xx_image_description_v4 *desc,
uint32_t identity)
cs_image_desc_ready (void *data,
struct xx_image_description_v4 *desc,
uint32_t identity)
{
struct xx_image_description_v4 **ptr = data;
CsImageDescListenerData *csi = data;
*ptr = desc;
g_hash_table_insert (csi->color->cs_to_desc,
gdk_color_state_ref (csi->color_state),
desc);
cs_image_listener_data_free (csi);
}
static struct xx_image_description_v4_listener std_image_desc_listener = {
std_image_desc_failed,
std_image_desc_ready,
static struct xx_image_description_v4_listener cs_image_desc_listener = {
cs_image_desc_failed,
cs_image_desc_ready,
};
static void
create_image_desc (GdkWaylandColor *color,
uint32_t primaries,
uint32_t transfer)
GdkColorState *cs,
gboolean sync)
{
CsImageDescListenerData data;
struct xx_image_description_creator_params_v4 *creator;
struct xx_image_description_v4 *desc;
ImageDescEntry entry;
ImageDescEntry *e;
const GdkCicp *cicp;
GdkCicp norm;
uint32_t primaries, tf;
entry.cp = primaries;
entry.tf = transfer;
entry.desc = NULL;
g_array_append_val (color->image_descs, entry);
e = &g_array_index (color->image_descs, ImageDescEntry, color->image_descs->len - 1);
cicp = gdk_color_state_get_cicp (cs);
if (!cicp)
{
GDK_DEBUG (MISC, "Unsupported color state %s: Not a CICP colorstate",
gdk_color_state_get_name (cs));
g_hash_table_insert (color->cs_to_desc, gdk_color_state_ref (cs), NULL);
return;
}
gdk_cicp_normalize (cicp, &norm);
primaries = cicp_to_wl_primaries (norm.color_primaries);
tf = cicp_to_wl_transfer (norm.transfer_function);
if ((color->color_manager_supported.primaries & (1 << primaries)) == 0 ||
(color->color_manager_supported.transfers & (1 << transfer)) == 0)
return;
(color->color_manager_supported.transfers & (1 << tf)) == 0)
{
GDK_DEBUG (MISC, "Unsupported color state %s: Primaries or transfer function unsupported",
gdk_color_state_get_name (cs));
g_hash_table_insert (color->cs_to_desc, gdk_color_state_ref (cs), NULL);
return;
}
data.color = color;
data.color_state = cs;
data.sync = sync;
data.done = FALSE;
creator = xx_color_manager_v4_new_parametric_creator (color->color_manager);
xx_image_description_creator_params_v4_set_primaries_named (creator, primaries);
xx_image_description_creator_params_v4_set_tf_named (creator, transfer);
xx_image_description_creator_params_v4_set_tf_named (creator, tf);
desc = xx_image_description_creator_params_v4_create (creator);
xx_image_description_v4_add_listener (desc, &std_image_desc_listener, &e->desc);
if (sync)
{
struct wl_event_queue *event_queue;
event_queue = wl_display_create_queue (color->display->wl_display);
wl_proxy_set_queue ((struct wl_proxy *) desc, event_queue);
xx_image_description_v4_add_listener (desc, &cs_image_desc_listener, &data);
while (!data.done)
gdk_wayland_display_dispatch_queue (GDK_DISPLAY (color->display), event_queue);
wl_event_queue_destroy (event_queue);
}
else
{
xx_image_description_v4_add_listener (desc, &cs_image_desc_listener, g_memdup (&data, sizeof data));
}
}
gboolean
@@ -298,26 +394,18 @@ gdk_wayland_color_prepare (GdkWaylandColor *color)
if (color->color_manager)
{
create_image_desc (color,
XX_COLOR_MANAGER_V4_PRIMARIES_SRGB,
XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_SRGB);
create_image_desc (color, GDK_COLOR_STATE_SRGB, FALSE);
if (color->color_manager_supported.transfers & (1 << XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR))
create_image_desc (color,
XX_COLOR_MANAGER_V4_PRIMARIES_SRGB,
XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
create_image_desc (color, GDK_COLOR_STATE_SRGB_LINEAR, FALSE);
if (color->color_manager_supported.primaries & (1 << XX_COLOR_MANAGER_V4_PRIMARIES_BT2020))
{
if (color->color_manager_supported.transfers & (1 << XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ))
create_image_desc (color,
XX_COLOR_MANAGER_V4_PRIMARIES_BT2020,
XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_ST2084_PQ);
create_image_desc (color, GDK_COLOR_STATE_REC2100_PQ, FALSE);
if (color->color_manager_supported.transfers & (1 << XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR))
create_image_desc (color,
XX_COLOR_MANAGER_V4_PRIMARIES_BT2020,
XX_COLOR_MANAGER_V4_TRANSFER_FUNCTION_LINEAR);
create_image_desc (color, GDK_COLOR_STATE_REC2100_LINEAR, FALSE);
}
}
@@ -633,25 +721,19 @@ static struct xx_image_description_v4 *
gdk_wayland_color_get_image_description (GdkWaylandColor *color,
GdkColorState *cs)
{
const GdkCicp *params;
GdkCicp normalized;
gpointer result;
params = gdk_color_state_get_cicp (cs);
gdk_cicp_normalize (params, &normalized);
if (g_hash_table_lookup_extended (color->cs_to_desc, cs, NULL, &result))
return result;
create_image_desc (color, cs, TRUE);
if (params)
for (int i = 0; i < color->image_descs->len; i++)
{
ImageDescEntry *e = &g_array_index (color->image_descs, ImageDescEntry, i);
if (wl_to_cicp_primaries (e->cp) == normalized.color_primaries &&
wl_to_cicp_transfer (e->tf) == normalized.transfer_function)
return e->desc;
}
create_image_desc (color,
cicp_to_wl_primaries (normalized.color_primaries),
cicp_to_wl_transfer (normalized.transfer_function));
return NULL;
if (!g_hash_table_lookup_extended (color->cs_to_desc, cs, NULL, &result))
{
g_assert_not_reached ();
}
return result;
}
void