From 76b1a1285640e3e7cef294d7951db51b92874a50 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 1 Sep 2024 10:04:42 +0200 Subject: [PATCH 1/4] tree: never return dummy workspace from ensure_workspace --- src/tree/output.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tree/output.rs b/src/tree/output.rs index 1bedcd31..0df37498 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -436,7 +436,9 @@ impl OutputNode { pub fn ensure_workspace(self: &Rc) -> Rc { if let Some(ws) = self.workspace.get() { - return ws; + if !ws.is_dummy { + return ws; + } } let name = 'name: { for i in 1.. { From 07b55e42c9ca02562bb302c17391ac833c450cc9 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 1 Sep 2024 10:18:54 +0200 Subject: [PATCH 2/4] linked-list: make detached nodes safe --- src/utils/linkedlist.rs | 49 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/utils/linkedlist.rs b/src/utils/linkedlist.rs index c3866757..d3c7084c 100644 --- a/src/utils/linkedlist.rs +++ b/src/utils/linkedlist.rs @@ -1,5 +1,5 @@ use { - crate::utils::{numcell::NumCell, ptr_ext::PtrExt}, + crate::utils::numcell::NumCell, std::{ cell::Cell, fmt::{Debug, Formatter}, @@ -29,20 +29,8 @@ impl Default for LinkedList { impl LinkedList { pub fn new() -> Self { - let node = Box::into_raw(Box::new(NodeData { - rc: NumCell::new(LINKED_NODE_REF_COUNT), - prev: Cell::new(NonNull::dangling()), - next: Cell::new(NonNull::dangling()), - data: None, - })); - unsafe { - node.deref().prev.set(NonNull::new_unchecked(node)); - node.deref().next.set(NonNull::new_unchecked(node)); - Self { - root: LinkedNode { - data: NonNull::new_unchecked(node), - }, - } + Self { + root: LinkedNode::new(None), } } @@ -290,12 +278,14 @@ impl NodeRef { self.peer(|d| &d.next) } - unsafe fn detach(&self) { - let data = self.data.as_ref(); - data.prev.get().as_ref().next.set(data.next.get()); - data.next.get().as_ref().prev.set(data.prev.get()); - data.prev.set(self.data); - data.next.set(self.data); + pub fn detach(&self) { + unsafe { + let data = self.data.as_ref(); + data.prev.get().as_ref().next.set(data.next.get()); + data.next.get().as_ref().prev.set(data.prev.get()); + data.prev.set(self.data); + data.next.set(self.data); + } } } @@ -322,6 +312,23 @@ impl Drop for LinkedNode { } impl LinkedNode { + fn new(t: Option) -> Self { + let node = Box::leak(Box::new(NodeData { + rc: NumCell::new(LINKED_NODE_REF_COUNT), + prev: Cell::new(NonNull::dangling()), + next: Cell::new(NonNull::dangling()), + data: t, + })); + let ptr = NonNull::from(&mut *node); + node.prev.set(ptr); + node.next.set(ptr); + LinkedNode { data: node.into() } + } + + pub fn detached(t: T) -> Self { + Self::new(Some(t)) + } + pub fn to_ref(&self) -> NodeRef { unsafe { self.data.as_ref().rc.fetch_add(1); From dbb9bd2299a65c8c6e19fbc24e6172279064b1b6 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 1 Sep 2024 10:28:37 +0200 Subject: [PATCH 3/4] util: add generic event listener framework --- src/utils.rs | 1 + src/utils/event_listener.rs | 69 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/utils/event_listener.rs diff --git a/src/utils.rs b/src/utils.rs index 91463c6a..d2afdf17 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -14,6 +14,7 @@ pub mod copyhashmap; pub mod debug_fn; pub mod double_click_state; pub mod errorfmt; +pub mod event_listener; pub mod fdcloser; pub mod gfx_api_ext; pub mod hash_map_ext; diff --git a/src/utils/event_listener.rs b/src/utils/event_listener.rs new file mode 100644 index 00000000..aef88ff2 --- /dev/null +++ b/src/utils/event_listener.rs @@ -0,0 +1,69 @@ +use { + crate::utils::linkedlist::{LinkedList, LinkedListIter, LinkedNode}, + std::{ + cell::Cell, + rc::{Rc, Weak}, + }, +}; + +pub struct EventSource { + listeners: LinkedList>, + on_attach: Cell>>, +} + +pub struct EventListener { + link: LinkedNode>, +} + +impl Default for EventSource { + fn default() -> Self { + Self { + listeners: Default::default(), + on_attach: Default::default(), + } + } +} + +impl EventSource { + pub fn iter(&self) -> EventSourceIter { + EventSourceIter { + iter: self.listeners.iter(), + } + } +} + +pub struct EventSourceIter { + iter: LinkedListIter>, +} + +impl Iterator for EventSourceIter { + type Item = Rc; + + fn next(&mut self) -> Option { + for weak in self.iter.by_ref() { + if let Some(t) = weak.upgrade() { + return Some(t); + } + } + None + } +} + +impl EventListener { + pub fn new(t: Weak) -> Self { + Self { + link: LinkedNode::detached(t), + } + } + + pub fn attach(&self, source: &EventSource) { + source.listeners.add_last_existing(&self.link); + if let Some(on_attach) = source.on_attach.take() { + on_attach(); + } + } + + pub fn detach(&self) { + self.link.detach(); + } +} From b28ea64509b35fc2cd950b8967286d2764f4087b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 1 Sep 2024 10:55:29 +0200 Subject: [PATCH 4/4] screencast: schedule toplevel screencasts with other screencasts --- src/compositor.rs | 1 + src/ifs/jay_compositor.rs | 2 +- src/ifs/jay_screencast.rs | 33 +++++++++++++++++++++++++++++---- src/renderer.rs | 5 ----- src/tasks/connector.rs | 1 + src/tree/output.rs | 12 ++++++++++-- src/tree/toplevel.rs | 25 ++++++++++++++++++++++++- src/tree/workspace.rs | 26 ++++++++++++++++++++++++-- 8 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/compositor.rs b/src/compositor.rs index ee3d617a..f854ead1 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -494,6 +494,7 @@ fn create_dummy_output(state: &Rc) { screencopies: Default::default(), title_visible: Cell::new(false), schedule, + latch_event: Default::default(), }); let dummy_workspace = Rc::new(WorkspaceNode { id: state.node_ids.next(), diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index dd778d0c..51d011ae 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -325,7 +325,7 @@ impl JayCompositorRequestHandler for JayCompositor { } fn create_screencast(&self, req: CreateScreencast, _slf: &Rc) -> Result<(), Self::Error> { - let sc = Rc::new(JayScreencast::new(req.id, &self.client)); + let sc = Rc::new_cyclic(|slf| JayScreencast::new(req.id, &self.client, slf)); track!(self.client, sc); self.client.add_client_obj(&sc)?; Ok(()) diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index 9c4d7301..91811100 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -8,10 +8,11 @@ use { object::{Object, Version}, scale::Scale, state::State, - tree::{OutputNode, ToplevelNode, WorkspaceNode, WorkspaceNodeId}, + tree::{LatchListener, OutputNode, ToplevelNode, WorkspaceNode, WorkspaceNodeId}, utils::{ clonecell::{CloneCell, UnsafeCellCloneSafe}, errorfmt::ErrorFmt, + event_listener::EventListener, numcell::NumCell, option_ext::OptionExt, }, @@ -29,7 +30,7 @@ use { std::{ cell::{Cell, RefCell}, ops::DerefMut, - rc::Rc, + rc::{Rc, Weak}, }, thiserror::Error, }; @@ -75,6 +76,7 @@ pub struct JayScreencast { pending: Pending, need_realloc: Cell, realloc_scheduled: Cell, + latch_listener: EventListener, } #[derive(Clone)] @@ -83,6 +85,12 @@ enum Target { Toplevel(Rc), } +impl LatchListener for JayScreencast { + fn after_latch(self: Rc) { + self.schedule_toplevel_screencast(); + } +} + unsafe impl UnsafeCellCloneSafe for Target {} enum PendingTarget { @@ -119,7 +127,7 @@ impl JayScreencast { false } - pub fn new(id: JayScreencastId, client: &Rc) -> Self { + pub fn new(id: JayScreencastId, client: &Rc, slf: &Weak) -> Self { Self { id, client: client.clone(), @@ -139,10 +147,11 @@ impl JayScreencast { pending: Default::default(), need_realloc: Cell::new(false), realloc_scheduled: Cell::new(false), + latch_listener: EventListener::new(slf.clone()), } } - pub fn schedule_toplevel_screencast(self: &Rc) { + fn schedule_toplevel_screencast(self: &Rc) { if !self.running.get() { return; } @@ -319,6 +328,7 @@ impl JayScreencast { } fn detach(&self) { + self.latch_listener.detach(); if let Some(target) = self.target.take() { match target { Target::Output(output) => { @@ -427,6 +437,18 @@ impl JayScreencast { self.client.state.damage(rect); } } + + pub fn update_latch_listener(&self) { + let Some(Target::Toplevel(tl)) = self.target.get() else { + return; + }; + let data = tl.tl_data(); + if data.visible.get() { + self.latch_listener.attach(&data.output().latch_event); + } else { + self.latch_listener.detach(); + } + } } impl JayScreencastRequestHandler for JayScreencast { @@ -540,6 +562,9 @@ impl JayScreencastRequestHandler for JayScreencast { let data = t.tl_data(); data.jay_screencasts .set((self.client.id, self.id), slf.clone()); + if data.visible.get() { + self.latch_listener.attach(&data.output().latch_event); + } new_target = Some(Target::Toplevel(t)); } } diff --git a/src/renderer.rs b/src/renderer.rs index f9353103..e1445d03 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -360,11 +360,6 @@ impl Renderer<'_> { bounds: Option<&Rect>, render_highlight: bool, ) { - if self.result.is_some() { - for screencast in tl_data.jay_screencasts.lock().values() { - screencast.schedule_toplevel_screencast(); - } - } if render_highlight { self.render_highlight(tl_data, bounds); } diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index db1cc6f6..ff1c839c 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -185,6 +185,7 @@ impl ConnectorHandler { screencopies: Default::default(), title_visible: Default::default(), schedule, + latch_event: Default::default(), }); on.update_visible(); on.update_rects(); diff --git a/src/tree/output.rs b/src/tree/output.rs index 0df37498..0f157482 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -37,8 +37,8 @@ use { }, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, - hash_map_ext::HashMapExt, linkedlist::LinkedList, scroller::Scroller, - transform_ext::TransformExt, + event_listener::EventSource, hash_map_ext::HashMapExt, linkedlist::LinkedList, + scroller::Scroller, transform_ext::TransformExt, }, wire::{JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id}, }, @@ -80,6 +80,11 @@ pub struct OutputNode { pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc>, pub title_visible: Cell, pub schedule: Rc, + pub latch_event: EventSource, +} + +pub trait LatchListener { + fn after_latch(self: Rc); } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] @@ -148,6 +153,9 @@ impl OutputNode { y_off: i32, size: Option<(i32, i32)>, ) { + for listener in self.latch_event.iter() { + listener.after_latch(); + } if let Some(workspace) = self.workspace.get() { if !workspace.may_capture.get() { return; diff --git a/src/tree/toplevel.rs b/src/tree/toplevel.rs index 5f9ccbaf..725f220e 100644 --- a/src/tree/toplevel.rs +++ b/src/tree/toplevel.rs @@ -42,6 +42,7 @@ pub trait ToplevelNode: ToplevelNodeBase { fn tl_set_parent(&self, parent: Rc); fn tl_extents_changed(&self); fn tl_set_workspace(&self, ws: &Rc); + fn tl_workspace_output_changed(&self); fn tl_change_extents(self: Rc, rect: &Rect); fn tl_set_visible(&self, visible: bool); fn tl_destroy(&self); @@ -112,8 +113,20 @@ impl ToplevelNode for T { fn tl_set_workspace(&self, ws: &Rc) { let data = self.tl_data(); - data.workspace.set(Some(ws.clone())); + let prev = data.workspace.set(Some(ws.clone())); self.tl_set_workspace_ext(ws); + let prev_id = prev.map(|p| p.output.get().id); + let new_id = Some(ws.output.get().id); + if prev_id != new_id { + self.tl_workspace_output_changed(); + } + } + + fn tl_workspace_output_changed(&self) { + let data = self.tl_data(); + for sc in data.jay_screencasts.lock().values() { + sc.update_latch_listener(); + } } fn tl_change_extents(self: Rc, rect: &Rect) { @@ -484,6 +497,9 @@ impl ToplevelData { pub fn set_visible(&self, node: &dyn Node, visible: bool) { self.visible.set(visible); self.seat_state.set_visible(node, visible); + for sc in self.jay_screencasts.lock().values() { + sc.update_latch_listener(); + } if !visible { return; } @@ -508,4 +524,11 @@ impl ToplevelData { parent.cnode_child_attention_request_changed(node, true); } } + + pub fn output(&self) -> Rc { + match self.workspace.get() { + None => self.state.dummy_output.get().unwrap(), + Some(ws) => ws.output.get(), + } + } } diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index 7daf641f..5f77afcb 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -7,7 +7,9 @@ use { jay_workspace::JayWorkspace, wl_output::OutputId, wl_seat::{tablet::TabletTool, NodeSeatState, WlSeatGlobal}, - wl_surface::WlSurface, + wl_surface::{ + x_surface::xwindow::Xwindow, xdg_surface::xdg_toplevel::XdgToplevel, WlSurface, + }, }, rect::Rect, renderer::Renderer, @@ -16,7 +18,7 @@ use { tree::{ container::ContainerNode, walker::NodeVisitor, ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeVisitorBase, OutputNode, - StackedNode, ToplevelNode, + PlaceholderNode, StackedNode, ToplevelNode, }, utils::{ clonecell::CloneCell, @@ -102,6 +104,26 @@ impl WorkspaceNode { fn visit_surface(&mut self, node: &Rc) { node.set_output(self.0); } + + fn visit_container(&mut self, node: &Rc) { + node.tl_workspace_output_changed(); + node.node_visit_children(self); + } + + fn visit_toplevel(&mut self, node: &Rc) { + node.tl_workspace_output_changed(); + node.node_visit_children(self); + } + + fn visit_xwindow(&mut self, node: &Rc) { + node.tl_workspace_output_changed(); + node.node_visit_children(self); + } + + fn visit_placeholder(&mut self, node: &Rc) { + node.tl_workspace_output_changed(); + node.node_visit_children(self); + } } let mut visitor = OutputSetter(output); self.node_visit_children(&mut visitor);