Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ShadowApex committed Feb 15, 2025
1 parent 89e861a commit 55c7085
Show file tree
Hide file tree
Showing 12 changed files with 1,563 additions and 71 deletions.
1,223 changes: 1,157 additions & 66 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ libiio = "*"
libevdev = "*"

[dependencies]
cec-rs = "11.0.2"
clap = { version = "4.5.27", features = ["derive"] }
clap_complete = "4.5.42"
env_logger = "0.11.3"
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ target/release/$(NAME): $(ALL_RS) Cargo.lock
all: build debug ## Build release and debug builds

.PHONY: run
run: setup debug ## Build and run
run: debug ## Build and run
sudo LOG_LEVEL=$(LOG_LEVEL) ./target/debug/$(NAME)

.PHONY: clean
Expand Down
45 changes: 45 additions & 0 deletions rootfs/usr/share/inputplumber/devices/60-cec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/ShadowBlip/InputPlumber/main/rootfs/usr/share/inputplumber/schema/composite_device_v1.json
# Schema version number
version: 1

# The type of configuration schema
kind: CompositeDevice

# Name of the composite device mapping
name: CEC

# Maximum number of source devices per CompositeDevice.
maximum_sources: 2

# Only use this profile if *any* of the given matches matches. If this list is
# empty, then the source devices will *always* be checked.
# /sys/class/dmi/id/product_name
matches: []

# One or more source devices to combine into a single virtual device. The events
# from these devices will be watched and translated according to the key map.
source_devices:
- group: gamepad
udev:
attributes:
- name: protocols
value: '\[cec\]'
sys_name: "event*"
subsystem: input
- group: gamepad
udev:
sys_name: "cec*"
subsystem: "cec"

# Optional configuration for the composite device
options:
# If true, InputPlumber will automatically try to manage the input device. If
# this is false, InputPlumber will not try to manage the device unless an
# external service enables management of the device. Defaults to 'false'
auto_manage: false

# The target input device(s) to emulate by default
target_devices:
- xbox-elite
- mouse
- keyboard
6 changes: 6 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub enum Commands {
#[command(subcommand)]
cmd: TargetsCommand,
},
/// Query a given device
Query { udev: Option<bool> },
}

