mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-13 09:52:00 -04:00
inputplumber: Add service for putting InputPlumber in deck
mode if applicable
This commit is contained in:
parent
8c1979e451
commit
154775b3cc
3 changed files with 144 additions and 0 deletions
|
@ -18,6 +18,7 @@ use zbus::connection::{Builder, Connection};
|
|||
|
||||
use crate::daemon::{channel, Daemon, DaemonCommand, DaemonContext};
|
||||
use crate::ds_inhibit::Inhibitor;
|
||||
use crate::inputplumber::DeckService;
|
||||
use crate::manager::root::SteamOSManager;
|
||||
use crate::path;
|
||||
use crate::sls::ftrace::Ftrace;
|
||||
|
@ -123,6 +124,9 @@ impl DaemonContext for RootContext {
|
|||
let ftrace = Ftrace::init(&connection).await?;
|
||||
daemon.add_service(ftrace);
|
||||
|
||||
let ip = DeckService::init(connection);
|
||||
daemon.add_service(ip);
|
||||
|
||||
self.reload_ds_inhibit(daemon).await?;
|
||||
|
||||
Ok(())
|
||||
|
|
139
src/inputplumber.rs
Normal file
139
src/inputplumber.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright © 2023 Collabora Ltd.
|
||||
* Copyright © 2024 Valve Software
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::{debug, info};
|
||||
use zbus::fdo::{InterfacesAdded, ObjectManagerProxy};
|
||||
use zbus::names::OwnedInterfaceName;
|
||||
use zbus::zvariant::ObjectPath;
|
||||
use zbus::Connection;
|
||||
|
||||
use crate::hardware::{device_type, DeviceType};
|
||||
use crate::Service;
|
||||
|
||||
#[zbus::proxy(
|
||||
interface = "org.shadowblip.Input.CompositeDevice",
|
||||
default_service = "org.shadowblip.InputPlumber"
|
||||
)]
|
||||
trait CompositeDevice {
|
||||
#[zbus(property)]
|
||||
fn target_devices(&self) -> Result<Vec<String>>;
|
||||
|
||||
async fn set_target_devices(&self, devices: &[&str]) -> Result<()>;
|
||||
}
|
||||
|
||||
#[zbus::proxy(
|
||||
interface = "org.shadowblip.Input.Target",
|
||||
default_service = "org.shadowblip.InputPlumber"
|
||||
)]
|
||||
trait Target {
|
||||
#[zbus(property)]
|
||||
fn device_type(&self) -> Result<String>;
|
||||
}
|
||||
|
||||
pub struct DeckService {
|
||||
connection: Connection,
|
||||
composite_device_iface_name: OwnedInterfaceName,
|
||||
}
|
||||
|
||||
impl DeckService {
|
||||
pub fn init(connection: Connection) -> DeckService {
|
||||
DeckService {
|
||||
connection,
|
||||
composite_device_iface_name: OwnedInterfaceName::try_from(
|
||||
"org.shadowblip.Input.CompositeDevice",
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_devices(&self, object_manager: &ObjectManagerProxy<'_>) -> Result<()> {
|
||||
if device_type().await.unwrap_or(DeviceType::Unknown) == DeviceType::LegionGoS {
|
||||
// There is a bug on the Legion Go S where querying this information
|
||||
// messes up the mapping for the `deck` target device. It's not clear
|
||||
// exactly what's causing this, so we just skip on the Legion Go S
|
||||
// since it makes a `deck` target device by default.
|
||||
return Ok(());
|
||||
}
|
||||
for (path, ifaces) in object_manager.get_managed_objects().await?.into_iter() {
|
||||
if ifaces.contains_key(&self.composite_device_iface_name) {
|
||||
self.make_deck(&path).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn make_deck_from_ifaces_added(&self, msg: InterfacesAdded) -> Result<()> {
|
||||
let args = msg.args()?;
|
||||
if !args
|
||||
.interfaces_and_properties
|
||||
.contains_key(&self.composite_device_iface_name.as_ref())
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
debug!("New CompositeDevice found at {}", args.object_path());
|
||||
self.make_deck(args.object_path()).await
|
||||
}
|
||||
|
||||
async fn is_deck(&self, device: &CompositeDeviceProxy<'_>) -> Result<bool> {
|
||||
let targets = device.target_devices().await?;
|
||||
if targets.len() != 1 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let target = TargetProxy::builder(&self.connection)
|
||||
.path(targets[0].as_str())?
|
||||
.build()
|
||||
.await?;
|
||||
Ok(target.device_type().await? == "deck")
|
||||
}
|
||||
|
||||
async fn make_deck(&self, path: &ObjectPath<'_>) -> Result<()> {
|
||||
if !path
|
||||
.as_str()
|
||||
.starts_with("/org/shadowblip/InputPlumber/CompositeDevice")
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let proxy = CompositeDeviceProxy::builder(&self.connection)
|
||||
.path(path)?
|
||||
.build()
|
||||
.await?;
|
||||
if !self.is_deck(&proxy).await? {
|
||||
debug!("Changing CompositeDevice {} into `deck` type", path);
|
||||
proxy.set_target_devices(&["deck"]).await
|
||||
} else {
|
||||
debug!("CompositeDevice {} is already `deck` type", path);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for DeckService {
|
||||
const NAME: &'static str = "inputplumber";
|
||||
|
||||
async fn run(&mut self) -> Result<()> {
|
||||
let object_manager = ObjectManagerProxy::new(
|
||||
&self.connection,
|
||||
"org.shadowblip.InputPlumber",
|
||||
"/org/shadowblip/InputPlumber",
|
||||
)
|
||||
.await?;
|
||||
let mut iface_added = object_manager.receive_interfaces_added().await?;
|
||||
|
||||
if let Err(e) = self.check_devices(&object_manager).await {
|
||||
info!("Can't query initial InputPlumber devices: {e}");
|
||||
}
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(iface) = iface_added.next() => self.make_deck_from_ifaces_added(iface).await?,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ use tracing::{debug, error, info, warn};
|
|||
|
||||
mod ds_inhibit;
|
||||
mod error;
|
||||
mod inputplumber;
|
||||
mod job;
|
||||
mod manager;
|
||||
mod platform;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue