Tray icons

This commit is contained in:
Luke D. Jones
2022-11-08 21:55:09 +13:00
parent 4f8745ae19
commit a6fe7645e9
8 changed files with 590 additions and 36 deletions

View File

@@ -12,6 +12,10 @@ egui = { git = "https://github.com/flukejones/egui" }
eframe= { git = "https://github.com/flukejones/egui" }
#eframe= { git = "https://github.com/emilk/egui", default-features = false, features = ["dark-light", "default_fonts", "wgpu"] }
tray-item = "0.7.1"
libappindicator = "0.7" # Tray icon
gtk = "0.15"
daemon = { path = "../daemon" }
rog_anime = { path = "../rog-anime" }
rog_dbus = { path = "../rog-dbus" }
@@ -21,6 +25,9 @@ rog_platform = { path = "../rog-platform" }
supergfxctl = { git = "https://gitlab.com/asus-linux/supergfxctl.git", default-features = false }
#supergfxctl = { path = "../../supergfxctl", default-features = false }
log.workspace = true
env_logger.workspace = true
tokio.workspace = true
serde.workspace = true
toml.workspace = true
@@ -30,5 +37,7 @@ zbus.workspace = true
dirs.workspace = true
notify-rust.workspace = true
nix = "^0.20.0"
png_pong.workspace = true
nix = "^0.25"
tempfile = "3.2.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -2,16 +2,18 @@ use std::{
f64::consts::PI,
sync::{
atomic::{AtomicBool, AtomicU8, Ordering},
mpsc::Receiver,
Arc,
},
time::{Duration, Instant},
time::{Duration, Instant}, io::Write,
};
use egui::{Button, RichText};
use rog_platform::supported::SupportedFunctions;
use crate::{
config::Config, error::Result, page_states::PageDataStates, Page, RogDbusClientBlocking,
config::Config, error::Result, page_states::PageDataStates, tray::TrayToApp, Page,
RogDbusClientBlocking, get_ipc_file, SHOW_GUI,
};
pub struct RogApp<'a> {
@@ -29,6 +31,7 @@ pub struct RogApp<'a> {
pub oscillator_freq: Arc<AtomicU8>,
/// A toggle that toggles true/false when the oscillator reaches 0
pub oscillator_toggle: Arc<AtomicBool>,
pub app_cmd: Arc<Receiver<TrayToApp>>,
}
impl<'a> RogApp<'a> {
@@ -36,6 +39,7 @@ impl<'a> RogApp<'a> {
pub fn new(
config: Config,
states: PageDataStates,
app_cmd: Arc<Receiver<TrayToApp>>,
_cc: &eframe::CreationContext<'_>,
) -> Result<Self> {
let (dbus, _) = RogDbusClientBlocking::new()?;
@@ -98,20 +102,38 @@ impl<'a> RogApp<'a> {
oscillator3,
oscillator_toggle,
oscillator_freq,
app_cmd,
})
}
fn check_app_cmds(&mut self, _ctx: &egui::Context, _frame: &mut eframe::Frame) {
let Self { app_cmd, .. } = self;
if let Ok(cmd) = app_cmd.try_recv() {
match cmd {
TrayToApp::Open => {
dbg!();
get_ipc_file().unwrap().write_all(&[SHOW_GUI]).ok();
},
TrayToApp::Quit => _frame.close(),
}
}
}
}
impl<'a> eframe::App for RogApp<'a> {
/// Called each time the UI needs repainting, which may be many times per second.
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
self.check_app_cmds(ctx, frame);
let Self {
supported,
asus_dbus: dbus,
states,
..
} = self;
states
.refresh_if_notfied(supported, dbus)
.map(|repaint| {

View File

@@ -18,6 +18,7 @@ pub mod page_states;
pub mod pages;
pub mod startup_error;
pub mod widgets;
pub mod tray;
#[cfg(feature = "mocking")]
pub use mocking::RogDbusClientBlocking;
@@ -96,7 +97,7 @@ pub fn get_ipc_file() -> Result<File, crate::error::Error> {
let tmp_dir = std::env::temp_dir().join("rog-gui");
let fifo_path = tmp_dir.join("ipc.pipe");
if let Err(e) = unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
if !matches!(e, nix::Error::Sys(nix::errno::Errno::EEXIST)) {
if !matches!(e, nix::errno::Errno::EEXIST) {
return Err(e)?;
}
}

View File

@@ -1,34 +1,48 @@
use eframe::NativeOptions;
use eframe::{IconData, NativeOptions};
use log::{error, LevelFilter};
use rog_aura::layouts::KeyLayout;
use rog_control_center::tray::{AppToTray, TrayToApp};
use rog_control_center::{
config::Config, error::Result, get_ipc_file, notify::start_notifications, on_tmp_dir_exists,
page_states::PageDataStates, print_versions, startup_error::AppErrorShow, RogApp,
RogDbusClientBlocking, SHOWING_GUI, SHOW_GUI,
page_states::PageDataStates, print_versions, startup_error::AppErrorShow, tray::init_tray,
RogApp, RogDbusClientBlocking, SHOWING_GUI, SHOW_GUI,
};
use rog_platform::supported::SupportedFunctions;
use tokio::runtime::Runtime;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Mutex;
use std::{
fs::OpenOptions,
io::{Read, Write},
path::PathBuf,
sync::{atomic::AtomicBool, Arc},
};
use tokio::runtime::Runtime;
#[cfg(not(feature = "mocking"))]
const DATA_DIR: &str = "/usr/share/rog-gui/";
#[cfg(feature = "mocking")]
const DATA_DIR: &str = env!("CARGO_MANIFEST_DIR");
const BOARD_NAME: &str = "/sys/class/dmi/id/board_name";
const APP_ICON_PATH: &str = "/usr/share/icons/hicolor/512x512/apps/rog-control-center.png";
fn main() -> Result<()> {
print_versions();
let mut logger = env_logger::Builder::new();
logger
.target(env_logger::Target::Stdout)
.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()))
.filter(None, LevelFilter::Info)
.init();
// start tokio
let rt = Runtime::new().expect("Unable to create Runtime");
// Enter the runtime so that `tokio::spawn` is available immediately.
let _enter = rt.enter();
let (send, recv) = channel();
let update_tray = Arc::new(Mutex::new(send));
let app_cmd = Arc::new(init_tray(recv));
let native_options = eframe::NativeOptions {
vsync: true,
decorated: true,
@@ -36,6 +50,7 @@ fn main() -> Result<()> {
min_window_size: Some(egui::vec2(840.0, 600.0)),
max_window_size: Some(egui::vec2(840.0, 600.0)),
run_and_return: true,
icon_data: Some(load_icon()),
..Default::default()
};
@@ -93,13 +108,18 @@ fn main() -> Result<()> {
Err(_) => on_tmp_dir_exists().unwrap(),
};
let states =
setup_page_state_and_notifs(layout.clone(), &config, native_options.clone(), &dbus)
.unwrap();
let states = setup_page_state_and_notifs(
layout.clone(),
&config,
native_options.clone(),
&dbus,
update_tray,
)
.unwrap();
loop {
if !start_closed {
start_app(states.clone(), native_options.clone())?;
start_app(states.clone(), native_options.clone(), app_cmd.clone())?;
}
let config = Config::load().unwrap();
@@ -130,6 +150,7 @@ fn setup_page_state_and_notifs(
config: &Config,
native_options: NativeOptions,
dbus: &RogDbusClientBlocking,
update_tray: Arc<Mutex<Sender<AppToTray>>>,
) -> Result<PageDataStates> {
// Cheap method to alert to notifications rather than spinning a thread for each
// This is quite different when done in a retained mode app
@@ -149,6 +170,7 @@ fn setup_page_state_and_notifs(
profiles_notified.clone(),
fans_notified.clone(),
notifs_enabled.clone(),
update_tray,
)?;
let supported = match dbus.proxies().supported().supported_functions() {
@@ -177,13 +199,57 @@ fn setup_page_state_and_notifs(
)
}
fn start_app(states: PageDataStates, native_options: NativeOptions) -> Result<()> {
fn start_app(
states: PageDataStates,
native_options: NativeOptions,
app_cmd: Arc<Receiver<TrayToApp>>,
) -> Result<()> {
let mut ipc_file = get_ipc_file().unwrap();
ipc_file.write_all(&[SHOWING_GUI]).unwrap();
eframe::run_native(
"ROG Control Center",
native_options,
Box::new(move |cc| Box::new(RogApp::new(Config::load().unwrap(), states, cc).unwrap())),
Box::new(move |cc| {
Box::new(RogApp::new(Config::load().unwrap(), states, app_cmd, cc).unwrap())
}),
);
Ok(())
}
/// 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);
let mut buf = Vec::new();
let mut rgba = Vec::new();
let mut height = 512;
let mut width = 512;
if path.exists() {
if let Ok(mut file) = OpenOptions::new()
.read(true)
.open(path)
.map_err(|e| error!("Error opening app icon: {e:?}"))
{
file.read_to_end(&mut buf)
.map_err(|e| error!("Error reading app icon: {e:?}"))
.ok();
let data = std::io::Cursor::new(buf);
let decoder = png_pong::Decoder::new(data).unwrap().into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().unwrap().unwrap();
if let png_pong::PngRaster::Rgba8(ras) = raster {
rgba = ras.as_u8_slice().to_vec();
width = ras.width();
height = ras.height();
}
}
} else {
error!("Missing {APP_ICON_PATH}")
}
IconData {
height,
width,
rgba,
}
}

View File

@@ -1,4 +1,4 @@
use crate::error::Result;
use crate::{error::Result, tray::AppToTray};
use notify_rust::{Hint, Notification, NotificationHandle};
use rog_dbus::{
zbus_anime::AnimeProxy, zbus_led::LedProxy, zbus_platform::RogBiosProxy,
@@ -10,6 +10,7 @@ use std::{
fmt::Display,
sync::{
atomic::{AtomicBool, Ordering},
mpsc::Sender,
Arc, Mutex,
},
};
@@ -73,6 +74,7 @@ pub fn start_notifications(
profiles_notified: Arc<AtomicBool>,
_fans_notified: Arc<AtomicBool>,
notifs_enabled: Arc<AtomicBool>,
update_tray: Arc<Mutex<Sender<AppToTray>>>,
) -> Result<()> {
let last_notification: SharedHandle = Arc::new(Mutex::new(None));
@@ -227,6 +229,9 @@ pub fn start_notifications(
),
lock
);
if let Ok(lock) = update_tray.try_lock() {
lock.send(AppToTray::DgpuStatus(*status)).ok();
}
}
}
}