mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Cleanup notifs, sys state, ac/bat commands
This commit is contained in:
@@ -9,7 +9,7 @@ use crate::AnimeType;
|
||||
|
||||
/// Mostly intended to be used with ASUS gifs, but can be used for other
|
||||
/// purposes (like images)
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct AnimeDiagonal(AnimeType, Vec<Vec<u8>>, Option<Duration>);
|
||||
|
||||
impl AnimeDiagonal {
|
||||
|
||||
@@ -702,7 +702,7 @@
|
||||
power_zones: [Keyboard],
|
||||
),
|
||||
(
|
||||
board_name: "GU605M",
|
||||
device_name: "GU605M",
|
||||
product_id: "",
|
||||
layout_name: "ga401q",
|
||||
basic_modes: [Static, Breathe, Strobe, Rainbow, Pulse],
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::fs::create_dir;
|
||||
use config_traits::{StdConfig, StdConfigLoad1};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::update_and_notify::EnabledNotifications;
|
||||
use crate::notify::EnabledNotifications;
|
||||
|
||||
const CFG_DIR: &str = "rog";
|
||||
const CFG_FILE_NAME: &str = "rog-control-center.cfg";
|
||||
@@ -15,14 +15,13 @@ pub struct Config {
|
||||
pub enable_tray_icon: bool,
|
||||
pub ac_command: String,
|
||||
pub bat_command: String,
|
||||
pub enable_notifications: bool,
|
||||
pub dark_mode: bool,
|
||||
// intended for use with devices like the ROG Ally
|
||||
pub start_fullscreen: bool,
|
||||
pub fullscreen_width: u32,
|
||||
pub fullscreen_height: u32,
|
||||
// This field must be last
|
||||
pub enabled_notifications: EnabledNotifications,
|
||||
pub notifications: EnabledNotifications,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -30,13 +29,12 @@ impl Default for Config {
|
||||
Self {
|
||||
run_in_background: true,
|
||||
startup_in_background: false,
|
||||
enable_notifications: true,
|
||||
enable_tray_icon: true,
|
||||
dark_mode: true,
|
||||
start_fullscreen: false,
|
||||
fullscreen_width: 1920,
|
||||
fullscreen_height: 1080,
|
||||
enabled_notifications: EnabledNotifications::default(),
|
||||
notifications: EnabledNotifications::default(),
|
||||
ac_command: String::new(),
|
||||
bat_command: String::new(),
|
||||
}
|
||||
@@ -94,8 +92,7 @@ impl From<Config461> for Config {
|
||||
start_fullscreen: false,
|
||||
fullscreen_width: 1920,
|
||||
fullscreen_height: 1080,
|
||||
enable_notifications: c.enable_notifications,
|
||||
enabled_notifications: c.enabled_notifications,
|
||||
notifications: c.enabled_notifications,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,10 @@ pub mod config;
|
||||
pub mod error;
|
||||
#[cfg(feature = "mocking")]
|
||||
pub mod mocking;
|
||||
pub mod system_state;
|
||||
pub mod notify;
|
||||
pub mod tray;
|
||||
pub mod types;
|
||||
pub mod ui;
|
||||
pub mod update_and_notify;
|
||||
|
||||
use nix::sys::stat;
|
||||
use nix::unistd;
|
||||
|
||||
@@ -14,11 +14,10 @@ use log::{info, LevelFilter};
|
||||
use rog_control_center::cli_options::CliStart;
|
||||
use rog_control_center::config::Config;
|
||||
use rog_control_center::error::Result;
|
||||
use rog_control_center::notify::start_notifications;
|
||||
use rog_control_center::slint::ComponentHandle;
|
||||
use rog_control_center::system_state::SystemState;
|
||||
use rog_control_center::tray::init_tray;
|
||||
use rog_control_center::ui::setup_window;
|
||||
use rog_control_center::update_and_notify::{start_notifications, EnabledNotifications};
|
||||
use rog_control_center::{
|
||||
get_ipc_file, on_tmp_dir_exists, print_versions, MainWindow, QUIT_APP, SHOWING_GUI, SHOW_GUI,
|
||||
};
|
||||
@@ -102,7 +101,7 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
if is_rog_ally {
|
||||
config.enable_notifications = false;
|
||||
config.notifications.enabled = false;
|
||||
config.enable_tray_icon = false;
|
||||
config.run_in_background = false;
|
||||
config.startup_in_background = false;
|
||||
@@ -115,16 +114,12 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
config.write();
|
||||
|
||||
let enabled_notifications = EnabledNotifications::tokio_mutex(&config);
|
||||
|
||||
// TODO: config mutex to share config in various places
|
||||
let states = setup_page_state_and_notifs(&enabled_notifications, &config).await?;
|
||||
|
||||
let enable_tray_icon = config.enable_tray_icon;
|
||||
let startup_in_background = config.startup_in_background;
|
||||
let config = Arc::new(Mutex::new(config));
|
||||
start_notifications(config.clone())?;
|
||||
if enable_tray_icon {
|
||||
init_tray(supported_properties, states.clone(), config.clone());
|
||||
init_tray(supported_properties, config.clone());
|
||||
}
|
||||
|
||||
thread_local! { pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default()};
|
||||
@@ -216,24 +211,6 @@ async fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn setup_page_state_and_notifs(
|
||||
enabled_notifications: &Arc<Mutex<EnabledNotifications>>,
|
||||
config: &Config,
|
||||
) -> Result<Arc<Mutex<SystemState>>> {
|
||||
let page_states = Arc::new(Mutex::new(
|
||||
SystemState::new(
|
||||
enabled_notifications.clone(),
|
||||
config.enable_tray_icon,
|
||||
config.run_in_background,
|
||||
)
|
||||
.await?,
|
||||
));
|
||||
|
||||
start_notifications(config, &page_states, enabled_notifications)?;
|
||||
|
||||
Ok(page_states)
|
||||
}
|
||||
|
||||
// /// Bah.. the icon dosn't work on wayland anyway, but we'll leave it in for
|
||||
// now. fn load_icon() -> IconData {
|
||||
// let path = PathBuf::from(APP_ICON_PATH);
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use log::error;
|
||||
use supergfxctl::pci_device::{GfxMode, GfxPower};
|
||||
#[cfg(not(feature = "mocking"))]
|
||||
use supergfxctl::zbus_proxy::DaemonProxy as GfxProxy;
|
||||
use zbus::Connection;
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(feature = "mocking")]
|
||||
use crate::mocking::DaemonProxyBlocking as GfxProxyBlocking;
|
||||
use crate::update_and_notify::EnabledNotifications;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GfxState {
|
||||
pub has_supergfx: bool,
|
||||
pub mode: GfxMode,
|
||||
pub power_status: GfxPower,
|
||||
}
|
||||
|
||||
impl GfxState {
|
||||
pub async fn new(dbus: &GfxProxy<'_>) -> Result<Self> {
|
||||
Ok(Self {
|
||||
has_supergfx: dbus.mode().await.is_ok(),
|
||||
mode: dbus.mode().await.unwrap_or(GfxMode::None),
|
||||
power_status: dbus.power().await.unwrap_or(GfxPower::Unknown),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GfxState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
has_supergfx: false,
|
||||
mode: GfxMode::None,
|
||||
power_status: GfxPower::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// State stored from system daemons. This is shared with: tray, zbus
|
||||
/// notifications thread and the GUI app thread.
|
||||
pub struct SystemState {
|
||||
pub enabled_notifications: Arc<Mutex<EnabledNotifications>>,
|
||||
pub gfx_state: GfxState,
|
||||
pub error: Option<String>,
|
||||
/// Specific field for the tray only so that we can know when it does need
|
||||
/// update. The tray should set this to false when done.
|
||||
pub tray_should_update: bool,
|
||||
pub app_should_update: bool,
|
||||
pub tray_enabled: bool,
|
||||
pub run_in_bg: bool,
|
||||
}
|
||||
|
||||
impl SystemState {
|
||||
/// Creates self, including the relevant dbus connections and proixies for
|
||||
/// internal use
|
||||
pub async fn new(
|
||||
enabled_notifications: Arc<Mutex<EnabledNotifications>>,
|
||||
tray_enabled: bool,
|
||||
run_in_bg: bool,
|
||||
) -> Result<Self> {
|
||||
let conn = Connection::system().await?;
|
||||
|
||||
let gfx_dbus = GfxProxy::builder(&conn)
|
||||
.destination(":org.supergfxctl.Daemon")?
|
||||
.build()
|
||||
.await
|
||||
.expect("Couldn't connect to supergfxd");
|
||||
Ok(Self {
|
||||
enabled_notifications,
|
||||
gfx_state: GfxState::new(&gfx_dbus)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let e = format!("Could not get supergfxd state: {e}");
|
||||
error!("{e}");
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
error: None,
|
||||
tray_should_update: true,
|
||||
app_should_update: true,
|
||||
tray_enabled,
|
||||
run_in_bg,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_notified(&mut self) {
|
||||
self.tray_should_update = true;
|
||||
self.app_should_update = true;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ use supergfxctl::zbus_proxy::DaemonProxyBlocking as GfxProxy;
|
||||
use versions::Versioning;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::system_state::SystemState;
|
||||
use crate::{get_ipc_file, QUIT_APP, SHOW_GUI};
|
||||
|
||||
const TRAY_LABEL: &str = "ROG Control Center";
|
||||
@@ -88,13 +87,17 @@ fn do_action(event: TrayEvent<TrayAction>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_tray_icon_and_tip(lock: &SystemState, tray: &TrayIcon<TrayAction>, supergfx_active: bool) {
|
||||
fn set_tray_icon_and_tip(
|
||||
mode: GfxMode,
|
||||
power: GfxPower,
|
||||
tray: &mut TrayIcon<TrayAction>,
|
||||
supergfx_active: bool,
|
||||
) {
|
||||
if let Some(icons) = ICONS.get() {
|
||||
let gpu_status = lock.gfx_state.power_status;
|
||||
match gpu_status {
|
||||
match power {
|
||||
GfxPower::Suspended => tray.set_icon(Some(icons.rog_blue.clone())),
|
||||
GfxPower::Off => {
|
||||
if lock.gfx_state.mode == GfxMode::Vfio {
|
||||
if mode == GfxMode::Vfio {
|
||||
tray.set_icon(Some(icons.rog_red.clone()))
|
||||
} else {
|
||||
tray.set_icon(Some(icons.rog_green.clone()))
|
||||
@@ -113,24 +116,16 @@ fn set_tray_icon_and_tip(lock: &SystemState, tray: &TrayIcon<TrayAction>, superg
|
||||
}
|
||||
};
|
||||
|
||||
let current_gpu_mode = lock.gfx_state.mode;
|
||||
|
||||
tray.set_tooltip(format!(
|
||||
"ROG: gpu mode = {current_gpu_mode:?}, gpu power = {gpu_status:?}"
|
||||
));
|
||||
tray.set_tooltip(format!("ROG: gpu mode = {mode:?}, gpu power = {power:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
/// The tray is controlled somewhat by `Arc<Mutex<SystemState>>`
|
||||
pub fn init_tray(
|
||||
_supported_properties: Vec<Properties>,
|
||||
states: Arc<Mutex<SystemState>>,
|
||||
config: Arc<Mutex<Config>>,
|
||||
) {
|
||||
pub fn init_tray(_supported_properties: Vec<Properties>, config: Arc<Mutex<Config>>) {
|
||||
std::thread::spawn(move || {
|
||||
let rog_red = read_icon(&PathBuf::from("asus_notif_red.png"));
|
||||
|
||||
if let Ok(tray) = TrayIconBuilder::<TrayAction>::new()
|
||||
if let Ok(mut tray) = TrayIconBuilder::<TrayAction>::new()
|
||||
.with_icon(rog_red.clone())
|
||||
.with_tooltip(TRAY_LABEL)
|
||||
.with_menu(build_menu())
|
||||
@@ -170,15 +165,14 @@ pub fn init_tray(
|
||||
|
||||
info!("Started ROGTray");
|
||||
loop {
|
||||
if let Ok(mut lock) = states.lock() {
|
||||
if lock.tray_should_update {
|
||||
set_tray_icon_and_tip(&lock, &tray, supergfx_active);
|
||||
lock.tray_should_update = false;
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
if !lock.enable_tray_icon {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Ok(lock) = config.try_lock() {
|
||||
if !lock.enable_tray_icon {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Ok(mode) = gfx_proxy.mode() {
|
||||
if let Ok(power) = gfx_proxy.power() {
|
||||
set_tray_icon_and_tip(mode, power, &mut tray, supergfx_active);
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_millis(50));
|
||||
|
||||
@@ -166,7 +166,7 @@ pub fn setup_app_settings_page(ui: &MainWindow, config: Arc<Mutex<Config>>) {
|
||||
let config_copy = config.clone();
|
||||
global.on_set_enable_notifications(move |enable| {
|
||||
if let Ok(mut lock) = config_copy.try_lock() {
|
||||
lock.enable_notifications = enable;
|
||||
lock.notifications.enabled = enable;
|
||||
lock.write();
|
||||
}
|
||||
});
|
||||
@@ -175,6 +175,6 @@ pub fn setup_app_settings_page(ui: &MainWindow, config: Arc<Mutex<Config>>) {
|
||||
global.set_run_in_background(lock.run_in_background);
|
||||
global.set_startup_in_background(lock.startup_in_background);
|
||||
global.set_enable_tray_icon(lock.enable_tray_icon);
|
||||
global.set_enable_notifications(lock.enable_notifications);
|
||||
global.set_enable_notifications(lock.notifications.enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,442 +0,0 @@
|
||||
//! `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.
|
||||
//!
|
||||
//! This module very much functions like a stand-alone app on its own thread.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::process::Command;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use log::{error, info, trace, warn};
|
||||
use notify_rust::{Hint, Notification, NotificationHandle, Urgency};
|
||||
use rog_dbus::zbus_platform::PlatformProxy;
|
||||
use rog_platform::platform::GpuMode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use supergfxctl::actions::UserActionRequired as GfxUserAction;
|
||||
use supergfxctl::pci_device::{GfxMode, GfxPower};
|
||||
use supergfxctl::zbus_proxy::DaemonProxy as SuperProxy;
|
||||
use tokio::time::sleep;
|
||||
use zbus::export::futures_util::StreamExt;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::error::Result;
|
||||
use crate::system_state::SystemState;
|
||||
|
||||
const NOTIF_HEADER: &str = "ROG Control";
|
||||
|
||||
static mut POWER_AC_CMD: Option<Command> = None;
|
||||
static mut POWER_BAT_CMD: Option<Command> = None;
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct EnabledNotifications {
|
||||
pub receive_power_states: bool,
|
||||
pub receive_notify_gfx: bool,
|
||||
pub receive_notify_gfx_status: bool,
|
||||
}
|
||||
|
||||
impl EnabledNotifications {
|
||||
pub fn tokio_mutex(config: &Config) -> Arc<Mutex<Self>> {
|
||||
Arc::new(Mutex::new(config.enabled_notifications.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn gpu_to_gfx(value: GpuMode) -> GfxMode {
|
||||
match value {
|
||||
GpuMode::Optimus => GfxMode::Hybrid,
|
||||
GpuMode::Integrated => GfxMode::Integrated,
|
||||
GpuMode::Egpu => GfxMode::AsusEgpu,
|
||||
GpuMode::Vfio => GfxMode::Vfio,
|
||||
GpuMode::Ultimate => GfxMode::AsusMuxDgpu,
|
||||
GpuMode::Error => GfxMode::None,
|
||||
GpuMode::NotSupported => GfxMode::None,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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::builder(&conn).build().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.$signal {
|
||||
trace!("zbus signal {}", stringify!($signal));
|
||||
$notifier($msg, &out.$($out_arg)+()).ok();
|
||||
}
|
||||
}
|
||||
if let Ok(mut lock) = page_states1.lock() {
|
||||
lock.$($args)+ = *out.$($out_arg)+();
|
||||
lock.set_notified();
|
||||
}
|
||||
}
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
pub fn start_notifications(
|
||||
config: &Config,
|
||||
page_states: &Arc<Mutex<SystemState>>,
|
||||
enabled_notifications: &Arc<Mutex<EnabledNotifications>>,
|
||||
) -> Result<()> {
|
||||
// Setup the AC/BAT commands that will run on poweer status change
|
||||
unsafe {
|
||||
let prog: Vec<&str> = config.ac_command.split_whitespace().collect();
|
||||
if prog.len() > 1 {
|
||||
let mut cmd = Command::new(prog[0]);
|
||||
|
||||
for arg in prog.iter().skip(1) {
|
||||
cmd.arg(*arg);
|
||||
}
|
||||
POWER_AC_CMD = Some(cmd);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
let prog: Vec<&str> = config.bat_command.split_whitespace().collect();
|
||||
if prog.len() > 1 {
|
||||
let mut cmd = Command::new(prog[0]);
|
||||
|
||||
for arg in prog.iter().skip(1) {
|
||||
cmd.arg(*arg);
|
||||
}
|
||||
POWER_BAT_CMD = Some(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
let page_states1 = page_states.clone();
|
||||
tokio::spawn(async move {
|
||||
let conn = zbus::Connection::system()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
let proxy = PlatformProxy::new(&conn)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("zbus signal: receive_notify_gpu_mux_mode: {e}");
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut actual_mux_mode = GpuMode::Error;
|
||||
if let Ok(mode) = proxy.gpu_mux_mode().await {
|
||||
actual_mux_mode = GpuMode::from(mode);
|
||||
}
|
||||
|
||||
info!("Started zbus signal thread: receive_notify_gpu_mux_mode");
|
||||
while let Some(e) = proxy.receive_gpu_mux_mode_changed().await.next().await {
|
||||
if let Ok(out) = e.get().await {
|
||||
let mode = GpuMode::from(out);
|
||||
if mode == actual_mux_mode {
|
||||
continue;
|
||||
}
|
||||
if let Ok(mut lock) = page_states1.lock() {
|
||||
lock.gfx_state.mode = gpu_to_gfx(mode);
|
||||
lock.set_notified();
|
||||
}
|
||||
do_mux_notification("Reboot required. BIOS GPU MUX mode set to", &mode).ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
use supergfxctl::pci_device::Device;
|
||||
let dev = Device::find().unwrap_or_default();
|
||||
let mut found_dgpu = false; // just for logging
|
||||
for dev in dev {
|
||||
if dev.is_dgpu() {
|
||||
let notifs_enabled1 = enabled_notifications.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.receive_notify_gfx_status {
|
||||
// Required check because status cycles through
|
||||
// active/unknown/suspended
|
||||
do_gpu_status_notif("dGPU status changed:", &status).ok();
|
||||
}
|
||||
}
|
||||
if let Ok(mut lock) = page_states1.lock() {
|
||||
lock.set_notified();
|
||||
}
|
||||
}
|
||||
if let Ok(mut lock) = page_states1.lock() {
|
||||
lock.gfx_state.power_status = status;
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
let page_states1 = page_states.clone();
|
||||
let enabled_notifications1 = enabled_notifications.clone();
|
||||
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::builder(&conn)
|
||||
.build()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("zbus signal: receive_notify_action: {e}");
|
||||
e
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if let Ok(mode) = proxy.mode().await {
|
||||
if let Ok(mut lock) = page_states1.lock() {
|
||||
lock.gfx_state.mode = mode;
|
||||
lock.gfx_state.has_supergfx = true;
|
||||
}
|
||||
} else {
|
||||
info!("supergfxd not running or not responding");
|
||||
return;
|
||||
}
|
||||
|
||||
let page_states2 = page_states1.clone();
|
||||
recv_notif!(
|
||||
SuperProxy,
|
||||
receive_notify_gfx,
|
||||
last_notification,
|
||||
enabled_notifications1,
|
||||
page_states2,
|
||||
(gfx_state.mode),
|
||||
(mode),
|
||||
"Gfx mode changed to",
|
||||
do_notification
|
||||
);
|
||||
|
||||
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();
|
||||
let mode = if let Ok(lock) = page_states1.lock() {
|
||||
convert_gfx_mode(lock.gfx_state.mode)
|
||||
} else {
|
||||
GpuMode::Error
|
||||
};
|
||||
match action {
|
||||
supergfxctl::actions::UserActionRequired::Reboot => {
|
||||
do_mux_notification("Graphics mode change requires reboot", &mode)
|
||||
}
|
||||
_ => do_gfx_action_notif(<&str>::from(action), *action, mode),
|
||||
}
|
||||
.map_err(|e| {
|
||||
error!("zbus signal: do_gfx_action_notif: {e}");
|
||||
e
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_gfx_mode(gfx: GfxMode) -> GpuMode {
|
||||
match gfx {
|
||||
GfxMode::Hybrid => GpuMode::Optimus,
|
||||
GfxMode::Integrated => GpuMode::Integrated,
|
||||
GfxMode::NvidiaNoModeset => GpuMode::Optimus,
|
||||
GfxMode::Vfio => GpuMode::Vfio,
|
||||
GfxMode::AsusEgpu => GpuMode::Egpu,
|
||||
GfxMode::AsusMuxDgpu => GpuMode::Ultimate,
|
||||
GfxMode::None => GpuMode::Error,
|
||||
}
|
||||
}
|
||||
|
||||
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(-1)
|
||||
//.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()?)
|
||||
}
|
||||
|
||||
// TODO:
|
||||
fn _ac_power_notification(message: &str, on: &bool) -> Result<NotificationHandle> {
|
||||
let data = if *on {
|
||||
unsafe {
|
||||
if let Some(cmd) = POWER_AC_CMD.as_mut() {
|
||||
if let Err(e) = cmd.spawn() {
|
||||
error!("AC power command error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
"plugged".to_owned()
|
||||
} else {
|
||||
unsafe {
|
||||
if let Some(cmd) = POWER_BAT_CMD.as_mut() {
|
||||
if let Err(e) = cmd.spawn() {
|
||||
error!("Battery power command error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
"unplugged".to_owned()
|
||||
};
|
||||
Ok(base_notification(message, &data).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(message: &str, action: GfxUserAction, mode: GpuMode) -> Result<()> {
|
||||
if matches!(action, GfxUserAction::Reboot) {
|
||||
do_mux_notification("Graphics mode change requires reboot", &mode).ok();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut notif = Notification::new();
|
||||
notif
|
||||
.summary(NOTIF_HEADER)
|
||||
.body(&format!("Changing to {mode}. {message}"))
|
||||
.timeout(2000)
|
||||
//.hint(Hint::Resident(true))
|
||||
.hint(Hint::Category("device".into()))
|
||||
.urgency(Urgency::Critical)
|
||||
.timeout(-1)
|
||||
.icon("dialog-warning")
|
||||
.hint(Hint::Transient(true));
|
||||
|
||||
if matches!(action, GfxUserAction::Logout) {
|
||||
notif.action("gfx-mode-session-action", "Logout");
|
||||
let handle = notif.show()?;
|
||||
if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
|
||||
if desktop.to_lowercase() == "gnome" {
|
||||
handle.wait_for_action(|id| {
|
||||
if id == "gfx-mode-session-action" {
|
||||
let mut cmd = Command::new("gnome-session-quit");
|
||||
cmd.spawn().ok();
|
||||
} else if id == "__closed" {
|
||||
// TODO: cancel the switching
|
||||
}
|
||||
});
|
||||
} else if desktop.to_lowercase() == "kde" {
|
||||
handle.wait_for_action(|id| {
|
||||
if id == "gfx-mode-session-action" {
|
||||
let mut cmd = Command::new("qdbus");
|
||||
cmd.args(["org.kde.ksmserver", "/KSMServer", "logout", "1", "0", "0"]);
|
||||
cmd.spawn().ok();
|
||||
} else if id == "__closed" {
|
||||
// TODO: cancel the switching
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// todo: handle alternatives
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notif.show()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Actual `GpuMode` unused as data is never correct until switched by reboot
|
||||
fn do_mux_notification(message: &str, m: &GpuMode) -> Result<()> {
|
||||
let mut notif = base_notification(message, &m.to_string());
|
||||
notif
|
||||
.action("gfx-mode-session-action", "Reboot")
|
||||
.urgency(Urgency::Critical)
|
||||
.icon("system-reboot-symbolic")
|
||||
.hint(Hint::Transient(true));
|
||||
let handle = notif.show()?;
|
||||
|
||||
std::thread::spawn(|| {
|
||||
if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
|
||||
if desktop.to_lowercase() == "gnome" {
|
||||
handle.wait_for_action(|id| {
|
||||
if id == "gfx-mode-session-action" {
|
||||
let mut cmd = Command::new("gnome-session-quit");
|
||||
cmd.arg("--reboot");
|
||||
cmd.spawn().ok();
|
||||
} else if id == "__closed" {
|
||||
// TODO: cancel the switching
|
||||
}
|
||||
});
|
||||
} else if desktop.to_lowercase() == "kde" {
|
||||
handle.wait_for_action(|id| {
|
||||
if id == "gfx-mode-session-action" {
|
||||
let mut cmd = Command::new("qdbus");
|
||||
cmd.args(["org.kde.ksmserver", "/KSMServer", "logout", "1", "1", "0"]);
|
||||
cmd.spawn().ok();
|
||||
} else if id == "__closed" {
|
||||
// TODO: cancel the switching
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"POT-Creation-Date: 2024-04-17 09:39+0000\n"
|
||||
"POT-Creation-Date: 2024-04-18 01:47+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
Reference in New Issue
Block a user