use super::handle::{Toplevel, ToplevelEvent}; use crate::wayland::LazyGlobal; use smithay_client_toolkit::environment::{Environment, GlobalHandler}; use std::cell::RefCell; use std::rc::{self, Rc}; use tracing::warn; use wayland_client::protocol::wl_registry::WlRegistry; use wayland_client::{Attached, DispatchData}; use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::{ zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1, zwlr_foreign_toplevel_manager_v1::{self, ZwlrForeignToplevelManagerV1}, }; struct ToplevelHandlerInner { manager: LazyGlobal, registry: Option>, toplevels: Vec, } impl ToplevelHandlerInner { const fn new() -> Self { let toplevels = vec![]; Self { registry: None, manager: LazyGlobal::Unknown, toplevels, } } } pub struct ToplevelHandler { inner: Rc>, status_listeners: Rc>>>>, } impl ToplevelHandler { pub fn init() -> Self { let inner = Rc::new(RefCell::new(ToplevelHandlerInner::new())); Self { inner, status_listeners: Rc::new(RefCell::new(Vec::new())), } } } impl GlobalHandler for ToplevelHandler { fn created( &mut self, registry: Attached, id: u32, version: u32, _ddata: DispatchData, ) { let mut inner = RefCell::borrow_mut(&self.inner); if inner.registry.is_none() { inner.registry = Some(registry); } if matches!(inner.manager, LazyGlobal::Unknown) { inner.manager = LazyGlobal::Seen { id, version } } else { warn!( "Compositor advertised zwlr_foreign_toplevel_manager_v1 multiple times, ignoring." ); } } fn get(&self) -> Option> { let mut inner = RefCell::borrow_mut(&self.inner); match inner.manager { LazyGlobal::Bound(ref mgr) => Some(mgr.clone()), LazyGlobal::Unknown => None, LazyGlobal::Seen { id, version } => { let registry = inner.registry.as_ref().expect("Failed to get registry"); // current max protocol version = 3 let version = std::cmp::min(version, 3); let manager = registry.bind::(version, id); { let inner = self.inner.clone(); let status_listeners = self.status_listeners.clone(); manager.quick_assign(move |_, event, _ddata| { let mut inner = RefCell::borrow_mut(&inner); let status_listeners = status_listeners.clone(); match event { zwlr_foreign_toplevel_manager_v1::Event::Toplevel { toplevel: handle, } => { let toplevel = Toplevel::init(&handle.clone(), move |event, ddata| { notify_status_listeners( &handle, &event, ddata, &status_listeners, ); }); inner.toplevels.push(toplevel); } zwlr_foreign_toplevel_manager_v1::Event::Finished => {} _ => unreachable!(), } }); } inner.manager = LazyGlobal::Bound((*manager).clone()); Some((*manager).clone()) } } } } type ToplevelStatusCallback = dyn FnMut(ZwlrForeignToplevelHandleV1, ToplevelEvent, DispatchData) + 'static; /// Notifies the callbacks of an event on the toplevel fn notify_status_listeners( toplevel: &ZwlrForeignToplevelHandleV1, event: &ToplevelEvent, mut ddata: DispatchData, listeners: &RefCell>>>, ) { listeners.borrow_mut().retain(|lst| { rc::Weak::upgrade(lst).map_or(false, |cb| { (cb.borrow_mut())(toplevel.clone(), event.clone(), ddata.reborrow()); true }) }); } pub struct ToplevelStatusListener { _cb: Rc>, } pub trait ToplevelHandling { fn listen(&mut self, f: F) -> ToplevelStatusListener where F: FnMut(ZwlrForeignToplevelHandleV1, ToplevelEvent, DispatchData) + 'static; } impl ToplevelHandling for ToplevelHandler { fn listen(&mut self, f: F) -> ToplevelStatusListener where F: FnMut(ZwlrForeignToplevelHandleV1, ToplevelEvent, DispatchData) + 'static, { let rc = Rc::new(RefCell::new(f)) as Rc<_>; self.status_listeners.borrow_mut().push(Rc::downgrade(&rc)); ToplevelStatusListener { _cb: rc } } } pub fn listen_for_toplevels(env: &Environment, f: F) -> ToplevelStatusListener where E: ToplevelHandling, F: FnMut(ZwlrForeignToplevelHandleV1, ToplevelEvent, DispatchData) + 'static, { env.with_inner(move |inner| ToplevelHandling::listen(inner, f)) }