Wayland: Remove usage of the Wayland color management protocol to inform compositors of the color space used by kitty

As is typical with Wayland, the protocol is poorly designed and
implemented even worse. Hyprland 0.53 has completely broken color
management.
https://github.com/hyprwm/Hyprland/discussions/12788
In addition it and mangowc crash when using color management with nouveau drivers.
https://github.com/kovidgoyal/kitty/issues/9030

KDE kwin does not support the sRGB transfer function. And the geniuses
at Wayland are any way planning to deprecate sRGB as a transfer function.

Only GNOME mutter seems to get it right.

Then there are people that are likely going to shoehorn this into EGL
instead of leaving it under application control via the protocol anyway.
https://github.com/KhronosGroup/EGL-Registry/issues/197

Sigh. Wayland.
This commit is contained in:
Kovid Goyal 2026-01-15 09:53:09 +05:30
parent d35c786358
commit 9ef934cf54
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
5 changed files with 3 additions and 124 deletions

View file

@ -192,6 +192,9 @@ Detailed list of changes
with frames that dispose onto background with non-zero delay using the native
engine (:iss:`9376`)
- Wayland: Remove usage of the wayland color management protocol to inform
compositors of the color space used by kitty
0.45.0 [2025-12-24]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -88,7 +88,6 @@
"staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml",
"staging/xdg-system-bell/xdg-system-bell-v1.xml",
"staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml",
"staging/color-management/color-management-v1.xml",
"kwin-blur-v1.xml",
"wlr-layer-shell-unstable-v1.xml"

92
glfw/wl_init.c vendored
View file

