Refactor ROGCC to use dbus to communicate with self instead of pipe file

This commit is contained in:
Luke D. Jones
2024-12-24 12:59:19 +13:00
parent 0f2d89858e
commit ab7a4bbad3
8 changed files with 250 additions and 253 deletions

View File

@@ -1,6 +1,5 @@
use std::borrow::BorrowMut;
use std::env::args;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::{Arc, Mutex};
@@ -10,7 +9,7 @@ use std::time::Duration;
use config_traits::{StdConfig, StdConfigLoad1};
use dmi_id::DMIID;
use gumdrop::Options;
use log::{info, LevelFilter};
use log::{info, warn, LevelFilter};
use rog_control_center::cli_options::CliStart;
use rog_control_center::config::Config;
use rog_control_center::error::Result;
@@ -18,41 +17,71 @@ use rog_control_center::notify::start_notifications;
use rog_control_center::slint::ComponentHandle;
use rog_control_center::tray::init_tray;
use rog_control_center::ui::setup_window;
use rog_control_center::{
get_ipc_file, on_tmp_dir_exists, print_versions, MainWindow, QUIT_APP, SHOWING_GUI, SHOW_GUI,
use rog_control_center::zbus::{
AppState, ROGCCZbus, ROGCCZbusProxyBlocking, ZBUS_IFACE, ZBUS_PATH,
};
use rog_control_center::{print_versions, MainWindow};
use tokio::runtime::Runtime;
#[tokio::main]
async fn main() -> Result<()> {
#[cfg(feature = "tokio-debug")]
console_subscriber::init();
let mut logger = env_logger::Builder::new();
logger
.filter_level(LevelFilter::Warn)
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.init();
// Try to open a proxy and check for app state first
{
let user_con = zbus::blocking::Connection::session()?;
if let Ok(proxy) = ROGCCZbusProxyBlocking::new(&user_con) {
if let Ok(state) = proxy.state() {
info!("App is already running: {state:?}, opening the window");
// if there is a proxy connection assume the app is already running
proxy.set_state(AppState::MainWindowShouldOpen)?;
std::process::exit(0);
}
}
}
// version checks
let self_version = env!("CARGO_PKG_VERSION");
let conn = zbus::blocking::Connection::system()?;
let proxy = rog_dbus::zbus_platform::PlatformProxyBlocking::new(&conn)?;
let asusd_version = proxy.version().unwrap();
let zbus_con = zbus::blocking::Connection::system()?;
let platform_proxy = rog_dbus::zbus_platform::PlatformProxyBlocking::new(&zbus_con)?;
let asusd_version = platform_proxy.version().unwrap();
if asusd_version != self_version {
println!("Version mismatch: asusctl = {self_version}, asusd = {asusd_version}");
return Ok(());
}
// 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();
#[cfg(feature = "tokio-debug")]
console_subscriber::init();
let state_zbus = ROGCCZbus::new();
let app_state = state_zbus.clone_state();
let _conn = zbus::connection::Builder::session()?
.name(ZBUS_IFACE)?
.serve_at(ZBUS_PATH, state_zbus)?
.build()
.await
.map_err(|err| {
warn!("{}: add_to_server {}", ZBUS_PATH, err);
err
})?;
let dmi = DMIID::new().unwrap_or_default();
let board_name = dmi.board_name;
let prod_family = dmi.product_family;
info!("Running on {board_name}, product: {prod_family}");
let is_rog_ally = prod_family == "RC71L";
// tmp-dir must live to the end of program life
let _tmp_dir = match tempfile::Builder::new()
.prefix("rog-gui")
.rand_bytes(0)
.tempdir()
{
Ok(tmp) => tmp,
Err(_) => on_tmp_dir_exists().unwrap(),
};
let args: Vec<String> = args().skip(1).collect();
let cli_parsed = match CliStart::parse_args_default(&args) {
@@ -66,15 +95,7 @@ async fn main() -> Result<()> {
return Ok(());
}
let mut logger = env_logger::Builder::new();
logger
.filter_level(LevelFilter::Warn)
.parse_default_env()
.target(env_logger::Target::Stdout)
.format_timestamp(None)
.init();
let supported_properties = proxy.supported_properties().unwrap_or_default();
let supported_properties = platform_proxy.supported_properties().unwrap_or_default();
// Startup
let mut config = Config::new().load();
@@ -99,8 +120,6 @@ async fn main() -> Result<()> {
if config.startup_in_background {
config.run_in_background = true;
} else {
get_ipc_file().unwrap().write_all(&[SHOW_GUI, 0]).unwrap();
}
config.write();
@@ -108,10 +127,6 @@ async fn main() -> Result<()> {
let startup_in_background = config.startup_in_background;
let config = Arc::new(Mutex::new(config));
// 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();
start_notifications(config.clone(), &rt)?;
if enable_tray_icon {
@@ -121,7 +136,11 @@ async fn main() -> Result<()> {
thread_local! { pub static UI: std::cell::RefCell<Option<MainWindow>> = Default::default()};
// i_slint_backend_selector::with_platform(|_| Ok(())).unwrap();
let mut do_once = !startup_in_background;
if !startup_in_background {
if let Ok(mut lock) = app_state.lock() {
*lock = AppState::MainWindowShouldOpen;
}
}
if std::env::var("RUST_TRANSLATIONS").is_ok() {
// don't care about content
@@ -133,42 +152,40 @@ async fn main() -> Result<()> {
}
thread::spawn(move || {
let mut buf = [0u8; 2];
// blocks until it is read, typically the read will happen after a second
// process writes to the IPC (so there is data to actually read)
let mut state = AppState::StartingUp;
loop {
if do_once {
buf[0] = SHOW_GUI;
do_once = false;
} else {
get_ipc_file().unwrap().read_exact(&mut buf).unwrap();
// save as a var, don't hold the lock the entire time or deadlocks happen
if let Ok(lock) = app_state.lock() {
state = *lock;
}
if buf[0] == SHOW_GUI {
// There's a balancing act with read/write timing of IPC, there needs to be a
// small sleep after this to give any other process a chance to
// read the IPC before looping
get_ipc_file()
.unwrap()
.write_all(&[SHOWING_GUI, 0])
.unwrap();
if state == AppState::MainWindowShouldOpen {
if let Ok(mut lock) = app_state.lock() {
*lock = AppState::MainWindowOpen;
}
sleep(Duration::from_millis(50));
let config_copy = config.clone();
let app_state_copy = app_state.clone();
slint::invoke_from_event_loop(move || {
UI.with(|ui| {
let app_state_copy = app_state_copy.clone();
let mut ui = ui.borrow_mut();
if let Some(ui) = ui.as_mut() {
ui.window().show().unwrap();
ui.window().on_close_requested(|| {
get_ipc_file().unwrap().write_all(&[0, 0]).unwrap();
ui.window().on_close_requested(move || {
if let Ok(mut lock) = app_state_copy.lock() {
*lock = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow
});
} else {
let newui = setup_window(config_copy);
newui.window().show().unwrap();
newui.window().on_close_requested(|| {
get_ipc_file().unwrap().write_all(&[0, 0]).unwrap();
newui.window().on_close_requested(move || {
if let Ok(mut lock) = app_state_copy.lock() {
*lock = AppState::MainWindowClosed;
}
slint::CloseRequestResponse::HideWindow
});
ui.replace(newui);
@@ -176,29 +193,26 @@ async fn main() -> Result<()> {
});
})
.unwrap();
} else {
if buf[1] == QUIT_APP {
slint::quit_event_loop().unwrap();
exit(0);
}
if buf[0] != SHOWING_GUI {
if let Ok(lock) = config.lock() {
if !lock.run_in_background {
slint::quit_event_loop().unwrap();
exit(0);
}
} else if state == AppState::QuitApp {
slint::quit_event_loop().unwrap();
exit(0);
} else if state != AppState::MainWindowOpen {
if let Ok(lock) = config.lock() {
if !lock.run_in_background {
slint::quit_event_loop().unwrap();
exit(0);
}
slint::invoke_from_event_loop(move || {
UI.with(|ui| {
let mut ui = ui.take();
if let Some(ui) = ui.borrow_mut() {
ui.window().hide().unwrap();
}
});
})
.unwrap();
}
slint::invoke_from_event_loop(move || {
UI.with(|ui| {
let mut ui = ui.take();
if let Some(ui) = ui.borrow_mut() {
ui.window().hide().unwrap();
}
});
})
.unwrap();
}
}
});