use { crate::{ client::Client, cursor::KnownCursor, fixed::Fixed, ifs::{ wl_seat::{NodeSeatState, WlSeatGlobal}, wl_surface::{SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError}, }, rect::Rect, render::Renderer, state::State, tree::{ FindTreeResult, FoundNode, Node, NodeId, NodeVisitor, StackedNode, ToplevelData, ToplevelNode, }, utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode}, wire::WlSurfaceId, wire_xcon::CreateNotify, xwayland::XWaylandEvent, }, bstr::BString, jay_config::Direction, std::{ cell::{Cell, RefCell}, ops::{Deref, Not}, rc::Rc, }, thiserror::Error, }; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum XInputModel { None, Passive, Local, Global, } impl Default for XInputModel { fn default() -> Self { Self::Passive } } #[derive(Default, Debug)] pub struct IcccmHints { pub flags: Cell, pub input: Cell, pub initial_state: Cell, pub icon_pixmap: Cell, pub icon_window: Cell, pub icon_x: Cell, pub icon_y: Cell, pub icon_mask: Cell, pub window_group: Cell, } #[derive(Default, Debug)] pub struct SizeHints { pub flags: Cell, pub x: Cell, pub y: Cell, pub width: Cell, pub height: Cell, pub min_width: Cell, pub min_height: Cell, pub max_width: Cell, pub max_height: Cell, pub width_inc: Cell, pub height_inc: Cell, pub min_aspect_num: Cell, pub min_aspect_den: Cell, pub max_aspect_num: Cell, pub max_aspect_den: Cell, pub base_width: Cell, pub base_height: Cell, pub win_gravity: Cell, } #[derive(Default, Debug)] pub struct MotifHints { pub flags: Cell, pub decorations: Cell, } #[derive(Default, Debug)] pub struct XwindowInfo { pub has_alpha: Cell, pub override_redirect: Cell, pub extents: Cell, pub pending_extents: Cell, pub instance: RefCell>, pub class: RefCell>, pub title: RefCell>, pub role: RefCell>, pub protocols: CopyHashMap, pub window_types: CopyHashMap, pub never_focus: Cell, pub utf8_title: Cell, pub icccm_hints: IcccmHints, pub normal_hints: SizeHints, pub motif_hints: MotifHints, pub startup_id: RefCell>, pub fullscreen: Cell, pub modal: Cell, pub maximized_vert: Cell, pub maximized_horz: Cell, pub minimized: Cell, pub pid: Cell>, pub input_model: Cell, pub mapped: Cell, pub wants_floating: Cell, } pub struct XwindowData { pub state: Rc, pub window_id: u32, pub client: Rc, pub surface_id: Cell>, pub window: CloneCell>>, pub info: XwindowInfo, pub children: CopyHashMap>, pub parent: CloneCell>>, pub stack_link: RefCell>>>, pub map_link: Cell>>>, pub startup_info: RefCell>, pub destroyed: Cell, } tree_id!(XwindowId); pub struct Xwindow { pub id: XwindowId, pub seat_state: NodeSeatState, pub data: Rc, pub surface: Rc, pub display_link: RefCell>>>, pub toplevel_data: ToplevelData, } impl XwindowData { pub fn new(state: &Rc, event: &CreateNotify, client: &Rc) -> Self { let extents = Rect::new_sized( event.x as _, event.y as _, event.width as _, event.height as _, ) .unwrap(); // log::info!("xwin {} new {:?} or {}", event.window, extents, event.override_redirect); Self { state: state.clone(), window_id: event.window, client: client.clone(), surface_id: Cell::new(None), window: Default::default(), info: XwindowInfo { override_redirect: Cell::new(event.override_redirect != 0), pending_extents: Cell::new(extents), ..Default::default() }, children: Default::default(), parent: Default::default(), stack_link: Default::default(), map_link: Default::default(), startup_info: Default::default(), destroyed: Cell::new(false), } } pub fn is_ancestor_of(&self, mut other: Rc) -> bool { loop { if other.window_id == self.window_id { return true; } other = match other.parent.get() { Some(p) => p, _ => return false, } } } pub fn title_changed(&self) { let title = self.info.title.borrow_mut(); if let Some(w) = self.window.get() { if let Some(p) = w.toplevel_data.parent.get() { p.node_child_title_changed(w.deref(), title.as_deref().unwrap_or("")); } } } } pub enum Change { None, Map, Unmap, } impl Xwindow { pub fn new(data: &Rc, surface: &Rc) -> Self { Self { id: data.state.node_ids.next(), seat_state: Default::default(), data: data.clone(), surface: surface.clone(), display_link: Default::default(), toplevel_data: ToplevelData::new( &data.state, data.info.title.borrow_mut().clone().unwrap_or_default(), Some(surface.client.clone()), ), } } pub fn destroy(&self) { self.break_loops(); self.data.window.take(); } pub fn break_loops(&self) { self.tl_destroy(); self.surface.set_toplevel(None); } pub fn install(self: &Rc) -> Result<(), XWindowError> { self.surface.set_role(SurfaceRole::XSurface)?; if self.surface.ext.get().is_some() { return Err(XWindowError::AlreadyAttached); } self.surface.ext.set(self.clone()); self.surface.set_toplevel(Some(self.clone())); Ok(()) } pub fn is_mapped(&self) -> bool { self.toplevel_data.parent.get().is_some() || self.display_link.borrow_mut().is_some() } pub fn may_be_mapped(&self) -> bool { self.surface.buffer.get().is_some() && self.data.info.mapped.get() } fn map_change(&self) -> Change { match (self.may_be_mapped(), self.is_mapped()) { (true, false) => Change::Map, (false, true) => Change::Unmap, _ => Change::None, } } pub fn map_status_changed(self: &Rc) { let map_change = self.map_change(); match map_change { Change::None => return, Change::Unmap => { self.data .info .pending_extents .set(self.data.info.extents.take()); self.tl_destroy(); } Change::Map if self.data.info.override_redirect.get() => { self.clone() .tl_change_extents(&self.data.info.pending_extents.get()); *self.display_link.borrow_mut() = Some(self.data.state.root.stacked.add_last(self.clone())); self.data.state.tree_changed(); } Change::Map if self.data.info.wants_floating.get() => { let ws = self.data.state.float_map_ws(); let ext = self.data.info.pending_extents.get(); self.data .state .map_floating(self.clone(), ext.width(), ext.height(), &ws); self.data.title_changed(); } Change::Map => { self.data.state.map_tiled(self.clone()); self.data.title_changed(); } } match map_change { Change::Unmap => self.tl_set_visible(false), Change::Map => self.tl_set_visible(true), Change::None => {} } self.data.state.tree_changed(); } } impl SurfaceExt for Xwindow { fn post_commit(self: Rc) { self.map_status_changed(); } fn on_surface_destroy(&self) -> Result<(), WlSurfaceError> { self.tl_destroy(); self.surface.unset_ext(); self.data.window.set(None); self.data.surface_id.set(None); self.data .state .xwayland .queue .push(XWaylandEvent::SurfaceDestroyed(self.surface.id)); Ok(()) } fn extents_changed(&self) { self.toplevel_data.pos.set(self.surface.extents.get()); self.tl_extents_changed(); } } impl Node for Xwindow { fn node_id(&self) -> NodeId { self.id.into() } fn node_seat_state(&self) -> &NodeSeatState { &self.seat_state } fn node_visit(self: Rc, visitor: &mut dyn NodeVisitor) { visitor.visit_xwindow(&self); } fn node_visit_children(&self, visitor: &mut dyn NodeVisitor) { visitor.visit_surface(&self.surface); } fn node_visible(&self) -> bool { self.surface.visible.get() } fn node_absolute_position(&self) -> Rect { self.data.info.extents.get() } fn node_do_focus(self: Rc, seat: &Rc, _direction: Direction) { seat.focus_toplevel(self.clone()); } fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult { if let Some(buffer) = self.surface.buffer.get() { if x < buffer.rect.width() && y < buffer.rect.height() { tree.push(FoundNode { node: self.surface.clone(), x, y, }); return FindTreeResult::AcceptsInput; } } FindTreeResult::Other } fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32) { renderer.render_surface(&self.surface, x, y) } fn node_client(&self) -> Option> { Some(self.data.client.clone()) } fn node_toplevel(self: Rc) -> Option> { Some(self) } fn node_on_pointer_enter(self: Rc, seat: &Rc, _x: Fixed, _y: Fixed) { seat.enter_toplevel(self.clone()); } fn node_on_pointer_focus(&self, seat: &Rc) { seat.set_known_cursor(KnownCursor::Default); } } impl ToplevelNode for Xwindow { tl_node_impl!(); fn tl_data(&self) -> &ToplevelData { &self.toplevel_data } fn tl_default_focus_child(&self) -> Option> { Some(self.surface.clone()) } fn tl_accepts_keyboard_focus(&self) -> bool { self.data.info.never_focus.get().not() && self.data.info.input_model.get() != XInputModel::None } fn tl_on_activate(&self) { self.data .state .xwayland .queue .push(XWaylandEvent::Activate(self.data.clone())); } fn tl_change_extents(self: Rc, rect: &Rect) { // log::info!("xwin {} change_extents {:?}", self.data.window_id, rect); let old = self.data.info.extents.replace(*rect); if old != *rect { if self.toplevel_data.is_floating.get() { self.toplevel_data.float_width.set(rect.width()); self.toplevel_data.float_height.set(rect.height()); } if !self.data.info.override_redirect.get() { self.data .state .xwayland .queue .push(XWaylandEvent::Configure(self.clone())); } if old.position() != rect.position() { self.surface.set_absolute_position(rect.x1(), rect.y1()); } } } fn tl_close(self: Rc) { self.data .state .xwayland .queue .push(XWaylandEvent::Close(self.data.clone())); } fn tl_set_visible(&self, visible: bool) { self.surface.set_visible(visible); self.seat_state.set_visible(self, visible); } fn tl_destroy(&self) { self.toplevel_data.destroy_node(self); self.display_link.borrow_mut().take(); self.surface.destroy_node(); } } impl StackedNode for Xwindow { stacked_node_impl!(); fn stacked_set_visible(&self, visible: bool) { self.tl_set_visible(visible); } } #[derive(Debug, Error)] pub enum XWindowError { #[error("The surface is already attached")] AlreadyAttached, #[error(transparent)] WlSurfaceError(#[from] WlSurfaceError), }