@ -502,53 +502,6 @@ static const struct wl_seat_listener seatListener = {
seatHandleCapabilities,
seatHandleName,
};
static void
ignored_color_manager_event(void *data UNUSED, struct wp_color_manager_v1 *wp_color_manager_v1 UNUSED, uint32_t x UNUSED) {}
static void
on_color_manger_features_done(void *data UNUSED, struct wp_color_manager_v1 *wp_color_manager_v1 UNUSED) {
_glfw.wl.color_manager.capabilities_reported = true;
}
static void
on_supported_color_primaries(void *data UNUSED, struct wp_color_manager_v1 *wp_color_manager_v1 UNUSED, uint32_t x) {
switch(x) {
case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB:
_glfw.wl.color_manager.supported_primaries.srgb = true; break;
}
}
static void
on_supported_color_feature(void *data UNUSED, struct wp_color_manager_v1 *wp_color_manager_v1 UNUSED, uint32_t x) {
switch(x) {
case WP_COLOR_MANAGER_V1_FEATURE_PARAMETRIC:
_glfw.wl.color_manager.supported_features.parametric = true; break;
case WP_COLOR_MANAGER_V1_FEATURE_SET_PRIMARIES:
_glfw.wl.color_manager.supported_features.set_primaries = true; break;
}
}
static void
on_supported_color_transfer_function(void *data UNUSED, struct wp_color_manager_v1 *wp_color_manager_v1 UNUSED, uint32_t x) {
switch(x) {
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB:
_glfw.wl.color_manager.supported_transfer_functions.srgb = true; break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22:
_glfw.wl.color_manager.supported_transfer_functions.gamma22 = true; break;
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR:
_glfw.wl.color_manager.supported_transfer_functions.ext_linear = true; break;
}
}
static const struct wp_color_manager_v1_listener color_manager_listener = {
.supported_intent = ignored_color_manager_event,
.supported_feature = on_supported_color_feature,
.supported_primaries_named = on_supported_color_primaries,
.supported_tf_named = on_supported_color_transfer_function,
.done = on_color_manger_features_done,
};
static void wmBaseHandlePing(void* data UNUSED,
struct xdg_wm_base* wmBase,
uint32_t serial)
@ -709,9 +662,6 @@ static void registryHandleGlobal(void* data UNUSED,
_glfw.wl.xdg_system_bell_v1 = wl_registry_bind(registry, name, &xdg_system_bell_v1_interface, 1);
} else if (is(xdg_toplevel_tag_manager_v1)) {
_glfw.wl.xdg_toplevel_tag_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_tag_manager_v1_interface, 1);
} else if (is(wp_color_manager_v1)) {
_glfw.wl.wp_color_manager_v1 = wl_registry_bind(registry, name, &wp_color_manager_v1_interface, 1);
wp_color_manager_v1_add_listener(_glfw.wl.wp_color_manager_v1, &color_manager_listener, NULL);
}
#undef is
}
@ -827,7 +777,6 @@ get_compositor_missing_capabilities(void) {
#define P(x) p += snprintf(p, sizeof(buf) - (p - buf), "%s ", x);
if (_glfw.wl.xdg_wm_base_version < 6) P("window-state-suspended");
if (_glfw.wl.xdg_wm_base_version < 5) P("window-capabilities");
if (!_glfw.wl.color_manager.has_needed_capabilities) P("color-manager");
#undef P
#undef C
while (p > buf && (p - 1)[0] == ' ') { p--; *p = 0; }
@ -836,25 +785,6 @@ get_compositor_missing_capabilities(void) {
GLFWAPI const char* glfwWaylandMissingCapabilities(void) { return get_compositor_missing_capabilities(); }
static void
image_description_failed(void *data UNUSED, struct wp_image_description_v1 *d, uint32_t cause, const char *msg) {
wp_image_description_v1_destroy(d);
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create color mamagement profile description with cause: %d and error: %s", cause, msg);
_glfw.wl.color_manager.image_description_done = true;
}
static void
image_description_ready(void *data UNUSED, struct wp_image_description_v1 *d, uint32_t identity UNUSED) {
_glfw.wl.color_manager.image_description_done = true;
_glfw.wl.color_manager.image_description = d;
}
static const struct wp_image_description_v1_listener image_description_listener = {
.failed = image_description_failed,
.ready = image_description_ready,
};
int _glfwPlatformInit(bool *supports_window_occlusion)
{
int i;
@ -915,23 +845,6 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
// Sync so we got all initial output events
wl_display_roundtrip(_glfw.wl.display);
// Sync so we get all color manager capabilities
if (_glfw.wl.wp_color_manager_v1) {
while (!_glfw.wl.color_manager.capabilities_reported) wl_display_roundtrip(_glfw.wl.display);
_glfw.wl.color_manager.has_needed_capabilities = \
_glfw.wl.color_manager.supported_transfer_functions.srgb &&
_glfw.wl.color_manager.supported_features.parametric &&
_glfw.wl.color_manager.supported_features.set_primaries;
if (_glfw.wl.color_manager.has_needed_capabilities) {
struct wp_image_description_creator_params_v1 *c = wp_color_manager_v1_create_parametric_creator(
_glfw.wl.wp_color_manager_v1);
wp_image_description_creator_params_v1_set_tf_named(c, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB);
wp_image_description_creator_params_v1_set_primaries_named(c, WP_COLOR_MANAGER_V1_PRIMARIES_SRGB);
wp_image_description_v1_add_listener(wp_image_description_creator_params_v1_create(c),
&image_description_listener, NULL);
}
}
for (i = 0; i < _glfw.monitorCount; ++i)
{
monitor = _glfw.monitors[i];
@ -1045,11 +958,6 @@ void _glfwPlatformTerminate(void)
xdg_system_bell_v1_destroy(_glfw.wl.xdg_system_bell_v1);
if (_glfw.wl.xdg_toplevel_tag_manager_v1)
xdg_toplevel_tag_manager_v1_destroy(_glfw.wl.xdg_toplevel_tag_manager_v1);
if (_glfw.wl.wp_color_manager_v1) {
if (_glfw.wl.color_manager.image_description)
wp_image_description_v1_destroy(_glfw.wl.color_manager.image_description);
wp_color_manager_v1_destroy(_glfw.wl.wp_color_manager_v1);
}
if (_glfw.wl.wp_single_pixel_buffer_manager_v1)
wp_single_pixel_buffer_manager_v1_destroy(_glfw.wl.wp_single_pixel_buffer_manager_v1);
if (_glfw.wl.wp_cursor_shape_manager_v1)

11
glfw/wl_platform.h vendored
View file

@ -70,7 +70,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#include "wayland-xdg-toplevel-icon-v1-client-protocol.h"
#include "wayland-xdg-system-bell-v1-client-protocol.h"
#include "wayland-xdg-toplevel-tag-v1-client-protocol.h"
#include "wayland-color-management-v1-client-protocol.h"
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define _glfw_dlclose(handle) dlclose(handle)
@ -172,7 +171,6 @@ typedef struct _GLFWwindowWayland
bool hovered;
bool transparent;
struct wl_surface* surface;
struct wp_color_management_surface_v1 *color_management;
bool waiting_for_swap_to_commit;
struct wl_egl_window* native;
struct wl_callback* callback;
@ -348,7 +346,6 @@ typedef struct _GLFWlibraryWayland
struct xdg_toplevel_icon_manager_v1* xdg_toplevel_icon_manager_v1;
struct xdg_system_bell_v1* xdg_system_bell_v1;
struct xdg_toplevel_tag_manager_v1* xdg_toplevel_tag_manager_v1;
struct wp_color_manager_v1* wp_color_manager_v1;
struct wp_cursor_shape_manager_v1* wp_cursor_shape_manager_v1;
struct wp_cursor_shape_device_v1* wp_cursor_shape_device_v1;
struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager_v1;
@ -399,14 +396,6 @@ typedef struct _GLFWlibraryWayland
PFN_wl_egl_window_resize window_resize;
} egl;
struct {
struct { bool gamma22, ext_linear, srgb; } supported_transfer_functions;
struct { bool srgb; } supported_primaries;
struct { bool parametric, set_primaries; } supported_features;
bool capabilities_reported, image_description_done, has_needed_capabilities;
struct wp_image_description_v1 *image_description;
} color_manager;
struct {
glfw_wl_xdg_activation_request *array;
size_t capacity, sz;

20
glfw/wl_window.c vendored
View file

@ -573,13 +573,6 @@ static const struct wp_fractional_scale_v1_listener fractional_scale_listener =
.preferred_scale = &fractional_scale_preferred_scale,
};
static void
ensure_color_manager_ready(void) {
if (_glfw.wl.wp_color_manager_v1 && !_glfw.wl.color_manager.image_description_done) {
while (!_glfw.wl.color_manager.image_description_done) wl_display_roundtrip(_glfw.wl.display);
}
}
static bool
create_surface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) {
window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor);
@ -587,16 +580,6 @@ create_surface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) {
wl_surface_add_listener(window->wl.surface, &surfaceListener, window);
wl_surface_set_user_data(window->wl.surface, window);
if (_glfw.wl.color_manager.has_needed_capabilities) {
ensure_color_manager_ready();
if (_glfw.wl.color_manager.image_description && !window->wl.color_management) {
window->wl.color_management = wp_color_manager_v1_get_surface(
_glfw.wl.wp_color_manager_v1, window->wl.surface);
wp_color_management_surface_v1_set_image_description(
window->wl.color_management, _glfw.wl.color_manager.image_description, WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL);
}
}
// If we already have been notified of the primary monitor scale, assume
// the window will be created on it and so avoid a rescale roundtrip in the common
// case of the window being shown on the primary monitor or all monitors having the same scale.
@ -1548,9 +1531,6 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
if (window->wl.layer_shell.zwlr_layer_surface_v1)
zwlr_layer_surface_v1_destroy(window->wl.layer_shell.zwlr_layer_surface_v1);
if (window->wl.color_management)
wp_color_management_surface_v1_destroy(window->wl.color_management);
if (window->wl.surface)
wl_surface_destroy(window->wl.surface);