feat: show dialog when attempting to reconnect to daemon

This commit is contained in:
Ilya Zlobintsev 2024-09-28 10:40:12 +03:00
parent b1229a3e1e
commit c77fc555ac
3 changed files with 69 additions and 34 deletions

View File

@ -3,7 +3,6 @@ mod connection;
mod macros;
pub use lact_schema as schema;
use lact_schema::request::ProfileBase;
use amdgpu_sysfs::gpu_handle::{
power_profile_mode::PowerProfileModesTable, PerformanceLevel, PowerLevelKind,
@ -12,7 +11,7 @@ use anyhow::Context;
use connection::{tcp::TcpConnection, unix::UnixConnection, DaemonConnection};
use nix::unistd::getuid;
use schema::{
request::{ConfirmCommand, SetClocksCommand},
request::{ConfirmCommand, ProfileBase, SetClocksCommand},
ClocksInfo, DeviceInfo, DeviceListEntry, DeviceStats, FanOptions, PowerStates, ProfilesInfo,
Request, Response, SystemInfo,
};
@ -21,14 +20,19 @@ use std::{
future::Future, marker::PhantomData, os::unix::net::UnixStream, path::PathBuf, pin::Pin,
rc::Rc, time::Duration,
};
use tokio::{net::ToSocketAddrs, sync::Mutex};
use tokio::{
net::ToSocketAddrs,
sync::{broadcast, Mutex},
};
use tracing::{error, info};
const STATUS_MSG_CHANNEL_SIZE: usize = 16;
const RECONNECT_INTERVAL_MS: u64 = 250;
#[derive(Clone)]
pub struct DaemonClient {
stream: Rc<Mutex<Box<dyn DaemonConnection>>>,
status_tx: broadcast::Sender<ConnectionStatusMsg>,
pub embedded: bool,
}
@ -41,6 +45,7 @@ impl DaemonClient {
Ok(Self {
stream: Rc::new(Mutex::new(stream)),
embedded: false,
status_tx: broadcast::Sender::new(STATUS_MSG_CHANNEL_SIZE),
})
}
@ -50,6 +55,7 @@ impl DaemonClient {
Ok(Self {
stream: Rc::new(Mutex::new(stream)),
embedded: false,
status_tx: broadcast::Sender::new(STATUS_MSG_CHANNEL_SIZE),
})
}
@ -58,9 +64,14 @@ impl DaemonClient {
Ok(Self {
stream: Rc::new(Mutex::new(Box::new(connection))),
embedded,
status_tx: broadcast::Sender::new(STATUS_MSG_CHANNEL_SIZE),
})
}
pub fn status_receiver(&self) -> broadcast::Receiver<ConnectionStatusMsg> {
self.status_tx.subscribe()
}
fn make_request<'a, 'r, T: Deserialize<'r>>(
&'a self,
request: Request<'a>,
@ -76,6 +87,7 @@ impl DaemonClient {
}),
Err(err) => {
error!("Could not make request: {err}, reconnecting to socket");
let _ = self.status_tx.send(ConnectionStatusMsg::Disconnected);
loop {
match stream.new_connection().await {
@ -83,11 +95,15 @@ impl DaemonClient {
info!("Established new socket connection");
*stream = new_connection;
drop(stream);
let _ = self.status_tx.send(ConnectionStatusMsg::Reconnected);
return self.make_request(request).await;
}
Err(err) => {
error!("Could not reconnect: {err:#}, retrying in {RECONNECT_INTERVAL_MS}ms");
std::thread::sleep(Duration::from_millis(RECONNECT_INTERVAL_MS));
tokio::time::sleep(Duration::from_millis(RECONNECT_INTERVAL_MS))
.await;
}
}
}
@ -249,3 +265,9 @@ impl<'a, T: Deserialize<'a>> ResponseBuffer<T> {
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ConnectionStatusMsg {
Disconnected,
Reconnected,
}

View File

@ -25,7 +25,7 @@ use gtk::{
MessageType, ResponseType,
};
use header::{Header, HeaderMsg};
use lact_client::DaemonClient;
use lact_client::{ConnectionStatusMsg, DaemonClient};
use lact_daemon::MODULE_CONF_PATH;
use lact_schema::{
args::GuiArgs,
@ -62,7 +62,7 @@ impl AsyncComponent for AppModel {
type CommandOutput = ();
view! {
#[name = "root_window"]
#[root]
gtk::ApplicationWindow {
set_title: Some("LACT"),
set_default_width: 600,
@ -78,6 +78,17 @@ impl AsyncComponent for AppModel {
model.root_stack.container.clone(),
model.apply_revealer.widget(),
}
},
#[name = "reconnecting_dialog"]
gtk::MessageDialog::new(
Some(&root),
gtk::DialogFlags::MODAL,
gtk::MessageType::Error,
gtk::ButtonsType::None,
"Daemon connection lost, reconnecting...",
) -> gtk::MessageDialog {
set_title: Some("Connection Lost"),
}
}
@ -105,6 +116,19 @@ impl AsyncComponent for AppModel {
.expect("Could not establish any daemon connection"),
};
let mut conn_status_rx = daemon_client.status_receiver();
relm4::spawn_local(clone!(
#[strong]
sender,
async move {
loop {
if let Ok(msg) = conn_status_rx.recv().await {
sender.input(AppMsg::ConnectionStatus(msg));
}
}
}
));
register_actions(&sender);
let system_info_buf = daemon_client
@ -194,7 +218,7 @@ impl AsyncComponent for AppModel {
root: &Self::Root,
) {
trace!("update {msg:#?}");
if let Err(err) = self.handle_msg(msg, sender.clone(), root).await {
if let Err(err) = self.handle_msg(msg, sender.clone(), root, widgets).await {
show_error(root, &err);
}
self.update_view(widgets, sender);
@ -207,13 +231,13 @@ impl AppModel {
msg: AppMsg,
sender: AsyncComponentSender<Self>,
root: &gtk::ApplicationWindow,
widgets: &AppModelWidgets,
) -> Result<(), Rc<anyhow::Error>> {
match msg {
AppMsg::Error(err) => Err(err),
AppMsg::Error(err) => return Err(err),
AppMsg::ReloadProfiles => {
self.reload_profiles().await?;
sender.input(AppMsg::ReloadData { full: false });
Ok(())
}
AppMsg::ReloadData { full } => {
let gpu_id = self.current_gpu_id()?;
@ -222,12 +246,10 @@ impl AppModel {
} else {
self.update_gpu_data(gpu_id, sender).await?;
}
Ok(())
}
AppMsg::SelectProfile(profile) => {
self.daemon_client.set_profile(profile).await?;
sender.input(AppMsg::ReloadData { full: false });
Ok(())
}
AppMsg::CreateProfile(name, base) => {
self.daemon_client
@ -235,30 +257,26 @@ impl AppModel {
.await?;
self.daemon_client.set_profile(Some(name)).await?;
sender.input(AppMsg::ReloadProfiles);
Ok(())
}
AppMsg::DeleteProfile(profile) => {
self.daemon_client.delete_profile(profile).await?;
sender.input(AppMsg::ReloadProfiles);
Ok(())
}
AppMsg::Stats(stats) => {
self.root_stack.info_page.set_stats(&stats);
self.root_stack.thermals_page.set_stats(&stats, false);
self.root_stack.oc_page.set_stats(&stats, false);
self.graphs_window.set_stats(&stats);
Ok(())
}
AppMsg::ApplyChanges => self
.apply_settings(self.current_gpu_id()?, root, &sender)
.await
.map_err(|err| {
sender.input(AppMsg::ReloadData { full: false });
err.into()
}),
AppMsg::ApplyChanges => {
self.apply_settings(self.current_gpu_id()?, root, &sender)
.await
.inspect_err(|_| {
sender.input(AppMsg::ReloadData { full: false });
})?;
}
AppMsg::RevertChanges => {
sender.input(AppMsg::ReloadData { full: false });
Ok(())
}
AppMsg::ResetClocks => {
let gpu_id = self.current_gpu_id()?;
@ -269,8 +287,6 @@ impl AppModel {
.confirm_pending_config(ConfirmCommand::Confirm)
.await?;
sender.input(AppMsg::ReloadData { full: false });
Ok(())
}
AppMsg::ResetPmfw => {
let gpu_id = self.current_gpu_id()?;
@ -279,34 +295,30 @@ impl AppModel {
.confirm_pending_config(ConfirmCommand::Confirm)
.await?;
sender.input(AppMsg::ReloadData { full: false });
Ok(())
}
AppMsg::ShowGraphsWindow => {
self.graphs_window.show();
Ok(())
}
AppMsg::DumpVBios => {
self.dump_vbios(&self.current_gpu_id()?, root).await;
Ok(())
}
AppMsg::DebugSnapshot => {
self.generate_debug_snapshot(root).await;
Ok(())
}
AppMsg::EnableOverdrive => {
toggle_overdrive(&self.daemon_client, true, root.clone()).await;
Ok(())
}
AppMsg::DisableOverdrive => {
toggle_overdrive(&self.daemon_client, false, root.clone()).await;
Ok(())
}
AppMsg::ResetConfig => {
self.daemon_client.reset_config().await?;
sender.input(AppMsg::ReloadData { full: true });
Ok(())
}
AppMsg::ConnectionStatus(status) => match status {
ConnectionStatusMsg::Disconnected => widgets.reconnecting_dialog.present(),
ConnectionStatusMsg::Reconnected => widgets.reconnecting_dialog.hide(),
},
AppMsg::AskConfirmation(options, confirmed_msg) => {
let sender = sender.clone();
@ -318,10 +330,9 @@ impl AppModel {
}
});
controller.detach_runtime();
Ok(())
}
}
Ok(())
}
fn current_gpu_id(&self) -> anyhow::Result<String> {

View File

@ -1,4 +1,5 @@
use super::confirmation_dialog::ConfirmationOptions;
use lact_client::ConnectionStatusMsg;
use lact_schema::{request::ProfileBase, DeviceStats};
use std::rc::Rc;
@ -21,6 +22,7 @@ pub enum AppMsg {
SelectProfile(Option<String>),
CreateProfile(String, ProfileBase),
DeleteProfile(String),
ConnectionStatus(ConnectionStatusMsg),
AskConfirmation(ConfirmationOptions, Box<AppMsg>),
}