From 248eb324a519515581b0dc63ab15f5bc2cef7fb0 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 26 Feb 2025 16:16:38 +0100 Subject: [PATCH] config: allow disabling color-management --- jay-config/src/_private/client.rs | 4 + jay-config/src/_private/ipc.rs | 3 + jay-config/src/lib.rs | 9 +++ src/cli.rs | 8 +- src/cli/color_management.rs | 75 +++++++++++++++++++ src/compositor.rs | 1 + src/config/handler.rs | 7 ++ src/globals.rs | 7 +- src/ifs.rs | 1 + .../color_management/wp_color_manager_v1.rs | 5 ++ src/ifs/jay_color_management.rs | 64 ++++++++++++++++ src/ifs/jay_compositor.rs | 19 ++++- src/state.rs | 1 + src/tools/tool_client.rs | 2 +- toml-config/src/config.rs | 6 +- toml-config/src/config/parsers.rs | 1 + .../src/config/parsers/color_management.rs | 48 ++++++++++++ toml-config/src/config/parsers/config.rs | 16 ++++ toml-config/src/lib.rs | 11 ++- toml-spec/spec/spec.generated.json | 15 ++++ toml-spec/spec/spec.generated.md | 40 ++++++++++ toml-spec/spec/spec.yaml | 35 +++++++++ wire/jay_color_management.txt | 15 ++++ wire/jay_compositor.txt | 4 + 24 files changed, 388 insertions(+), 9 deletions(-) create mode 100644 src/cli/color_management.rs create mode 100644 src/ifs/jay_color_management.rs create mode 100644 toml-config/src/config/parsers/color_management.rs create mode 100644 wire/jay_color_management.txt diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index f4f263f4..d60f2864 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -763,6 +763,10 @@ impl Client { self.send(&ClientMessage::SetUiDragThreshold { threshold }); } + pub fn set_color_management_enabled(&self, enabled: bool) { + self.send(&ClientMessage::SetColorManagementEnabled { enabled }); + } + pub fn connector_connected(&self, connector: Connector) -> bool { let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector }); get_response!(res, false, ConnectorConnected { connected }); diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index a68b05ec..6b3e5116 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -530,6 +530,9 @@ pub enum ClientMessage<'a> { SetIdleGracePeriod { period: Duration, }, + SetColorManagementEnabled { + enabled: bool, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index bc55cc56..164551d3 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -264,3 +264,12 @@ pub fn set_ui_drag_enabled(enabled: bool) { pub fn set_ui_drag_threshold(threshold: i32) { get!().set_ui_drag_threshold(threshold); } + +/// Enables or disables the color-management protocol. +/// +/// The default is `false`. +/// +/// Affected applications must be restarted for this to take effect. +pub fn set_color_management_enabled(enabled: bool) { + get!().set_color_management_enabled(enabled); +} diff --git a/src/cli.rs b/src/cli.rs index 50c92b31..f809a36a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,5 @@ mod color; +mod color_management; mod damage_tracking; mod duration; mod generate; @@ -17,8 +18,8 @@ mod xwayland; use { crate::{ cli::{ - damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs, randr::RandrArgs, - xwayland::XwaylandArgs, + color_management::ColorManagementArgs, damage_tracking::DamageTrackingArgs, + idle::IdleCmd, input::InputArgs, randr::RandrArgs, xwayland::XwaylandArgs, }, compositor::start_compositor, format::{Format, ref_formats}, @@ -78,6 +79,8 @@ pub enum Cmd { DamageTracking(DamageTrackingArgs), /// Inspect/modify xwayland settings. Xwayland(XwaylandArgs), + /// Inspect/modify the color-management settings. + ColorManagement(ColorManagementArgs), #[cfg(feature = "it")] RunTests, } @@ -235,6 +238,7 @@ pub fn main() { Cmd::Input(a) => input::main(cli.global, a), Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a), Cmd::Xwayland(a) => xwayland::main(cli.global, a), + Cmd::ColorManagement(a) => color_management::main(cli.global, a), #[cfg(feature = "it")] Cmd::RunTests => crate::it::run_tests(), } diff --git a/src/cli/color_management.rs b/src/cli/color_management.rs new file mode 100644 index 00000000..b70a8c9d --- /dev/null +++ b/src/cli/color_management.rs @@ -0,0 +1,75 @@ +use { + crate::{ + cli::GlobalArgs, + tools::tool_client::{Handle, ToolClient, with_tool_client}, + wire::{JayColorManagementId, jay_color_management, jay_compositor}, + }, + clap::{Args, Subcommand}, + std::{cell::Cell, rc::Rc}, +}; + +#[derive(Args, Debug)] +pub struct ColorManagementArgs { + #[clap(subcommand)] + pub command: Option, +} + +#[derive(Subcommand, Debug, Default)] +pub enum ColorManagementCmd { + /// Print the color-management status. + #[default] + Status, + /// Enable the color-management protocol. + Enable, + /// Disable the color-management protocol. + Disable, +} + +pub fn main(global: GlobalArgs, args: ColorManagementArgs) { + with_tool_client(global.log_level.into(), |tc| async move { + let cm = ColorManagement { tc: tc.clone() }; + cm.run(args).await; + }); +} + +struct ColorManagement { + tc: Rc, +} + +impl ColorManagement { + async fn run(self, args: ColorManagementArgs) { + let tc = &self.tc; + let comp = tc.jay_compositor().await; + let id = tc.id(); + tc.send(jay_compositor::GetColorManagement { self_id: comp, id }); + match args.command.unwrap_or_default() { + ColorManagementCmd::Status => self.status(id).await, + ColorManagementCmd::Enable => self.set_enabled(id, true).await, + ColorManagementCmd::Disable => self.set_enabled(id, false).await, + } + } + + async fn status(self, id: JayColorManagementId) { + let tc = &self.tc; + tc.send(jay_color_management::Get { self_id: id }); + let enabled = Rc::new(Cell::new(false)); + jay_color_management::Enabled::handle(tc, id, enabled.clone(), |iv, msg| { + iv.set(msg.enabled != 0); + }); + tc.round_trip().await; + if enabled.get() { + println!("Enabled"); + } else { + println!("Disabled"); + } + } + + async fn set_enabled(self, id: JayColorManagementId, enabled: bool) { + let tc = &self.tc; + tc.send(jay_color_management::SetEnabled { + self_id: id, + enabled: enabled as _, + }); + tc.round_trip().await; + } +} diff --git a/src/compositor.rs b/src/compositor.rs index ab01b1d5..06ca97d2 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -283,6 +283,7 @@ fn start_compositor2( tray_item_ids: Default::default(), data_control_device_ids: Default::default(), workspace_managers: Default::default(), + color_management_enabled: Cell::new(false), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); diff --git a/src/config/handler.rs b/src/config/handler.rs index 5d8537c5..f18ccc79 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -926,6 +926,10 @@ impl ConfigProxyHandler { self.state.explicit_sync_enabled.set(enabled); } + fn handle_set_color_management_enabled(&self, enabled: bool) { + self.state.color_management_enabled.set(enabled); + } + fn handle_get_socket_path(&self) { match self.state.acceptor.get() { Some(a) => { @@ -1986,6 +1990,9 @@ impl ConfigProxyHandler { ClientMessage::SetIdleGracePeriod { period } => { self.handle_set_idle_grace_period(period) } + ClientMessage::SetColorManagementEnabled { enabled } => { + self.handle_set_color_management_enabled(enabled) + } } Ok(()) } diff --git a/src/globals.rs b/src/globals.rs index a50d0e62..738e647c 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -135,6 +135,10 @@ pub trait Global: GlobalBase { fn xwayland_only(&self) -> bool { false } + fn exposed(&self, state: &State) -> bool { + let _ = state; + true + } } pub struct Globals { @@ -297,7 +301,8 @@ impl Globals { ($singleton:expr) => { for global in globals.values() { if global.singleton() == $singleton { - if caps.contains(global.required_caps()) + if global.exposed(®istry.client.state) + && caps.contains(global.required_caps()) && (xwayland || !global.xwayland_only()) { registry.send_global(global); diff --git a/src/ifs.rs b/src/ifs.rs index 4108e80c..d33343b9 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -10,6 +10,7 @@ pub mod ext_output_image_capture_source_manager_v1; pub mod ext_session_lock_manager_v1; pub mod ext_session_lock_v1; pub mod ipc; +pub mod jay_color_management; pub mod jay_compositor; pub mod jay_damage_tracking; pub mod jay_ei_session; diff --git a/src/ifs/color_management/wp_color_manager_v1.rs b/src/ifs/color_management/wp_color_manager_v1.rs index 2bfbb2ef..72119287 100644 --- a/src/ifs/color_management/wp_color_manager_v1.rs +++ b/src/ifs/color_management/wp_color_manager_v1.rs @@ -14,6 +14,7 @@ use { }, leaks::Tracker, object::{Object, Version}, + state::State, wire::{ WpColorManagerV1Id, wp_color_manager_v1::{SupportedIntent, *}, @@ -198,6 +199,10 @@ impl Global for WpColorManagerV1Global { fn version(&self) -> u32 { 1 } + + fn exposed(&self, state: &State) -> bool { + state.color_management_enabled.get() + } } simple_add_global!(WpColorManagerV1Global); diff --git a/src/ifs/jay_color_management.rs b/src/ifs/jay_color_management.rs new file mode 100644 index 00000000..565160f3 --- /dev/null +++ b/src/ifs/jay_color_management.rs @@ -0,0 +1,64 @@ +use { + crate::{ + client::{Client, ClientError}, + leaks::Tracker, + object::{Object, Version}, + wire::{JayColorManagementId, jay_color_management::*}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct JayColorManagement { + pub id: JayColorManagementId, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl JayColorManagement { + fn send_enabled(&self) { + self.client.event(Enabled { + self_id: self.id, + enabled: self.client.state.color_management_enabled.get() as u32, + }); + } +} + +impl JayColorManagementRequestHandler for JayColorManagement { + type Error = JayColorManagementError; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } + + fn get(&self, _req: Get, _slf: &Rc) -> Result<(), Self::Error> { + self.send_enabled(); + Ok(()) + } + + fn set_enabled(&self, req: SetEnabled, _slf: &Rc) -> Result<(), Self::Error> { + self.client + .state + .color_management_enabled + .set(req.enabled != 0); + Ok(()) + } +} + +object_base! { + self = JayColorManagement; + version = self.version; +} + +impl Object for JayColorManagement {} + +simple_add_obj!(JayColorManagement); + +#[derive(Debug, Error)] +pub enum JayColorManagementError { + #[error(transparent)] + ClientError(Box), +} +efrom!(JayColorManagementError, ClientError); diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index 86893814..2da874f1 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -4,6 +4,7 @@ use { client::{CAP_JAY_COMPOSITOR, Client, ClientCaps, ClientError}, globals::{Global, GlobalName}, ifs::{ + jay_color_management::JayColorManagement, jay_ei_session_builder::JayEiSessionBuilder, jay_idle::JayIdle, jay_input::JayInput, @@ -72,7 +73,7 @@ impl Global for JayCompositorGlobal { } fn version(&self) -> u32 { - 13 + 14 } fn required_caps(&self) -> ClientCaps { @@ -439,6 +440,22 @@ impl JayCompositorRequestHandler for JayCompositor { obj.done(tl); Ok(()) } + + fn get_color_management( + &self, + req: GetColorManagement, + _slf: &Rc, + ) -> Result<(), Self::Error> { + let obj = Rc::new(JayColorManagement { + id: req.id, + client: self.client.clone(), + tracker: Default::default(), + version: self.version, + }); + track!(self.client, obj); + self.client.add_client_obj(&obj)?; + Ok(()) + } } object_base! { diff --git a/src/state.rs b/src/state.rs index d3b2694d..07580bc3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -233,6 +233,7 @@ pub struct State { pub tray_item_ids: TrayItemIds, pub data_control_device_ids: DataControlDeviceIds, pub workspace_managers: WorkspaceManagerState, + pub color_management_enabled: Cell, } // impl Drop for State { diff --git a/src/tools/tool_client.rs b/src/tools/tool_client.rs index 12f801fc..04ab2684 100644 --- a/src/tools/tool_client.rs +++ b/src/tools/tool_client.rs @@ -332,7 +332,7 @@ impl ToolClient { self_id: s.registry, name: s.jay_compositor.0, interface: JayCompositor.name(), - version: s.jay_compositor.1.min(13), + version: s.jay_compositor.1.min(14), id: id.into(), }); self.jay_compositor.set(Some(id)); diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index 43730a22..d13a3522 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -11,7 +11,10 @@ use { crate::{ config::{ context::Context, - parsers::config::{ConfigParser, ConfigParserError}, + parsers::{ + color_management::ColorManagement, + config::{ConfigParser, ConfigParserError}, + }, }, toml::{self}, }, @@ -358,6 +361,7 @@ pub struct Config { pub libei: Libei, pub ui_drag: UiDrag, pub xwayland: Option, + pub color_management: Option, } #[derive(Debug, Error)] diff --git a/toml-config/src/config/parsers.rs b/toml-config/src/config/parsers.rs index 57a8d41d..a04a1ce1 100644 --- a/toml-config/src/config/parsers.rs +++ b/toml-config/src/config/parsers.rs @@ -8,6 +8,7 @@ use { pub mod action; mod color; +pub mod color_management; pub mod config; mod connector; mod connector_match; diff --git a/toml-config/src/config/parsers/color_management.rs b/toml-config/src/config/parsers/color_management.rs new file mode 100644 index 00000000..53a64bb9 --- /dev/null +++ b/toml-config/src/config/parsers/color_management.rs @@ -0,0 +1,48 @@ +use { + crate::{ + config::{ + context::Context, + extractor::{Extractor, ExtractorError, bol, opt}, + parser::{DataType, ParseResult, Parser, UnexpectedDataType}, + }, + toml::{ + toml_span::{DespanExt, Span, Spanned}, + toml_value::Value, + }, + }, + indexmap::IndexMap, + thiserror::Error, +}; + +#[derive(Debug, Error)] +pub enum ColorManagementParserError { + #[error(transparent)] + Expected(#[from] UnexpectedDataType), + #[error(transparent)] + Extract(#[from] ExtractorError), +} + +pub struct ColorManagementParser<'a>(pub &'a Context<'a>); + +#[derive(Clone, Debug)] +pub struct ColorManagement { + pub enabled: Option, +} + +impl Parser for ColorManagementParser<'_> { + type Value = ColorManagement; + type Error = ColorManagementParserError; + const EXPECTED: &'static [DataType] = &[DataType::Table]; + + fn parse_table( + &mut self, + span: Span, + table: &IndexMap, Spanned>, + ) -> ParseResult { + let mut ext = Extractor::new(self.0, span, table); + let (enabled,) = ext.extract((opt(bol("enabled")),))?; + Ok(ColorManagement { + enabled: enabled.despan(), + }) + } +} diff --git a/toml-config/src/config/parsers/config.rs b/toml-config/src/config/parsers/config.rs index 4ca65a88..e1ad68bc 100644 --- a/toml-config/src/config/parsers/config.rs +++ b/toml-config/src/config/parsers/config.rs @@ -7,6 +7,7 @@ use { parser::{DataType, ParseResult, Parser, UnexpectedDataType}, parsers::{ action::ActionParser, + color_management::ColorManagementParser, connector::ConnectorsParser, drm_device::DrmDevicesParser, drm_device_match::DrmDeviceMatchParser, @@ -117,6 +118,7 @@ impl Parser for ConfigParser<'_> { ui_drag_val, xwayland_val, ), + (color_management_val,), ) = ext.extract(( ( opt(val("keymap")), @@ -154,6 +156,7 @@ impl Parser for ConfigParser<'_> { opt(val("ui-drag")), opt(val("xwayland")), ), + (opt(val("color-management")),), ))?; let mut keymap = None; if let Some(value) = keymap_val { @@ -366,6 +369,18 @@ impl Parser for ConfigParser<'_> { } } } + let mut color_management = None; + if let Some(value) = color_management_val { + match value.parse(&mut ColorManagementParser(self.0)) { + Ok(v) => color_management = Some(v), + Err(e) => { + log::warn!( + "Could not parse the color-management settings: {}", + self.0.error(e) + ); + } + } + } Ok(Config { keymap, repeat_rate, @@ -396,6 +411,7 @@ impl Parser for ConfigParser<'_> { libei, ui_drag, xwayland, + color_management, }) } } diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 7fa7cb2d..4883548c 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -23,9 +23,9 @@ use { is_reload, keyboard::{Keymap, ModifiedKeySym}, logging::set_log_level, - on_devices_enumerated, on_idle, quit, reload, set_default_workspace_capture, - set_explicit_sync_enabled, set_idle, set_idle_grace_period, set_ui_drag_enabled, - set_ui_drag_threshold, + on_devices_enumerated, on_idle, quit, reload, set_color_management_enabled, + set_default_workspace_capture, set_explicit_sync_enabled, set_idle, set_idle_grace_period, + set_ui_drag_enabled, set_ui_drag_threshold, status::{set_i3bar_separator, set_status, set_status_command, unset_status_command}, switch_to_vt, theme::{reset_colors, reset_font, reset_sizes, set_font}, @@ -1078,6 +1078,11 @@ fn load_config(initial_load: bool, persistent: &Rc) { set_x_scaling_mode(mode); } } + if let Some(cm) = config.color_management { + if let Some(enabled) = cm.enabled { + set_color_management_enabled(enabled); + } + } } fn create_command(exec: &Exec) -> Command { diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 94862eb6..4b3dfc8b 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -427,6 +427,17 @@ "type": "string", "description": "A color.\n\nThe format should be one of the following:\n\n- `#rgb`\n- `#rrggbb`\n- `#rgba`\n- `#rrggbba`\n" }, + "ColorManagement": { + "description": "Describes color-management settings.\n\n- Example:\n\n ```toml\n [color-management]\n enabled = true\n ```\n", + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether the color management protocol is enabled.\n\nThis has no effect on running applications.\n\nThe default is `false`.\n" + } + }, + "required": [] + }, "ComplexShortcut": { "description": "Describes a complex shortcut.\n\n- Example:\n\n ```toml\n [complex-shortcuts.XF86AudioRaiseVolume]\n mod-mask = \"alt\"\n action = { type = \"exec\", exec = [\"pactl\", \"set-sink-volume\", \"0\", \"+10%\"] }\n ```\n", "type": "object", @@ -597,6 +608,10 @@ "xwayland": { "description": "Configures the Xwayland settings.\n\n- Example:\n\n ```toml\n xwayland = { scaling-mode = \"downscaled\" }\n ```\n", "$ref": "#/$defs/Xwayland" + }, + "color-management": { + "description": "Configures the color-management settings.\n\n- Example:\n\n ```toml\n [color-management]\n enabled = true\n ```\n", + "$ref": "#/$defs/ColorManagement" } }, "required": [] diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index a494591f..3b9c8352 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -590,6 +590,33 @@ The format should be one of the following: Values of this type should be strings. + +### `ColorManagement` + +Describes color-management settings. + +- Example: + + ```toml + [color-management] + enabled = true + ``` + +Values of this type should be tables. + +The table has the following fields: + +- `enabled` (optional): + + Whether the color management protocol is enabled. + + This has no effect on running applications. + + The default is `false`. + + The value of this field should be a boolean. + + ### `ComplexShortcut` @@ -1178,6 +1205,19 @@ The table has the following fields: The value of this field should be a [Xwayland](#types-Xwayland). +- `color-management` (optional): + + Configures the color-management settings. + + - Example: + + ```toml + [color-management] + enabled = true + ``` + + The value of this field should be a [ColorManagement](#types-ColorManagement). + ### `Connector` diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index 9035e2f1..89752fbb 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -2286,6 +2286,18 @@ Config: ```toml xwayland = { scaling-mode = "downscaled" } ``` + color-management: + ref: ColorManagement + required: false + description: | + Configures the color-management settings. + + - Example: + + ```toml + [color-management] + enabled = true + ``` Idle: @@ -2727,3 +2739,26 @@ XScalingMode: Additionally, this mode requires the X window to scale its contents itself. In the example above, you might achieve this by setting the environment variable `GDK_SCALE=2`. + + +ColorManagement: + kind: table + description: | + Describes color-management settings. + + - Example: + + ```toml + [color-management] + enabled = true + ``` + fields: + enabled: + description: | + Whether the color management protocol is enabled. + + This has no effect on running applications. + + The default is `false`. + kind: boolean + required: false diff --git a/wire/jay_color_management.txt b/wire/jay_color_management.txt new file mode 100644 index 00000000..8d75eb0a --- /dev/null +++ b/wire/jay_color_management.txt @@ -0,0 +1,15 @@ +request destroy { + +} + +request get { + +} + +request set_enabled { + enabled: u32, +} + +event enabled { + enabled: u32, +} diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index 84f70406..79866072 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -101,6 +101,10 @@ request get_toplevel (since = 12) { toplevel_id: str, } +request get_color_management (since = 14) { + id: id(jay_color_management), +} + # events event client_id {