From 962da19175918322e237bda0d95a657da06a708e Mon Sep 17 00:00:00 2001 From: William Edwards Date: Thu, 21 Nov 2024 12:28:27 -0800 Subject: [PATCH] feat(CompositeDeviceConfig): add 'maximum_sources' to define max source devices per composite device --- .../schema/composite_device_v1.json | 7 +++- src/config/mod.rs | 3 +- src/input/manager.rs | 42 +++++++++++++++++-- src/udev/device.rs | 5 +++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/rootfs/usr/share/inputplumber/schema/composite_device_v1.json b/rootfs/usr/share/inputplumber/schema/composite_device_v1.json index f988cc1..41c5ba0 100644 --- a/rootfs/usr/share/inputplumber/schema/composite_device_v1.json +++ b/rootfs/usr/share/inputplumber/schema/composite_device_v1.json @@ -20,10 +20,15 @@ "type": "string" }, "single_source": { - "description": "If true, this composite device should only use one source device. Defaults to false.", + "description": "DEPRECATED: use 'maximum_sources' instead. If true, this composite device should only use one source device. Defaults to false.", "type": "boolean", "default": false }, + "maximum_sources": { + "description": "Maximum number of source devices that this composite device can manage. When this composite device reaches this maximum and a new matching source device is detected, a new composite device will be created instead of adding the source device to the existing one. Any value less than 1 indicates no maximum. Defaults to 0 (unlimited).", + "type": "integer", + "default": 0 + }, "matches": { "description": "Only use this profile if *any* of the given DMI system matches match. If this list is empty, then the source devices will *always* be checked.", "type": "array", diff --git a/src/config/mod.rs b/src/config/mod.rs index 06184b1..e9ad6be 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -379,7 +379,8 @@ pub struct CompositeDeviceConfig { pub kind: String, pub name: String, pub matches: Vec, - pub single_source: Option, + pub single_source: Option, // DEPRECATED; use 'maximum_sources' instead + pub maximum_sources: Option, pub capability_map_id: Option, pub source_devices: Vec, pub target_devices: Option>, diff --git a/src/input/manager.rs b/src/input/manager.rs index ab83d7e..35c0ba1 100644 --- a/src/input/manager.rs +++ b/src/input/manager.rs @@ -706,11 +706,36 @@ impl Manager { log::trace!("{:?} is a single source device. Skipping.", config.name); continue; } + if config.maximum_sources.unwrap_or(0) == 1 { + log::trace!("{:?} is a single source device. Skipping.", config.name); + continue; + } log::trace!( "Composite device has {} source devices defined", config.source_devices.len() ); + // If the CompositeDevice only allows a maximum number of source devices, + // check to see if that limit has been reached. If that limit is reached, + // then a new CompositeDevice will be created for the source device. + if let Some(max_sources) = config.maximum_sources { + // If maximum_sources is less than 1 (e.g. 0, -1) then consider + // the maximum to be 'unlimited'. + if max_sources > 0 { + // Check to see how many source devices this composite device is + // currently managing. + if let Some(sources) = self.composite_device_sources.get(composite_device) { + let sources_count = sources.len() as i32; + if sources_count >= max_sources { + log::trace!( + "{composite_device:?} maximum source devices reached: {max_sources}. Skipping." + ); + continue; + } + } + } + } + // Check if this device matches any source udev configs of the running // CompositeDevice. for source_device in config.source_devices.iter() { @@ -1650,17 +1675,27 @@ impl Manager { }; // Wait until the device has initialized with udev - // TODO: Add max wait time for udev initialization + const MAX_TRIES: u8 = 80; + let mut attempt: u8 = 0; loop { + // Break after max attempts reached + if attempt > MAX_TRIES { + log::warn!("Unable to create initialized UdevDevice for {base_path}/{name} after {MAX_TRIES} attempts."); + continue 'outer; + } + + // Try to get the device from udev to check its initialization state { let Ok(device) = ::udev::Device::from_subsystem_sysname( subsystem.to_string(), name.clone(), ) else { - log::warn!( + log::debug!( "Unable to create UdevDevice from {base_path}/{name} to check initialization" ); - continue 'outer; + attempt += 1; + tokio::time::sleep(Duration::from_millis(10)).await; + continue; }; if device.is_initialized() { @@ -1670,6 +1705,7 @@ impl Manager { log::trace!("{base_path}/{name} is not yet initialized by udev"); tokio::time::sleep(Duration::from_millis(10)).await; + attempt += 1; } // Create a udev device for the device diff --git a/src/udev/device.rs b/src/udev/device.rs index 9382314..f1718c9 100644 --- a/src/udev/device.rs +++ b/src/udev/device.rs @@ -520,6 +520,11 @@ impl UdevDevice { self.sysname.clone() } + /// Returns the syspath of the device. + /// + /// The path is an absolute path and includes the sys mount point. For example, the syspath for + /// `tty0` could be `/sys/devices/virtual/tty/tty0`, which includes the sys mount point, + /// `/sys`. pub fn syspath(&self) -> String { self.syspath.clone() }