Skip to content

Commit

Permalink
fix(SourceDevice): Allow blocking of input events from specific sourc…
Browse files Browse the repository at this point in the history
…e devices.
  • Loading branch information
pastaq authored and ShadowApex committed Mar 31, 2024
1 parent 185a350 commit 678e9c6
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 23 deletions.
2 changes: 2 additions & 0 deletions rootfs/usr/share/inputplumber/devices/50-legion_go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ matches:
# from these devices will be watched and translated according to the key map.
source_devices:
- group: keyboard # Block data
blocked: true
hidraw:
vendor_id: 0x17ef
product_id: 0x6182
Expand Down Expand Up @@ -56,6 +57,7 @@ source_devices:
product_id: "6182"
name: "Generic X-Box pad"
- group: keyboard
blocked: true
unique: false
evdev:
vendor_id: "17ef"
Expand Down
5 changes: 5 additions & 0 deletions rootfs/usr/share/inputplumber/schema/composite_device_v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@
"gamepad"
]
},
"blocked": {
"description": "If true, device will be grabbed but no events from this device will reach target devices. Defaults to false.",
"type": "boolean",
"default": false
},
"evdev": {
"$ref": "#/definitions/Evdev"
},
Expand Down
31 changes: 30 additions & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use thiserror::Error;

use crate::{
dmi::data::DMIData,
input::event::{native::NativeEvent, value::InputValue},
input::{
event::{native::NativeEvent, value::InputValue},
manager::SourceDeviceInfo,
},
procfs,
};

Expand Down Expand Up @@ -254,6 +257,7 @@ pub struct SourceDevice {
pub evdev: Option<Evdev>,
pub hidraw: Option<Hidraw>,
pub unique: Option<bool>,
pub blocked: Option<bool>,
}

#[derive(Debug, Deserialize, Clone, PartialEq)]
Expand Down Expand Up @@ -318,6 +322,31 @@ impl CompositeDeviceConfig {
.collect()
}

/// Returns a [SourceDevice] if it matches the given [SourceDeviceInfo].
pub fn get_matching_device(&self, device: &SourceDeviceInfo) -> Option<SourceDevice> {
match device {
SourceDeviceInfo::EvdevDeviceInfo(evdev) => {
for config in self.source_devices.iter() {
if let Some(evdev_config) = config.evdev.as_ref() {
if self.has_matching_evdev(evdev, evdev_config) {
return Some(config.clone());
}
}
}
}
SourceDeviceInfo::HIDRawDeviceInfo(hidraw) => {
for config in self.source_devices.iter() {
if let Some(hidraw_config) = config.hidraw.as_ref() {
if self.has_matching_hidraw(hidraw, hidraw_config) {
return Some(config.clone());
}
}
}
}
}
None
}

