diff --git a/swayrbar/src/bar.rs b/swayrbar/src/bar.rs index 4f96abe..437e0f5 100644 --- a/swayrbar/src/bar.rs +++ b/swayrbar/src/bar.rs @@ -29,6 +29,8 @@ use std::{sync::Arc, thread}; use swaybar_types as sbt; use swayipc as si; +type NameInstanceAndReason = (String, String, String); + pub fn start() { env_logger::Builder::from_env(Env::default().default_filter_or("warn")) .init(); @@ -37,8 +39,10 @@ pub fn start() { let refresh_interval = config.refresh_interval; let mods: Arc>> = Arc::new(create_modules(config)); let mods_for_input = mods.clone(); - let trigger = - Arc::new((Mutex::new((String::new(), String::new())), Condvar::new())); + let trigger: Arc<(Mutex, Condvar)> = Arc::new(( + Mutex::new((String::new(), String::new(), String::new())), + Condvar::new(), + )); let trigger_for_input = trigger.clone(); thread::spawn(move || handle_input(mods_for_input, trigger_for_input)); @@ -81,7 +85,7 @@ fn create_modules(config: config::Config) -> Vec> { fn handle_input( mods: Arc>>, - trigger: Arc<(Mutex, Condvar)>, + trigger: Arc<(Mutex, Condvar)>, ) { let mut sb = String::new(); io::stdin() @@ -116,9 +120,10 @@ fn handle_input( log::debug!("Received click: {:?}", click); if let Some((name, instance)) = handle_click(click, mods.clone()) { let (mtx, cvar) = &*trigger; - let mut name_and_instance = mtx.lock().unwrap(); - name_and_instance.0 = name; - name_and_instance.1 = instance; + let mut name_inst_reason = mtx.lock().unwrap(); + name_inst_reason.0 = name; + name_inst_reason.1 = instance; + name_inst_reason.2 = String::from("click event"); cvar.notify_one(); } } @@ -138,10 +143,12 @@ fn handle_click( Some(cmd) => execute_command(&cmd), None => execute_command(cmd), } - // Wait a bit so that the action of the click has shown its - // effect, e.g., the window has been switched. - thread::sleep(Duration::from_millis(25)); let cfg = m.get_config(); + // No refresh for click events for window modules because the + // refresh will be triggered by a sway event anyhow. + if cfg.name == module::window::NAME { + return None; + } return Some((cfg.name.clone(), cfg.instance.clone())); } } @@ -179,7 +186,7 @@ fn sway_subscribe() -> si::Fallible { fn handle_sway_events( window_mods: Vec, - trigger: Arc<(Mutex, Condvar)>, + trigger: Arc<(Mutex, Condvar)>, ) { let mut resets = 0; let max_resets = 10; @@ -209,10 +216,12 @@ fn handle_sway_events( ); for m in &window_mods { let (mtx, cvar) = &*trigger; - let mut name_and_instance = + let mut name_inst_reason = mtx.lock().unwrap(); - name_and_instance.0 = m.0.to_owned(); - name_and_instance.1 = m.1.to_owned(); + name_inst_reason.0 = m.0.to_owned(); + name_inst_reason.1 = m.1.to_owned(); + name_inst_reason.2 = + String::from("sway event"); cvar.notify_one(); } } @@ -241,7 +250,7 @@ fn handle_sway_events( fn generate_status( mods: &[Box], - trigger: Arc<(Mutex, Condvar)>, + trigger: Arc<(Mutex, Condvar)>, refresh_interval: u64, ) { println!("{{\"version\": 1, \"click_events\": true}}"); @@ -249,17 +258,21 @@ fn generate_status( // opening [ and never the closing bracket. println!("["); - let mut name_and_instance: Option = None; + let mut name_inst_reason: Option = None; loop { let mut blocks = vec![]; + let name_and_instance = &name_inst_reason.map(|x| (x.0, x.1)); for m in mods { - blocks.push(m.build(&name_and_instance)); + blocks.push(m.build(name_and_instance)); } let json = serde_json::to_string_pretty(&blocks) .unwrap_or_else(|_| "".to_string()); println!("{},", json); + // FIXME: We sometimes miss click or sway events, most probably because + // the corresponding notify_one() is executed when we are not waiting + // here. let (lock, cvar) = &*trigger; let result = cvar .wait_timeout( @@ -268,11 +281,12 @@ fn generate_status( ) .unwrap(); if result.1.timed_out() { - name_and_instance = None; + name_inst_reason = None; } else { - name_and_instance = Some((*result.0).clone()); + name_inst_reason = Some((*result.0).clone()); log::debug!( - "Status writing thread woke up early by click event for {}/{}.", + "Status writing thread woke up early by {} for {}/{}.", + &result.0 .2, &result.0 .0, &result.0 .1 ); diff --git a/swayrbar/src/module/window.rs b/swayrbar/src/module/window.rs index 2215717..8a2131b 100644 --- a/swayrbar/src/module/window.rs +++ b/swayrbar/src/module/window.rs @@ -25,7 +25,7 @@ use crate::shared::ipc; use crate::shared::ipc::NodeMethods; use swaybar_types as s; -const NAME: &str = "window"; +pub const NAME: &str = "window"; const INITIAL_PID: i32 = -128; const NO_WINDOW_PID: i32 = -1; @@ -35,6 +35,7 @@ struct State { name: String, app_name: String, pid: i32, + cached_text: String, } pub struct BarModuleWindow { @@ -42,19 +43,25 @@ pub struct BarModuleWindow { state: Mutex, } -fn refresh_state(state: &mut State) { +fn refresh_state(state: &mut State, fmt_str: &str, html_escape: bool) { let root = ipc::get_root_node(false); let focused_win = root .iter() .find(|n| n.focused && n.get_type() == ipc::Type::Window); - log::debug!("Focused win: {:?}", focused_win); + match focused_win { Some(win) => { state.name = win.get_name().to_owned(); state.app_name = win.get_app_name().to_owned(); state.pid = win.pid.unwrap_or(UNKNOWN_PID); + state.cached_text = subst_placeholders(fmt_str, html_escape, state); + } + None => { + state.name.clear(); + state.app_name.clear(); + state.pid = NO_WINDOW_PID; + state.cached_text.clear(); } - None => state.pid = NO_WINDOW_PID, }; } @@ -74,6 +81,7 @@ impl BarModuleFn for BarModuleWindow { name: String::new(), app_name: String::new(), pid: INITIAL_PID, + cached_text: String::new(), }), }) } @@ -113,23 +121,17 @@ impl BarModuleFn for BarModuleWindow { if state.pid == INITIAL_PID || (nai.is_some() && should_refresh(self, nai)) { - refresh_state(&mut state); - } - - let text = if state.pid == NO_WINDOW_PID { - String::new() - } else { - subst_placeholders( + refresh_state( + &mut state, &self.config.format, self.config.is_html_escape(), - &state, - ) - }; + ); + } s::Block { name: Some(NAME.to_owned()), instance: Some(self.config.instance.clone()), - full_text: text, + full_text: state.cached_text.clone(), align: Some(s::Align::Left), markup: Some(s::Markup::Pango), short_text: None,