From d8c1087ebe89193bdecbfe6c71245bf227f2483f Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 7 Feb 2024 13:58:44 +0100 Subject: [PATCH] dbus: Add power-daemon hotplug handling --- src/backend/kms/mod.rs | 2 +- src/dbus/mod.rs | 66 ++++++++++++++++++++++++++++++++ src/dbus/power.rs | 86 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/state.rs | 5 ++- 5 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 src/dbus/mod.rs create mode 100644 src/dbus/power.rs diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 7308d277..6345f712 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -716,7 +716,7 @@ impl State { Ok(()) } - fn device_changed(&mut self, dev: dev_t) -> Result<()> { + pub(crate) fn device_changed(&mut self, dev: dev_t) -> Result<()> { if !self.backend.kms().session.is_active() { return Ok(()); } diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs new file mode 100644 index 00000000..93d41ecc --- /dev/null +++ b/src/dbus/mod.rs @@ -0,0 +1,66 @@ +use crate::state::{BackendData, State}; +use anyhow::{Context, Result}; +use calloop::{InsertError, LoopHandle, RegistrationToken}; + +mod power; + +pub fn init(evlh: &LoopHandle<'static, State>) -> Result> { + let mut tokens = Vec::new(); + + match power::init() { + Ok(power_daemon) => { + let (tx, rx) = calloop::channel::channel(); + + let token = evlh + .insert_source(rx, |event, _, state| match event { + calloop::channel::Event::Msg(_) => { + let nodes = match &mut state.backend { + BackendData::Kms(kms) => { + kms.devices.keys().cloned().collect::>() + } + _ => Vec::new(), + }; + for node in nodes { + if let Err(err) = state.device_changed(node.dev_id()) { + tracing::error!(?err, "Failed to update drm device {}.", node); + } + } + () + } + calloop::channel::Event::Closed => (), + }) + .map_err(|InsertError { error, .. }| error) + .with_context(|| "Failed to add channel to event_loop")?; + + // start helper thread + let result = std::thread::Builder::new() + .name("system76-power-hotplug".to_string()) + .spawn(move || { + if let Ok(mut msg_iter) = power_daemon.receive_hot_plug_detect() { + while let Some(msg) = msg_iter.next() { + if tx.send(msg).is_err() { + break; + } + } + } + }) + .with_context(|| "Failed to start helper thread"); + + match result { + Ok(_handle) => { + tokens.push(token); + // detach thread + } + Err(err) => { + evlh.remove(token); + return Err(err); + } + } + } + Err(err) => { + tracing::info!(?err, "Failed to connect to com.system76.PowerDaemon"); + } + }; + + Ok(tokens) +} diff --git a/src/dbus/power.rs b/src/dbus/power.rs new file mode 100644 index 00000000..e494f1c7 --- /dev/null +++ b/src/dbus/power.rs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +//! # DBus interface proxy for: `com.system76.PowerDaemon` +//! +//! This code was generated by `zbus-xmlgen` `3.0.0` from DBus introspection data. +//! Source: `Interface '/com/system76/PowerDaemon' from service 'com.system76.PowerDaemon' on system bus`. +//! +//! You may prefer to adapt it, instead of using it verbatim. +//! +//! More information can be found in the +//! [Writing a client proxy](https://dbus.pages.freedesktop.org/zbus/client.html) +//! section of the zbus documentation. +//! +//! This DBus object implements +//! [standard DBus interfaces](https://dbus.freedesktop.org/doc/dbus-specification.html), +//! (`org.freedesktop.DBus.*`) for which the following zbus proxies can be used: +//! +//! * [`zbus::fdo::IntrospectableProxy`] +//! +//! …consequently `zbus-xmlgen` did not generate code for the above interfaces. + +use zbus::{blocking::Connection, dbus_proxy}; + +#[dbus_proxy( + interface = "com.system76.PowerDaemon", + default_path = "/com/system76/PowerDaemon" +)] +trait PowerDaemon { + /// Balanced method + fn balanced(&self) -> zbus::Result<()>; + + /// Battery method + fn battery(&self) -> zbus::Result<()>; + + /// GetChargeProfiles method + fn get_charge_profiles( + &self, + ) -> zbus::Result>>; + + /// GetChargeThresholds method + fn get_charge_thresholds(&self) -> zbus::Result<(u8, u8)>; + + /// GetDefaultGraphics method + fn get_default_graphics(&self) -> zbus::Result; + + /// GetExternalDisplaysRequireDGPU method + fn get_external_displays_require_dgpu(&self) -> zbus::Result; + + /// GetGraphics method + fn get_graphics(&self) -> zbus::Result; + + /// GetGraphicsPower method + fn get_graphics_power(&self) -> zbus::Result; + + /// GetProfile method + fn get_profile(&self) -> zbus::Result; + + /// GetSwitchable method + fn get_switchable(&self) -> zbus::Result; + + /// Performance method + fn performance(&self) -> zbus::Result<()>; + + /// SetChargeThresholds method + fn set_charge_thresholds(&self, thresholds: &(u8, u8)) -> zbus::Result<()>; + + /// SetGraphics method + fn set_graphics(&self, vendor: &str) -> zbus::Result<()>; + + /// SetGraphicsPower method + fn set_graphics_power(&self, power: bool) -> zbus::Result<()>; + + /// HotPlugDetect signal + #[dbus_proxy(signal)] + fn hot_plug_detect(&self, port: u64) -> zbus::Result<()>; + + /// PowerProfileSwitch signal + #[dbus_proxy(signal)] + fn power_profile_switch(&self, profile: &str) -> zbus::Result<()>; +} + +pub fn init() -> anyhow::Result> { + let conn = Connection::system()?; + let proxy = PowerDaemonProxyBlocking::new(&conn)?; + proxy.introspect()?; + Ok(proxy) +} diff --git a/src/main.rs b/src/main.rs index a32ad7d2..5845e3f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use crate::{ pub mod backend; pub mod config; +pub mod dbus; #[cfg(feature = "debug")] pub mod debug; pub mod input; diff --git a/src/state.rs b/src/state.rs index 94f9f00c..e2b19710 100644 --- a/src/state.rs +++ b/src/state.rs @@ -150,7 +150,6 @@ pub struct Common { //pub output_conf: ConfigurationManager, pub shell: Shell, - seats: Vec>, last_active_seat: Option>, @@ -392,6 +391,10 @@ impl State { let shell = Shell::new(&config, dh); + if let Err(err) = crate::dbus::init(&handle) { + tracing::warn!(?err, "Failed to initialize dbus handlers"); + } + State { common: Common { config,