use { crate::{ client::{Client, ClientError}, globals::GlobalBase, ifs::wl_surface::{ ext_session_lock_surface_v1::ExtSessionLockSurfaceV1, x_surface::xwindow::Xwindow, xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::XdgToplevel}, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, }, leaks::Tracker, object::{Object, Version}, rect::Rect, tree::{ self, ContainerNode, DisplayNode, FloatNode, Node, NodeVisitor, OutputNode, PlaceholderNode, ToplevelData, ToplevelNodeBase, ToplevelType, WorkspaceNode, }, utils::{opaque::OpaqueError, opt::Opt, toplevel_identifier::ToplevelIdentifier}, wire::{JayTreeQueryId, jay_tree_query::*}, }, isnt::std_1::primitive::IsntStrExt, std::{ cell::{Cell, RefCell}, ops::Deref, rc::Rc, str::FromStr, }, thiserror::Error, }; pub const TREE_TY_DISPLAY: u32 = 1; pub const TREE_TY_OUTPUT: u32 = 2; pub const TREE_TY_WORKSPACE: u32 = 3; pub const TREE_TY_FLOAT: u32 = 4; pub const TREE_TY_CONTAINER: u32 = 5; pub const TREE_TY_PLACEHOLDER: u32 = 6; pub const TREE_TY_XDG_TOPLEVEL: u32 = 7; pub const TREE_TY_X_WINDOW: u32 = 8; pub const TREE_TY_XDG_POPUP: u32 = 9; pub const TREE_TY_LAYER_SURFACE: u32 = 10; pub const TREE_TY_LOCK_SURFACE: u32 = 11; pub struct JayTreeQuery { pub id: JayTreeQueryId, pub client: Rc, pub tracker: Tracker, pub version: Version, recursive: Cell, root: RefCell>, } enum Root { Display, WorkspaceNode(Rc>), WorkspaceName(String), ToplevelId(ToplevelIdentifier), } impl JayTreeQuery { pub fn new(client: &Rc, id: JayTreeQueryId, version: Version) -> Self { Self { id, client: client.clone(), tracker: Default::default(), version, recursive: Cell::new(false), root: Default::default(), } } fn send_node_position(&self, node: &dyn Node) { let rect = node.node_absolute_position(); self.send_position(rect); } fn send_position(&self, rect: Rect) { self.client.event(Position { self_id: self.id, x: rect.x1(), y: rect.y1(), w: rect.width(), h: rect.height(), }); } fn send_not_found(&self) { self.client.event(NotFound { self_id: self.id }); } fn send_done(&self) { self.client.event(Done { self_id: self.id }); } fn send_end(&self) { self.client.event(End { self_id: self.id }); } fn send_start(&self, ty: u32) { self.client.event(Start { self_id: self.id, ty, }); } fn send_client(&self, node: &impl Node) { if let Some(id) = node.node_client_id() { self.client.event(ClientId { self_id: self.id, id: id.raw(), }); } } fn send_workspace_name(&self, name: &str) { self.client.event(WorkspaceName { self_id: self.id, name, }); } fn send_output_name(&self, name: &str) { self.client.event(OutputName { self_id: self.id, name, }); } fn send_toplevel(&self, data: &ToplevelData) { self.client.event(Start { self_id: self.id, ty: match &data.kind { ToplevelType::Container => TREE_TY_CONTAINER, ToplevelType::Placeholder(_) => TREE_TY_PLACEHOLDER, ToplevelType::XdgToplevel(_) => TREE_TY_XDG_TOPLEVEL, ToplevelType::XWindow(_) => TREE_TY_X_WINDOW, }, }); self.client.event(ToplevelId { self_id: self.id, id: &data.identifier.get().to_string(), }); self.send_position(data.desired_extents.get()); if let Some(cl) = data.client.as_ref().map(|c| c.id.raw()) { self.client.event(ClientId { self_id: self.id, id: cl, }); } self.client.event(Title { self_id: self.id, title: &data.title.borrow(), }); if let Some(w) = data.workspace.get() { self.send_workspace_name(&w.name); } match &data.kind { ToplevelType::Container => {} ToplevelType::Placeholder(id) => { if let Some(id) = *id { self.client.event(PlaceholderFor { self_id: self.id, id: &id.to_string(), }); } } ToplevelType::XdgToplevel(d) => { self.client.event(AppId { self_id: self.id, app_id: &data.app_id.borrow(), }); let tag = &*d.tag.borrow(); if tag.is_not_empty() { self.client.event(Tag { self_id: self.id, tag, }); } } ToplevelType::XWindow(d) => { if let Some(class) = &*d.info.class.borrow() { self.client.event(XClass { self_id: self.id, class, }); } if let Some(instance) = &*d.info.instance.borrow() { self.client.event(XInstance { self_id: self.id, instance, }); } if let Some(role) = &*d.info.role.borrow() { self.client.event(XRole { self_id: self.id, role, }); } } } if data.is_floating.get() { self.client.event(Floating { self_id: self.id }); } if data.visible.get() { self.client.event(Visible { self_id: self.id }); } if data.wants_attention.get() { self.client.event(Urgent { self_id: self.id }); } for seat_id in data.seat_foci.lock().keys() { for seat in data.state.globals.seats.lock().values() { if seat.id() == *seat_id { self.client.event(Focused { self_id: self.id, global: seat.name().raw(), }); } } } if data.is_fullscreen.get() { self.client.event(Fullscreen { self_id: self.id }); } if let Some(ws) = data.workspace.get() { self.client.event(Workspace { self_id: self.id, name: &ws.name, }); } } } impl JayTreeQueryRequestHandler for JayTreeQuery { type Error = JayTreeQueryError; fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { self.client.remove_obj(self)?; Ok(()) } fn execute(&self, _req: Execute, _slf: &Rc) -> Result<(), Self::Error> { let Some(root) = &*self.root.borrow() else { return Err(JayTreeQueryError::NoRootSet); }; match root { Root::Display => Visitor(self).visit_display(&self.client.state.root), Root::WorkspaceNode(n) => match n.get() { Some(n) => Visitor(self).visit_workspace(&n), None => self.send_not_found(), }, Root::WorkspaceName(n) => match self.client.state.workspaces.get(n) { Some(n) => Visitor(self).visit_workspace(&n), None => self.send_not_found(), }, Root::ToplevelId(id) => match self .client .state .toplevels .get(id) .and_then(|t| t.upgrade()) { Some(t) => t.node_visit(&mut Visitor(self)), None => self.send_not_found(), }, } self.send_done(); Ok(()) } fn set_root_display(&self, _req: SetRootDisplay, _slf: &Rc) -> Result<(), Self::Error> { *self.root.borrow_mut() = Some(Root::Display); Ok(()) } fn set_recursive(&self, req: SetRecursive, _slf: &Rc) -> Result<(), Self::Error> { self.recursive.set(req.recursive != 0); Ok(()) } fn set_root_workspace( &self, req: SetRootWorkspace, _slf: &Rc, ) -> Result<(), Self::Error> { let ws = self.client.lookup(req.workspace)?; let opt = match ws.workspace.get() { Some(ws) => ws.opt.clone(), _ => Default::default(), }; let root = &mut *self.root.borrow_mut(); *root = Some(Root::WorkspaceNode(opt)); Ok(()) } fn set_root_workspace_name( &self, req: SetRootWorkspaceName, _slf: &Rc, ) -> Result<(), Self::Error> { let root = &mut *self.root.borrow_mut(); *root = Some(Root::WorkspaceName(req.workspace.to_owned())); Ok(()) } fn set_root_toplevel(&self, req: SetRootToplevel, _slf: &Rc) -> Result<(), Self::Error> { let tl = self.client.lookup(req.toplevel)?; let root = &mut *self.root.borrow_mut(); *root = Some(Root::ToplevelId(tl.toplevel.tl_data().identifier.get())); Ok(()) } fn set_root_window_id( &self, req: SetRootWindowId<'_>, _slf: &Rc, ) -> Result<(), Self::Error> { let id = ToplevelIdentifier::from_str(req.id).map_err(JayTreeQueryError::InvalidToplevelId)?; let root = &mut *self.root.borrow_mut(); *root = Some(Root::ToplevelId(id)); Ok(()) } } struct Visitor<'a>(&'a JayTreeQuery); impl tree::NodeVisitorBase for Visitor<'_> { fn visit_container(&mut self, node: &Rc) { let s = self.0; s.send_toplevel(node.tl_data()); if s.recursive.get() { node.node_visit_children(self); } s.send_end(); } fn visit_toplevel(&mut self, node: &Rc) { let s = self.0; s.send_toplevel(node.tl_data()); node.xdg.for_each_popup(|popup| { NodeVisitor::visit_popup(self, popup); }); s.send_end(); } fn visit_popup(&mut self, node: &Rc) { let s = self.0; s.send_start(TREE_TY_XDG_POPUP); s.send_node_position(&**node); s.send_end(); } fn visit_display(&mut self, node: &Rc) { let s = self.0; s.send_start(TREE_TY_DISPLAY); s.send_node_position(&**node); if s.recursive.get() { for output in node.outputs.lock().values() { NodeVisitor::visit_output(self, output); } for stacked in node.stacked.iter() { if stacked.stacked_has_workspace_link() { continue; } stacked.deref().clone().node_visit(self); } } s.send_end(); } fn visit_output(&mut self, node: &Rc) { let s = self.0; s.send_start(TREE_TY_OUTPUT); s.send_node_position(&**node); s.send_output_name(&node.global.connector.name); if s.recursive.get() { node.node_visit_children(self); } s.send_end(); } fn visit_float(&mut self, node: &Rc) { let s = self.0; s.send_start(TREE_TY_FLOAT); s.send_node_position(&**node); if s.recursive.get() { node.node_visit_children(self); } s.send_end(); } fn visit_workspace(&mut self, node: &Rc) { let s = self.0; s.send_start(TREE_TY_WORKSPACE); s.send_node_position(&**node); s.send_workspace_name(&node.name); s.send_output_name(&node.output.get().global.connector.name); for stacked in node.stacked.iter() { if stacked.stacked_is_xdg_popup() { continue; } stacked.deref().clone().node_visit(self); } if s.recursive.get() { node.node_visit_children(self); } s.send_end(); } fn visit_layer_surface(&mut self, node: &Rc) { let s = self.0; s.send_start(TREE_TY_LAYER_SURFACE); s.send_client(&**node); s.send_node_position(&**node); node.for_each_popup(|popup| { NodeVisitor::visit_popup(self, popup); }); s.send_end(); } fn visit_xwindow(&mut self, node: &Rc) { let s = self.0; s.send_toplevel(node.tl_data()); s.send_end(); } fn visit_placeholder(&mut self, node: &Rc) { let s = self.0; s.send_toplevel(node.tl_data()); s.send_end(); } fn visit_lock_surface(&mut self, node: &Rc) { let s = self.0; s.send_start(TREE_TY_LOCK_SURFACE); s.send_client(&**node); s.send_node_position(&**node); s.send_end(); } } object_base! { self = JayTreeQuery; version = self.version; } impl Object for JayTreeQuery {} simple_add_obj!(JayTreeQuery); #[derive(Debug, Error)] pub enum JayTreeQueryError { #[error(transparent)] ClientError(Box), #[error("Toplevel id is ill-formed")] InvalidToplevelId(OpaqueError), #[error("No root node was set")] NoRootSet, } efrom!(JayTreeQueryError, ClientError);