use crate::backend::Mode; use crate::cursor::KnownCursor; use crate::fixed::Fixed; use crate::ifs::wl_output::WlOutputGlobal; use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use crate::rect::Rect; use crate::render::{Renderer, Texture}; use crate::state::State; use crate::text; use crate::theme::Color; use crate::tree::walker::NodeVisitor; use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}; use crate::utils::clonecell::CloneCell; use crate::utils::errorfmt::ErrorFmt; use crate::utils::linkedlist::LinkedList; use std::cell::RefCell; use std::fmt::{Debug, Formatter}; use std::ops::{Deref, Sub}; use std::rc::Rc; tree_id!(OutputNodeId); pub struct OutputNode { pub id: OutputNodeId, pub global: Rc, pub workspaces: LinkedList>, pub workspace: CloneCell>>, pub seat_state: NodeSeatState, pub layers: [LinkedList>; 4], pub render_data: RefCell, pub state: Rc, pub is_dummy: bool, } impl OutputNode { pub fn update_render_data(&self) { let mut rd = self.render_data.borrow_mut(); rd.titles.clear(); rd.inactive_workspaces.clear(); let mut pos = 0; let font = self.state.theme.font.borrow_mut(); let th = self.state.theme.title_height.get(); let active_id = self.workspace.get().map(|w| w.id); for ws in self.workspaces.iter() { let mut title_width = th; 'create_texture: { if let Some(ctx) = self.state.render_ctx.get() { if th == 0 || ws.name.is_empty() { break 'create_texture; } let title = match text::render_fitting(&ctx, th, &font, &ws.name, Color::GREY) { Ok(t) => t, Err(e) => { log::error!("Could not render title {}: {}", ws.name, ErrorFmt(e)); break 'create_texture; } }; let mut x = pos + 1; if title.width() + 2 > title_width { title_width = title.width() + 2; } else { x = pos + (title_width - title.width()) / 2; } rd.titles.push(OutputTitle { x, y: 0, tex: title, }); } } let rect = Rect::new_sized(pos, 0, title_width, th).unwrap(); if Some(ws.id) == active_id { rd.active_workspace = rect; } else { rd.inactive_workspaces.push(rect); } pos += title_width; } } pub fn ensure_workspace(self: &Rc) { if !self.workspaces.is_empty() { return; } let name = 'name: { for i in 1.. { let name = i.to_string(); if !self.state.workspaces.contains(&name) { break 'name name; } } unreachable!(); }; let workspace = Rc::new(WorkspaceNode { id: self.state.node_ids.next(), output: CloneCell::new(self.clone()), position: Default::default(), container: Default::default(), stacked: Default::default(), seat_state: Default::default(), name: name.clone(), output_link: Default::default(), }); self.state.workspaces.set(name, workspace.clone()); workspace .output_link .set(Some(self.workspaces.add_last(workspace.clone()))); self.show_workspace(&workspace); self.update_render_data(); } pub fn show_workspace(&self, ws: &Rc) { self.workspace.set(Some(ws.clone())); ws.clone().change_extents(&self.workspace_rect()); } fn workspace_rect(&self) -> Rect { let rect = self.global.pos.get(); let th = self.state.theme.title_height.get(); Rect::new_sized( rect.x1(), rect.y1() + th, rect.width(), rect.height().sub(th).max(0), ) .unwrap() } pub fn update_mode(&self, mode: Mode) { if self.global.mode.get() == mode { return; } let pos = self.global.pos.get(); let rect = Rect::new_sized(pos.x1(), pos.y1(), mode.width, mode.height).unwrap(); self.global.pos.set(rect); if let Some(c) = self.workspace.get() { c.change_extents(&self.workspace_rect()); } for layer in &self.layers { for surface in layer.iter() { surface.deref().clone().change_extents(&rect); } } self.global.send_mode(); } } pub struct OutputTitle { pub x: i32, pub y: i32, pub tex: Rc, } #[derive(Default)] pub struct OutputRenderData { pub active_workspace: Rect, pub inactive_workspaces: Vec, pub titles: Vec, } impl Debug for OutputNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("OutputNode").finish_non_exhaustive() } } impl Node for OutputNode { fn id(&self) -> NodeId { self.id.into() } fn seat_state(&self) -> &NodeSeatState { &self.seat_state } fn destroy_node(&self, detach: bool) { if detach { self.state.root.clone().remove_child(self); } self.workspace.set(None); let workspaces: Vec<_> = self.workspaces.iter().map(|e| e.deref().clone()).collect(); for workspace in workspaces { workspace.destroy_node(false); } self.seat_state.destroy_node(self); } fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { visitor.visit_output(&self); } fn visit_children(&self, visitor: &mut dyn NodeVisitor) { for ws in self.workspaces.iter() { visitor.visit_workspace(ws.deref()); } for layers in &self.layers { for surface in layers.iter() { visitor.visit_layer_surface(surface.deref()); } } } fn absolute_position(&self) -> Rect { self.global.pos.get() } fn find_tree_at(&self, x: i32, mut y: i32, tree: &mut Vec) -> FindTreeResult { let th = self.state.theme.title_height.get(); if y > th { y -= th; if let Some(ws) = self.workspace.get() { tree.push(FoundNode { node: ws.clone(), x, y, }); ws.find_tree_at(x, y, tree); } } FindTreeResult::AcceptsInput } fn remove_child(self: Rc, _child: &dyn Node) { unimplemented!(); } fn leave(&self, seat: &WlSeatGlobal) { seat.leave_output(); } fn pointer_enter(self: Rc, seat: &Rc, _x: Fixed, _y: Fixed) { seat.enter_output(&self) } fn pointer_focus(&self, seat: &Rc) { seat.set_known_cursor(KnownCursor::Default); } fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { renderer.render_output(self, x, y); } fn is_output(&self) -> bool { true } fn into_output(self: Rc) -> Option> { Some(self) } }