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

feat(Manager): add gamepad reordering #284

Merged
merged 2 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
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
41 changes: 41 additions & 0 deletions src/cli/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ impl Display for InterceptMode {
pub enum DevicesCommand {
/// List all running composite devices
List,
/// List or set the player order of composite devices
Order { device_ids: Option<Vec<u8>> },
/// Enable/disable managing all supported input devices
ManageAll {
#[arg(long, action)]
Expand Down Expand Up @@ -223,6 +225,45 @@ pub async fn handle_devices(conn: Connection, cmd: DevicesCommand) -> Result<(),
let verb = if enable { "Enabled" } else { "Disabled" };
println!("{verb} management of all supported devices");
}
DevicesCommand::Order { device_ids } => {
let manager = ManagerInterfaceProxy::builder(&conn).build().await?;
if let Some(ids) = device_ids {
let paths: Vec<String> = ids
.into_iter()
.map(|id| format!("/org/shadowblip/InputPlumber/CompositeDevice{id}"))
.collect();
manager.set_gamepad_order(paths).await?;
}

// Fetch the current gamepad order
let order = manager.gamepad_order().await?;

// Query information about each device
let mut devices = Vec::with_capacity(order.len());
for path in order {
let device = CompositeDeviceInterfaceProxy::builder(&conn)
.path(path.clone())
.unwrap()
.build()
.await;
let Some(device) = device.ok() else {
continue;
};

let number = path.replace("/org/shadowblip/InputPlumber/CompositeDevice", "");
let name = device.name().await.unwrap_or_default();

let row = DeviceRow { id: number, name };

devices.push(row);
}

let mut table = Table::new(devices);
table
.with(Style::modern_rounded())
.with(Panel::header("Composite Devices"));
println!("{table}");
}
}

Ok(())
Expand Down
31 changes: 29 additions & 2 deletions src/dbus/interface/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,35 @@ impl ManagerInterface {
}

#[zbus(property)]
async fn intercept_mode(&self) -> fdo::Result<String> {
Ok("InputPlumber".to_string())
async fn gamepad_order(&self) -> fdo::Result<Vec<String>> {
let (sender, mut receiver) = mpsc::channel(1);
self.tx
.send_timeout(
ManagerCommand::GetGamepadOrder { sender },
Duration::from_millis(500),
)
.await
.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()));
};

Ok(response)
}

#[zbus(property)]
async fn set_gamepad_order(&self, order: Vec<String>) -> zbus::Result<()> {
self.tx
.send_timeout(
ManagerCommand::SetGamepadOrder { dbus_paths: order },
Duration::from_millis(500),
)
.await
.map_err(|err| fdo::Error::Failed(err.to_string()))?;

Ok(())
}

/// If set to 'true', InputPlumber will try to manage all input devices
Expand Down
11 changes: 11 additions & 0 deletions src/input/composite_device/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,15 @@ impl CompositeDeviceClient {
}
Err(ClientError::ChannelClosed)
}

/// Returns true if the target devices are suspended
pub async fn is_suspended(&self) -> Result<bool, ClientError> {
let (tx, mut rx) = channel(1);
self.tx.send(CompositeCommand::IsSuspended(tx)).await?;

if let Some(result) = rx.recv().await {
return Ok(result);
}
Err(ClientError::ChannelClosed)
}
}
1 change: 1 addition & 0 deletions src/input/composite_device/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ pub enum CompositeCommand {
Stop,
Suspend(mpsc::Sender<()>),
Resume(mpsc::Sender<()>),
IsSuspended(mpsc::Sender<bool>),
}
34 changes: 11 additions & 23 deletions src/input/composite_device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,13 @@ impl CompositeDevice {
log::error!("Failed to send resume response: {e:?}");
}
}
CompositeCommand::IsSuspended(sender) => {
let is_suspended = !self.target_devices_suspended.is_empty();
log::debug!("Checking if device is suspended: {is_suspended}");
if let Err(e) = sender.send(is_suspended).await {
log::error!("Failed to send suspended response: {e:?}");
}
}
}
}

Expand Down Expand Up @@ -2064,9 +2071,6 @@ impl CompositeDevice {
// Clear the list of suspended target devices
self.target_devices_suspended.clear();

// Create a list of target devices that should be stopped on suspend
let mut targets_to_stop = HashMap::new();

// Record what target devices are currently used so they can be restored
// when the system is resumed.
for (path, target) in self.target_devices.clone().into_iter() {
Expand All @@ -2078,21 +2082,7 @@ impl CompositeDevice {
}
};

// The "deck" target device does not support suspend
if target_type.as_str() == "deck" {
targets_to_stop.insert(path, target);
}

self.target_devices_suspended.push(target_type);
}
log::info!(
"Target devices before suspend: {:?}",
self.target_devices_suspended
);

// Tear down any target devices that do not support suspend
for (path, target) in targets_to_stop.into_iter() {
log::info!("Stopping target device: {path}");
self.target_devices.remove(&path);
for (_, target_devices) in self.target_devices_by_capability.iter_mut() {
target_devices.remove(&path);
Expand All @@ -2104,6 +2094,10 @@ impl CompositeDevice {
// Wait a few beats to ensure that the target device is really gone
tokio::time::sleep(Duration::from_millis(200)).await;
}
log::info!(
"Target devices before suspend: {:?}",
self.target_devices_suspended
);
}

/// Called when notified by the input manager that system resume is about
Expand All @@ -2114,12 +2108,6 @@ impl CompositeDevice {
self.target_devices_suspended
);

// Only handle resume if a deck controller target device was used
if !self.target_devices_suspended.contains(&"deck".to_string()) {
self.target_devices_suspended.clear();
return;
}

// Set the target devices back to the ones used before suspend
if let Err(err) = self
.set_target_devices(self.target_devices_suspended.clone())
Expand Down
Loading