diff --git a/protocols.md b/protocols.md index efe8d96f..b873e16e 100644 --- a/protocols.md +++ b/protocols.md @@ -43,4 +43,5 @@ - xdg_positioner - set_reactive - xdg_toplevel - - + - set_fullscreen + - unset_fullscreen diff --git a/src/client.rs b/src/client.rs index 7d589469..2d51a685 100644 --- a/src/client.rs +++ b/src/client.rs @@ -366,8 +366,8 @@ impl Client { self.state.slow_clients.push(self.clone()); } } - self.flush_request.trigger(); } + self.flush_request.trigger(); } pub fn flush(&self) { diff --git a/src/compositor.rs b/src/compositor.rs index c22c0cc7..fbaf3461 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -158,6 +158,7 @@ fn start_compositor2( socket_path: Default::default(), serial: Default::default(), idle_inhibitor_ids: Default::default(), + run_toplevel, }); create_dummy_output(&state); let socket_path = Acceptor::install(&state)?; @@ -322,7 +323,8 @@ fn create_dummy_output(state: &Rc) { seat_state: Default::default(), name: "dummy".to_string(), output_link: Default::default(), - visible: Cell::new(false), + visible: Default::default(), + fullscreen: Default::default(), }); dummy_workspace.output_link.set(Some( dummy_output.workspaces.add_last(dummy_workspace.clone()), diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index c50dd8b6..23762a7e 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -278,7 +278,11 @@ impl WlSeatGlobal { } pub fn focus_toplevel(self: &Rc, n: Rc) { - self.focus_node(n.focus_surface(self.id)); + let node = match n.focus_surface(self.id) { + Some(n) => n, + _ => n.into_node(), + }; + self.focus_node(node); } fn ungrab_kb(self: &Rc) { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 197aa3cb..320a89f5 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -776,9 +776,9 @@ impl SizedNode for WlSurface { pn.node_replace_child(tl.as_node(), cn); } - fn close(&self) { + fn close(self: &Rc) { if let Some(tl) = self.toplevel.get() { - tl.close(); + tl.into_node().node_close(); } } @@ -838,9 +838,9 @@ impl SizedNode for WlSurface { } } - fn toggle_floating(self: &Rc, _seat: &Rc) { + fn toggle_floating(self: &Rc, seat: &Rc) { if let Some(tl) = self.toplevel.get() { - tl.toggle_floating(); + tl.into_node().node_toggle_floating(seat); } } diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index ae9c3ec3..edc4ac64 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -114,7 +114,7 @@ impl XdgSurface { fn set_absolute_desired_extents(&self, ext: &Rect) { let prev = self.absolute_desired_extents.replace(*ext); - if ext.x1() != prev.x1() || ext.y1() != prev.y1() { + if ext.position() != prev.position() { let (mut x1, mut y1) = (ext.x1(), ext.y1()); if let Some(geo) = self.geometry.get() { x1 -= geo.x1(); @@ -303,7 +303,7 @@ impl XdgSurface { fn set_visible(&self, visible: bool) { self.surface.node_set_visible(visible); for popup in self.popups.lock().values() { - popup.node_set_visible(visible); + popup.set_visible(visible); } } } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index d4a68388..5982e5c8 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -299,7 +299,7 @@ impl SizedNode for XdgPopup { } fn set_visible(&self, visible: bool) { - log::info!("set visible = {}", visible); + // log::info!("set visible = {}", visible); self.xdg.set_visible(visible); self.xdg.seat_state.set_visible(self, visible); } @@ -369,7 +369,7 @@ impl XdgSurfaceExt for XdgPopup { *wl = Some(ws.stacked.add_last(self.clone())); *dl = Some(state.root.stacked.add_last(self.clone())); state.tree_changed(); - self.node_set_visible( + self.set_visible( self.parent .get() .map(|p| p.surface.visible.get()) @@ -380,8 +380,8 @@ impl XdgSurfaceExt for XdgPopup { if wl.take().is_some() { drop(wl); drop(dl); - self.node_set_visible(false); - self.node_destroy(true); + self.set_visible(false); + self.destroy_node(true); self.send_popup_done(); } } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index aae07c05..c0de76e6 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -38,6 +38,7 @@ use { }, thiserror::Error, }; +use crate::tree::{FullscreenData, SizedFullscreenNode}; #[derive(Copy, Clone, Debug, FromPrimitive)] pub enum ResizeEdge { @@ -88,6 +89,7 @@ pub struct XdgToplevel { title: RefCell, pub tracker: Tracker, toplevel_data: ToplevelData, + fullscreen_data: FullscreenData, } impl Debug for XdgToplevel { @@ -120,9 +122,16 @@ impl XdgToplevel { title: RefCell::new("".to_string()), tracker: Default::default(), toplevel_data: Default::default(), + fullscreen_data: Default::default(), } } + fn send_current_configure(&self) { + let rect = self.xdg.absolute_desired_extents.get(); + self.send_configure_checked(rect.width(), rect.height()); + self.xdg.do_send_configure(); + } + fn send_configure_checked(&self, mut width: i32, mut height: i32) { width = width.max(1); height = height.max(1); @@ -204,6 +213,7 @@ impl XdgToplevel { if let Some(parent) = self.parent_node.get() { parent.node_child_title_changed(self, &title); } + self.fullscreen_data.set_title(&title); Ok(()) } @@ -280,21 +290,34 @@ impl XdgToplevel { Ok(()) } - fn set_fullscreen(&self, parser: MsgParser<'_, '_>) -> Result<(), SetFullscreenError> { - let _req: SetFullscreen = self.xdg.surface.client.parse(self, parser)?; - self.states.borrow_mut().insert(STATE_FULLSCREEN); - let rect = self.xdg.absolute_desired_extents.get(); - self.send_configure_checked(rect.width(), rect.height()); - self.xdg.do_send_configure(); + fn set_fullscreen(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), SetFullscreenError> { + let client = &self.xdg.surface.client; + let req: SetFullscreen = client.parse(self.deref(), parser)?; + 'set_fullscreen: { + let output = if req.output.is_some() { + match client.lookup(req.output)?.global.node.get() { + Some(node) => node, + _ => { + log::error!("Output global has no node attached"); + break 'set_fullscreen; + }, + } + } else if let Some(ws) = self.xdg.workspace.get() { + ws.output.get() + } else { + break 'set_fullscreen; + }; + client.state.set_fullscreen(self.clone(), &output); + } + self.send_current_configure(); Ok(()) } - fn unset_fullscreen(&self, parser: MsgParser<'_, '_>) -> Result<(), UnsetFullscreenError> { - let _req: UnsetFullscreen = self.xdg.surface.client.parse(self, parser)?; + fn unset_fullscreen(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), UnsetFullscreenError> { + let _req: UnsetFullscreen = self.xdg.surface.client.parse(self.deref(), parser)?; self.states.borrow_mut().remove(&STATE_FULLSCREEN); - let rect = self.xdg.absolute_desired_extents.get(); - self.send_configure_checked(rect.width(), rect.height()); - self.xdg.do_send_configure(); + self.xdg.surface.client.state.unset_fullscreen(self.clone()); + self.send_current_configure(); Ok(()) } @@ -408,6 +431,10 @@ impl SizedNode for XdgToplevel { } fn set_visible(&self, visible: bool) { + // log::info!("set_visible {}", visible); + // if !visible { + // log::info!("\n{:?}", Backtrace::new()); + // } self.xdg.set_visible(visible); self.xdg.seat_state.set_visible(self, visible); } @@ -416,21 +443,11 @@ impl SizedNode for XdgToplevel { self.xdg.workspace.get() } - fn is_contained_in(&self, other: NodeId) -> bool { - if let Some(parent) = self.parent_node.get() { - if parent.node_id() == other { - return true; - } - return parent.node_is_contained_in(other); - } - false - } - fn do_focus(self: &Rc, seat: &Rc, _direction: Direction) { seat.focus_toplevel(self.clone()); } - fn close(&self) { + fn close(self: &Rc) { self.send_close(); } @@ -483,6 +500,20 @@ impl SizedNode for XdgToplevel { fn client(&self) -> Option> { Some(self.xdg.surface.client.clone()) } + + fn toggle_floating(self: &Rc, _seat: &Rc) { + let parent = match self.parent_node.get() { + Some(p) => p, + _ => return, + }; + if parent.node_is_float() { + parent.node_remove_child2(self.deref(), true); + self.map_tiled(); + } else if let Some(ws) = self.xdg.workspace.get() { + parent.node_remove_child2(self.deref(), true); + self.map_floating(&ws); + } + } } impl ToplevelNode for XdgToplevel { @@ -502,8 +533,8 @@ impl ToplevelNode for XdgToplevel { true } - fn default_surface(&self) -> Rc { - self.xdg.surface.clone() + fn default_surface(&self) -> Option> { + Some(self.xdg.surface.clone()) } fn set_active(&self, active: bool) { @@ -527,24 +558,6 @@ impl ToplevelNode for XdgToplevel { fn activate(&self) { // nothing } - - fn toggle_floating(self: Rc) { - let parent = match self.parent_node.get() { - Some(p) => p, - _ => return, - }; - if parent.node_is_float() { - parent.node_remove_child(&*self); - self.map_tiled(); - } else if let Some(ws) = self.xdg.workspace.get() { - parent.node_remove_child(&*self); - self.map_floating(&ws); - } - } - - fn close(&self) { - self.send_close(); - } } impl XdgSurfaceExt for XdgToplevel { @@ -601,6 +614,24 @@ impl XdgSurfaceExt for XdgToplevel { } } +impl SizedFullscreenNode for XdgToplevel { + fn data(&self) -> &FullscreenData { + &self.fullscreen_data + } + + fn on_set_fullscreen(&self, _workspace: &Rc) { + self.states.borrow_mut().insert(STATE_FULLSCREEN); + } + + fn on_unset_fullscreen(&self) { + // nothing + } + + fn title(&self) -> String { + self.title.borrow_mut().clone() + } +} + #[derive(Debug, Error)] pub enum XdgToplevelError { #[error("Could not process `destroy` request")] diff --git a/src/ifs/wl_surface/xwindow.rs b/src/ifs/wl_surface/xwindow.rs index 438d8412..5503e03c 100644 --- a/src/ifs/wl_surface/xwindow.rs +++ b/src/ifs/wl_surface/xwindow.rs @@ -379,21 +379,11 @@ impl SizedNode for Xwindow { self.workspace.get() } - fn is_contained_in(&self, other: NodeId) -> bool { - if let Some(parent) = self.parent_node.get() { - if parent.node_id() == other { - return true; - } - return parent.node_is_contained_in(other); - } - false - } - fn do_focus(self: &Rc, seat: &Rc, _direction: Direction) { seat.focus_toplevel(self.clone()); } - fn close(&self) { + fn close(self: &Rc) { self.data .state .xwayland @@ -465,6 +455,23 @@ impl SizedNode for Xwindow { fn client(&self) -> Option> { Some(self.data.client.clone()) } + + fn toggle_floating(self: &Rc, _seat: &Rc) { + let parent = match self.parent_node.get() { + Some(p) => p, + _ => return, + }; + if parent.node_is_float() { + parent.node_remove_child2(self.deref(), true); + self.data.state.map_tiled(self.clone()); + } else if let Some(ws) = self.workspace.get() { + parent.node_remove_child2(self.deref(), true); + let (width, height) = self.toplevel_data.float_size(&ws); + self.data + .state + .map_floating(self.clone(), width, height, &ws); + } + } } impl ToplevelNode for Xwindow { @@ -485,8 +492,8 @@ impl ToplevelNode for Xwindow { && self.data.info.input_model.get() != XInputModel::None } - fn default_surface(&self) -> Rc { - self.surface.clone() + fn default_surface(&self) -> Option> { + Some(self.surface.clone()) } fn set_active(&self, active: bool) { @@ -502,31 +509,6 @@ impl ToplevelNode for Xwindow { .queue .push(XWaylandEvent::Activate(self.data.clone())); } - - fn toggle_floating(self: Rc) { - let parent = match self.parent_node.get() { - Some(p) => p, - _ => return, - }; - if parent.node_is_float() { - parent.node_remove_child(&*self); - self.data.state.map_tiled(self.clone()); - } else if let Some(ws) = self.workspace.get() { - parent.node_remove_child(&*self); - let (width, height) = self.toplevel_data.float_size(&ws); - self.data - .state - .map_floating(self.clone(), width, height, &ws); - } - } - - fn close(&self) { - self.data - .state - .xwayland - .queue - .push(XWaylandEvent::Close(self.data.clone())); - } } #[derive(Debug, Error)] diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index bfa007d1..35baafe8 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -49,6 +49,12 @@ impl Renderer<'_> { } pub fn render_output(&mut self, output: &OutputNode, x: i32, y: i32) { + if let Some(ws) = output.workspace.get() { + if let Some(fs) = ws.fullscreen.get() { + fs.as_node().node_render(self, x, y); + return; + } + } let opos = output.global.pos.get(); macro_rules! render_layer { ($layer:expr) => { diff --git a/src/state.rs b/src/state.rs index 0ef2b2c6..cf24da29 100644 --- a/src/state.rs +++ b/src/state.rs @@ -48,6 +48,9 @@ use { time::Duration, }, }; +use crate::ifs::wl_seat::collect_kb_foci; +use crate::tree::{PlaceholderNode, FullscreenNode, FullscreenedData, ToplevelNode}; +use crate::utils::run_toplevel::RunToplevel; pub struct State { pub xkb_ctx: XkbContext, @@ -92,6 +95,7 @@ pub struct State { pub xwayland: XWaylandState, pub socket_path: CloneCell>, pub serial: NumCell>, + pub run_toplevel: Rc, } pub struct XWaylandState { @@ -313,6 +317,7 @@ impl State { name: name.to_string(), output_link: Cell::new(None), visible: Cell::new(false), + fullscreen: Default::default(), }); workspace .output_link @@ -390,4 +395,86 @@ impl State { } serial as _ } + + pub fn set_fullscreen(self: &Rc, node: Rc, output: &Rc) { + let ws = output.ensure_workspace(); + if ws.fullscreen.get().is_some() { + log::info!("Cannot fullscreen a node on a workspace that already has a fullscreen node attached"); + return; + } + let mut data = node.data().data.borrow_mut(); + if data.is_some() { + log::info!("Cannot fullscreen a node that is already fullscreen"); + return; + } + let parent = match node.as_node().node_parent() { + None => { + log::warn!("Cannot fullscreen a node without a parent"); + return; + } + Some(p) => p, + }; + let placeholder = Rc::new(PlaceholderNode::new_for(self, node.clone())); + parent.node_replace_child(node.as_node(), placeholder.clone()); + let mut kb_foci = Default::default(); + if let Some(container) = ws.container.get() { + kb_foci = collect_kb_foci(container.clone()); + container.set_visible(false); + } + *data = Some(FullscreenedData { + placeholder, + workspace: ws.clone(), + }); + node.data().is_fullscreen.set(true); + ws.fullscreen.set(Some(node.clone())); + node.clone().into_node().node_set_parent(ws.clone()); + node.clone().into_node().node_set_workspace(&ws); + node.clone().into_node().node_change_extents(&output.global.pos.get()); + for seat in kb_foci { + node.clone().into_node().node_do_focus(&seat, Direction::Unspecified); + } + node.on_set_fullscreen(&ws); + } + + pub fn unset_fullscreen(self: &Rc, node: Rc) { + if !node.data().is_fullscreen.get() { + log::warn!("Cannot unset fullscreen on a node that is not fullscreen"); + } + let fd = match node.data().data.borrow_mut().take() { + Some(fd) => fd, + _ => { + log::error!("is_fullscreen = true but data is None"); + return; + } + }; + match fd.workspace.fullscreen.get() { + None => { + log::error!("Node is supposed to be fullscreened on a workspace but workspace has not fullscreen node."); + return; + } + Some(f) if f.as_node().node_id() != node.as_node().node_id() => { + log::error!("Node is supposed to be fullscreened on a workspace but the workspace has a different node attached."); + return; + } + _ => { }, + } + fd.workspace.fullscreen.take(); + if let Some(container) = fd.workspace.container.get() { + container.set_visible(true); + } + if fd.placeholder.is_destroyed() { + self.map_tiled(node.into_node()); + return; + } + let parent = fd.placeholder.parent().unwrap(); + parent.node_replace_child(fd.placeholder.deref(), node.clone().into_node()); + if node.as_node().node_visible() { + let kb_foci = collect_kb_foci(fd.placeholder.clone()); + for seat in kb_foci { + node.clone().into_node().node_do_focus(&seat, Direction::Unspecified); + } + } + fd.placeholder.as_node().node_seat_state().destroy_node(fd.placeholder.as_node()); + node.on_unset_fullscreen(); + } } diff --git a/src/tree.rs b/src/tree.rs index 08c4f103..135bb703 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -18,7 +18,7 @@ use { rc::Rc, }, }; -pub use {container::*, display::*, float::*, output::*, toplevel::*, walker::*, workspace::*}; +pub use {container::*, display::*, float::*, output::*, toplevel::*, walker::*, workspace::*, fullscreen::*, placeholder::*}; mod container; mod display; @@ -27,6 +27,8 @@ mod output; mod toplevel; mod walker; mod workspace; +mod fullscreen; +mod placeholder; pub struct NodeIds { next: NumCell, @@ -89,11 +91,6 @@ pub trait SizedNode: Sized + 'static { None } - fn is_contained_in(&self, other: NodeId) -> bool { - let _ = other; - false - } - fn child_title_changed(self: &Rc, child: &dyn Node, title: &str) { let _ = child; let _ = title; @@ -144,7 +141,7 @@ pub trait SizedNode: Sized + 'static { let _ = direction; } - fn close(&self) { + fn close(self: &Rc) { // nothing } @@ -237,7 +234,12 @@ pub trait SizedNode: Sized + 'static { } fn remove_child(self: &Rc, child: &dyn Node) { + self.remove_child2(child, false); + } + + fn remove_child2(self: &Rc, child: &dyn Node, preserve_focus: bool) { let _ = child; + let _ = preserve_focus; } fn child_size_changed(&self, child: &dyn Node, width: i32, height: i32) { @@ -381,7 +383,6 @@ pub trait Node { fn node_last_active_child(self: Rc) -> Rc; fn node_set_visible(&self, visible: bool); fn node_get_workspace(&self) -> Option>; - fn node_is_contained_in(&self, other: NodeId) -> bool; fn node_child_title_changed(self: Rc, child: &dyn Node, title: &str); fn node_get_parent_mono(&self) -> Option; fn node_get_parent_split(&self) -> Option; @@ -394,7 +395,7 @@ pub trait Node { fn node_create_split(self: Rc, split: ContainerSplit); fn node_focus_self(self: Rc, seat: &Rc); fn node_do_focus(self: Rc, seat: &Rc, direction: Direction); - fn node_close(&self); + fn node_close(self: Rc); fn node_move_focus(self: Rc, seat: &Rc, direction: Direction); fn node_move_self(self: Rc, direction: Direction); fn node_move_focus_from_child( @@ -424,6 +425,7 @@ pub trait Node { fn node_find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult; fn node_replace_child(self: Rc, old: &dyn Node, new: Rc); fn node_remove_child(self: Rc, child: &dyn Node); + fn node_remove_child2(self: Rc, child: &dyn Node, preserve_focus: bool); fn node_child_size_changed(&self, child: &dyn Node, width: i32, height: i32); fn node_child_active_changed(self: Rc, child: &dyn Node, active: bool, depth: u32); fn node_leave(&self, seat: &WlSeatGlobal); @@ -486,9 +488,6 @@ impl Node for T { fn node_get_workspace(&self) -> Option> { ::get_workspace(self) } - fn node_is_contained_in(&self, other: NodeId) -> bool { - ::is_contained_in(self, other) - } fn node_child_title_changed(self: Rc, child: &dyn Node, title: &str) { ::child_title_changed(&self, child, title) } @@ -525,8 +524,8 @@ impl Node for T { fn node_do_focus(self: Rc, seat: &Rc, direction: Direction) { ::do_focus(&self, seat, direction) } - fn node_close(&self) { - ::close(self) + fn node_close(self: Rc) { + ::close(&self) } fn node_move_focus(self: Rc, seat: &Rc, direction: Direction) { ::move_focus(&self, seat, direction) @@ -593,6 +592,9 @@ impl Node for T { fn node_remove_child(self: Rc, child: &dyn Node) { ::remove_child(&self, child) } + fn node_remove_child2(self: Rc, child: &dyn Node, preserve_focus: bool) { + ::remove_child2(&self, child, preserve_focus) + } fn node_child_size_changed(&self, child: &dyn Node, width: i32, height: i32) { ::child_size_changed(self, child, width, height) } diff --git a/src/tree/container.rs b/src/tree/container.rs index 2ca7828d..72683051 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -682,17 +682,23 @@ impl ContainerNode { } fn activate_child(self: &Rc, child: &NodeRef) { + self.activate_child2(child, false); + } + + fn activate_child2(self: &Rc, child: &NodeRef, preserve_focus: bool) { if let Some(mc) = self.mono_child.get() { if mc.node.node_id() == child.node.node_id() { return; } - let seats = collect_kb_foci(mc.node.clone()); - mc.node.node_set_visible(false); - for seat in seats { - child - .node - .clone() - .node_do_focus(&seat, Direction::Unspecified); + if !preserve_focus { + let seats = collect_kb_foci(mc.node.clone()); + mc.node.node_set_visible(false); + for seat in seats { + child + .node + .clone() + .node_do_focus(&seat, Direction::Unspecified); + } } self.mono_child.set(Some(child.clone())); child.node.node_set_visible(true); @@ -789,14 +795,6 @@ impl SizedNode for ContainerNode { Some(self.workspace.get()) } - fn is_contained_in(&self, other: NodeId) -> bool { - let parent = self.parent.get(); - if parent.node_id() == other { - return true; - } - parent.node_is_contained_in(other) - } - fn child_title_changed(self: &Rc, child: &dyn Node, title: &str) { let child = match self.child_nodes.borrow_mut().get(&child.node_id()) { Some(cn) => cn.to_ref(), @@ -899,9 +897,9 @@ impl SizedNode for ContainerNode { } } - fn close(&self) { + fn close(self: &Rc) { for child in self.children.iter() { - child.node.node_close(); + child.node.clone().node_close(); } } @@ -985,7 +983,7 @@ impl SizedNode for ContainerNode { } let (split, prev) = direction_to_split(direction); // CASE 2: We're moving the child within the container. - if split == self.split.get() { + if split == self.split.get() || (split == ContainerSplit::Horizontal && self.mono_child.get().is_some()) { let cc = match self.child_nodes.borrow_mut().get(&child.node_id()) { Some(l) => l.to_ref(), None => return, @@ -996,7 +994,7 @@ impl SizedNode for ContainerNode { }; if let Some(neighbor) = neighbor { if neighbor.node.node_accepts_child(&*child) { - self.remove_child(&*child); + self.remove_child2(&*child, true); neighbor.node.clone().node_insert_child(child, direction); return; } @@ -1022,7 +1020,7 @@ impl SizedNode for ContainerNode { Some(p) => p, _ => return, }; - self.clone().node_remove_child(&*child); + self.remove_child2(&*child, true); match prev { true => parent.add_child_before(&*neighbor, child.clone()), false => parent.add_child_after(&*neighbor, child.clone()), @@ -1151,7 +1149,7 @@ impl SizedNode for ContainerNode { fn toggle_floating(self: &Rc, _seat: &Rc) { let parent = self.parent.get(); - parent.clone().node_remove_child(self.deref()); + parent.clone().node_remove_child2(self.deref(), true); if parent.node_is_float() { self.state.map_tiled(self.clone()); } else { @@ -1192,7 +1190,10 @@ impl SizedNode for ContainerNode { fn replace_child(self: &Rc, old: &dyn Node, new: Rc) { let node = match self.child_nodes.borrow_mut().remove(&old.node_id()) { Some(c) => c, - None => return, + None => { + log::error!("Trying to replace a node that isn't a child of this container"); + return + }, }; let (have_mc, was_mc) = match self.mono_child.get() { None => (false, false), @@ -1209,6 +1210,7 @@ impl SizedNode for ContainerNode { title_rect: Cell::new(node.title_rect.get()), focus_history: Cell::new(None), }); + new.node_set_visible(node.node.node_visible()); drop(node); let mut body = None; if was_mc { @@ -1226,7 +1228,7 @@ impl SizedNode for ContainerNode { } } - fn remove_child(self: &Rc, child: &dyn Node) { + fn remove_child2(self: &Rc, child: &dyn Node, preserve_focus: bool) { let node = match self.child_nodes.borrow_mut().remove(&child.node_id()) { Some(c) => c, None => return, @@ -1242,7 +1244,7 @@ impl SizedNode for ContainerNode { } } if let Some(child) = &new { - self.activate_child(child); + self.activate_child2(child, preserve_focus); } } } diff --git a/src/tree/display.rs b/src/tree/display.rs index c152f7a4..0ad87ed2 100644 --- a/src/tree/display.rs +++ b/src/tree/display.rs @@ -4,7 +4,6 @@ use { cursor::KnownCursor, ifs::{ wl_seat::{NodeSeatState, WlSeatGlobal}, - zwlr_layer_shell_v1::{OVERLAY, TOP}, }, rect::Rect, render::Renderer, @@ -114,47 +113,7 @@ impl SizedNode for DisplayNode { x, y, }); - if output.find_layer_surface_at(x, y, &[OVERLAY, TOP], tree) - == FindTreeResult::AcceptsInput - { - return FindTreeResult::AcceptsInput; - } - tree.pop(); - break; - } - } - for stacked in self.stacked.rev_iter() { - let ext = stacked.node_absolute_position(); - if stacked.node_absolute_position_constrains_input() && !ext.contains(x, y) { - // TODO: make constrain always true - continue; - } - let (x, y) = ext.translate(x, y); - let idx = tree.len(); - tree.push(FoundNode { - node: stacked.deref().clone(), - x, - y, - }); - match stacked.node_find_tree_at(x, y, tree) { - FindTreeResult::AcceptsInput => { - return FindTreeResult::AcceptsInput; - } - FindTreeResult::Other => { - tree.drain(idx..); - } - } - } - for output in outputs.values() { - let pos = output.global.pos.get(); - if pos.contains(x, y) { - let (x, y) = pos.translate(x, y); - tree.push(FoundNode { - node: output.clone(), - x, - y, - }); - output.node_find_tree_at(x, y, tree); + output.find_tree_at(x, y, tree); break; } } diff --git a/src/tree/float.rs b/src/tree/float.rs index 5467525f..ac5f3dcf 100644 --- a/src/tree/float.rs +++ b/src/tree/float.rs @@ -430,6 +430,8 @@ impl SizedNode for FloatNode { } } else if state == KeyState::Released { seat_data.op_active = false; + let ws = seat.get_output().ensure_workspace(); + self.set_workspace(&ws); } } @@ -465,7 +467,7 @@ impl SizedNode for FloatNode { self.schedule_layout(); } - fn remove_child(self: &Rc, _child: &dyn Node) { + fn remove_child2(self: &Rc, _child: &dyn Node, _preserve_focus: bool) { self.child.set(None); self.display_link.set(None); self.workspace_link.set(None); diff --git a/src/tree/fullscreen.rs b/src/tree/fullscreen.rs new file mode 100644 index 00000000..bfe99e4c --- /dev/null +++ b/src/tree/fullscreen.rs @@ -0,0 +1,74 @@ +use std::cell::{Cell, RefCell}; +use std::ops::Deref; +use std::rc::Rc; +use crate::tree::{PlaceholderNode, Node, WorkspaceNode, SizedNode}; + +pub trait SizedFullscreenNode: SizedNode { + fn data(&self) -> &FullscreenData; + fn on_set_fullscreen(&self, workspace: &Rc); + fn on_unset_fullscreen(&self); + fn title(&self) -> String; + + fn as_node(&self) -> &dyn Node { + self + } + + fn into_node(self: Rc) -> Rc { + self + } +} + +pub trait FullscreenNode { + fn data(&self) -> &FullscreenData; + fn on_set_fullscreen(&self, workspace: &Rc); + fn on_unset_fullscreen(&self); + fn as_node(&self) -> &dyn Node; + fn into_node(self: Rc) -> Rc; + fn title(&self) -> String; +} + +impl FullscreenNode for T { + fn data(&self) -> &FullscreenData { + ::data(self) + } + + fn on_set_fullscreen(&self, workspace: &Rc) { + ::on_set_fullscreen(self, workspace) + } + + fn on_unset_fullscreen(&self) { + ::on_unset_fullscreen(self) + } + + fn as_node(&self) -> &dyn Node { + ::as_node(self) + } + + fn into_node(self: Rc) -> Rc { + ::into_node(self) + } + + fn title(&self) -> String { + ::title(self) + } +} + +pub struct FullscreenedData { + pub placeholder: Rc, + pub workspace: Rc, +} + +#[derive(Default)] +pub struct FullscreenData { + pub is_fullscreen: Cell, + pub data: RefCell>, +} + +impl FullscreenData { + pub fn set_title(&self, title: &str) { + let data = self.data.borrow_mut(); + if let Some(data) = data.deref() { + data.placeholder.set_title(title); + } + } +} diff --git a/src/tree/output.rs b/src/tree/output.rs index d8cf449b..9b871379 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -27,6 +27,7 @@ use { rc::Rc, }, }; +use crate::ifs::zwlr_layer_shell_v1::{OVERLAY, TOP}; tree_id!(OutputNodeId); pub struct OutputNode { @@ -139,6 +140,7 @@ impl OutputNode { name: name.clone(), output_link: Default::default(), visible: Cell::new(true), + fullscreen: Default::default(), }); self.state.workspaces.set(name, workspace.clone()); workspace @@ -325,6 +327,48 @@ impl SizedNode for OutputNode { } fn find_tree_at(&self, x: i32, mut y: i32, tree: &mut Vec) -> FindTreeResult { + if let Some(ws) = self.workspace.get() { + if let Some(fs) = ws.fullscreen.get() { + tree.push(FoundNode { + node: fs.clone().into_node(), + x, + y, + }); + return fs.as_node().node_find_tree_at(x, y, tree); + } + } + let (x_abs, y_abs) = self.global.pos.get().translate_inv(x, y); + { + if self.find_layer_surface_at(x_abs, y_abs, &[OVERLAY, TOP], tree) + == FindTreeResult::AcceptsInput + { + return FindTreeResult::AcceptsInput; + } + } + { + for stacked in self.state.root.stacked.rev_iter() { + let ext = stacked.node_absolute_position(); + if stacked.node_absolute_position_constrains_input() && !ext.contains(x_abs, y_abs) { + // TODO: make constrain always true + continue; + } + let (x, y) = ext.translate(x_abs, y_abs); + let idx = tree.len(); + tree.push(FoundNode { + node: stacked.deref().clone(), + x, + y, + }); + match stacked.node_find_tree_at(x, y, tree) { + FindTreeResult::AcceptsInput => { + return FindTreeResult::AcceptsInput; + } + FindTreeResult::Other => { + tree.truncate(idx); + } + } + } + } let bar_height = self.state.theme.title_height.get() + 1; if y > bar_height { y -= bar_height; @@ -344,7 +388,7 @@ impl SizedNode for OutputNode { FindTreeResult::AcceptsInput } - fn remove_child(self: &Rc, _child: &dyn Node) { + fn remove_child2(self: &Rc, _child: &dyn Node, _preserve_focus: bool) { unimplemented!(); } diff --git a/src/tree/placeholder.rs b/src/tree/placeholder.rs new file mode 100644 index 00000000..b30fc001 --- /dev/null +++ b/src/tree/placeholder.rs @@ -0,0 +1,183 @@ +use std::cell::{Cell, RefCell}; +use std::ops::Deref; +use std::rc::Rc; +use jay_config::Direction; +use crate::client::{Client}; +use crate::cursor::KnownCursor; +use crate::fixed::Fixed; +use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; +use crate::ifs::wl_surface::WlSurface; +use crate::rect::Rect; +use crate::state::State; +use crate::tree::{FindTreeResult, FoundNode, FullscreenNode, Node, NodeId, NodeVisitor, SizedNode, ToplevelData, ToplevelNode, WorkspaceNode}; +use crate::utils::clonecell::CloneCell; + +tree_id!(DetachedNodeId); +pub struct PlaceholderNode { + id: DetachedNodeId, + state: Rc, + seat_state: NodeSeatState, + parent: CloneCell>>, + workspace: CloneCell>>, + title: RefCell, + visible: Cell, + pos: Cell, + client: Option>, + toplevel: ToplevelData, + active: Cell, + destroyed: Cell, +} + +impl PlaceholderNode { + pub fn new_for(state: &Rc, node: Rc) -> Self { + Self { + id: state.node_ids.next(), + state: state.clone(), + seat_state: Default::default(), + parent: Default::default(), + workspace: Default::default(), + title: RefCell::new(node.title()), + visible: Default::default(), + pos: Default::default(), + client: node.as_node().node_client(), + toplevel: Default::default(), + active: Default::default(), + destroyed: Default::default(), + } + } + + pub fn set_title(&self, title: &str) { + *self.title.borrow_mut() = title.to_string(); + if let Some(parent) = self.parent.get() { + parent.node_child_title_changed(self, title); + } + } + + pub fn is_destroyed(&self) -> bool { + self.destroyed.get() + } +} + +impl SizedNode for PlaceholderNode { + fn id(&self) -> NodeId { + self.id.into() + } + + fn seat_state(&self) -> &NodeSeatState { + &self.seat_state + } + + fn destroy_node(&self, detach: bool) { + if detach { + if let Some(parent) = self.parent.get() { + parent.node_remove_child(self); + } + } + self.parent.take(); + self.workspace.take(); + self.seat_state.destroy_node(self); + self.destroyed.set(true); + } + + fn visit(self: &Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_placeholder(self); + } + + fn visit_children(&self, _visitor: &mut dyn NodeVisitor) { + // nothing + } + + fn visible(&self) -> bool { + self.visible.get() + } + + fn parent(&self) -> Option> { + self.parent.get() + } + + fn set_visible(&self, visible: bool) { + self.visible.set(visible); + } + + fn get_workspace(&self) -> Option> { + self.workspace.get() + } + + fn do_focus(self: &Rc, seat: &Rc, _direction: Direction) { + seat.focus_toplevel(self.clone()); + } + + fn close(self: &Rc) { + let slf = self.clone(); + self.state.run_toplevel.schedule(move || { + slf.destroy_node(true); + }); + } + + fn absolute_position(&self) -> Rect { + self.pos.get() + } + + fn active_changed(&self, active: bool) { + self.active.set(active); + if let Some(parent) = self.parent.get() { + parent.node_child_active_changed(self, active, 1); + } + } + + fn find_tree_at(&self, _x: i32, _y: i32, _tree: &mut Vec) -> FindTreeResult { + FindTreeResult::AcceptsInput + } + + fn pointer_enter(self: &Rc, seat: &Rc, _x: Fixed, _y: Fixed) { + seat.set_known_cursor(KnownCursor::Default); + seat.enter_toplevel(self.clone()); + } + + fn change_extents(self: &Rc, rect: &Rect) { + self.pos.set(*rect); + } + + fn set_workspace(self: &Rc, ws: &Rc) { + self.workspace.set(Some(ws.clone())); + } + + fn set_parent(self: &Rc, parent: Rc) { + self.parent.set(Some(parent.clone())); + parent.node_child_title_changed(self.deref(), self.title.borrow_mut().deref()); + } + + fn client(&self) -> Option> { + self.client.clone() + } +} + +impl ToplevelNode for PlaceholderNode { + fn data(&self) -> &ToplevelData { + &self.toplevel + } + + fn as_node(&self) -> &dyn Node { + self + } + + fn into_node(self: Rc) -> Rc { + self + } + + fn accepts_keyboard_focus(&self) -> bool { + true + } + + fn default_surface(&self) -> Option> { + None + } + + fn set_active(&self, _active: bool) { + // nothing + } + + fn activate(&self) { + // nothing + } +} diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index 0e7f8d9b..8abbcfbe 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -13,11 +13,9 @@ pub trait ToplevelNode { fn as_node(&self) -> &dyn Node; fn into_node(self: Rc) -> Rc; fn accepts_keyboard_focus(&self) -> bool; - fn default_surface(&self) -> Rc; + fn default_surface(&self) -> Option>; fn set_active(&self, active: bool); fn activate(&self); - fn toggle_floating(self: Rc); - fn close(&self); } #[derive(Default)] @@ -61,11 +59,11 @@ impl<'a> dyn ToplevelNode + 'a { } } - pub fn focus_surface(&self, seat: SeatId) -> Rc { + pub fn focus_surface(&self, seat: SeatId) -> Option> { self.data() .focus_surface .get(&seat) - .unwrap_or_else(|| self.default_surface()) + .or_else(|| self.default_surface()) } pub fn parent(&self) -> Option> { diff --git a/src/tree/walker.rs b/src/tree/walker.rs index e48ba04a..042a2de1 100644 --- a/src/tree/walker.rs +++ b/src/tree/walker.rs @@ -10,6 +10,7 @@ use { }, std::rc::Rc, }; +use crate::tree::PlaceholderNode; pub trait NodeVisitorBase: Sized { fn visit_surface(&mut self, node: &Rc) { @@ -51,6 +52,10 @@ pub trait NodeVisitorBase: Sized { fn visit_xwindow(&mut self, node: &Rc) { node.node_visit_children(self); } + + fn visit_placeholder(&mut self, node: &Rc) { + node.node_visit_children(self); + } } pub trait NodeVisitor { @@ -64,6 +69,7 @@ pub trait NodeVisitor { fn visit_workspace(&mut self, node: &Rc); fn visit_layer_surface(&mut self, node: &Rc); fn visit_xwindow(&mut self, node: &Rc); + fn visit_placeholder(&mut self, node: &Rc); } impl NodeVisitor for T { @@ -106,6 +112,10 @@ impl NodeVisitor for T { fn visit_xwindow(&mut self, node: &Rc) { ::visit_xwindow(self, node) } + + fn visit_placeholder(&mut self, node: &Rc) { + ::visit_placeholder(self, node) + } } pub struct GenericNodeVisitor { @@ -166,6 +176,11 @@ impl)> NodeVisitor for GenericNodeVisitor { (self.f)(node.clone()); node.node_visit_children(self); } + + fn visit_placeholder(&mut self, node: &Rc) { + (self.f)(node.clone()); + node.node_visit_children(self); + } } // pub fn visit_containers)>(f: F) -> impl NodeVisitor { diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index d79236d3..1bcd0364 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -16,6 +16,7 @@ use { jay_config::Direction, std::{cell::Cell, fmt::Debug, rc::Rc}, }; +use crate::tree::FullscreenNode; tree_id!(WorkspaceNodeId); @@ -29,6 +30,7 @@ pub struct WorkspaceNode { pub name: String, pub output_link: Cell>>>, pub visible: Cell, + pub fullscreen: CloneCell>>, } impl WorkspaceNode { @@ -69,6 +71,9 @@ impl SizedNode for WorkspaceNode { if let Some(c) = self.container.get() { visitor.visit_container(&c); } + if let Some(fs) = self.fullscreen.get() { + fs.into_node().node_visit(visitor); + } } fn visible(&self) -> bool { @@ -116,7 +121,7 @@ impl SizedNode for WorkspaceNode { FindTreeResult::AcceptsInput } - fn remove_child(self: &Rc, _child: &dyn Node) { + fn remove_child2(self: &Rc, _child: &dyn Node, _preserve_focus: bool) { self.container.set(None); } diff --git a/src/utils/asyncevent.rs b/src/utils/asyncevent.rs index 38421c58..44a139df 100644 --- a/src/utils/asyncevent.rs +++ b/src/utils/asyncevent.rs @@ -20,9 +20,10 @@ impl AsyncEvent { } pub fn trigger(&self) { - self.triggers.fetch_add(1); - if let Some(waker) = self.waker.take() { - waker.wake(); + if self.triggers.fetch_add(1) == 0 { + if let Some(waker) = self.waker.take() { + waker.wake(); + } } }