From fab52b578fb6752cea693cb3ecafcd16db90f775 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Wed, 26 Feb 2025 11:14:55 -0500
Subject: [PATCH] wayland: Ensure that color descriptions are always retrieved
---
src/video/wayland/SDL_waylandcolor.c | 159 +++++++++++++++++++-------
src/video/wayland/SDL_waylandcolor.h | 14 +--
src/video/wayland/SDL_waylandvideo.c | 20 +---
src/video/wayland/SDL_waylandvideo.h | 1 +
src/video/wayland/SDL_waylandwindow.c | 14 +--
src/video/wayland/SDL_waylandwindow.h | 2 +
6 files changed, 135 insertions(+), 75 deletions(-)
diff --git a/src/video/wayland/SDL_waylandcolor.c b/src/video/wayland/SDL_waylandcolor.c
index 148a3416c1451..dfc69bb5f433e 100644
--- a/src/video/wayland/SDL_waylandcolor.c
+++ b/src/video/wayland/SDL_waylandcolor.c
@@ -27,31 +27,87 @@
#include "SDL_waylandvideo.h"
#include "SDL_waylandwindow.h"
#include "color-management-v1-client-protocol.h"
+#include "../../events/SDL_windowevents_c.h"
typedef struct Wayland_ColorInfoState
{
struct wp_image_description_v1 *wp_image_description;
struct wp_image_description_info_v1 *wp_image_description_info;
- Wayland_ColorInfo *info;
- bool result;
+ union
+ {
+ SDL_WindowData *window_data;
+ SDL_DisplayData *display_data;
+ };
+
+ enum
+ {
+ WAYLAND_COLOR_OBJECT_TYPE_WINDOW,
+ WAYLAND_COLOR_OBJECT_TYPE_DISPLAY
+ } object_type;
+
+ SDL_HDROutputProperties HDR;
+
+ // The ICC fd is only valid if the size is non-zero.
+ int icc_fd;
+ Uint32 icc_size;
+
+ bool deferred_event_processing;
} Wayland_ColorInfoState;
+static void Wayland_CancelColorInfoRequest(Wayland_ColorInfoState *state)
+{
+ if (state) {
+ if (state->wp_image_description_info) {
+ wp_image_description_info_v1_destroy(state->wp_image_description_info);
+ state->wp_image_description_info = NULL;
+ }
+ if (state->wp_image_description) {
+ wp_image_description_v1_destroy(state->wp_image_description);
+ state->wp_image_description = NULL;
+ }
+ }
+}
+
+void Wayland_FreeColorInfoState(Wayland_ColorInfoState *state)
+{
+ if (state) {
+ Wayland_CancelColorInfoRequest(state);
+
+ switch (state->object_type) {
+ case WAYLAND_COLOR_OBJECT_TYPE_WINDOW:
+ state->window_data->color_info_state = NULL;
+ break;
+ case WAYLAND_COLOR_OBJECT_TYPE_DISPLAY:
+ state->display_data->color_info_state = NULL;
+ break;
+ }
+
+ SDL_free(state);
+ }
+}
+
static void image_description_info_handle_done(void *data,
struct wp_image_description_info_v1 *wp_image_description_info_v1)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
-
- if (state->wp_image_description_info) {
- wp_image_description_info_v1_destroy(state->wp_image_description_info);
- state->wp_image_description_info = NULL;
- }
- if (state->wp_image_description) {
- wp_image_description_v1_destroy(state->wp_image_description);
- state->wp_image_description = NULL;
+ Wayland_CancelColorInfoRequest(state);
+
+ switch (state->object_type) {
+ case WAYLAND_COLOR_OBJECT_TYPE_WINDOW:
+ {
+ SDL_SetWindowHDRProperties(state->window_data->sdlwindow, &state->HDR, true);
+ if (state->icc_size) {
+ state->window_data->icc_fd = state->icc_fd;
+ state->window_data->icc_size = state->icc_size;
+ SDL_SendWindowEvent(state->window_data->sdlwindow, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
+ }
+ } break;
+ case WAYLAND_COLOR_OBJECT_TYPE_DISPLAY:
+ {
+ SDL_copyp(&state->display_data->HDR, &state->HDR);
+ } break;
}
-
- state->result = true;
}
static void image_description_info_handle_icc_file(void *data,
@@ -60,8 +116,8 @@ static void image_description_info_handle_icc_file(void *data,
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
- state->info->icc_fd = icc;
- state->info->icc_size = icc_size;
+ state->icc_fd = icc;
+ state->icc_size = icc_size;
}
static void image_description_info_handle_primaries(void *data,
@@ -102,7 +158,7 @@ static void image_description_info_handle_luminances(void *data,
uint32_t reference_lum)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
- state->info->HDR.HDR_headroom = (float)max_lum / (float)reference_lum;
+ state->HDR.HDR_headroom = (float)max_lum / (float)reference_lum;
}
static void image_description_info_handle_target_primaries(void *data,
@@ -157,13 +213,18 @@ static void PumpColorspaceEvents(Wayland_ColorInfoState *state)
// Run the image description sequence to completion in its own queue.
struct wl_event_queue *queue = WAYLAND_wl_display_create_queue(vid->display);
- WAYLAND_wl_proxy_set_queue((struct wl_proxy *)state->wp_image_description, queue);
+ if (state->deferred_event_processing) {
+ WAYLAND_wl_proxy_set_queue((struct wl_proxy *)state->wp_image_description_info, queue);
+ } else {
+ WAYLAND_wl_proxy_set_queue((struct wl_proxy *)state->wp_image_description, queue);
+ }
while (state->wp_image_description) {
WAYLAND_wl_display_dispatch_queue(vid->display, queue);
}
WAYLAND_wl_event_queue_destroy(queue);
+ Wayland_FreeColorInfoState(state);
}
static void image_description_handle_failed(void *data,
@@ -172,9 +233,11 @@ static void image_description_handle_failed(void *data,
const char *msg)
{
Wayland_ColorInfoState *state = (Wayland_ColorInfoState *)data;
+ Wayland_CancelColorInfoRequest(state);
- wp_image_description_v1_destroy(state->wp_image_description);
- state->wp_image_description = NULL;
+ if (state->deferred_event_processing) {
+ Wayland_FreeColorInfoState(state);
+ }
}
static void image_description_handle_ready(void *data,
@@ -186,6 +249,10 @@ static void image_description_handle_ready(void *data,
// This will inherit the queue of the factory image description object.
state->wp_image_description_info = wp_image_description_v1_get_information(state->wp_image_description);
wp_image_description_info_v1_add_listener(state->wp_image_description_info, &image_description_info_listener, data);
+
+ if (state->deferred_event_processing) {
+ PumpColorspaceEvents(state);
+ }
}
static const struct wp_image_description_v1_listener image_description_listener = {
@@ -193,32 +260,44 @@ static const struct wp_image_description_v1_listener image_description_listener
image_description_handle_ready
};
-bool Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, Wayland_ColorInfo *info)
+void Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, bool defer_event_processing)
{
- Wayland_ColorInfoState state;
- SDL_zero(state);
- state.info = info;
-
- state.wp_image_description = wp_color_management_surface_feedback_v1_get_preferred(window_data->wp_color_management_surface_feedback);
- wp_image_description_v1_add_listener(state.wp_image_description, &image_description_listener, &state);
-
- PumpColorspaceEvents(&state);
-
- return state.result;
+ // Cancel any pending request, as it is out-of-date.
+ Wayland_FreeColorInfoState(window_data->color_info_state);
+ Wayland_ColorInfoState *state = SDL_calloc(1, sizeof(Wayland_ColorInfoState));
+
+ if (state) {
+ window_data->color_info_state = state;
+ state->window_data = window_data;
+ state->object_type = WAYLAND_COLOR_OBJECT_TYPE_WINDOW;
+ state->deferred_event_processing = defer_event_processing;
+ state->wp_image_description = wp_color_management_surface_feedback_v1_get_preferred(window_data->wp_color_management_surface_feedback);
+ wp_image_description_v1_add_listener(state->wp_image_description, &image_description_listener, state);
+
+ if (!defer_event_processing) {
+ PumpColorspaceEvents(state);
+ }
+ }
}
-bool Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, Wayland_ColorInfo *info)
+void Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, bool defer_event_processing)
{
- Wayland_ColorInfoState state;
- SDL_zero(state);
- state.info = info;
-
- state.wp_image_description = wp_color_management_output_v1_get_image_description(display_data->wp_color_management_output);
- wp_image_description_v1_add_listener(state.wp_image_description, &image_description_listener, &state);
-
- PumpColorspaceEvents(&state);
-
- return state.result;
+ // Cancel any pending request, as it is out-of-date.
+ Wayland_FreeColorInfoState(display_data->color_info_state);
+ Wayland_ColorInfoState *state = SDL_calloc(1, sizeof(Wayland_ColorInfoState));
+
+ if (state) {
+ display_data->color_info_state = state;
+ state->display_data = display_data;
+ state->object_type = WAYLAND_COLOR_OBJECT_TYPE_DISPLAY;
+ state->deferred_event_processing = defer_event_processing;
+ state->wp_image_description = wp_color_management_output_v1_get_image_description(display_data->wp_color_management_output);
+ wp_image_description_v1_add_listener(state->wp_image_description, &image_description_listener, state);
+
+ if (!defer_event_processing) {
+ PumpColorspaceEvents(state);
+ }
+ }
}
#endif // SDL_VIDEO_DRIVER_WAYLAND
diff --git a/src/video/wayland/SDL_waylandcolor.h b/src/video/wayland/SDL_waylandcolor.h
index 6125374b85019..bef5d8c3e0569 100644
--- a/src/video/wayland/SDL_waylandcolor.h
+++ b/src/video/wayland/SDL_waylandcolor.h
@@ -26,16 +26,10 @@
#include "../SDL_sysvideo.h"
-typedef struct Wayland_ColorInfo
-{
- SDL_HDROutputProperties HDR;
+struct Wayland_ColorInfoState;
- // The ICC fd is only valid if the size is non-zero.
- int icc_fd;
- Uint32 icc_size;
-} Wayland_ColorInfo;
-
-extern bool Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, Wayland_ColorInfo *info);
-extern bool Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, Wayland_ColorInfo *info);
+extern void Wayland_FreeColorInfoState(struct Wayland_ColorInfoState *state);
+extern void Wayland_GetColorInfoForWindow(SDL_WindowData *window_data, bool defer_event_processing);
+extern void Wayland_GetColorInfoForOutput(SDL_DisplayData *display_data, bool defer_event_processing);
#endif // SDL_waylandcolor_h_
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 853c05eb1f90d..43019535f524c 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -1118,21 +1118,12 @@ static const struct wl_output_listener output_listener = {
display_handle_description // Version 4
};
-static void Wayland_GetOutputColorInfo(SDL_DisplayData *display)
-{
- Wayland_ColorInfo info;
- SDL_zero(info);
-
- if (Wayland_GetColorInfoForOutput(display, &info)) {
- SDL_copyp(&display->HDR, &info.HDR);
- }
-}
-
static void handle_output_image_description_changed(void *data,
struct wp_color_management_output_v1 *wp_color_management_output_v1)
{
+ SDL_DisplayData *display = (SDL_DisplayData *)data;
// wl_display.done is called after this event, so the display HDR status will be updated there.
- Wayland_GetOutputColorInfo(data);
+ Wayland_GetColorInfoForOutput(display, false);
}
static const struct wp_color_management_output_v1_listener wp_color_management_output_listener = {
@@ -1171,7 +1162,7 @@ static bool Wayland_add_display(SDL_VideoData *d, uint32_t id, uint32_t version)
if (data->videodata->wp_color_manager_v1) {
data->wp_color_management_output = wp_color_manager_v1_get_output(data->videodata->wp_color_manager_v1, output);
wp_color_management_output_v1_add_listener(data->wp_color_management_output, &wp_color_management_output_listener, data);
- Wayland_GetOutputColorInfo(data);
+ Wayland_GetColorInfoForOutput(data, true);
}
return true;
}
@@ -1191,6 +1182,7 @@ static void Wayland_free_display(SDL_VideoDisplay *display, bool send_event)
SDL_free(display_data->wl_output_name);
if (display_data->wp_color_management_output) {
+ Wayland_FreeColorInfoState(display_data->color_info_state);
wp_color_management_output_v1_destroy(display_data->wp_color_management_output);
}
@@ -1221,7 +1213,7 @@ static void Wayland_FinalizeDisplays(SDL_VideoData *vid)
static void Wayland_init_xdg_output(SDL_VideoData *d)
{
- for(int i = 0; i < d->output_count; ++i) {
+ for (int i = 0; i < d->output_count; ++i) {
SDL_DisplayData *disp = d->output_list[i];
disp->xdg_output = zxdg_output_manager_v1_get_xdg_output(disp->videodata->xdg_output_manager, disp->output);
zxdg_output_v1_add_listener(disp->xdg_output, &xdg_output_listener, disp);
@@ -1234,7 +1226,7 @@ static void Wayland_InitColorManager(SDL_VideoData *d)
SDL_DisplayData *disp = d->output_list[i];
disp->wp_color_management_output = wp_color_manager_v1_get_output(disp->videodata->wp_color_manager_v1, disp->output);
wp_color_management_output_v1_add_listener(disp->wp_color_management_output, &wp_color_management_output_listener, disp);
- Wayland_GetOutputColorInfo(disp);
+ Wayland_GetColorInfoForOutput(disp, true);
}
}
diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h
index 5b5ee24c0fa2c..8cde64c9acfd9 100644
--- a/src/video/wayland/SDL_waylandvideo.h
+++ b/src/video/wayland/SDL_waylandvideo.h
@@ -118,6 +118,7 @@ struct SDL_DisplayData
SDL_DisplayID display;
SDL_VideoDisplay placeholder;
int wl_output_done_count;
+ struct Wayland_ColorInfoState *color_info_state;
};
// Needed here to get wl_surface declaration, fixes GitHub#4594
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index c06f72ad0a382..212fd54f3535f 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -1656,17 +1656,7 @@ static void feedback_surface_preferred_changed(void *data,
uint32_t identity)
{
SDL_WindowData *wind = (SDL_WindowData *)data;
- Wayland_ColorInfo info;
- SDL_zero(info);
-
- if (Wayland_GetColorInfoForWindow(wind, &info)) {
- SDL_SetWindowHDRProperties(wind->sdlwindow, &info.HDR, true);
- if (info.icc_size) {
- wind->icc_fd = info.icc_fd;
- wind->icc_size = info.icc_size;
- SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
- }
- }
+ Wayland_GetColorInfoForWindow(wind, false);
}
static const struct wp_color_management_surface_feedback_v1_listener color_management_surface_feedback_listener = {
@@ -2626,6 +2616,7 @@ bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Proper
if (c->wp_color_manager_v1) {
data->wp_color_management_surface_feedback = wp_color_manager_v1_get_surface_feedback(c->wp_color_manager_v1, data->surface);
wp_color_management_surface_feedback_v1_add_listener(data->wp_color_management_surface_feedback, &color_management_surface_feedback_listener, data);
+ Wayland_GetColorInfoForWindow(data, true);
} else if (c->frog_color_management_factory_v1) {
data->frog_color_managed_surface = frog_color_management_factory_v1_get_color_managed_surface(c->frog_color_management_factory_v1, data->surface);
frog_color_managed_surface_add_listener(data->frog_color_managed_surface, &frog_surface_listener, data);
@@ -3092,6 +3083,7 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
if (wind->wp_color_management_surface_feedback) {
+ Wayland_FreeColorInfoState(wind->color_info_state);
wp_color_management_surface_feedback_v1_destroy(wind->wp_color_management_surface_feedback);
}
diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h
index 601589a2d34f0..619fd79e6d7d5 100644
--- a/src/video/wayland/SDL_waylandwindow.h
+++ b/src/video/wayland/SDL_waylandwindow.h
@@ -118,6 +118,8 @@ struct SDL_WindowData
struct frog_color_managed_surface *frog_color_managed_surface;
struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback;
+ struct Wayland_ColorInfoState *color_info_state;
+
SDL_AtomicInt swap_interval_ready;
SDL_DisplayData **outputs;