mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 17:33:19 +01:00
Add missing files
This commit is contained in:
465
rog-control-center/src/update_and_notify.rs
Normal file
465
rog-control-center/src/update_and_notify.rs
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
//! `update_and_notify` is responsible for both notifications *and* updating stored statuses
|
||||||
|
//! about the system state. This is done through either direct, intoify, zbus notifications
|
||||||
|
//! or similar methods.
|
||||||
|
|
||||||
|
use crate::{config::Config, error::Result, system_state::SystemState};
|
||||||
|
use log::{error, info, trace, warn};
|
||||||
|
use notify_rust::{Hint, Notification, NotificationHandle, Urgency};
|
||||||
|
use rog_dbus::{
|
||||||
|
zbus_anime::AnimeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy,
|
||||||
|
zbus_power::PowerProxy, zbus_profile::ProfileProxy,
|
||||||
|
};
|
||||||
|
use rog_platform::platform::GpuMode;
|
||||||
|
use rog_profiles::Profile;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
process::Command,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
use supergfxctl::{pci_device::GfxPower, zbus_proxy::DaemonProxy as SuperProxy};
|
||||||
|
use tokio::time::sleep;
|
||||||
|
use zbus::export::futures_util::{future, StreamExt};
|
||||||
|
|
||||||
|
const NOTIF_HEADER: &str = "ROG Control";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct EnabledNotifications {
|
||||||
|
pub receive_notify_post_boot_sound: bool,
|
||||||
|
pub receive_notify_panel_od: bool,
|
||||||
|
pub receive_notify_dgpu_disable: bool,
|
||||||
|
pub receive_notify_egpu_enable: bool,
|
||||||
|
pub receive_notify_gpu_mux_mode: bool,
|
||||||
|
pub receive_notify_charge_control_end_threshold: bool,
|
||||||
|
pub receive_notify_mains_online: bool,
|
||||||
|
pub receive_notify_profile: bool,
|
||||||
|
pub receive_notify_led: bool,
|
||||||
|
/// Anime
|
||||||
|
pub receive_power_states: bool,
|
||||||
|
pub receive_notify_gfx: bool,
|
||||||
|
pub receive_notify_gfx_status: bool,
|
||||||
|
pub all_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EnabledNotifications {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
receive_notify_post_boot_sound: false,
|
||||||
|
receive_notify_panel_od: true,
|
||||||
|
receive_notify_dgpu_disable: true,
|
||||||
|
receive_notify_egpu_enable: true,
|
||||||
|
receive_notify_gpu_mux_mode: true,
|
||||||
|
receive_notify_charge_control_end_threshold: true,
|
||||||
|
receive_notify_mains_online: false,
|
||||||
|
receive_notify_profile: true,
|
||||||
|
receive_notify_led: false,
|
||||||
|
receive_power_states: false,
|
||||||
|
receive_notify_gfx: false,
|
||||||
|
receive_notify_gfx_status: false,
|
||||||
|
all_enabled: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnabledNotifications {
|
||||||
|
pub fn tokio_mutex(config: &Config) -> Arc<Mutex<Self>> {
|
||||||
|
Arc::new(Mutex::new(config.enabled_notifications.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! notify {
|
||||||
|
($notifier:expr, $last_notif:ident) => {
|
||||||
|
if let Some(notif) = $last_notif.take() {
|
||||||
|
notif.close();
|
||||||
|
}
|
||||||
|
if let Ok(x) = $notifier {
|
||||||
|
$last_notif.replace(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: drop the macro and use generics plus closure
|
||||||
|
macro_rules! recv_notif {
|
||||||
|
($proxy:ident,
|
||||||
|
$signal:ident,
|
||||||
|
$last_notif:ident,
|
||||||
|
$notif_enabled:ident,
|
||||||
|
$page_states:ident,
|
||||||
|
($($args: tt)*),
|
||||||
|
($($out_arg:tt)+),
|
||||||
|
$msg:literal,
|
||||||
|
$notifier:ident) => {
|
||||||
|
|
||||||
|
let last_notif = $last_notif.clone();
|
||||||
|
let notifs_enabled1 = $notif_enabled.clone();
|
||||||
|
let page_states1 = $page_states.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.map_err(|e| {
|
||||||
|
log::error!("zbus signal: {}: {e}", stringify!($signal));
|
||||||
|
e
|
||||||
|
}).unwrap();
|
||||||
|
let proxy = $proxy::new(&conn).await.map_err(|e| {
|
||||||
|
log::error!("zbus signal: {}: {e}", stringify!($signal));
|
||||||
|
e
|
||||||
|
}).unwrap();
|
||||||
|
if let Ok(mut p) = proxy.$signal().await {
|
||||||
|
info!("Started zbus signal thread: {}", stringify!($signal));
|
||||||
|
while let Some(e) = p.next().await {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
if let Ok(config) = notifs_enabled1.lock() {
|
||||||
|
if config.all_enabled && config.$signal {
|
||||||
|
if let Ok(ref mut lock) = last_notif.lock() {
|
||||||
|
trace!("zbus signal {} locked last_notif", stringify!($signal));
|
||||||
|
notify!($notifier($msg, &out.$($out_arg)+()), lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(mut lock) = page_states1.lock() {
|
||||||
|
lock.$($args)+ = *out.$($out_arg)+();
|
||||||
|
lock.set_notified();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type SharedHandle = Arc<Mutex<Option<NotificationHandle>>>;
|
||||||
|
|
||||||
|
pub fn start_notifications(
|
||||||
|
page_states: Arc<Mutex<SystemState>>,
|
||||||
|
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
|
||||||
|
|
||||||
|
// BIOS notif
|
||||||
|
recv_notif!(
|
||||||
|
RogBiosProxy,
|
||||||
|
receive_notify_post_boot_sound,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(bios.post_sound),
|
||||||
|
(on),
|
||||||
|
"BIOS Post sound",
|
||||||
|
do_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
recv_notif!(
|
||||||
|
RogBiosProxy,
|
||||||
|
receive_notify_panel_od,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(bios.panel_overdrive),
|
||||||
|
(overdrive),
|
||||||
|
"Panel Overdrive enabled:",
|
||||||
|
do_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
recv_notif!(
|
||||||
|
RogBiosProxy,
|
||||||
|
receive_notify_dgpu_disable,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(bios.dgpu_disable),
|
||||||
|
(disable),
|
||||||
|
"BIOS dGPU disabled",
|
||||||
|
do_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
recv_notif!(
|
||||||
|
RogBiosProxy,
|
||||||
|
receive_notify_egpu_enable,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(bios.egpu_enable),
|
||||||
|
(enable),
|
||||||
|
"BIOS eGPU enabled",
|
||||||
|
do_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
recv_notif!(
|
||||||
|
RogBiosProxy,
|
||||||
|
receive_notify_gpu_mux_mode,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(bios.dedicated_gfx),
|
||||||
|
(mode),
|
||||||
|
"Reboot required. BIOS GPU MUX mode set to",
|
||||||
|
do_mux_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
// Charge notif
|
||||||
|
recv_notif!(
|
||||||
|
PowerProxy,
|
||||||
|
receive_notify_charge_control_end_threshold,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(power_state.charge_limit),
|
||||||
|
(limit),
|
||||||
|
"Battery charge limit changed to",
|
||||||
|
do_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
recv_notif!(
|
||||||
|
PowerProxy,
|
||||||
|
receive_notify_mains_online,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(power_state.ac_power),
|
||||||
|
(on),
|
||||||
|
"AC Power power is",
|
||||||
|
ac_power_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
// Profile notif
|
||||||
|
recv_notif!(
|
||||||
|
ProfileProxy,
|
||||||
|
receive_notify_profile,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(profiles.current),
|
||||||
|
(profile),
|
||||||
|
"Profile changed to",
|
||||||
|
do_thermal_notif
|
||||||
|
);
|
||||||
|
// notify!(do_thermal_notif(&out.profile), lock);
|
||||||
|
|
||||||
|
// LED notif
|
||||||
|
recv_notif!(
|
||||||
|
LedProxy,
|
||||||
|
receive_notify_led,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(aura.current_mode),
|
||||||
|
(data.mode),
|
||||||
|
"Keyboard LED mode changed to",
|
||||||
|
do_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
let page_states1 = page_states.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let conn = zbus::Connection::system()
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("zbus signal: receive_power_states: {e}");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let proxy = AnimeProxy::new(&conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("zbus signal: receive_power_states: {e}");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
if let Ok(p) = proxy.receive_power_states().await {
|
||||||
|
info!("Started zbus signal thread: receive_power_states");
|
||||||
|
p.for_each(|_| {
|
||||||
|
if let Ok(_lock) = page_states1.lock() {
|
||||||
|
// TODO: lock.anime.
|
||||||
|
}
|
||||||
|
future::ready(())
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Ok(lock) = page_states.try_lock() {
|
||||||
|
use supergfxctl::pci_device::Device;
|
||||||
|
let dev = Device::find().unwrap();
|
||||||
|
let mut found_dgpu = false; // just for logging
|
||||||
|
for dev in dev {
|
||||||
|
if dev.is_dgpu() {
|
||||||
|
let notifs_enabled1 = enabled_notifications.clone();
|
||||||
|
let last_notif = last_notification.clone();
|
||||||
|
let page_states1 = page_states.clone();
|
||||||
|
// Plain old thread is perfectly fine since most of this is potentially blocking
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut last_status = GfxPower::Unknown;
|
||||||
|
loop {
|
||||||
|
if let Ok(status) = dev.get_runtime_status() {
|
||||||
|
if status != GfxPower::Unknown && status != last_status {
|
||||||
|
if let Ok(config) = notifs_enabled1.lock() {
|
||||||
|
if config.all_enabled && config.receive_notify_gfx_status {
|
||||||
|
// Required check because status cycles through active/unknown/suspended
|
||||||
|
if let Ok(ref mut lock) = last_notif.lock() {
|
||||||
|
notify!(
|
||||||
|
do_gpu_status_notif(
|
||||||
|
"dGPU status changed:",
|
||||||
|
&status
|
||||||
|
),
|
||||||
|
lock
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(mut lock) = page_states1.lock() {
|
||||||
|
lock.gfx_state.power_status = status;
|
||||||
|
lock.set_notified();
|
||||||
|
}
|
||||||
|
last_status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
found_dgpu = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found_dgpu {
|
||||||
|
warn!("Did not find a dGPU on this system, dGPU status won't be avilable");
|
||||||
|
}
|
||||||
|
|
||||||
|
if lock.gfx_state.has_supergfx {
|
||||||
|
recv_notif!(
|
||||||
|
SuperProxy,
|
||||||
|
receive_notify_gfx,
|
||||||
|
last_notification,
|
||||||
|
enabled_notifications,
|
||||||
|
page_states,
|
||||||
|
(gfx_state.mode),
|
||||||
|
(mode),
|
||||||
|
"Gfx mode changed to",
|
||||||
|
do_notification
|
||||||
|
);
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let conn = zbus::Connection::system()
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("zbus signal: receive_notify_action: {e}");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let proxy = SuperProxy::new(&conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("zbus signal: receive_notify_action: {e}");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
if let Ok(mut p) = proxy.receive_notify_action().await {
|
||||||
|
info!("Started zbus signal thread: receive_notify_action");
|
||||||
|
while let Some(e) = p.next().await {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
let action = out.action();
|
||||||
|
do_gfx_action_notif(
|
||||||
|
"Gfx mode change requires",
|
||||||
|
&format!("{action:?}",),
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!("zbus signal: do_gfx_action_notif: {e}");
|
||||||
|
e
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base_notification<T>(message: &str, data: &T) -> Notification
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
{
|
||||||
|
let mut notif = Notification::new();
|
||||||
|
|
||||||
|
notif
|
||||||
|
.summary(NOTIF_HEADER)
|
||||||
|
.body(&format!("{message} {data}"))
|
||||||
|
.timeout(2000)
|
||||||
|
//.hint(Hint::Resident(true))
|
||||||
|
.hint(Hint::Category("device".into()));
|
||||||
|
|
||||||
|
notif
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_notification<T>(message: &str, data: &T) -> Result<NotificationHandle>
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
{
|
||||||
|
Ok(base_notification(message, data).show()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ac_power_notification(message: &str, on: &bool) -> Result<NotificationHandle> {
|
||||||
|
let data = if *on {
|
||||||
|
"plugged".to_owned()
|
||||||
|
} else {
|
||||||
|
"unplugged".to_owned()
|
||||||
|
};
|
||||||
|
Ok(base_notification(message, &data).show()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_thermal_notif(message: &str, profile: &Profile) -> Result<NotificationHandle> {
|
||||||
|
let icon = match profile {
|
||||||
|
Profile::Balanced => "asus_notif_yellow",
|
||||||
|
Profile::Performance => "asus_notif_red",
|
||||||
|
Profile::Quiet => "asus_notif_green",
|
||||||
|
};
|
||||||
|
let profile: &str = (*profile).into();
|
||||||
|
let mut notif = base_notification(message, &profile.to_uppercase());
|
||||||
|
Ok(notif.icon(icon).show()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_gpu_status_notif(message: &str, data: &GfxPower) -> Result<NotificationHandle> {
|
||||||
|
// eww
|
||||||
|
let mut notif = base_notification(message, &<&str>::from(data).to_owned());
|
||||||
|
let icon = match data {
|
||||||
|
GfxPower::Suspended => "asus_notif_blue",
|
||||||
|
GfxPower::Off => "asus_notif_green",
|
||||||
|
GfxPower::AsusDisabled => "asus_notif_white",
|
||||||
|
GfxPower::AsusMuxDiscreet | GfxPower::Active => "asus_notif_red",
|
||||||
|
GfxPower::Unknown => "gpu-integrated",
|
||||||
|
};
|
||||||
|
notif.icon(icon);
|
||||||
|
Ok(Notification::show(¬if)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_gfx_action_notif<T>(message: &str, data: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
{
|
||||||
|
let mut notif = base_notification(message, data);
|
||||||
|
notif.action("gnome-session-quit", "Logout");
|
||||||
|
notif.urgency(Urgency::Critical);
|
||||||
|
notif.timeout(3000);
|
||||||
|
notif.icon("dialog-warning");
|
||||||
|
notif.hint(Hint::Transient(true));
|
||||||
|
let handle = notif.show()?;
|
||||||
|
handle.wait_for_action(|id| {
|
||||||
|
if id == "gnome-session-quit" {
|
||||||
|
let mut cmd = Command::new("gnome-session-quit");
|
||||||
|
cmd.spawn().ok();
|
||||||
|
} else if id == "__closed" {
|
||||||
|
// TODO: cancel the switching
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actual `GpuMode` unused as data is never correct until switched by reboot
|
||||||
|
fn do_mux_notification(message: &str, _: &GpuMode) -> Result<NotificationHandle> {
|
||||||
|
let mut notif = base_notification(message, &"");
|
||||||
|
notif.urgency(Urgency::Critical);
|
||||||
|
notif.icon("system-reboot-symbolic");
|
||||||
|
notif.hint(Hint::Transient(true));
|
||||||
|
Ok(notif.show()?)
|
||||||
|
}
|
||||||
9
rust-toolchain
Normal file
9
rust-toolchain
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# If you see this, run "rustup self update" to get rustup 1.23 or newer.
|
||||||
|
|
||||||
|
# NOTE: above comment is for older `rustup` (before TOML support was added),
|
||||||
|
# which will treat the first line as the toolchain name, and therefore show it
|
||||||
|
# to the user in the error, instead of "error: invalid channel name '[toolchain]'".
|
||||||
|
|
||||||
|
[toolchain]
|
||||||
|
channel = "1.65.0"
|
||||||
|
components = [ "rustfmt", "clippy" ]
|
||||||
Reference in New Issue
Block a user