/// Returns true if a given hidraw device is within a list of hidraw configs.
pub fn has_matching_hidraw(&self, device: &DeviceInfo, hidraw_config: &Hidraw) -> bool {
log::debug!("Checking hidraw config: {:?}", hidraw_config);
Expand Down
4 changes: 2 additions & 2 deletions src/drivers/lego/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ impl Driver {

let report_id = buf[0];
let slice = &buf[..bytes_read];
log::trace!("Got Report ID: {report_id}");
log::trace!("Got Report Size: {bytes_read}");
//log::trace!("Got Report ID: {report_id}");
//log::trace!("Got Report Size: {bytes_read}");

match report_id {
DINPUTLEFT_DATA => {
Expand Down
51 changes: 42 additions & 9 deletions src/input/composite_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub enum InterceptMode {
/// dispatched as they come in.
#[derive(Debug, Clone)]
pub enum Command {
ProcessEvent(Event),
ProcessEvent(String, Event),
GetCapabilities(mpsc::Sender<HashSet<Capability>>),
SetInterceptMode(InterceptMode),
GetInterceptMode(mpsc::Sender<InterceptMode>),
Expand Down Expand Up @@ -280,6 +280,9 @@ pub struct CompositeDevice {
rx: broadcast::Receiver<Command>,
/// Source devices that this composite device will consume.
source_devices: Vec<SourceDevice>,
/// HashSet of source devices that are blocked from passing their input events to target
/// events.
source_devices_blocked: HashSet<String>,
/// Physical device path for source devices. E.g. ["/dev/input/event0"]
source_device_paths: Vec<String>,
/// All currently running source device threads
Expand Down Expand Up @@ -318,6 +321,7 @@ impl CompositeDevice {
tx,
rx,
source_devices: Vec::new(),
source_devices_blocked: HashSet::new(),
source_device_paths: Vec::new(),
source_device_tasks: JoinSet::new(),
source_devices_used: Vec::new(),
Expand Down Expand Up @@ -391,8 +395,8 @@ impl CompositeDevice {
while let Ok(cmd) = self.rx.recv().await {
//log::debug!("Received command: {:?}", cmd);
match cmd {
Command::ProcessEvent(event) => {
if let Err(e) = self.process_event(event).await {
Command::ProcessEvent(device_id, event) => {
if let Err(e) = self.process_event(device_id, event).await {
log::error!("Failed to process event: {:?}", e);
}
}
Expand Down Expand Up @@ -599,8 +603,17 @@ impl CompositeDevice {

/// Process a single event from a source device. Events are piped through
/// a translation layer, then dispatched to the appropriate target device(s)
async fn process_event(&mut self, raw_event: Event) -> Result<(), Box<dyn Error>> {
log::trace!("Received event: {:?}", raw_event);
async fn process_event(
&mut self,
device_id: String,
raw_event: Event,
) -> Result<(), Box<dyn Error>> {
log::trace!("Received event: {:?} from {device_id}", raw_event);

if self.source_devices_blocked.contains(&device_id) {
log::trace!("Blocking event! {:?}", raw_event);
return Ok(());
}

// Convert the event into a NativeEvent
let event: NativeEvent = match raw_event {
Expand Down Expand Up @@ -958,6 +971,7 @@ impl CompositeDevice {
if let Some(idx) = self.source_devices_used.iter().position(|str| str == &id) {
self.source_devices_used.remove(idx);
};
self.source_devices_blocked.remove(&id);
}
if id.starts_with("hidraw://") {
let name = id.strip_prefix("hidraw://").unwrap();
Expand All @@ -970,6 +984,7 @@ impl CompositeDevice {
if let Some(idx) = self.source_devices_used.iter().position(|str| str == &id) {
self.source_devices_used.remove(idx);
};
self.source_devices_blocked.remove(&id);
}

log::debug!(
Expand All @@ -987,11 +1002,11 @@ impl CompositeDevice {
/// Creates and adds a source device using the given [SourceDeviceInfo]
fn add_source_device(&mut self, device_info: SourceDeviceInfo) -> Result<(), Box<dyn Error>> {
let device_info = device_info.clone();
match device_info {
match device_info.clone() {
SourceDeviceInfo::EvdevDeviceInfo(info) => {
// Create an instance of the device
log::debug!("Adding source device: {:?}", info);
let device = source::evdev::EventDevice::new(info, self.tx.clone());
let device = source::evdev::EventDevice::new(info.clone(), self.tx.clone());

// Get the capabilities of the source device.
// TODO: When we *remove* a source device, we also need to remove
Expand All @@ -1012,7 +1027,16 @@ impl CompositeDevice {
let source_device = source::SourceDevice::EventDevice(device);
self.source_devices.push(source_device);
self.source_device_paths.push(device_path);
self.source_devices_used.push(id);
self.source_devices_used.push(id.clone());

// Check if this device should be blocked from sending events to target devices.
if let Some(device_config) = self.config.get_matching_device(&device_info) {
if let Some(blocked) = device_config.blocked {
if blocked {
self.source_devices_blocked.insert(id);
}
}
};
}

SourceDeviceInfo::HIDRawDeviceInfo(info) => {
Expand All @@ -1033,7 +1057,16 @@ impl CompositeDevice {
let source_device = source::SourceDevice::HIDRawDevice(device);
self.source_devices.push(source_device);
self.source_device_paths.push(device_path);
self.source_devices_used.push(id);
self.source_devices_used.push(id.clone());

// Check if this device should be blocked from sending events to target devices.
if let Some(device_config) = self.config.get_matching_device(&device_info) {
if let Some(blocked) = device_config.blocked {
if blocked {
self.source_devices_blocked.insert(id);
}
}
};
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/input/source/evdev.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use std::{collections::HashMap, error::Error};

use evdev::{AbsoluteAxisCode, Device, EventType, InputEvent, KeyCode};
use evdev::{AbsoluteAxisCode, Device, EventType, InputEvent};
use tokio::sync::broadcast;
use zbus::{fdo, Connection};
use zbus_macros::dbus_interface;

use crate::{
constants::BUS_PREFIX,
input::{
capability::{Capability, Gamepad, GamepadButton},
capability::Capability,
composite_device::Command,
event::{evdev::EvdevEvent, Event},
},
Expand Down Expand Up @@ -132,7 +132,8 @@ impl EventDevice {

// Send the event to the composite device
let event = Event::Evdev(evdev_event);
self.composite_tx.send(Command::ProcessEvent(event))?;
self.composite_tx
.send(Command::ProcessEvent(self.get_id(), event))?;
}
log::debug!("Stopped reading device events");

Expand Down
4 changes: 2 additions & 2 deletions src/input/source/hidraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,15 @@ impl HIDRawDevice {
if self.info.vendor_id() == steam_deck::VID && self.info.product_id() == steam_deck::PID {
log::info!("Detected Steam Deck");
let tx = self.composite_tx.clone();
let driver = steam_deck::DeckController::new(self.info.clone(), tx);
let driver = steam_deck::DeckController::new(self.info.clone(), tx, self.get_id());
driver.run().await?;
} else if self.info.vendor_id() == drivers::lego::driver::VID
&& (self.info.product_id() == drivers::lego::driver::PID
|| self.info.product_id() == drivers::lego::driver::PID2)
{
log::info!("Detected Legion Go");
let tx = self.composite_tx.clone();
let driver = lego::LegionController::new(self.info.clone(), tx);
let driver = lego::LegionController::new(self.info.clone(), tx, self.get_id());
driver.run().await?;
} else {
return Err(format!(
Expand Down
19 changes: 16 additions & 3 deletions src/input/source/hidraw/lego.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ use crate::{
pub struct LegionController {
info: DeviceInfo,
composite_tx: broadcast::Sender<Command>,
device_id: String,
}

impl LegionController {
pub fn new(info: DeviceInfo, composite_tx: broadcast::Sender<Command>) -> Self {
Self { info, composite_tx }
pub fn new(
info: DeviceInfo,
composite_tx: broadcast::Sender<Command>,
device_id: String,
) -> Self {
Self {
info,
composite_tx,
device_id,
}
}

pub async fn run(&self) -> Result<(), Box<dyn Error>> {
Expand All @@ -33,6 +42,7 @@ impl LegionController {

// Spawn a blocking task to read the events
let device_path = path.clone();
let device_id = self.device_id.clone();
let task =
tokio::task::spawn_blocking(move || -> Result<(), Box<dyn Error + Send + Sync>> {
let mut driver = Driver::new(device_path.clone())?;
Expand All @@ -44,7 +54,10 @@ impl LegionController {
if matches!(event.as_capability(), Capability::NotImplemented) {
continue;
}
tx.send(Command::ProcessEvent(Event::Native(event)))?;
tx.send(Command::ProcessEvent(
device_id.clone(),
Event::Native(event),
))?;
}

// Polling interval is about 4ms so we can sleep a little
Expand Down
19 changes: 16 additions & 3 deletions src/input/source/hidraw/steam_deck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,20 @@ pub const PID: u16 = 0x1205;
pub struct DeckController {
info: DeviceInfo,
composite_tx: broadcast::Sender<Command>,
device_id: String,
}

impl DeckController {
pub fn new(info: DeviceInfo, composite_tx: broadcast::Sender<Command>) -> Self {
Self { info, composite_tx }
pub fn new(
info: DeviceInfo,
composite_tx: broadcast::Sender<Command>,
device_id: String,
) -> Self {
Self {
info,
composite_tx,
device_id,
}
}

pub async fn run(&self) -> Result<(), Box<dyn Error>> {
Expand All @@ -36,6 +45,7 @@ impl DeckController {

// Spawn a blocking task to read the events
let device_path = path.clone();
let device_id = self.device_id.clone();
let task =
tokio::task::spawn_blocking(move || -> Result<(), Box<dyn Error + Send + Sync>> {
let mut driver = Driver::new(device_path.clone())?;
Expand All @@ -47,7 +57,10 @@ impl DeckController {
if matches!(event.as_capability(), Capability::NotImplemented) {
continue;
}
tx.send(Command::ProcessEvent(Event::Native(event)))?;
tx.send(Command::ProcessEvent(
device_id.clone(),
Event::Native(event),
))?;
}

// Polling interval is about 4ms so we can sleep a little
Expand Down

0 comments on commit 678e9c6

Please sign in to comment.