Skip to content

Commit

Permalink
feat(CompositeDevice): emit property change signal when source or tar…
Browse files Browse the repository at this point in the history
…get devices change
  • Loading branch information
ShadowApex committed May 2, 2024
1 parent 3a341b1 commit 0323dc1
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 17 deletions.
95 changes: 92 additions & 3 deletions src/input/composite_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ pub enum Command {
}

/// The [DBusInterface] provides a DBus interface that can be exposed for managing
/// a [Manager]. It works by sending command messages to a channel that the
/// [Manager] is listening on.
/// a [CompositeDevice]. It works by sending command messages to a channel that the
/// [CompositeDevice] is listening on.
pub struct DBusInterface {
tx: broadcast::Sender<Command>,
}
Expand Down Expand Up @@ -142,7 +142,10 @@ impl DBusInterface {
Ok(())
}

/// Set the target input devices the composite device should emulate.
/// Set the target input device types the composite device should emulate,
/// such as ["gamepad", "mouse", "keyboard"]. This method will stop all
/// current virtual devices for the composite device and create and attach
/// new target devices.
async fn set_target_devices(&self, target_device_types: Vec<String>) -> fdo::Result<()> {
self.tx
.send(Command::SetTargetDevices(target_device_types))
Expand Down Expand Up @@ -1627,6 +1630,10 @@ impl CompositeDevice {
) -> Result<(), Box<dyn Error>> {
self.add_source_device(device_info)?;
self.run_source_devices().await?;

// Signal to DBus that source devices have changed
self.signal_sources_changed().await;

log::debug!(
"Finished adding source device. All sources: {:?}",
self.source_devices_used
Expand Down Expand Up @@ -1663,6 +1670,9 @@ impl CompositeDevice {
self.source_devices_blocked.remove(&id);
}

// Signal to DBus that source devices have changed
self.signal_sources_changed().await;

log::debug!(
"Current source device paths: {:?}",
self.source_device_paths
Expand Down Expand Up @@ -2012,6 +2022,9 @@ impl CompositeDevice {
}
}

// Signal change in target devices to DBus
self.signal_targets_changed().await;

Ok(())
}

Expand All @@ -2029,9 +2042,85 @@ impl CompositeDevice {
format!("Failed to set composite device for target device: {:?}", e).into(),
);
}
log::debug!(
"Attached device {path} to {:?}",
self.dbus_path.as_ref().unwrap_or(&"".to_string())
);
self.target_devices.insert(path, target);
}
self.signal_targets_changed().await;

Ok(())
}

/// Emit a DBus signal when target devices change
async fn signal_targets_changed(&self) {
let Some(dbus_path) = self.dbus_path.clone() else {
log::error!("No DBus path for composite device exists to emit signal!");
return;
};
let conn = self.conn.clone();

tokio::task::spawn(async move {
// Get the object instance at the given path so we can send DBus signal
// updates
let iface_ref = match conn
.object_server()
.interface::<_, DBusInterface>(dbus_path.clone())
.await
{
Ok(iface) => iface,
Err(e) => {
log::error!(
"Failed to get DBus interface for composite device to signal: {e:?}"
);
return;
}
};
// Emit the target devices changed signal
let iface = iface_ref.get().await;
if let Err(e) = iface
.target_devices_changed(iface_ref.signal_context())
.await
{
log::error!("Failed to send target devices changed signal: {e:?}");
}
});
}

/// Emit a DBus signal when source devices change
async fn signal_sources_changed(&self) {
let Some(dbus_path) = self.dbus_path.clone() else {
log::error!("No DBus path for composite device exists to emit signal!");
return;
};
let conn = self.conn.clone();

tokio::task::spawn(async move {
// Get the object instance at the given path so we can send DBus signal
// updates
let iface_ref = match conn
.object_server()
.interface::<_, DBusInterface>(dbus_path.clone())
.await
{
Ok(iface) => iface,
Err(e) => {
log::error!(
"Failed to get DBus interface for composite device to signal: {e:?}"
);
return;
}
};

// Emit the target devices changed signal
let iface = iface_ref.get().await;
if let Err(e) = iface
.source_device_paths_changed(iface_ref.signal_context())
.await
{
log::error!("Failed to send source devices changed signal: {e:?}");
}
});
}
}
54 changes: 42 additions & 12 deletions src/input/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,33 @@ impl DBusInterface {
.map_err(|err| fdo::Error::Failed(err.to_string()))?;
Ok(())
}

