mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
ROGCC: add status for dgpu, charge ctl, panel-od to systray
This commit is contained in:
@@ -4,18 +4,27 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::Write,
|
io::Write,
|
||||||
sync::{
|
sync::{
|
||||||
|
atomic::{AtomicBool, AtomicU8, Ordering},
|
||||||
mpsc::{channel, Receiver},
|
mpsc::{channel, Receiver},
|
||||||
Arc, Mutex,
|
Arc, Mutex,
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use gtk::{gio::Icon, prelude::*, MenuItem};
|
use gtk::{gio::Icon, prelude::*};
|
||||||
|
use rog_dbus::{
|
||||||
|
zbus_platform::{RogBiosProxy, RogBiosProxyBlocking},
|
||||||
|
zbus_power::PowerProxy,
|
||||||
|
};
|
||||||
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
|
use rog_platform::{platform::GpuMode, supported::SupportedFunctions};
|
||||||
|
use zbus::export::futures_util::StreamExt;
|
||||||
|
|
||||||
use crate::{error::Result, get_ipc_file, SHOW_GUI};
|
use crate::{error::Result, get_ipc_file, SHOW_GUI};
|
||||||
use libappindicator::{AppIndicator, AppIndicatorStatus};
|
use libappindicator::{AppIndicator, AppIndicatorStatus};
|
||||||
use supergfxctl::pci_device::{GfxMode, GfxPower};
|
use supergfxctl::{
|
||||||
|
pci_device::{GfxMode, GfxPower},
|
||||||
|
zbus_proxy::DaemonProxyBlocking,
|
||||||
|
};
|
||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
@@ -75,29 +84,22 @@ impl RadioGroup {
|
|||||||
pub struct ROGTray {
|
pub struct ROGTray {
|
||||||
tray: AppIndicator,
|
tray: AppIndicator,
|
||||||
menu: gtk::Menu,
|
menu: gtk::Menu,
|
||||||
items: Vec<MenuItem>,
|
icon: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ROGTray {
|
impl ROGTray {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
let mut rog_tray = Self {
|
let rog_tray = Self {
|
||||||
tray: AppIndicator::new(TRAY_LABEL, TRAY_APP_ICON),
|
tray: AppIndicator::new(TRAY_LABEL, TRAY_APP_ICON),
|
||||||
menu: gtk::Menu::new(),
|
menu: gtk::Menu::new(),
|
||||||
items: Vec::new(),
|
icon: TRAY_APP_ICON,
|
||||||
};
|
};
|
||||||
// rog_tray.set_icon(TRAY_APP_ICON);
|
|
||||||
|
|
||||||
rog_tray.add_icon_menu_item("Open app", "asus_notif_red", move || {
|
|
||||||
get_ipc_file().unwrap().write_all(&[SHOW_GUI]).ok();
|
|
||||||
});
|
|
||||||
|
|
||||||
rog_tray.add_inactive_label("----");
|
|
||||||
|
|
||||||
Ok(rog_tray)
|
Ok(rog_tray)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_icon(&mut self, icon: &str) {
|
pub fn set_icon(&mut self, icon: &'static str) {
|
||||||
self.tray.set_icon(icon);
|
self.icon = icon;
|
||||||
|
self.tray.set_icon(self.icon);
|
||||||
self.tray.set_status(AppIndicatorStatus::Active);
|
self.tray.set_status(AppIndicatorStatus::Active);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +109,14 @@ impl ROGTray {
|
|||||||
item.set_sensitive(false);
|
item.set_sensitive(false);
|
||||||
self.menu.append(&item);
|
self.menu.append(&item);
|
||||||
self.menu.show_all();
|
self.menu.show_all();
|
||||||
self.tray.set_menu(&mut self.menu);
|
}
|
||||||
|
|
||||||
|
/// Add a separator
|
||||||
|
fn add_separator(&mut self) {
|
||||||
|
let item = gtk::SeparatorMenuItem::new();
|
||||||
|
item.set_sensitive(false);
|
||||||
|
self.menu.append(&item);
|
||||||
|
self.menu.show_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_radio_sub_menu(&mut self, header_label: &str, active_label: &str, sub_menu: RadioGroup) {
|
fn add_radio_sub_menu(&mut self, header_label: &str, active_label: &str, sub_menu: RadioGroup) {
|
||||||
@@ -131,14 +140,24 @@ impl ROGTray {
|
|||||||
F: Fn() + Send + 'static,
|
F: Fn() + Send + 'static,
|
||||||
{
|
{
|
||||||
let item = gtk::MenuItem::with_label(label);
|
let item = gtk::MenuItem::with_label(label);
|
||||||
self.items.push(item);
|
|
||||||
let item = self.items.last().unwrap();
|
|
||||||
item.connect_activate(move |_| {
|
item.connect_activate(move |_| {
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
self.menu.append(item);
|
self.menu.append(&item);
|
||||||
|
self.menu.show_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_check_menu_item<F>(&mut self, label: &str, is_active: bool, cb: F)
|
||||||
|
where
|
||||||
|
F: Fn(>k::CheckMenuItem) + Send + 'static,
|
||||||
|
{
|
||||||
|
let item = gtk::CheckMenuItem::with_label(label);
|
||||||
|
item.set_active(is_active);
|
||||||
|
item.connect_activate(move |this| {
|
||||||
|
cb(this);
|
||||||
|
});
|
||||||
|
self.menu.append(&item);
|
||||||
self.menu.show_all();
|
self.menu.show_all();
|
||||||
self.tray.set_menu(&mut self.menu);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a menu item with an icon to the right
|
/// Add a menu item with an icon to the right
|
||||||
@@ -149,19 +168,17 @@ impl ROGTray {
|
|||||||
let g_box = gtk::Box::new(gtk::Orientation::Horizontal, 6);
|
let g_box = gtk::Box::new(gtk::Orientation::Horizontal, 6);
|
||||||
let icon = gtk::Image::from_gicon(&Icon::for_string(icon).unwrap(), gtk::IconSize::Menu);
|
let icon = gtk::Image::from_gicon(&Icon::for_string(icon).unwrap(), gtk::IconSize::Menu);
|
||||||
let label = gtk::Label::new(Some(label));
|
let label = gtk::Label::new(Some(label));
|
||||||
let menu_item = gtk::MenuItem::new();
|
let item = gtk::MenuItem::new();
|
||||||
g_box.add(&icon);
|
g_box.add(&icon);
|
||||||
g_box.add(&label);
|
g_box.add(&label);
|
||||||
|
|
||||||
menu_item.add(&g_box);
|
item.add(&g_box);
|
||||||
menu_item.show_all();
|
item.show_all();
|
||||||
|
|
||||||
self.items.push(menu_item);
|
|
||||||
let item = self.items.last().unwrap();
|
|
||||||
item.connect_activate(move |_| {
|
item.connect_activate(move |_| {
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
self.menu.append(item);
|
self.menu.append(&item);
|
||||||
self.menu.show_all();
|
self.menu.show_all();
|
||||||
self.tray.set_menu(&mut self.menu);
|
self.tray.set_menu(&mut self.menu);
|
||||||
}
|
}
|
||||||
@@ -169,50 +186,100 @@ impl ROGTray {
|
|||||||
fn set_status(&mut self, status: AppIndicatorStatus) {
|
fn set_status(&mut self, status: AppIndicatorStatus) {
|
||||||
self.tray.set_status(status)
|
self.tray.set_status(status)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn init_gpu_menu(supported: &SupportedFunctions, tray: &mut ROGTray) {
|
fn menu_add_base(&mut self) {
|
||||||
let conn = zbus::blocking::Connection::system().unwrap();
|
self.add_icon_menu_item("Open app", "asus_notif_red", move || {
|
||||||
let gfx_dbus = supergfxctl::zbus_proxy::DaemonProxyBlocking::new(&conn).unwrap();
|
get_ipc_file().unwrap().write_all(&[SHOW_GUI]).ok();
|
||||||
|
});
|
||||||
|
|
||||||
let mode = gfx_dbus.mode().unwrap();
|
self.add_separator();
|
||||||
let mut gpu_menu = RadioGroup::new("Integrated", move |_| {
|
}
|
||||||
let mode = gfx_dbus.mode().unwrap();
|
|
||||||
if mode != GfxMode::Integrated {
|
fn menu_add_charge_limit(&mut self, limit: u8) {
|
||||||
gfx_dbus.set_mode(&GfxMode::Integrated).unwrap();
|
self.add_inactive_label(&format!("Charge limit: {limit}"));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
let gfx_dbus = supergfxctl::zbus_proxy::DaemonProxyBlocking::new(&conn).unwrap();
|
fn menu_add_panel_od(&mut self, panel_od: bool) {
|
||||||
gpu_menu.add("Hybrid", move |_| {
|
self.add_check_menu_item("Panel Overdrive", panel_od, move |this| {
|
||||||
let mode = gfx_dbus.mode().unwrap();
|
let conn = zbus::blocking::Connection::system().unwrap();
|
||||||
if mode != GfxMode::Hybrid {
|
let bios = RogBiosProxyBlocking::new(&conn).unwrap();
|
||||||
gfx_dbus.set_mode(&GfxMode::Hybrid).unwrap();
|
bios.set_panel_od(this.is_active()).unwrap();
|
||||||
}
|
|
||||||
});
|
|
||||||
if supported.rog_bios_ctrl.gpu_mux {
|
|
||||||
let gfx_dbus = rog_dbus::zbus_platform::RogBiosProxyBlocking::new(&conn).unwrap();
|
|
||||||
gpu_menu.add("Ultimate (Reboot required)", move |_| {
|
|
||||||
let mode = gfx_dbus.gpu_mux_mode().unwrap();
|
|
||||||
if mode != GpuMode::Discrete {
|
|
||||||
gfx_dbus.set_gpu_mux_mode(GpuMode::Discrete).unwrap();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if supported.rog_bios_ctrl.egpu_enable {
|
|
||||||
|
fn menu_add_gpu(&mut self, supported: &SupportedFunctions, current_mode: GfxMode) {
|
||||||
|
let conn = zbus::blocking::Connection::system().unwrap();
|
||||||
let gfx_dbus = supergfxctl::zbus_proxy::DaemonProxyBlocking::new(&conn).unwrap();
|
let gfx_dbus = supergfxctl::zbus_proxy::DaemonProxyBlocking::new(&conn).unwrap();
|
||||||
gpu_menu.add("eGPU", move |_| {
|
|
||||||
|
let mode = gfx_dbus.mode().unwrap();
|
||||||
|
let mut gpu_menu = RadioGroup::new("Integrated", move |_| {
|
||||||
let mode = gfx_dbus.mode().unwrap();
|
let mode = gfx_dbus.mode().unwrap();
|
||||||
if mode != GfxMode::Egpu {
|
if mode != GfxMode::Integrated {
|
||||||
gfx_dbus.set_mode(&GfxMode::Egpu).unwrap();
|
gfx_dbus.set_mode(&GfxMode::Integrated).unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let gfx_dbus = supergfxctl::zbus_proxy::DaemonProxyBlocking::new(&conn).unwrap();
|
||||||
|
gpu_menu.add("Hybrid", move |_| {
|
||||||
|
let mode = gfx_dbus.mode().unwrap();
|
||||||
|
if mode != GfxMode::Hybrid {
|
||||||
|
gfx_dbus.set_mode(&GfxMode::Hybrid).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if supported.rog_bios_ctrl.gpu_mux {
|
||||||
|
let gfx_dbus = rog_dbus::zbus_platform::RogBiosProxyBlocking::new(&conn).unwrap();
|
||||||
|
gpu_menu.add("Ultimate (Reboot required)", move |_| {
|
||||||
|
let mode = gfx_dbus.gpu_mux_mode().unwrap();
|
||||||
|
if mode != GpuMode::Discrete {
|
||||||
|
gfx_dbus.set_gpu_mux_mode(GpuMode::Discrete).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if supported.rog_bios_ctrl.egpu_enable {
|
||||||
|
let gfx_dbus = supergfxctl::zbus_proxy::DaemonProxyBlocking::new(&conn).unwrap();
|
||||||
|
gpu_menu.add("eGPU", move |_| {
|
||||||
|
let mode = gfx_dbus.mode().unwrap();
|
||||||
|
if mode != GfxMode::Egpu {
|
||||||
|
gfx_dbus.set_mode(&GfxMode::Egpu).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let active = match mode {
|
||||||
|
GfxMode::AsusMuxDiscreet => "Discreet".to_string(),
|
||||||
|
_ => mode.to_string(),
|
||||||
|
};
|
||||||
|
self.add_radio_sub_menu(
|
||||||
|
&format!("GPU Mode: {current_mode}"),
|
||||||
|
active.as_str(),
|
||||||
|
gpu_menu,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let active = match mode {
|
fn menu_clear(&mut self) {
|
||||||
GfxMode::AsusMuxDiscreet => "Discreet".to_string(),
|
self.menu = gtk::Menu::new();
|
||||||
_ => mode.to_string(),
|
}
|
||||||
};
|
|
||||||
tray.add_radio_sub_menu("GPU Mode", active.as_str(), gpu_menu);
|
/// Reset GTK menu to internal state, this can be called after clearing and rebuilding the menu too.
|
||||||
|
fn menu_update(&mut self) {
|
||||||
|
self.tray.set_menu(&mut self.menu);
|
||||||
|
self.set_icon(self.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do a flush, build, and update of the tray menu
|
||||||
|
fn rebuild_and_update(
|
||||||
|
&mut self,
|
||||||
|
supported: &SupportedFunctions,
|
||||||
|
current_gfx_mode: GfxMode,
|
||||||
|
charge_limit: u8,
|
||||||
|
panel_od: bool,
|
||||||
|
) {
|
||||||
|
self.menu_clear();
|
||||||
|
self.menu_add_base();
|
||||||
|
self.menu_add_charge_limit(charge_limit);
|
||||||
|
self.menu_add_panel_od(panel_od);
|
||||||
|
self.menu_add_gpu(supported, current_gfx_mode);
|
||||||
|
self.menu_update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_tray(
|
pub fn init_tray(
|
||||||
@@ -221,27 +288,71 @@ pub fn init_tray(
|
|||||||
) -> Receiver<TrayToApp> {
|
) -> Receiver<TrayToApp> {
|
||||||
let (send, recv) = channel();
|
let (send, recv) = channel();
|
||||||
let _send = Arc::new(Mutex::new(send));
|
let _send = Arc::new(Mutex::new(send));
|
||||||
|
let update_menu = Arc::new(AtomicBool::new(false));
|
||||||
|
let update_charge = Arc::new(AtomicU8::new(100));
|
||||||
|
let update_panel_od = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
// TODO: mostly follows same pattern as the notify macro stuff. Should make it gerneric
|
||||||
|
let update_menu1 = update_menu.clone();
|
||||||
|
let update_charge1 = update_charge.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = PowerProxy::new(&conn).await.unwrap();
|
||||||
|
let charge_limit = proxy.charge_control_end_threshold().await.unwrap();
|
||||||
|
update_charge1.store(charge_limit, Ordering::Relaxed);
|
||||||
|
update_menu1.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
if let Ok(mut p) = proxy.receive_notify_charge_control_end_threshold().await {
|
||||||
|
while let Some(e) = p.next().await {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
update_charge1.store(out.limit, Ordering::Relaxed);
|
||||||
|
update_menu1.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let update_menu1 = update_menu.clone();
|
||||||
|
let update_panel_od1 = update_panel_od.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let conn = zbus::Connection::system().await.unwrap();
|
||||||
|
let proxy = RogBiosProxy::new(&conn).await.unwrap();
|
||||||
|
let charge_limit = proxy.panel_od().await.unwrap();
|
||||||
|
update_panel_od1.store(charge_limit, Ordering::Relaxed);
|
||||||
|
update_menu1.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
if let Ok(mut p) = proxy.receive_notify_panel_od().await {
|
||||||
|
while let Some(e) = p.next().await {
|
||||||
|
if let Ok(out) = e.args() {
|
||||||
|
update_panel_od1.store(out.overdrive, Ordering::Relaxed);
|
||||||
|
update_menu1.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
let conn = zbus::blocking::Connection::system().unwrap();
|
||||||
|
let proxy = DaemonProxyBlocking::new(&conn).unwrap();
|
||||||
|
let mode = proxy.mode().unwrap();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
gtk::init().unwrap(); // Make this the main thread for gtk
|
gtk::init().unwrap(); // Make this the main thread for gtk
|
||||||
|
|
||||||
let mut tray = ROGTray::new().unwrap();
|
let mut tray = ROGTray::new().unwrap();
|
||||||
|
tray.rebuild_and_update(&supported, GfxMode::Hybrid, 100, false);
|
||||||
init_gpu_menu(&supported, &mut tray);
|
|
||||||
// let s1 = send.clone();
|
|
||||||
// tray.add_menu_item("Quit", move || {
|
|
||||||
// let lock = s1.lock().unwrap();
|
|
||||||
// lock.send(TrayToApp::Quit).ok();
|
|
||||||
// })
|
|
||||||
// .ok();
|
|
||||||
|
|
||||||
//***********************************
|
|
||||||
// finally do
|
|
||||||
tray.tray.set_menu(&mut tray.menu);
|
|
||||||
tray.set_icon(TRAY_APP_ICON);
|
tray.set_icon(TRAY_APP_ICON);
|
||||||
//***********************************
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
if update_menu.load(Ordering::Relaxed) {
|
||||||
|
tray.rebuild_and_update(
|
||||||
|
&supported,
|
||||||
|
mode,
|
||||||
|
update_charge.load(Ordering::Relaxed),
|
||||||
|
update_panel_od.load(Ordering::Relaxed),
|
||||||
|
);
|
||||||
|
update_menu.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(command) = recv_command.try_recv() {
|
if let Ok(command) = recv_command.try_recv() {
|
||||||
match command {
|
match command {
|
||||||
AppToTray::DgpuStatus(s) => {
|
AppToTray::DgpuStatus(s) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user