use super::toplevel::{ToplevelEvent, ToplevelInfo}; use super::toplevel_manager::listen_for_toplevels; use super::ToplevelChange; use super::{Env, ToplevelHandler}; use color_eyre::Report; use indexmap::IndexMap; use smithay_client_toolkit::environment::Environment; use smithay_client_toolkit::output::{with_output_info, OutputInfo}; use smithay_client_toolkit::reexports::calloop; use smithay_client_toolkit::{new_default_environment, WaylandSource}; use std::sync::{Arc, RwLock}; use std::time::Duration; use tokio::sync::{broadcast, oneshot}; use tokio::task::spawn_blocking; use tracing::{error, trace}; use wayland_client::protocol::wl_seat::WlSeat; use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::{ zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1, zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1, }; pub struct WaylandClient { pub outputs: Vec, pub seats: Vec, pub toplevels: Arc>>, toplevel_tx: broadcast::Sender, _toplevel_rx: broadcast::Receiver, } impl WaylandClient { pub(super) async fn new() -> Self { let (output_tx, output_rx) = oneshot::channel(); let (seat_tx, seat_rx) = oneshot::channel(); let (toplevel_tx, toplevel_rx) = broadcast::channel(32); let toplevel_tx2 = toplevel_tx.clone(); let toplevels = Arc::new(RwLock::new(IndexMap::new())); let toplevels2 = toplevels.clone(); // `queue` is not send so we need to handle everything inside the task spawn_blocking(move || { let (env, _display, queue) = new_default_environment!(Env, fields = [toplevel: ToplevelHandler::init()]) .expect("Failed to connect to Wayland compositor"); let outputs = Self::get_outputs(&env); output_tx .send(outputs) .expect("Failed to send outputs out of task"); let seats = env.get_all_seats(); seat_tx .send( seats .into_iter() .map(|seat| seat.detach()) .collect::>(), ) .expect("Failed to send seats out of task"); let _toplevel_manager = env.require_global::(); let _listener = listen_for_toplevels(env, move |handle, event, _ddata| { trace!("Received toplevel event: {:?}", event); if event.change == ToplevelChange::Close { toplevels2 .write() .expect("Failed to get write lock on toplevels") .remove(&event.toplevel.id); } else { toplevels2 .write() .expect("Failed to get write lock on toplevels") .insert(event.toplevel.id, (event.toplevel.clone(), handle)); } toplevel_tx2 .send(event) .expect("Failed to send toplevel event"); }); let mut event_loop = calloop::EventLoop::<()>::try_new().expect("Failed to create new event loop"); WaylandSource::new(queue) .quick_insert(event_loop.handle()) .expect("Failed to insert event loop into wayland event queue"); loop { // TODO: Avoid need for duration here - can we force some event when sending requests? if let Err(err) = event_loop.dispatch(Duration::from_millis(50), &mut ()) { error!( "{:?}", Report::new(err).wrap_err("Failed to dispatch pending wayland events") ); } } }); let outputs = output_rx .await .expect("Failed to receive outputs from task"); let seats = seat_rx.await.expect("Failed to receive seats from task"); Self { outputs, seats, toplevels, toplevel_tx, _toplevel_rx: toplevel_rx, } } pub fn subscribe_toplevels(&self) -> broadcast::Receiver { self.toplevel_tx.subscribe() } fn get_outputs(env: &Environment) -> Vec { let outputs = env.get_all_outputs(); outputs .iter() .filter_map(|output| with_output_info(output, Clone::clone)) .collect() } }