/// Attach the given target device to the given composite device
async fn attach_target_device(
&self,
target_path: String,
composite_path: String,
) -> fdo::Result<()> {
let (sender, mut receiver) = mpsc::channel(1);
self.tx
.send(ManagerCommand::AttachTargetDevice {
target_path: target_path.clone(),
composite_path: composite_path.clone(),
sender,
})
.map_err(|err| fdo::Error::Failed(err.to_string()))?;

// Read the response from the manager
let Some(response) = receiver.recv().await else {
return Err(fdo::Error::Failed("No response from manager".to_string()));
};
if let Err(e) = response {
let err = format!("Failed to attach target device {target_path} to composite device {composite_path}: {e:?}");
return Err(fdo::Error::Failed(err));
}

Ok(())
}
}

/// Manages input devices
Expand Down Expand Up @@ -345,28 +372,27 @@ impl Manager {
sender,
} => {
let Some(target) = self.target_devices.get(&target_path) else {
if let Err(e) = sender
.send(Err(ManagerError::AttachTargetDeviceFailed(
"Failed to find target device".into(),
)))
.await
{
let err = ManagerError::AttachTargetDeviceFailed(
"Failed to find target device".into(),
);
log::error!("{err}");
if let Err(e) = sender.send(Err(err)).await {
log::error!("Failed to send response: {e:?}");
}
continue;
};
let Some(device) = self.composite_devices.get(&composite_path) else {
if let Err(e) = sender
.send(Err(ManagerError::AttachTargetDeviceFailed(
"Failed to find composite device".into(),
)))
.await
{
let err = ManagerError::AttachTargetDeviceFailed(
"Failed to find composite device".into(),
);
log::error!("{err}");
if let Err(e) = sender.send(Err(err)).await {
log::error!("Failed to send response: {e:?}");
}
continue;
};

// Send the attach command to the composite device
let mut targets = HashMap::new();
targets.insert(target_path.clone(), target.clone());
if let Err(e) = device
Expand All @@ -375,6 +401,10 @@ impl Manager {
{
log::error!("Failed to send attach command: {e:?}");
}

if let Err(e) = sender.send(Ok(())).await {
log::error!("Failed to send response: {e:?}");
}
}
ManagerCommand::StopTargetDevice { path } => {
let Some(target) = self.target_devices.get(&path) else {
Expand Down
9 changes: 9 additions & 0 deletions src/input/target/dualsense.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,15 @@ impl DualSenseDevice {
log::debug!("Stopped listening for events");
device.destroy()?;

// Remove the DBus interface
if let Some(path) = self.dbus_path.clone() {
log::debug!("Removing DBus interface for {path}");
self.conn
.object_server()
.remove::<DBusInterface, String>(path)
.await?;
}

Ok(())
}

Expand Down
16 changes: 14 additions & 2 deletions src/input/target/gamepad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,14 @@ impl GenericGamepad {
}
}

log::debug!("Stopping device");
log::debug!(
"Stopping device {}",
self.dbus_path.clone().unwrap_or_default()
);

// Remove the DBus interface
if let Some(path) = self.dbus_path.clone() {
log::debug!("Removing DBus interface");
log::debug!("Removing DBus interface for {path}");
self.conn
.object_server()
.remove::<DBusInterface, String>(path)
Expand Down Expand Up @@ -275,6 +278,15 @@ impl GenericGamepad {
fn spawn_ff_thread(ff_device: Arc<Mutex<VirtualDevice>>, tx: broadcast::Sender<Command>) {
tokio::task::spawn_blocking(move || {
loop {
// Check to see if the main input thread still has a reference
// to the virtual device. If it does not, it means the device
// has stopped.
let num_refs = Arc::strong_count(&ff_device);
if num_refs == 1 {
log::debug!("Virtual device stopped. Stopping FF handler thread.");
break;
}

// Read any events
if let Err(e) = GenericGamepad::process_ff(&ff_device, &tx) {
log::warn!("Error processing FF events: {:?}", e);
Expand Down

0 comments on commit 0323dc1

Please sign in to comment.