From ceb50e8c88a83df13464c7305a58fee7ec8a292d Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 29 Mar 2026 19:42:53 +0200 Subject: [PATCH] Server: implement color management protocols Thank you wlroots! --- build.zig | 4 ++++ build.zig.zon | 4 ++-- river/Root.zig | 2 ++ river/Server.zig | 30 +++++++++++++++++++++++++++++- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 641e0a7..2377cfd 100644 --- a/build.zig +++ b/build.zig @@ -86,6 +86,8 @@ pub fn build(b: *Build) !void { scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml"); scanner.addSystemProtocol("stable/tablet/tablet-v2.xml"); + scanner.addSystemProtocol("staging/color-management/color-management-v1.xml"); + scanner.addSystemProtocol("staging/color-representation/color-representation-v1.xml"); scanner.addSystemProtocol("staging/cursor-shape/cursor-shape-v1.xml"); scanner.addSystemProtocol("staging/ext-session-lock/ext-session-lock-v1.xml"); scanner.addSystemProtocol("staging/tearing-control/tearing-control-v1.xml"); @@ -120,6 +122,8 @@ pub fn build(b: *Build) !void { scanner.generate("ext_session_lock_manager_v1", 1); scanner.generate("wp_cursor_shape_manager_v1", 1); scanner.generate("wp_tearing_control_manager_v1", 1); + scanner.generate("wp_color_manager_v1", 2); + scanner.generate("wp_color_representation_manager_v1", 1); scanner.generate("zriver_control_v1", 1); scanner.generate("zriver_status_manager_v1", 4); diff --git a/build.zig.zon b/build.zig.zon index a153044..b45edf1 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -17,8 +17,8 @@ .hash = "wayland-0.4.0-lQa1khbMAQAsLS2eBR7M5lofyEGPIbu2iFDmoz8lPC27", }, .wlroots = .{ - .url = "git+https://codeberg.org/ifreund/zig-wlroots?ref=master#22180881fd3f67b2449a34dd7dd978c3291ccc15", - .hash = "wlroots-0.20.0-dev-jmOlcu8pBACQUTdOlveX6Evwnm4BeqEANGGwgxR-pSHn", + .url = "git+https://codeberg.org/ifreund/zig-wlroots?ref=master#eb7631742812665c10ff3a9b4b069c3d0cc2017b", + .hash = "wlroots-0.20.0-dev-jmOlcuxBBAC74vcynqYuP8sjbyLrH6cnNlKzhXKg8zVO", }, .xkbcommon = .{ .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.3.0.tar.gz", diff --git a/river/Root.zig b/river/Root.zig index 5649bf6..b8dbe26 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -124,6 +124,8 @@ pub fn init(root: *Root) !void { const gamma_control_manager = try wlr.GammaControlManagerV1.create(server.wl_server); scene.setGammaControlManagerV1(gamma_control_manager); + if (server.color_manager) |color_manager| scene.setColorManagerV1(color_manager); + const interactive_content = try scene.tree.createSceneTree(); const drag_icons = try scene.tree.createSceneTree(); const hidden_tree = try scene.tree.createSceneTree(); diff --git a/river/Server.zig b/river/Server.zig index 258ddfd..eb5de2b 100644 --- a/river/Server.zig +++ b/river/Server.zig @@ -22,7 +22,9 @@ const assert = std.debug.assert; const mem = std.mem; const posix = std.posix; const wlr = @import("wlroots"); -const wl = @import("wayland").server.wl; +const wayland = @import("wayland"); +const wl = wayland.server.wl; +const wp = wayland.server.wp; const c = @import("c.zig").c; const util = @import("util.zig"); @@ -68,6 +70,9 @@ linux_dmabuf: ?*wlr.LinuxDmabufV1 = null, linux_drm_syncobj_manager: ?*wlr.LinuxDrmSyncobjManagerV1 = null, single_pixel_buffer_manager: *wlr.SinglePixelBufferManagerV1, +color_manager: ?*wlr.ColorManagerV1 = null, +color_representation_manager: *wlr.ColorRepresentationManagerV1, + viewporter: *wlr.Viewporter, fractional_scale_manager: *wlr.FractionalScaleManagerV1, compositor: *wlr.Compositor, @@ -151,6 +156,8 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void { .shm = try wlr.Shm.createWithRenderer(wl_server, 2, renderer), .single_pixel_buffer_manager = try wlr.SinglePixelBufferManagerV1.create(wl_server), + .color_representation_manager = try wlr.ColorRepresentationManagerV1.createWithRenderer(wl_server, 1, renderer), + .viewporter = try wlr.Viewporter.create(wl_server), .fractional_scale_manager = try wlr.FractionalScaleManagerV1.create(wl_server, 1), .compositor = compositor, @@ -198,6 +205,23 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void { } } + if (renderer.features.input_color_transform) { + const render_intents: []const wp.ColorManagerV1.RenderIntent = &.{.perceptual}; + const transfer_functions = renderer.transferFunctionList(); + defer std.c.free(transfer_functions.ptr); + const primaries = renderer.primariesList(); + defer std.c.free(primaries.ptr); + server.color_manager = try wlr.ColorManagerV1.create(wl_server, 2, .{ + .features = .{ + .parametric = true, + .set_mastering_display_primaries = true, + }, + .render_intents = render_intents, + .transfer_functions = transfer_functions, + .primaries = primaries, + }); + } + if (build_options.xwayland and runtime_xwayland) { server.xwayland = try wlr.Xwayland.create(wl_server, compositor, false); server.xwayland.?.events.new_surface.add(&server.new_xwayland_surface); @@ -310,6 +334,9 @@ fn allowlist(server: *Server, global: *const wl.Global) bool { if (server.linux_drm_syncobj_manager) |linux_drm_syncobj_manager| { if (global == linux_drm_syncobj_manager.global) return true; } + if (server.color_manager) |color_manager| { + if (global == color_manager.global) return true; + } // We must use the getInterface() approach for dynamically created globals // such as wl_output and wl_seat since the wl_global_create() function will @@ -327,6 +354,7 @@ fn allowlist(server: *Server, global: *const wl.Global) bool { return global == server.fixes.global or global == server.shm.global or global == server.single_pixel_buffer_manager.global or + global == server.color_representation_manager.global or global == server.viewporter.global or global == server.fractional_scale_manager.global or global == server.compositor.global or