diff --git a/com.steampowered.SteamOSManager1.xml b/com.steampowered.SteamOSManager1.xml
index cb77bb2..3748618 100644
--- a/com.steampowered.SteamOSManager1.xml
+++ b/com.steampowered.SteamOSManager1.xml
@@ -252,16 +252,6 @@
-->
-
-
-
-
-
-
-
-
diff --git a/src/manager.rs b/src/manager.rs
index 6e0547a..84eb5fc 100644
--- a/src/manager.rs
+++ b/src/manager.rs
@@ -515,9 +515,7 @@ mod test {
assert_eq!(remote_interface.len(), 1);
let remote_interface = remote_interface[0];
let remote_methods = collect_methods(remote_interface.methods());
- let remote_method_names: HashSet<&String> = remote_methods.keys().collect();
let remote_properties = collect_properties(remote_interface.properties());
- let remote_property_names: HashSet<&String> = remote_properties.keys().collect();
let local_interface_string = read("com.steampowered.SteamOSManager1.xml")
.await
@@ -532,13 +530,11 @@ mod test {
assert_eq!(local_interface.len(), 1);
let local_interface = local_interface[0];
let local_methods = collect_methods(local_interface.methods());
- let local_method_names: HashSet<&String> = local_methods.keys().collect();
let local_properties = collect_properties(local_interface.properties());
- let local_property_names: HashSet<&String> = local_properties.keys().collect();
- for key in local_method_names.union(&remote_method_names) {
- let local_method = local_methods.get(*key).expect(key);
- let remote_method = remote_methods.get(*key).expect(key);
+ for key in remote_methods.keys() {
+ let local_method = local_methods.get(key).expect(key);
+ let remote_method = remote_methods.get(key).expect(key);
assert_eq!(local_method.name(), remote_method.name());
assert_eq!(local_method.args().len(), remote_method.args().len());
@@ -550,9 +546,9 @@ mod test {
}
}
- for key in local_property_names.union(&remote_property_names) {
- let local_property = local_properties.get(*key).expect(key);
- let remote_property = remote_properties.get(*key).expect(key);
+ for key in remote_properties.keys() {
+ let local_property = local_properties.get(key).expect(key);
+ let remote_property = remote_properties.get(key).expect(key);
assert_eq!(local_property.name(), remote_property.name());
assert_eq!(local_property.ty(), remote_property.ty());
diff --git a/src/user.rs b/src/user.rs
index 29cc8f9..f90a3c0 100644
--- a/src/user.rs
+++ b/src/user.rs
@@ -15,15 +15,15 @@ use zbus::ConnectionBuilder;
use crate::daemon::Daemon;
use crate::user_manager::SteamOSManagerUser;
-async fn create_connection() -> Result {
+async fn create_connection(system_conn: &Connection) -> Result {
let connection = ConnectionBuilder::session()?
.name("com.steampowered.SteamOSManager1")?
.build()
.await?;
- let manager = SteamOSManagerUser::new(connection.clone()).await?;
+ let manager = SteamOSManagerUser::new(connection.clone(), system_conn).await?;
connection
.object_server()
- .at("/com/steampowered/SteamOSManager1/User", manager)
+ .at("/com/steampowered/SteamOSManager1", manager)
.await?;
Ok(connection)
}
@@ -43,7 +43,7 @@ pub async fn daemon() -> Result<()> {
bail!(e);
}
};
- let _session = match create_connection().await {
+ let _session = match create_connection(&system).await {
Ok(c) => c,
Err(e) => {
let _guard = tracing::subscriber::set_default(subscriber);
@@ -52,7 +52,7 @@ pub async fn daemon() -> Result<()> {
}
};
- let mut daemon = Daemon::new(subscriber, system.clone()).await?;
+ let mut daemon = Daemon::new(subscriber, system).await?;
daemon.run().await
}
diff --git a/src/user_manager.rs b/src/user_manager.rs
index ce93ddf..a57c470 100644
--- a/src/user_manager.rs
+++ b/src/user_manager.rs
@@ -8,27 +8,76 @@
use anyhow::Result;
use tracing::error;
-use zbus::{interface, Connection};
+use zbus::zvariant::Fd;
+use zbus::{interface, Connection, Proxy, SignalContext};
-use crate::{to_zbus_error, to_zbus_fdo_error, API_VERSION};
use crate::cec::{HdmiCecControl, HdmiCecState};
+use crate::{to_zbus_error, to_zbus_fdo_error, zbus_to_zbus_fdo, API_VERSION};
+
+macro_rules! method {
+ ($self:expr, $method:expr, $($args:expr),+) => {
+ $self.proxy
+ .call($method, &($($args,)*))
+ .await
+ .map_err(zbus_to_zbus_fdo)
+ };
+ ($self:expr, $method:expr) => {
+ $self.proxy
+ .call($method, &())
+ .await
+ .map_err(zbus_to_zbus_fdo)
+ };
+}
+
+macro_rules! getter {
+ ($self:expr, $prop:expr) => {
+ $self
+ .proxy
+ .get_property($prop)
+ .await
+ .map_err(zbus_to_zbus_fdo)
+ };
+}
+
+macro_rules! setter {
+ ($self:expr, $prop:expr, $value:expr) => {
+ $self
+ .proxy
+ .set_property($prop, $value)
+ .await
+ .map_err(|e| zbus::Error::FDO(Box::new(e)))
+ };
+}
pub struct SteamOSManagerUser {
connection: Connection,
+ proxy: Proxy<'static>,
hdmi_cec: HdmiCecControl<'static>,
}
impl SteamOSManagerUser {
- pub async fn new(connection: Connection) -> Result {
+ pub async fn new(connection: Connection, system_conn: &Connection) -> Result {
Ok(SteamOSManagerUser {
hdmi_cec: HdmiCecControl::new(&connection).await?,
connection,
+ proxy: Proxy::new(
+ system_conn,
+ "com.steampowered.SteamOSManager1",
+ "/com/steampowered/SteamOSManager1",
+ "com.steampowered.SteamOSManager1.Manager",
+ )
+ .await?,
})
}
}
-#[interface(name = "com.steampowered.SteamOSManager1.UserManager")]
+#[interface(name = "com.steampowered.SteamOSManager1.Manager")]
impl SteamOSManagerUser {
+ #[zbus(property(emits_changed_signal = "const"))]
+ async fn version(&self) -> u32 {
+ API_VERSION
+ }
+
#[zbus(property(emits_changed_signal = "false"))]
async fn hdmi_cec_state(&self) -> zbus::fdo::Result {
match self.hdmi_cec.get_enabled_state().await {
@@ -50,8 +99,275 @@ impl SteamOSManagerUser {
.map_err(to_zbus_error)
}
+ async fn prepare_factory_reset(&self) -> zbus::fdo::Result {
+ method!(self, "PrepareFactoryReset")
+ }
+
+ #[zbus(property(emits_changed_signal = "false"))]
+ async fn wifi_power_management_state(&self) -> zbus::fdo::Result {
+ getter!(self, "WifiPowerManagementState")
+ }
+
+ #[zbus(property)]
+ async fn set_wifi_power_management_state(&self, state: u32) -> zbus::Result<()> {
+ setter!(self, "WifiPowerManagementState", state)
+ }
+
+ #[zbus(property(emits_changed_signal = "false"))]
+ async fn fan_control_state(&self) -> zbus::fdo::Result {
+ getter!(self, "FanControlState")
+ }
+
+ #[zbus(property)]
+ async fn set_fan_control_state(&self, state: u32) -> zbus::Result<()> {
+ setter!(self, "SetFanControlState", state)
+ }
+
#[zbus(property(emits_changed_signal = "const"))]
- async fn version(&self) -> u32 {
- API_VERSION
+ async fn hardware_currently_supported(&self) -> zbus::fdo::Result {
+ getter!(self, "HardwareCurrentlySupported")
+ }
+
+ #[zbus(property(emits_changed_signal = "false"))]
+ async fn als_calibration_gain(&self) -> zbus::fdo::Result {
+ getter!(self, "AlsCalibrationGain")
+ }
+
+ async fn get_als_integration_time_file_descriptor(&self) -> zbus::fdo::Result {
+ let m = self
+ .proxy
+ .call_method::<&str, ()>("GetAlsIntegrationTimeFileDescriptor", &())
+ .await
+ .map_err(zbus_to_zbus_fdo)?;
+ match m.body().deserialize::() {
+ Ok(fd) => fd.try_to_owned().map_err(to_zbus_fdo_error),
+ Err(e) => Err(zbus_to_zbus_fdo(e)),
+ }
+ }
+
+ async fn update_bios(&self) -> zbus::fdo::Result<()> {
+ method!(self, "UpdateBios")
+ }
+
+ async fn update_dock(&self) -> zbus::fdo::Result<()> {
+ method!(self, "UpdateDock")
+ }
+
+ async fn trim_devices(&self) -> zbus::fdo::Result<()> {
+ method!(self, "TrimDevices")
+ }
+
+ async fn format_device(
+ &self,
+ device: &str,
+ label: &str,
+ validate: bool,
+ ) -> zbus::fdo::Result<()> {
+ method!(self, "FormatDevice", device, label, validate)
+ }
+
+ #[zbus(property(emits_changed_signal = "false"))]
+ async fn gpu_performance_level(&self) -> zbus::fdo::Result {
+ getter!(self, "GpuPerformanceLevel")
+ }
+
+ #[zbus(property)]
+ async fn set_gpu_performance_level(&self, level: u32) -> zbus::Result<()> {
+ setter!(self, "GpuPerformanceLevel", level)
+ }
+
+ #[zbus(property(emits_changed_signal = "false"))]
+ async fn manual_gpu_clock(&self) -> zbus::fdo::Result {
+ getter!(self, "ManualGpuClock")
+ }
+
+ #[zbus(property)]
+ async fn set_manual_gpu_clock(&self, clocks: u32) -> zbus::Result<()> {
+ setter!(self, "ManualGpuClock", clocks)
+ }
+
+ #[zbus(property(emits_changed_signal = "const"))]
+ async fn manual_gpu_clock_min(&self) -> zbus::fdo::Result {
+ getter!(self, "ManualGpuClockMin")
+ }
+
+ #[zbus(property(emits_changed_signal = "const"))]
+ async fn manual_gpu_clock_max(&self) -> zbus::fdo::Result {
+ getter!(self, "ManualGpuClockMax")
+ }
+
+ #[zbus(property(emits_changed_signal = "false"))]
+ async fn tdp_limit(&self) -> zbus::fdo::Result {
+ getter!(self, "TdpLimit")
+ }
+
+ #[zbus(property)]
+ async fn set_tdp_limit(&self, limit: u32) -> zbus::Result<()> {
+ setter!(self, "TdpLimit", limit)
+ }
+
+ #[zbus(property(emits_changed_signal = "const"))]
+ async fn tdp_limit_min(&self) -> zbus::fdo::Result {
+ getter!(self, "TdpLimitMin")
+ }
+
+ #[zbus(property(emits_changed_signal = "const"))]
+ async fn tdp_limit_max(&self) -> zbus::fdo::Result {
+ getter!(self, "TdpLimitMax")
+ }
+
+ #[zbus(property)]
+ async fn wifi_debug_mode_state(&self) -> zbus::fdo::Result {
+ getter!(self, "WifiDebugModeState")
+ }
+
+ async fn set_wifi_debug_mode(
+ &self,
+ mode: u32,
+ buffer_size: u32,
+ #[zbus(signal_context)] ctx: SignalContext<'_>,
+ ) -> zbus::fdo::Result<()> {
+ method!(self, "SetWifiDebugMode", mode, buffer_size)?;
+ self.wifi_debug_mode_state_changed(&ctx)
+ .await
+ .map_err(zbus_to_zbus_fdo)?;
+ Ok(())
+ }
+
+ #[zbus(property(emits_changed_signal = "false"))]
+ async fn wifi_backend(&self) -> zbus::fdo::Result {
+ getter!(self, "WifiBackend")
+ }
+
+ #[zbus(property)]
+ async fn set_wifi_backend(&self, backend: u32) -> zbus::Result<()> {
+ setter!(self, "WifiBackend", backend)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::{power, testing};
+ use std::collections::{HashMap, HashSet};
+ use std::iter::zip;
+ use tokio::fs::{create_dir_all, read, write};
+ use zbus::{Connection, ConnectionBuilder, Interface};
+ use zbus_xml::{Method, Node, Property};
+
+ struct TestHandle {
+ _handle: testing::TestHandle,
+ connection: Connection,
+ }
+
+ async fn start(name: &str) -> TestHandle {
+ let handle = testing::start();
+ let connection = ConnectionBuilder::session()
+ .unwrap()
+ .name(format!("com.steampowered.SteamOSManager1.UserTest.{name}"))
+ .unwrap()
+ .build()
+ .await
+ .unwrap();
+ let manager = SteamOSManagerUser::new(connection.clone(), &connection)
+ .await
+ .unwrap();
+ connection
+ .object_server()
+ .at("/com/steampowered/SteamOSManager1", manager)
+ .await
+ .expect("object_server at");
+
+ TestHandle {
+ _handle: handle,
+ connection,
+ }
+ }
+
+ fn collect_methods<'a>(methods: &'a [Method<'a>]) -> HashMap> {
+ let mut map = HashMap::new();
+ for method in methods.iter() {
+ map.insert(method.name().to_string(), method);
+ }
+ map
+ }
+
+ fn collect_properties<'a>(props: &'a [Property<'a>]) -> HashMap> {
+ let mut map = HashMap::new();
+ for prop in props.iter() {
+ map.insert(prop.name().to_string(), prop);
+ }
+ map
+ }
+
+ #[tokio::test]
+ async fn interface_matches() {
+ let test = start("Interface").await;
+
+ let manager_ref = test
+ .connection
+ .object_server()
+ .interface::<_, SteamOSManagerUser>("/com/steampowered/SteamOSManager1")
+ .await
+ .expect("interface");
+ let manager = manager_ref.get().await;
+ let mut remote_interface_string = String::from(
+ "",
+ );
+ manager.introspect_to_writer(&mut remote_interface_string, 0);
+ remote_interface_string.push_str("");
+ let remote_interfaces =
+ Node::from_reader::<&[u8]>(remote_interface_string.as_bytes()).expect("from_reader");
+ let remote_interface: Vec<_> = remote_interfaces
+ .interfaces()
+ .iter()
+ .filter(|iface| iface.name() == "com.steampowered.SteamOSManager1.Manager")
+ .collect();
+ assert_eq!(remote_interface.len(), 1);
+ let remote_interface = remote_interface[0];
+ let remote_methods = collect_methods(remote_interface.methods());
+ let remote_method_names: HashSet<&String> = remote_methods.keys().collect();
+ let remote_properties = collect_properties(remote_interface.properties());
+ let remote_property_names: HashSet<&String> = remote_properties.keys().collect();
+
+ let local_interface_string = read("com.steampowered.SteamOSManager1.xml")
+ .await
+ .expect("read");
+ let local_interfaces =
+ Node::from_reader::<&[u8]>(local_interface_string.as_ref()).expect("from_reader");
+ let local_interface: Vec<_> = local_interfaces
+ .interfaces()
+ .iter()
+ .filter(|iface| iface.name() == "com.steampowered.SteamOSManager1.Manager")
+ .collect();
+ assert_eq!(local_interface.len(), 1);
+ let local_interface = local_interface[0];
+ let local_methods = collect_methods(local_interface.methods());
+ let local_method_names: HashSet<&String> = local_methods.keys().collect();
+ let local_properties = collect_properties(local_interface.properties());
+ let local_property_names: HashSet<&String> = local_properties.keys().collect();
+
+ for key in local_method_names.union(&remote_method_names) {
+ let local_method = local_methods.get(*key).expect(key);
+ let remote_method = remote_methods.get(*key).expect(key);
+
+ assert_eq!(local_method.name(), remote_method.name());
+ assert_eq!(local_method.args().len(), remote_method.args().len());
+ for (local_arg, remote_arg) in
+ zip(local_method.args().iter(), remote_method.args().iter())
+ {
+ assert_eq!(local_arg.direction(), remote_arg.direction());
+ assert_eq!(local_arg.ty(), remote_arg.ty());
+ }
+ }
+
+ for key in local_property_names.union(&remote_property_names) {
+ let local_property = local_properties.get(*key).expect(key);
+ let remote_property = remote_properties.get(*key).expect(key);
+
+ assert_eq!(local_property.name(), remote_property.name());
+ assert_eq!(local_property.ty(), remote_property.ty());
+ assert_eq!(local_property.access(), remote_property.access());
+ }
}
}