pub async fn main_cli(args: Args) -> Result<(), Box<dyn Error>> {
Expand All @@ -65,6 +67,10 @@ pub async fn main_cli(args: Args) -> Result<(), Box<dyn Error>> {
Commands::Device { id: number, cmd } => handle_device(connection, cmd, number).await?,
Commands::Devices { cmd } => handle_devices(connection, cmd).await?,
Commands::Targets { cmd } => handle_targets(connection, cmd).await?,
Commands::Query { udev } => {
println!("Got udev: {udev:?}");
//
}
}

Ok(())
Expand Down
9 changes: 8 additions & 1 deletion src/input/composite_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ use crate::{
Event,
},
output_event::UinputOutputEvent,
source::{evdev::EventDevice, hidraw::HidRawDevice, iio::IioDevice, SourceDevice},
source::{
cec::CecDevice, evdev::EventDevice, hidraw::HidRawDevice, iio::IioDevice, SourceDevice,
},
},
udev::{device::UdevDevice, hide_device, unhide_device},
};
Expand Down Expand Up @@ -1454,6 +1456,11 @@ impl CompositeDevice {
let device = IioDevice::new(device, self.client(), source_config.clone())?;
SourceDevice::Iio(device)
}
"cec" => {
log::debug!("Adding source device: {:?}", device.name());
let device = CecDevice::new(device, self.client(), source_config.clone())?;
SourceDevice::Cec(device)
}
_ => {
return Err(format!(
"Unspported subsystem: {subsystem}, unable to add source device {}",
Expand Down
66 changes: 63 additions & 3 deletions src/input/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,56 @@ impl Manager {
log::debug!("Finished adding event device {id}");
}

"cec" => {
if device.devnode().is_empty() {
log::warn!("cec device discarded for missing devnode: {dev_name} ({dev_sysname}) at {dev_path}");
return Ok(());
}

log::debug!("cec device added: {} ({})", device.name(), device.sysname());

// Create a DBus interface for the event device
let conn = self.dbus.clone();
let path = iio::get_dbus_path(sys_name.clone());

log::debug!("Attempting to listen on dbus for device {dev_name} ({dev_sysname}) | {dev_path}");
let dbus_path = path.clone();
task::spawn(async move {
let result = SourceUdevDeviceInterface::listen_on_dbus(
conn.clone(),
dbus_path.as_str(),
sysname.as_str(),
dev.clone(),
)
.await;
if let Err(e) = result {
log::error!("Error creating source udev dbus interface: {e:?}");
}

//let result = SourceCecInterface::listen_on_dbus(conn, sysname, dev).await;
//if let Err(e) = result {
// log::error!("Error creating source evdev dbus interface: {e:?}");
//}
log::debug!("Finished adding source device on dbus");
});

// Add the device as a source device
self.source_device_dbus_paths.insert(id.clone(), path);

// Check to see if the device is virtual
if device.is_virtual() {
log::debug!("{dev_name} ({dev_sysname}) is virtual, skipping consideration for {dev_path}");
return Ok(());
} else {
log::trace!("Device {dev_name} ({dev_sysname}) is real - {dev_path}");
}

// Signal that a source device was added
log::debug!("Spawing task to add source device: {id}");
self.on_source_device_added(id.clone(), device).await?;
log::debug!("Finished adding event device {id}");
}

_ => {
return Err(format!("Device subsystem not supported: {subsystem:?}").into());
}
Expand Down Expand Up @@ -1428,6 +1478,11 @@ impl Manager {
.remove::<SourceIioImuInterface, ObjectPath>(path.clone())
.await
}
//"cec" => {
// conn.object_server()
// .remove::<SourceCecInterface, ObjectPath>(path.clone())
// .await
//}
_ => Err(zbus::Error::Failure(format!(
"Invalid subsystem: '{subsystem}'"
))),
Expand Down Expand Up @@ -1542,10 +1597,12 @@ impl Manager {
let subsystem = {
match base_path.as_str() {
"/dev" => {
if !name.starts_with("hidraw") {
None
} else {
if name.starts_with("hidraw") {
Some("hidraw")
} else if name.starts_with("cec") {
Some("cec")
} else {
None
}
}
"/dev/input" => Some("input"),
Expand Down Expand Up @@ -1647,6 +1704,9 @@ impl Manager {
let iio_devices = udev::discover_devices("iio")?;
let iio_devices = iio_devices.into_iter().map(|dev| dev.into()).collect();
Manager::discover_devices(cmd_tx, iio_devices).await?;
let cec_devices = udev::discover_devices("cec")?;
let cec_devices = cec_devices.into_iter().map(|dev| dev.into()).collect();
Manager::discover_devices(cmd_tx, cec_devices).await?;

Ok(())
}
Expand Down
69 changes: 69 additions & 0 deletions src/input/source/cec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::error::Error;

use device::CecRawDevice;

use crate::{
config,
input::{capability::Capability, composite_device::client::CompositeDeviceClient},
udev::device::UdevDevice,
};

use super::{client::SourceDeviceClient, SourceDeviceCompatible, SourceDriver};

pub mod device;

/// [CecDevice] represents an input device using CEC
#[derive(Debug)]
pub enum CecDevice {
Device(SourceDriver<CecRawDevice>),
}

impl SourceDeviceCompatible for CecDevice {
fn get_device_ref(&self) -> &UdevDevice {
match self {
Self::Device(source_driver) => source_driver.info_ref(),
}
}

fn get_id(&self) -> String {
match self {
Self::Device(source_driver) => source_driver.get_id(),
}
}

fn client(&self) -> SourceDeviceClient {
match self {
Self::Device(source_driver) => source_driver.client(),
}
}

async fn run(self) -> Result<(), Box<dyn Error>> {
match self {
Self::Device(source_driver) => source_driver.run().await,
}
}

fn get_capabilities(&self) -> Result<Vec<Capability>, super::InputError> {
match self {
Self::Device(source_driver) => source_driver.get_capabilities(),
}
}

fn get_device_path(&self) -> String {
match self {
Self::Device(source_driver) => source_driver.get_device_path(),
}
}
}

impl CecDevice {
pub fn new(
device_info: UdevDevice,
composite_device: CompositeDeviceClient,
conf: Option<config::SourceDevice>,
) -> Result<Self, Box<dyn Error + Send + Sync>> {
let device = CecRawDevice::new();
let source_device = SourceDriver::new(composite_device, device, device_info, conf);
Ok(Self::Device(source_device))
}
}
36 changes: 36 additions & 0 deletions src/input/source/cec/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use cec_rs::{CecConnectionCfgBuilder, CecDeviceType, CecDeviceTypeVec};

use crate::input::{
capability::Capability,
source::{InputError, SourceInputDevice, SourceOutputDevice},
};

#[derive(Debug)]
pub struct CecRawDevice {}

impl CecRawDevice {
pub fn new() -> Self {
let cfg = CecConnectionCfgBuilder::default()
.device_types(CecDeviceTypeVec::new(CecDeviceType::AudioSystem))
.build();
Self {}
}
}

impl Default for CecRawDevice {
fn default() -> Self {
Self::new()
}
}

impl SourceInputDevice for CecRawDevice {
fn poll(&mut self) -> Result<Vec<crate::input::event::native::NativeEvent>, InputError> {
todo!()
}

fn get_capabilities(&self) -> Result<Vec<Capability>, InputError> {
todo!()
}
}

impl SourceOutputDevice for CecRawDevice {}
Loading

0 comments on commit 55c7085

Please sign in to comment.