feat: add power-profiles-daemon as an additional power profile backend
This commit is contained in:
parent
db47edf9b6
commit
25c24ce184
6 changed files with 365 additions and 217 deletions
|
|
@ -1,12 +1,10 @@
|
||||||
|
use crate::backend::{power_profile_subscription, Power, PowerProfileRequest, PowerProfileUpdate};
|
||||||
use crate::backlight::{
|
use crate::backlight::{
|
||||||
screen_backlight_subscription, ScreenBacklightRequest, ScreenBacklightUpdate,
|
screen_backlight_subscription, ScreenBacklightRequest, ScreenBacklightUpdate,
|
||||||
};
|
};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::dgpu::{dgpu_subscription, Entry, GpuUpdate};
|
use crate::dgpu::{dgpu_subscription, Entry, GpuUpdate};
|
||||||
use crate::fl;
|
use crate::fl;
|
||||||
use crate::power_daemon::{
|
|
||||||
power_profile_subscription, Power, PowerProfileRequest, PowerProfileUpdate,
|
|
||||||
};
|
|
||||||
use crate::upower_device::{device_subscription, DeviceDbusEvent};
|
use crate::upower_device::{device_subscription, DeviceDbusEvent};
|
||||||
use crate::upower_kbdbacklight::{
|
use crate::upower_kbdbacklight::{
|
||||||
kbd_backlight_subscription, KeyboardBacklightRequest, KeyboardBacklightUpdate,
|
kbd_backlight_subscription, KeyboardBacklightRequest, KeyboardBacklightUpdate,
|
||||||
|
|
|
||||||
220
cosmic-applet-battery/src/backend/mod.rs
Normal file
220
cosmic-applet-battery/src/backend/mod.rs
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
use cosmic::iced::{self, futures::SinkExt, subscription};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use tokio::sync::mpsc::UnboundedReceiver;
|
||||||
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
|
use zbus::Connection;
|
||||||
|
use zbus::Result;
|
||||||
|
|
||||||
|
use self::power_daemon::PowerDaemonProxy;
|
||||||
|
use self::power_profiles::PowerProfilesProxy;
|
||||||
|
|
||||||
|
mod power_daemon;
|
||||||
|
mod power_profiles;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, Default)]
|
||||||
|
pub enum Power {
|
||||||
|
Battery,
|
||||||
|
#[default]
|
||||||
|
Balanced,
|
||||||
|
Performance,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Backend<'a> {
|
||||||
|
S76PowerDaemon(PowerDaemonProxy<'a>),
|
||||||
|
PowerProfilesDaemon(PowerProfilesProxy<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Default)]
|
||||||
|
pub enum BackendType {
|
||||||
|
#[default]
|
||||||
|
S76PowerDaemon,
|
||||||
|
PowerProfilesDaemon,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackendType {
|
||||||
|
fn next(self) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::S76PowerDaemon => Self::PowerProfilesDaemon,
|
||||||
|
Self::PowerProfilesDaemon => Self::S76PowerDaemon,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_power_profile(daemon: Backend<'_>) -> Result<Power> {
|
||||||
|
match daemon {
|
||||||
|
Backend::S76PowerDaemon(p) => {
|
||||||
|
let power = p.get_profile().await?;
|
||||||
|
match power.as_str() {
|
||||||
|
"Battery" => Ok(Power::Battery),
|
||||||
|
"Balanced" => Ok(Power::Balanced),
|
||||||
|
"Performance" => Ok(Power::Performance),
|
||||||
|
_ => panic!("Unknown power profile: {}", power),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Backend::PowerProfilesDaemon(ppd) => {
|
||||||
|
let power = ppd.active_profile().await?;
|
||||||
|
match power.as_str() {
|
||||||
|
"power-saver" => Ok(Power::Battery),
|
||||||
|
"balanced" => Ok(Power::Balanced),
|
||||||
|
"performance" => Ok(Power::Performance),
|
||||||
|
_ => panic!("Unknown power profile: {}", power),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_power_profile(daemon: Backend<'_>, power: Power) -> Result<()> {
|
||||||
|
match daemon {
|
||||||
|
Backend::S76PowerDaemon(p) => match power {
|
||||||
|
Power::Battery => p.battery().await,
|
||||||
|
Power::Balanced => p.balanced().await,
|
||||||
|
Power::Performance => p.performance().await,
|
||||||
|
},
|
||||||
|
Backend::PowerProfilesDaemon(ppd) => match power {
|
||||||
|
Power::Battery => ppd.set_active_profile("power-saver").await,
|
||||||
|
Power::Balanced => ppd.set_active_profile("balanced").await,
|
||||||
|
Power::Performance => ppd.set_active_profile("performance").await,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power_profile_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||||
|
id: I,
|
||||||
|
) -> iced::Subscription<PowerProfileUpdate> {
|
||||||
|
subscription::channel(id, 50, move |mut output| async move {
|
||||||
|
let mut state = State::Ready;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
state = start_listening(state, &mut output).await;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum State {
|
||||||
|
Ready,
|
||||||
|
Connecting(BackendType),
|
||||||
|
Waiting(
|
||||||
|
Connection,
|
||||||
|
UnboundedReceiver<PowerProfileRequest>,
|
||||||
|
BackendType,
|
||||||
|
),
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start_listening(
|
||||||
|
state: State,
|
||||||
|
output: &mut futures::channel::mpsc::Sender<PowerProfileUpdate>,
|
||||||
|
) -> State {
|
||||||
|
match state {
|
||||||
|
State::Ready => {
|
||||||
|
// Default to s76 powerdaemon
|
||||||
|
State::Connecting(BackendType::default())
|
||||||
|
}
|
||||||
|
State::Connecting(backend_type) => {
|
||||||
|
let conn = match Connection::system().await.map_err(|e| e.to_string()) {
|
||||||
|
Ok(conn) => conn,
|
||||||
|
Err(e) => {
|
||||||
|
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
||||||
|
return State::Finished;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let backend = match backend_type {
|
||||||
|
BackendType::S76PowerDaemon => {
|
||||||
|
match PowerDaemonProxy::new(&conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
{
|
||||||
|
Ok(p) => Backend::S76PowerDaemon(p),
|
||||||
|
Err(e) => {
|
||||||
|
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
||||||
|
return State::Connecting(backend_type.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BackendType::PowerProfilesDaemon => {
|
||||||
|
match PowerProfilesProxy::new(&conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
{
|
||||||
|
Ok(p) => Backend::PowerProfilesDaemon(p),
|
||||||
|
Err(e) => {
|
||||||
|
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
||||||
|
return State::Connecting(backend_type.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Successful connection
|
||||||
|
let profile = match get_power_profile(backend).await.map_err(|e| e.to_string()) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
||||||
|
return State::Connecting(backend_type.next());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||||
|
_ = output.send(PowerProfileUpdate::Init(profile, tx)).await;
|
||||||
|
State::Waiting(conn, rx, backend_type)
|
||||||
|
}
|
||||||
|
State::Waiting(conn, mut rx, backend_type) => {
|
||||||
|
let backend = match backend_type {
|
||||||
|
BackendType::S76PowerDaemon => {
|
||||||
|
match PowerDaemonProxy::new(&conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
{
|
||||||
|
Ok(p) => Backend::S76PowerDaemon(p),
|
||||||
|
Err(e) => {
|
||||||
|
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
||||||
|
return State::Connecting(backend_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BackendType::PowerProfilesDaemon => {
|
||||||
|
match PowerProfilesProxy::new(&conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
{
|
||||||
|
Ok(p) => Backend::PowerProfilesDaemon(p),
|
||||||
|
Err(e) => {
|
||||||
|
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
||||||
|
return State::Connecting(backend_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match rx.recv().await {
|
||||||
|
Some(PowerProfileRequest::Get) => {
|
||||||
|
if let Ok(profile) = get_power_profile(backend).await {
|
||||||
|
_ = output.send(PowerProfileUpdate::Update { profile }).await;
|
||||||
|
}
|
||||||
|
State::Waiting(conn, rx, backend_type)
|
||||||
|
}
|
||||||
|
Some(PowerProfileRequest::Set(profile)) => {
|
||||||
|
let _ = set_power_profile(backend, profile).await;
|
||||||
|
_ = output.send(PowerProfileUpdate::Update { profile }).await;
|
||||||
|
State::Waiting(conn, rx, backend_type)
|
||||||
|
}
|
||||||
|
None => State::Finished,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Finished => iced::futures::future::pending().await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum PowerProfileRequest {
|
||||||
|
Get,
|
||||||
|
Set(Power),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum PowerProfileUpdate {
|
||||||
|
Init(Power, UnboundedSender<PowerProfileRequest>),
|
||||||
|
Update { profile: Power },
|
||||||
|
Error(String),
|
||||||
|
}
|
||||||
79
cosmic-applet-battery/src/backend/power_daemon.rs
Normal file
79
cosmic-applet-battery/src/backend/power_daemon.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
// 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::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<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
|
||||||
|
|
||||||
|
/// GetChargeThresholds method
|
||||||
|
fn get_charge_thresholds(&self) -> zbus::Result<(u8, u8)>;
|
||||||
|
|
||||||
|
/// GetDefaultGraphics method
|
||||||
|
fn get_default_graphics(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// GetExternalDisplaysRequireDGPU method
|
||||||
|
fn get_external_displays_require_dgpu(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// GetGraphics method
|
||||||
|
fn get_graphics(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// GetGraphicsPower method
|
||||||
|
fn get_graphics_power(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// GetProfile method
|
||||||
|
fn get_profile(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// GetSwitchable method
|
||||||
|
fn get_switchable(&self) -> zbus::Result<bool>;
|
||||||
|
|
||||||
|
/// 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<()>;
|
||||||
|
}
|
||||||
64
cosmic-applet-battery/src/backend/power_profiles.rs
Normal file
64
cosmic-applet-battery/src/backend/power_profiles.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
//! # DBus interface proxy for: `org.freedesktop.UPower.PowerProfiles`
|
||||||
|
//!
|
||||||
|
//! This code was generated by `zbus-xmlgen` `3.1.1` from DBus introspection data.
|
||||||
|
//! Source: `powerprofilesdaemon.xml`.
|
||||||
|
//!
|
||||||
|
//! 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.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use zbus::dbus_proxy;
|
||||||
|
|
||||||
|
#[dbus_proxy(
|
||||||
|
interface = "org.freedesktop.UPower.PowerProfiles",
|
||||||
|
default_path = "/org/freedesktop/UPower/PowerProfiles",
|
||||||
|
assume_defaults = true
|
||||||
|
)]
|
||||||
|
trait PowerProfiles {
|
||||||
|
/// HoldProfile method
|
||||||
|
fn hold_profile(&self, profile: &str, reason: &str, application_id: &str) -> zbus::Result<u32>;
|
||||||
|
|
||||||
|
/// ReleaseProfile method
|
||||||
|
fn release_profile(&self, cookie: u32) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// ProfileReleased signal
|
||||||
|
#[dbus_proxy(signal)]
|
||||||
|
fn profile_released(&self, cookie: u32) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// Actions property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn actions(&self) -> zbus::Result<Vec<String>>;
|
||||||
|
|
||||||
|
/// ActiveProfile property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn active_profile(&self) -> zbus::Result<String>;
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn set_active_profile(&self, value: &str) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
/// ActiveProfileHolds property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn active_profile_holds(
|
||||||
|
&self,
|
||||||
|
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
|
||||||
|
|
||||||
|
/// PerformanceDegraded property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn performance_degraded(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// PerformanceInhibited property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn performance_inhibited(&self) -> zbus::Result<String>;
|
||||||
|
|
||||||
|
/// Profiles property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn profiles(
|
||||||
|
&self,
|
||||||
|
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
|
||||||
|
|
||||||
|
/// Version property
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn version(&self) -> zbus::Result<String>;
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
mod backlight;
|
mod backlight;
|
||||||
mod app;
|
mod app;
|
||||||
|
mod backend;
|
||||||
mod config;
|
mod config;
|
||||||
mod dgpu;
|
mod dgpu;
|
||||||
mod localize;
|
mod localize;
|
||||||
mod power_daemon;
|
|
||||||
mod upower;
|
mod upower;
|
||||||
mod upower_device;
|
mod upower_device;
|
||||||
mod upower_kbdbacklight;
|
mod upower_kbdbacklight;
|
||||||
|
|
|
||||||
|
|
@ -1,213 +0,0 @@
|
||||||
// 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 cosmic::iced::{self, futures::SinkExt, subscription};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use tokio::sync::mpsc::UnboundedReceiver;
|
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
|
||||||
use zbus::Result;
|
|
||||||
use zbus::{dbus_proxy, Connection};
|
|
||||||
|
|
||||||
#[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<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
|
|
||||||
|
|
||||||
/// GetChargeThresholds method
|
|
||||||
fn get_charge_thresholds(&self) -> zbus::Result<(u8, u8)>;
|
|
||||||
|
|
||||||
/// GetDefaultGraphics method
|
|
||||||
fn get_default_graphics(&self) -> zbus::Result<String>;
|
|
||||||
|
|
||||||
/// GetExternalDisplaysRequireDGPU method
|
|
||||||
fn get_external_displays_require_dgpu(&self) -> zbus::Result<bool>;
|
|
||||||
|
|
||||||
/// GetGraphics method
|
|
||||||
fn get_graphics(&self) -> zbus::Result<String>;
|
|
||||||
|
|
||||||
/// GetGraphicsPower method
|
|
||||||
fn get_graphics_power(&self) -> zbus::Result<bool>;
|
|
||||||
|
|
||||||
/// GetProfile method
|
|
||||||
fn get_profile(&self) -> zbus::Result<String>;
|
|
||||||
|
|
||||||
/// GetSwitchable method
|
|
||||||
fn get_switchable(&self) -> zbus::Result<bool>;
|
|
||||||
|
|
||||||
/// 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<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Default)]
|
|
||||||
pub enum Power {
|
|
||||||
Battery,
|
|
||||||
#[default]
|
|
||||||
Balanced,
|
|
||||||
Performance,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_power_profile(daemon: PowerDaemonProxy<'_>) -> Result<Power> {
|
|
||||||
let power = daemon.get_profile().await?;
|
|
||||||
match power.as_str() {
|
|
||||||
"Battery" => Ok(Power::Battery),
|
|
||||||
"Balanced" => Ok(Power::Balanced),
|
|
||||||
"Performance" => Ok(Power::Performance),
|
|
||||||
_ => panic!("Unknown power profile: {}", power),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set_power_profile(daemon: PowerDaemonProxy<'_>, power: Power) -> Result<()> {
|
|
||||||
match power {
|
|
||||||
Power::Battery => daemon.battery().await,
|
|
||||||
Power::Balanced => daemon.balanced().await,
|
|
||||||
Power::Performance => daemon.performance().await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn power_profile_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
|
||||||
id: I,
|
|
||||||
) -> iced::Subscription<PowerProfileUpdate> {
|
|
||||||
subscription::channel(id, 50, move |mut output| async move {
|
|
||||||
let mut state = State::Ready;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
state = start_listening(state, &mut output).await;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum State {
|
|
||||||
Ready,
|
|
||||||
Waiting(Connection, UnboundedReceiver<PowerProfileRequest>),
|
|
||||||
Finished,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn start_listening(
|
|
||||||
state: State,
|
|
||||||
output: &mut futures::channel::mpsc::Sender<PowerProfileUpdate>,
|
|
||||||
) -> State {
|
|
||||||
match state {
|
|
||||||
State::Ready => {
|
|
||||||
let conn = match Connection::system().await.map_err(|e| e.to_string()) {
|
|
||||||
Ok(conn) => conn,
|
|
||||||
Err(e) => {
|
|
||||||
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
|
||||||
return State::Finished;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
|
||||||
|
|
||||||
let power_proxy = match PowerDaemonProxy::new(&conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
{
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
|
||||||
|
|
||||||
return State::Waiting(conn, rx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let profile = match get_power_profile(power_proxy)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
{
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
|
||||||
return State::Waiting(conn, rx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
_ = output.send(PowerProfileUpdate::Init(profile, tx)).await;
|
|
||||||
State::Waiting(conn, rx)
|
|
||||||
}
|
|
||||||
State::Waiting(conn, mut rx) => {
|
|
||||||
let power_proxy = match PowerDaemonProxy::new(&conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())
|
|
||||||
{
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
_ = output.send(PowerProfileUpdate::Error(e)).await;
|
|
||||||
return State::Waiting(conn, rx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match rx.recv().await {
|
|
||||||
Some(PowerProfileRequest::Get) => {
|
|
||||||
if let Ok(profile) = get_power_profile(power_proxy).await {
|
|
||||||
_ = output.send(PowerProfileUpdate::Update { profile }).await;
|
|
||||||
}
|
|
||||||
State::Waiting(conn, rx)
|
|
||||||
}
|
|
||||||
Some(PowerProfileRequest::Set(profile)) => {
|
|
||||||
let _ = set_power_profile(power_proxy, profile).await;
|
|
||||||
_ = output.send(PowerProfileUpdate::Update { profile }).await;
|
|
||||||
State::Waiting(conn, rx)
|
|
||||||
}
|
|
||||||
None => State::Finished,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::Finished => iced::futures::future::pending().await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum PowerProfileRequest {
|
|
||||||
Get,
|
|
||||||
Set(Power),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum PowerProfileUpdate {
|
|
||||||
Init(Power, UnboundedSender<PowerProfileRequest>),
|
|
||||||
Update { profile: Power },
|
|
||||||
Error(String),
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue