From 6dcae66570cf5434e077ec823cded33771b4239c Mon Sep 17 00:00:00 2001 From: Jake Stanger Date: Thu, 25 Aug 2022 21:53:42 +0100 Subject: [PATCH] fix: avoid creating loads of sway/mpd clients --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 19 +++-- src/modules/focused.rs | 53 ++++++------ src/modules/launcher/mod.rs | 39 ++++----- src/modules/mpd/client.rs | 76 +++++++++-------- src/modules/mpd/mod.rs | 6 +- src/modules/workspaces.rs | 37 +++++---- src/sway/mod.rs | 158 ++++++++++++++++++++++++++++++------ 9 files changed, 254 insertions(+), 136 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4183749..b6a6f20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1139,6 +1139,7 @@ dependencies = [ "gtk", "gtk-layer-shell", "ksway", + "lazy_static", "mpd_client", "notify", "regex", diff --git a/Cargo.toml b/Cargo.toml index 6405bb9..1caa13c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ serde_json = "1.0.82" serde_yaml = "0.9.4" toml = "0.5.9" cornfig = "0.2.0" +lazy_static = "1.4.0" regex = "1.6.0" stray = "0.1.1" dirs = "4.0.0" diff --git a/src/main.rs b/src/main.rs index 4af43e5..2f85ff3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,14 +11,13 @@ mod sway; use crate::bar::create_bar; use crate::config::{Config, MonitorConfig}; use crate::style::load_css; -use crate::sway::{get_client_error, SwayOutput}; +use crate::sway::{get_client, SwayOutput}; use color_eyre::eyre::Result; use color_eyre::Report; use dirs::config_dir; use gtk::gdk::Display; use gtk::prelude::*; use gtk::Application; -use ksway::client::Client; use ksway::IpcCommand; use std::env; use std::process::exit; @@ -97,14 +96,16 @@ async fn main() -> Result<()> { } fn create_bars(app: &Application, display: &Display, config: &Config) -> Result<()> { - let mut sway_client = match Client::connect() { - Ok(client) => Ok(client), - Err(err) => Err(get_client_error(err)), - }?; + let outputs = { + let sway = get_client(); + let mut sway = sway.lock().expect("Failed to get lock on Sway IPC client"); - let outputs = match sway_client.ipc(IpcCommand::GetOutputs) { - Ok(outputs) => Ok(outputs), - Err(err) => Err(get_client_error(err)), + let outputs = sway.ipc(IpcCommand::GetOutputs); + + match outputs { + Ok(outputs) => Ok(outputs), + Err(err) => Err(err), + } }?; let outputs = serde_json::from_slice::>(&outputs)?; diff --git a/src/modules/focused.rs b/src/modules/focused.rs index 99fb723..f69334d 100644 --- a/src/modules/focused.rs +++ b/src/modules/focused.rs @@ -1,14 +1,12 @@ use crate::icon; use crate::modules::{Module, ModuleInfo}; -use crate::sway::{SwayClient, WindowEvent}; +use crate::sway::get_client; use color_eyre::Result; use glib::Continue; use gtk::prelude::*; use gtk::{IconTheme, Image, Label, Orientation}; -use ksway::IpcEvent; use serde::Deserialize; use tokio::task::spawn_blocking; -use tracing::error; #[derive(Debug, Deserialize, Clone)] pub struct FocusedModule { @@ -42,42 +40,39 @@ impl Module for FocusedModule { container.add(&icon); container.add(&label); - let mut sway = SwayClient::connect()?; - - let srx = sway.subscribe(vec![IpcEvent::Window])?; let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - let focused = sway - .get_open_windows()? - .into_iter() - .find(|node| node.focused); + let focused = { + let sway = get_client(); + let mut sway = sway.lock().expect("Failed to get lock on Sway IPC client"); + sway.get_open_windows()? + .into_iter() + .find(|node| node.focused) + }; if let Some(focused) = focused { tx.send(focused)?; } - spawn_blocking(move || loop { - while let Ok((_, payload)) = srx.try_recv() { - match serde_json::from_slice::(&payload) { - Ok(payload) => { - let update = match payload.change.as_str() { - "focus" => true, - "title" => payload.container.focused, - _ => false, - }; + spawn_blocking(move || { + let srx = { + let sway = get_client(); + let mut sway = sway.lock().expect("Failed to get lock on Sway IPC client"); + sway.subscribe_window() + }; - if update { - tx.send(payload.container) - .expect("Failed to sendf focus update"); - } - } - Err(err) => error!("{:?}", err), + while let Ok(payload) = srx.recv() { + let update = match payload.change.as_str() { + "focus" => true, + "title" => payload.container.focused, + _ => false, + }; + + if update { + tx.send(payload.container) + .expect("Failed to sendf focus update"); } } - - if let Err(err) = sway.poll() { - error!("{:?}", err); - } }); { diff --git a/src/modules/launcher/mod.rs b/src/modules/launcher/mod.rs index b2efd04..6f340dc 100644 --- a/src/modules/launcher/mod.rs +++ b/src/modules/launcher/mod.rs @@ -5,17 +5,15 @@ use crate::collection::Collection; use crate::modules::launcher::item::{ButtonConfig, LauncherItem, LauncherWindow, OpenState}; use crate::modules::launcher::popup::Popup; use crate::modules::{Module, ModuleInfo}; -use crate::sway::{SwayClient, SwayNode, WindowEvent}; +use crate::sway::{get_client, SwayNode}; use color_eyre::{Report, Result}; use gtk::prelude::*; use gtk::{IconTheme, Orientation}; -use ksway::IpcEvent; use serde::Deserialize; use std::rc::Rc; use tokio::spawn; use tokio::sync::mpsc; use tokio::task::spawn_blocking; -use tracing::error; #[derive(Debug, Deserialize, Clone)] pub struct LauncherModule { @@ -210,8 +208,6 @@ impl Module for LauncherModule { icon_theme.set_custom_theme(Some(&theme)); } - let mut sway = SwayClient::connect()?; - let popup = Popup::new( "popup-launcher", info.app, @@ -237,28 +233,28 @@ impl Module for LauncherModule { button_config, ); - let open_windows = sway.get_open_windows()?; + let open_windows = { + let sway = get_client(); + let mut sway = sway.lock().expect("Failed to get lock on Sway IPC client"); + sway.get_open_windows() + }?; for window in open_windows { launcher.add_window(window); } - let srx = sway.subscribe(vec![IpcEvent::Window])?; let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - spawn_blocking(move || loop { - while let Ok((_, payload)) = srx.try_recv() { - match serde_json::from_slice::(&payload) { - Ok(payload) => { - tx.send(payload) - .expect("Failed to send window event payload"); - } - Err(err) => error!("{:?}", err), - } - } + spawn_blocking(move || { + let srx = { + let sway = get_client(); + let mut sway = sway.lock().expect("Failed to get lock on Sway IPC client"); + sway.subscribe_window() + }; - if let Err(err) = sway.poll() { - error!("{:?}", err); + while let Ok(payload) = srx.recv() { + tx.send(payload) + .expect("Failed to send window event payload"); } }); @@ -278,14 +274,15 @@ impl Module for LauncherModule { } spawn(async move { - let mut sway = SwayClient::connect()?; + let sway = get_client(); + while let Some(event) = ui_rx.recv().await { let selector = match event { FocusEvent::AppId(app_id) => format!("[app_id={}]", app_id), FocusEvent::Class(class) => format!("[class={}]", class), FocusEvent::ConId(id) => format!("[con_id={}]", id), }; - + let mut sway = sway.lock().expect("Failed to get lock on Sway IPC client"); sway.run(format!("{} focus", selector))?; } diff --git a/src/modules/mpd/client.rs b/src/modules/mpd/client.rs index e1e2204..e35cc83 100644 --- a/src/modules/mpd/client.rs +++ b/src/modules/mpd/client.rs @@ -1,55 +1,67 @@ +use lazy_static::lazy_static; use mpd_client::commands::responses::Status; use mpd_client::raw::MpdProtocolError; use mpd_client::{Client, Connection}; +use std::collections::HashMap; use std::path::PathBuf; +use std::sync::Arc; use std::time::Duration; use tokio::net::{TcpStream, UnixStream}; -use tokio::spawn; +use tokio::sync::Mutex; use tokio::time::sleep; -pub async fn wait_for_connection( - hosts: Vec, +lazy_static! { + static ref CLIENTS: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); +} + +pub async fn get_connection(host: &str) -> Option> { + let mut clients = CLIENTS.lock().await; + + match clients.get(host) { + Some(client) => Some(Arc::clone(client)), + None => { + let client = wait_for_connection(host, Duration::from_secs(5), None).await?; + let client = Arc::new(client); + clients.insert(host.to_string(), Arc::clone(&client)); + Some(client) + } + } +} + +async fn wait_for_connection( + host: &str, interval: Duration, max_retries: Option, ) -> Option { let mut retries = 0; + let max_retries = max_retries.unwrap_or(usize::MAX); - spawn(async move { - let max_retries = max_retries.unwrap_or(usize::MAX); - loop { - if retries == max_retries { - break None; - } - - if let Some(conn) = try_get_mpd_conn(&hosts).await { - break Some(conn.0); - } - - retries += 1; - sleep(interval).await; + loop { + if retries == max_retries { + break None; } - }) - .await - .expect("Error occurred while handling tasks") + + if let Some(conn) = try_get_mpd_conn(host).await { + break Some(conn.0); + } + + retries += 1; + sleep(interval).await; + } } /// Cycles through each MPD host and /// returns the first one which connects, /// or none if there are none -async fn try_get_mpd_conn(hosts: &[String]) -> Option { - for host in hosts { - let connection = if is_unix_socket(host) { - connect_unix(host).await - } else { - connect_tcp(host).await - }; +async fn try_get_mpd_conn(host: &str) -> Option { + let connection = if is_unix_socket(host) { + connect_unix(host).await + } else { + connect_tcp(host).await + }; - if let Ok(connection) = connection { - return Some(connection); - } - } - - None + connection.ok() } fn is_unix_socket(host: &str) -> bool { diff --git a/src/modules/mpd/mod.rs b/src/modules/mpd/mod.rs index da236d1..a9ae7b2 100644 --- a/src/modules/mpd/mod.rs +++ b/src/modules/mpd/mod.rs @@ -2,7 +2,7 @@ mod client; mod popup; use self::popup::Popup; -use crate::modules::mpd::client::{get_duration, get_elapsed, wait_for_connection}; +use crate::modules::mpd::client::{get_connection, get_duration, get_elapsed}; use crate::modules::mpd::popup::{MpdPopup, PopupEvent}; use crate::modules::{Module, ModuleInfo}; use color_eyre::Result; @@ -120,7 +120,7 @@ impl Module