diff --git a/src/compositor.rs b/src/compositor.rs index fa6e9835..04d9b058 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -68,6 +68,7 @@ use { clone3::ensure_reaper, clonecell::CloneCell, errorfmt::ErrorFmt, + event_listener::handle_lazy_event_sources, fdcloser::FdCloser, nice::{did_elevate_scheduler, elevate_scheduler}, numcell::NumCell, @@ -377,6 +378,7 @@ fn start_compositor2( copy_device_registry: Rc::new(CopyDeviceRegistry::new(&ring, &engine, &eventfd_cache)), supports_presentation_feedback: Default::default(), eventfd_cache, + lazy_event_sources: Default::default(), }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); @@ -577,6 +579,10 @@ fn start_global_event_handlers(state: &Rc) -> Vec> { Phase::PostLayout, handle_xdg_surface_configure_events(state.clone()), ), + eng.spawn( + "lazy event sources", + handle_lazy_event_sources(state.clone()), + ), ] } diff --git a/src/state.rs b/src/state.rs index 4eee9bf7..af28f4d0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -110,7 +110,7 @@ use { clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, - event_listener::{EventListener, EventSource}, + event_listener::{EventListener, EventSource, LazyEventSources}, fdcloser::FdCloser, hash_map_ext::HashMapExt, linkedlist::LinkedList, @@ -290,6 +290,7 @@ pub struct State { pub copy_device_registry: Rc, pub supports_presentation_feedback: Cell, pub eventfd_cache: Rc, + pub lazy_event_sources: Rc, } // impl Drop for State { @@ -1128,6 +1129,7 @@ impl State { self.cpu_worker.clear(); self.wait_for_syncobj.clear(); self.xdg_surface_configure_events.clear(); + self.lazy_event_sources.clear(); } pub fn remove_toplevel_id(&self, id: ToplevelIdentifier) { diff --git a/src/utils/event_listener.rs b/src/utils/event_listener.rs index 7e5e5964..c0eb0ab5 100644 --- a/src/utils/event_listener.rs +++ b/src/utils/event_listener.rs @@ -1,11 +1,28 @@ use { - crate::utils::linkedlist::{LinkedList, LinkedListIter, LinkedNode}, + crate::{ + state::State, + utils::{ + linkedlist::{LinkedList, LinkedListIter, LinkedNode}, + queue::AsyncQueue, + }, + }, std::{ cell::Cell, + ops::Deref, rc::{Rc, Weak}, }, }; +pub async fn handle_lazy_event_sources(state: Rc) { + loop { + let source = state.lazy_event_sources.queue.pop().await; + source.queued.set(false); + for listener in source.listeners.iter() { + listener.triggered(); + } + } +} + pub struct EventSource { listeners: LinkedList>, on_attach: Cell>>, @@ -15,6 +32,21 @@ pub struct EventListener { link: LinkedNode>, } +#[derive(Default)] +pub struct LazyEventSources { + queue: AsyncQueue>, +} + +pub trait LazyEventSourceListener { + fn triggered(self: Rc); +} + +pub struct LazyEventSource { + sources: Rc, + queued: Cell, + listeners: EventSource, +} + impl Default for EventSource { fn default() -> Self { Self { @@ -39,6 +71,10 @@ impl EventSource { self.listeners.is_not_empty() } + pub fn is_empty(&self) -> bool { + self.listeners.is_empty() + } + pub fn on_attach(&self, f: Box) { self.on_attach.set(Some(f)); } @@ -83,3 +119,39 @@ impl EventListener { self.link.upgrade() } } + +impl Deref for LazyEventSource { + type Target = EventSource; + + fn deref(&self) -> &Self::Target { + &self.listeners + } +} + +impl LazyEventSource { + #[expect(dead_code)] + pub fn trigger(self: &Rc) { + if self.listeners.is_empty() { + return; + } + if self.queued.replace(true) { + return; + } + self.sources.queue.push(self.clone()); + } +} + +impl LazyEventSources { + #[expect(dead_code)] + pub fn create_source(self: &Rc) -> Rc { + Rc::new(LazyEventSource { + sources: self.clone(), + queued: Default::default(), + listeners: Default::default(), + }) + } + + pub fn clear(&self) { + self.queue.clear(); + } +}