diff --git a/src/main.rs b/src/main.rs index 4c5511f..7c64e3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,9 @@ pub mod subsonic_vala; mod playbin2; +mod signal; +pub use signal::{Signal, SignalHandler}; + use gettextrs::{bind_textdomain_codeset, bindtextdomain, setlocale, textdomain, LocaleCategory}; use gtk::prelude::*; use gtk::{gio, glib}; diff --git a/src/signal.rs b/src/signal.rs new file mode 100644 index 0000000..7691e1c --- /dev/null +++ b/src/signal.rs @@ -0,0 +1,87 @@ +use std::cell::{Cell, RefCell}; +use std::rc::{Rc, Weak}; + +#[derive(Default)] +pub struct Signal { + handlers: RefCell bool>>>, + just_connected: RefCell bool>>>, +} + +pub struct SignalHandler(Weak>); + +impl SignalHandler { + pub fn disconnect(self) -> bool { + match self.0.upgrade() { + None => false, + Some(cell) => { + cell.set(true); + true + } + } + } +} + +impl Signal { + fn connect_impl(&self, f: impl FnMut(T) -> bool + 'static) { + self.just_connected.borrow_mut().push(Box::new(f)); + } + + pub fn connect(&self, mut f: impl FnMut(T) -> bool + 'static) -> SignalHandler { + let disconnect = Rc::new(Cell::new(false)); + let disconnect_weak = Rc::downgrade(&disconnect); + + self.connect_impl(move |t| match disconnect.get() { + false => false, + true => { + f(t); + true + } + }); + + SignalHandler(disconnect_weak) + } + + pub fn connect_listener( + &self, + listener: &Rc, + mut f: impl FnMut(Rc, T) -> bool + 'static, + ) -> SignalHandler { + let listener = Rc::downgrade(listener); + + self.connect(move |t| match listener.upgrade() { + None => false, + Some(listener) => f(listener, t), + }) + } +} + +impl Signal +where + T: Clone, +{ + pub fn emit(&self, t: T) { + let mut handlers = self + .handlers + .try_borrow_mut() + .expect("tried to re-emit signal during emission"); + handlers.append(self.just_connected.borrow_mut().as_mut()); + + let mut i = 0; + let mut skip = 0; + loop { + if handlers[i + skip](t.clone()) { + i += 1; + } else { + skip += 1; + } + + if i + skip == handlers.len() { + break; + } + + handlers.swap(i, i + skip); + } + + handlers.truncate(i); + } +}