diff --git a/swayrbar/src/bar.rs b/swayrbar/src/bar.rs index 437e0f5..aec3cab 100644 --- a/swayrbar/src/bar.rs +++ b/swayrbar/src/bar.rs @@ -20,6 +20,7 @@ use crate::module; use crate::module::{BarModuleFn, NameAndInstance}; use env_logger::Env; use serde_json; +use std::collections::VecDeque; use std::io; use std::process as p; use std::sync::Condvar; @@ -30,6 +31,7 @@ use swaybar_types as sbt; use swayipc as si; type NameInstanceAndReason = (String, String, String); +type Trigger = Arc<(Mutex>, Condvar)>; pub fn start() { env_logger::Builder::from_env(Env::default().default_filter_or("warn")) @@ -39,10 +41,8 @@ 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<(Mutex, Condvar)> = Arc::new(( - Mutex::new((String::new(), String::new(), String::new())), - Condvar::new(), - )); + let trigger: Trigger = + Arc::new((Mutex::new(VecDeque::new()), Condvar::new())); let trigger_for_input = trigger.clone(); thread::spawn(move || handle_input(mods_for_input, trigger_for_input)); @@ -83,10 +83,7 @@ fn create_modules(config: config::Config) -> Vec> { mods } -fn handle_input( - mods: Arc>>, - trigger: Arc<(Mutex, Condvar)>, -) { +fn handle_input(mods: Arc>>, trigger: Trigger) { let mut sb = String::new(); io::stdin() .read_line(&mut sb) @@ -120,10 +117,8 @@ 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_inst_reason = mtx.lock().unwrap(); - name_inst_reason.0 = name; - name_inst_reason.1 = instance; - name_inst_reason.2 = String::from("click event"); + let mut queue = mtx.lock().unwrap(); + queue.push_back((name, instance, String::from("click event"))); cvar.notify_one(); } } @@ -146,6 +141,9 @@ fn handle_click( let cfg = m.get_config(); // No refresh for click events for window modules because the // refresh will be triggered by a sway event anyhow. + // + // TODO: That's too much coupling. The bar module shouldn't do + // specific stuff for certain modules. if cfg.name == module::window::NAME { return None; } @@ -184,10 +182,7 @@ fn sway_subscribe() -> si::Fallible { ]) } -fn handle_sway_events( - window_mods: Vec, - trigger: Arc<(Mutex, Condvar)>, -) { +fn handle_sway_events(window_mods: Vec, trigger: Trigger) { let mut resets = 0; let max_resets = 10; @@ -216,12 +211,12 @@ fn handle_sway_events( ); for m in &window_mods { let (mtx, cvar) = &*trigger; - let mut name_inst_reason = - mtx.lock().unwrap(); - name_inst_reason.0 = m.0.to_owned(); - name_inst_reason.1 = m.1.to_owned(); - name_inst_reason.2 = - String::from("sway event"); + let mut queue = mtx.lock().unwrap(); + queue.push_back(( + m.0.to_owned(), + m.1.to_owned(), + String::from("sway event"), + )); cvar.notify_one(); } } @@ -248,31 +243,31 @@ fn handle_sway_events( } } +fn generate_status_1( + mods: &[Box], + name_and_instance: &Option, +) { + let mut blocks = vec![]; + for m in mods { + blocks.push(m.build(name_and_instance)); + } + let json = serde_json::to_string_pretty(&blocks) + .unwrap_or_else(|_| "".to_string()); + println!("{},", json); +} + fn generate_status( mods: &[Box], - trigger: Arc<(Mutex, Condvar)>, + trigger: Trigger, refresh_interval: u64, ) { println!("{{\"version\": 1, \"click_events\": true}}"); // status_command should output an infinite array meaning we emit an // opening [ and never the closing bracket. println!("["); - - let mut name_inst_reason: Option = None; + generate_status_1(mods, &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)); - } - 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( @@ -280,16 +275,19 @@ fn generate_status( Duration::from_millis(refresh_interval), ) .unwrap(); - if result.1.timed_out() { - name_inst_reason = None; + let mut queue = result.0; + if queue.is_empty() { + generate_status_1(mods, &None); } else { - name_inst_reason = Some((*result.0).clone()); log::debug!( - "Status writing thread woke up early by {} for {}/{}.", - &result.0 .2, - &result.0 .0, - &result.0 .1 + "Status writing thread woke up early events:\n{:?}", + queue ); + while let Some(name_inst_reason) = queue.pop_front() { + let name_and_instance = + Some(name_inst_reason).map(|x| (x.0, x.1)); + generate_status_1(mods, &name_and_instance); + } } } }