Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Manager): Use loop to check for IIO devices as inotify doesn't work on sysfs. #62

Merged
merged 1 commit into from
Apr 27, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 114 additions & 79 deletions src/input/manager.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashMap;
use std::error::Error;
use std::fs;
use std::time::Duration;

use tokio::sync::broadcast;
use tokio::sync::mpsc;
Expand Down Expand Up @@ -33,6 +34,7 @@ use crate::input::target::xb360::XBox360Controller;
use crate::input::target::TargetDeviceType;
use crate::procfs;
use crate::watcher;
use crate::watcher::WatchEvent;

use super::composite_device::Handle;
use super::target::TargetCommand;
Expand Down Expand Up @@ -1086,14 +1088,75 @@ impl Manager {
// Create a channel to handle watch events
let (watcher_tx, mut watcher_rx) = mpsc::channel(BUFFER_SIZE);

log::debug!("Performing initial input device discovery");
Manager::discover_human_interface_devices(&watcher_tx).await?;
Manager::discover_event_devices(&watcher_tx).await?;
Manager::discover_iio_devices(&watcher_tx).await?;
log::debug!("Initial input device discovery complete");

// Start a task to dispatch filesystem watch events to the `run()` loop
let cmd_tx = self.tx.clone();
tokio::spawn(async move {
log::debug!("Dispatching filesystem watch events");
while let Some(event) = watcher_rx.recv().await {
log::debug!("Received watch event: {:?}", event);
match event {
// Create events
WatchEvent::Create { name, base_path } => {
if base_path == INPUT_PATH && name.starts_with("event") {
let result = cmd_tx.send(Command::EventDeviceAdded { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if name.starts_with("hidraw") {
let result = cmd_tx.send(Command::HIDRawAdded { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if base_path == IIO_PATH {
let result = cmd_tx.send(Command::IIODeviceAdded { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
}
}
// Delete events
WatchEvent::Delete { name, base_path } => {
if base_path == INPUT_PATH && name.starts_with("event") {
let result = cmd_tx.send(Command::EventDeviceRemoved { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if name.starts_with("hidraw") {
let result = cmd_tx.send(Command::HIDRawRemoved { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if base_path == IIO_PATH {
let result = cmd_tx.send(Command::IIODeviceRemoved { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
}
}
_ => {}
}
}
});

Ok(())
}

async fn discover_human_interface_devices(
watcher_tx: &mpsc::Sender<WatchEvent>,
) -> Result<(), Box<dyn Error>> {
// Start watcher thread to listen for hidraw device changes
if std::path::Path::new(DEV_PATH).exists() {
let tx = watcher_tx.clone();
tokio::task::spawn_blocking(move || {
log::debug!("Started watcher thread");
log::info!("Started hidraw device discovery thread");
watcher::watch(DEV_PATH.into(), tx)
});
log::debug!("Performing initial input device discovery");
// Perform an initial hidraw device discovery
let paths = std::fs::read_dir(DEV_PATH)?;
for entry in paths {
Expand All @@ -1111,7 +1174,7 @@ impl Manager {
}
log::debug!("Discovered hidraw device: {:?}", path);
let result = watcher_tx
.send(watcher::WatchEvent::Create {
.send(WatchEvent::Create {
name: path,
base_path: DEV_PATH.into(),
})
Expand All @@ -1122,11 +1185,17 @@ impl Manager {
}
}

Ok(())
}

async fn discover_event_devices(
watcher_tx: &mpsc::Sender<WatchEvent>,
) -> Result<(), Box<dyn Error>> {
// Start watcher thread to listen for event device changes
if std::path::Path::new(INPUT_PATH).exists() {
let tx = watcher_tx.clone();
tokio::task::spawn_blocking(move || {
log::debug!("Started watcher thread");
log::info!("Started evdev discovery thread");
watcher::watch(INPUT_PATH.into(), tx)
});
// Perform an initial event device discovery
Expand All @@ -1146,7 +1215,7 @@ impl Manager {
}
log::debug!("Discovered event device: {:?}", path);
let result = watcher_tx
.send(watcher::WatchEvent::Create {
.send(WatchEvent::Create {
name: path,
base_path: INPUT_PATH.into(),
})
Expand All @@ -1156,91 +1225,57 @@ impl Manager {
}
}
}
Ok(())
}

async fn discover_iio_devices(
watcher_tx: &mpsc::Sender<WatchEvent>,
) -> Result<(), Box<dyn Error>> {
// Start watcher thread to listen for iio device changes
if std::path::Path::new(IIO_PATH).exists() {
let tx = watcher_tx.clone();
tokio::task::spawn_blocking(move || {
log::debug!("Started watcher thread");
watcher::watch(IIO_PATH.into(), tx)
});

// Perform an initial iio device discovery
let paths = std::fs::read_dir(IIO_PATH)?;
for entry in paths {
if let Err(e) = entry {
log::warn!("Unable to read from directory: {:?}", e);
continue;
}
let path = entry.unwrap().file_name();
let path = path.into_string().ok();
let Some(path) = path else {
continue;
};
log::debug!("Discovered iio device: {:?}", path);
let result = watcher_tx
.send(watcher::WatchEvent::Create {
name: path,
base_path: IIO_PATH.into(),
})
.await;
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
}
}
log::debug!("Initial input device discovery complete");

// Start a task to dispatch filesystem watch events to the `run()` loop
let cmd_tx = self.tx.clone();
tokio::spawn(async move {
log::debug!("Dispatching filesystem watch events");
while let Some(event) = watcher_rx.recv().await {
log::debug!("Received watch event: {:?}", event);
match event {
// Create events
watcher::WatchEvent::Create { name, base_path } => {
if base_path == INPUT_PATH && name.starts_with("event") {
let result = cmd_tx.send(Command::EventDeviceAdded { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if name.starts_with("hidraw") {
let result = cmd_tx.send(Command::HIDRawAdded { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if base_path == IIO_PATH {
let result = cmd_tx.send(Command::IIODeviceAdded { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
tokio::task::spawn(async move {
log::info!("Started iio device discovery loop.");
// Apply some duct tape here...
// Perform iio device discovery
let mut discovered_paths: Vec<String> = Vec::new();
loop {
let paths = match std::fs::read_dir(IIO_PATH) {
Ok(paths) => paths,
Err(e) => {
log::error!("Got error reading path. {e:?}");
return;
}
}
// Delete events
watcher::WatchEvent::Delete { name, base_path } => {
if base_path == INPUT_PATH && name.starts_with("event") {
let result = cmd_tx.send(Command::EventDeviceRemoved { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if name.starts_with("hidraw") {
let result = cmd_tx.send(Command::HIDRawRemoved { name });
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
} else if base_path == IIO_PATH {
let result = cmd_tx.send(Command::IIODeviceRemoved { name });
};
for entry in paths {
if let Err(e) = entry {
log::warn!("Unable to read from directory: {:?}", e);
continue;
}
log::debug!("Found path: {entry:?}");
let path = entry.unwrap().file_name();
let path = path.into_string().ok();
let Some(path) = path else {
continue;
};
log::debug!("Discovered iio device: {:?}", path);
if !discovered_paths.contains(&path) {
let result = tx
.send(WatchEvent::Create {
name: path.clone(),
base_path: IIO_PATH.into(),
})
.await;
if let Err(e) = result {
log::error!("Unable to send command: {:?}", e);
}
discovered_paths.push(path)
}
}
_ => {}
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
});

});
}
Ok(())
}

Expand Down
Loading