diff --git a/src/compositor.rs b/src/compositor.rs index 64a05570..38ae0372 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -451,6 +451,7 @@ fn create_dummy_output(state: &Rc) { update_render_data_scheduled: Cell::new(false), screencasts: Default::default(), hardware_cursor_needs_render: Cell::new(false), + screencopies: Default::default(), }); let dummy_workspace = Rc::new(WorkspaceNode { id: state.node_ids.next(), @@ -467,7 +468,8 @@ fn create_dummy_output(state: &Rc) { visible_on_desired_output: Default::default(), desired_output: CloneCell::new(dummy_output.global.output_id.clone()), jay_workspaces: Default::default(), - capture: Cell::new(false), + may_capture: Cell::new(false), + has_capture: Cell::new(false), title_texture: Cell::new(None), attention_requests: Default::default(), }); diff --git a/src/config/handler.rs b/src/config/handler.rs index b1f2f263..a5b93022 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -655,7 +655,7 @@ impl ConfigProxyHandler { fn handle_get_workspace_capture(&self, workspace: Workspace) -> Result<(), CphError> { let name = self.get_workspace(workspace)?; let capture = match self.state.workspaces.get(name.as_str()) { - Some(ws) => ws.capture.get(), + Some(ws) => ws.may_capture.get(), None => self.state.default_workspace_capture.get(), }; self.respond(Response::GetWorkspaceCapture { capture }); @@ -669,7 +669,8 @@ impl ConfigProxyHandler { ) -> Result<(), CphError> { let name = self.get_workspace(workspace)?; if let Some(ws) = self.state.workspaces.get(name.as_str()) { - ws.capture.set(capture); + ws.may_capture.set(capture); + ws.update_has_captures(); ws.output.get().schedule_update_render_data(); self.state.damage(); } diff --git a/src/ifs/jay_screencast.rs b/src/ifs/jay_screencast.rs index bcee7256..cd4215ad 100644 --- a/src/ifs/jay_screencast.rs +++ b/src/ifs/jay_screencast.rs @@ -8,7 +8,7 @@ use { object::{Object, Version}, scale::Scale, state::State, - tree::{OutputNode, ToplevelNode, WorkspaceNodeId}, + tree::{OutputNode, ToplevelNode, WorkspaceNode, WorkspaceNodeId}, utils::{ clonecell::{CloneCell, UnsafeCellCloneSafe}, errorfmt::ErrorFmt, @@ -106,6 +106,18 @@ struct ScreencastBuffer { } impl JayScreencast { + pub fn shows_ws(&self, ws: &WorkspaceNode) -> bool { + if self.show_all.get() { + return true; + } + for &id in &*self.show_workspaces.borrow() { + if id == ws.id { + return true; + } + } + false + } + pub fn new(id: JayScreencastId, client: &Rc) -> Self { Self { id, @@ -309,10 +321,7 @@ impl JayScreencast { if let Some(target) = self.target.take() { match target { Target::Output(output) => { - output.screencasts.remove(&(self.client.id, self.id)); - if output.screencasts.is_empty() { - output.state.damage(); - } + output.remove_screencast(self); } Target::Toplevel(tl) => { let data = tl.tl_data(); @@ -514,10 +523,7 @@ impl JayScreencastRequestHandler for JayScreencast { self.do_destroy(); return Ok(()); }; - if o.screencasts.is_empty() { - o.state.damage(); - } - o.screencasts.set((self.client.id, self.id), slf.clone()); + o.add_screencast(slf); new_target = Some(Target::Output(o)); } PendingTarget::Toplevel(t) => { @@ -546,11 +552,14 @@ impl JayScreencastRequestHandler for JayScreencast { need_realloc = true; } } + let mut capture_rules_changed = false; if let Some(show_all) = self.pending.show_all.take() { self.show_all.set(show_all); + capture_rules_changed = true; } if let Some(new_workspaces) = self.pending.show_workspaces.borrow_mut().take() { *self.show_workspaces.borrow_mut() = new_workspaces; + capture_rules_changed = true; } if let Some(running) = self.pending.running.take() { self.running.set(running); @@ -560,6 +569,12 @@ impl JayScreencastRequestHandler for JayScreencast { slf.schedule_realloc(); } + if capture_rules_changed { + if let Some(Target::Output(o)) = self.target.get() { + o.screencast_changed(); + } + } + Ok(()) } diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 39255533..75cd943c 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -2,22 +2,14 @@ use { crate::{ backend, client::{Client, ClientError, ClientId}, - gfx_api::GfxTexture, globals::{Global, GlobalName}, - ifs::{ - wl_buffer::WlBufferStorage, wl_surface::WlSurface, - zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, zxdg_output_v1::ZxdgOutputV1, - }, + ifs::{wl_surface::WlSurface, zxdg_output_v1::ZxdgOutputV1}, leaks::Tracker, object::{Object, Version}, rect::Rect, state::{ConnectorData, State}, - time::Time, tree::{calculate_logical_size, OutputNode}, - utils::{ - clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, - linkedlist::LinkedList, transform_ext::TransformExt, - }, + utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, transform_ext::TransformExt}, wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id}, }, ahash::AHashMap, @@ -25,7 +17,6 @@ use { std::{ cell::{Cell, RefCell}, collections::hash_map::Entry, - ops::Deref, rc::Rc, }, thiserror::Error, @@ -68,8 +59,6 @@ pub struct WlOutputGlobal { pub width_mm: i32, pub height_mm: i32, pub bindings: RefCell>>>, - pub unused_captures: LinkedList>, - pub pending_captures: LinkedList>, pub destroyed: Cell, pub legacy_scale: Cell, pub persistent: Rc, @@ -125,8 +114,6 @@ impl WlOutputGlobal { width_mm, height_mm, bindings: Default::default(), - unused_captures: Default::default(), - pending_captures: Default::default(), destroyed: Cell::new(false), legacy_scale: Cell::new(scale.round_up()), persistent: persistent_state.clone(), @@ -210,88 +197,6 @@ impl WlOutputGlobal { Ok(()) } - pub fn perform_screencopies( - &self, - tex: &Rc, - render_hardware_cursors: bool, - x_off: i32, - y_off: i32, - size: Option<(i32, i32)>, - ) { - if self.pending_captures.is_empty() { - return; - } - let now = Time::now().unwrap(); - let mut captures = vec![]; - for capture in self.pending_captures.iter() { - captures.push(capture.deref().clone()); - let wl_buffer = match capture.buffer.take() { - Some(b) => b, - _ => { - log::warn!("Capture frame is pending but has no buffer attached"); - capture.send_failed(); - continue; - } - }; - if wl_buffer.destroyed() { - capture.send_failed(); - continue; - } - if let Some(WlBufferStorage::Shm { mem, stride }) = - wl_buffer.storage.borrow_mut().deref() - { - let res = self.state.perform_shm_screencopy( - tex, - self.pos.get(), - x_off, - y_off, - size, - &capture, - mem, - *stride, - wl_buffer.format, - Transform::None, - ); - if let Err(e) = res { - log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e)); - capture.send_failed(); - continue; - } - } else { - let fb = match wl_buffer.famebuffer.get() { - Some(fb) => fb, - _ => { - log::warn!("Capture buffer has no framebuffer"); - capture.send_failed(); - continue; - } - }; - let res = self.state.perform_screencopy( - tex, - &fb, - self.pos.get(), - render_hardware_cursors, - x_off - capture.rect.x1(), - y_off - capture.rect.y1(), - size, - Transform::None, - ); - if let Err(e) = res { - log::warn!("Could not perform screencopy: {}", ErrorFmt(e)); - capture.send_failed(); - continue; - } - } - if capture.with_damage.get() { - capture.send_damage(); - } - capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _); - } - for capture in captures { - capture.output_link.take(); - } - } - pub fn pixel_size(&self) -> (i32, i32) { let mode = self.mode.get(); self.persistent diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs index dbd9782a..f8f121d5 100644 --- a/src/ifs/zwlr_screencopy_frame_v1.rs +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -9,7 +9,6 @@ use { leaks::Tracker, object::{Object, Version}, rect::Rect, - utils::linkedlist::LinkedNode, wire::{zwlr_screencopy_frame_v1::*, WlBufferId, ZwlrScreencopyFrameV1Id}, }, std::{cell::Cell, ops::Deref, rc::Rc}, @@ -28,7 +27,6 @@ pub struct ZwlrScreencopyFrameV1 { pub overlay_cursor: bool, pub used: Cell, pub with_damage: Cell, - pub output_link: Cell>>>, pub buffer: Cell>>, pub version: Version, } @@ -90,19 +88,16 @@ impl ZwlrScreencopyFrameV1 { } fn do_copy( - &self, + self: &Rc, buffer_id: WlBufferId, with_damage: bool, ) -> Result<(), ZwlrScreencopyFrameV1Error> { if self.used.replace(true) { return Err(ZwlrScreencopyFrameV1Error::AlreadyUsed); } - let link = match self.output_link.take() { - Some(l) => l, - _ => { - self.send_failed(); - return Ok(()); - } + let Some(node) = self.output.node.get() else { + self.send_failed(); + return Ok(()); }; let buffer = self.client.lookup(buffer_id)?; if (buffer.rect.width(), buffer.rect.height()) != (self.rect.width(), self.rect.height()) { @@ -122,27 +117,35 @@ impl ZwlrScreencopyFrameV1 { self.output.connector.connector.damage(); } self.with_damage.set(with_damage); - self.output.pending_captures.add_last_existing(&link); - self.output_link.set(Some(link)); + node.screencopies + .set((self.client.id, self.id), self.clone()); + node.screencast_changed(); Ok(()) } + + fn detach(&self) { + if let Some(node) = self.output.node.get() { + node.screencopies.remove(&(self.client.id, self.id)); + node.screencast_changed(); + } + } } impl ZwlrScreencopyFrameV1RequestHandler for ZwlrScreencopyFrameV1 { type Error = ZwlrScreencopyFrameV1Error; - fn copy(&self, req: Copy, _slf: &Rc) -> Result<(), Self::Error> { - self.do_copy(req.buffer, false) + fn copy(&self, req: Copy, slf: &Rc) -> Result<(), Self::Error> { + slf.do_copy(req.buffer, false) } fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); self.client.remove_obj(self)?; - self.output_link.take(); Ok(()) } - fn copy_with_damage(&self, req: CopyWithDamage, _slf: &Rc) -> Result<(), Self::Error> { - self.do_copy(req.buffer, true) + fn copy_with_damage(&self, req: CopyWithDamage, slf: &Rc) -> Result<(), Self::Error> { + slf.do_copy(req.buffer, true) } } @@ -155,7 +158,7 @@ simple_add_obj!(ZwlrScreencopyFrameV1); impl Object for ZwlrScreencopyFrameV1 { fn break_loops(&self) { - self.output_link.take(); + self.detach(); } } diff --git a/src/ifs/zwlr_screencopy_manager_v1.rs b/src/ifs/zwlr_screencopy_manager_v1.rs index 7acbf3d2..866c3691 100644 --- a/src/ifs/zwlr_screencopy_manager_v1.rs +++ b/src/ifs/zwlr_screencopy_manager_v1.rs @@ -125,7 +125,6 @@ impl ZwlrScreencopyManagerV1 { overlay_cursor, used: Cell::new(false), with_damage: Cell::new(false), - output_link: Cell::new(None), buffer: Cell::new(None), version: self.version, }); @@ -136,9 +135,6 @@ impl ZwlrScreencopyManagerV1 { frame.send_linux_dmabuf(); frame.send_buffer_done(); } - frame - .output_link - .set(Some(output.global.unused_captures.add_last(frame.clone()))); Ok(()) } } diff --git a/src/renderer.rs b/src/renderer.rs index 964e9c58..a4c97968 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -132,11 +132,9 @@ impl Renderer<'_> { x, y, ); - let has_captures = - !output.screencasts.is_empty() || !output.global.pending_captures.is_empty(); let rd = output.render_data.borrow_mut(); if let Some(aw) = &rd.active_workspace { - let c = match has_captures && aw.captured { + let c = match aw.captured { true => theme.colors.captured_focused_title_background.get(), false => theme.colors.focused_title_background.get(), }; @@ -147,10 +145,7 @@ impl Renderer<'_> { .fill_boxes2(slice::from_ref(&rd.underline), &c, x, y); let c = theme.colors.unfocused_title_background.get(); self.base.fill_boxes2(&rd.inactive_workspaces, &c, x, y); - let c = match has_captures { - true => theme.colors.captured_unfocused_title_background.get(), - false => theme.colors.unfocused_title_background.get(), - }; + let c = theme.colors.captured_unfocused_title_background.get(); self.base .fill_boxes2(&rd.captured_inactive_workspaces, &c, x, y); let c = theme.colors.attention_requested_background.get(); diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 07b7d93c..9cf069c2 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -148,6 +148,7 @@ impl ConnectorHandler { screencasts: Default::default(), update_render_data_scheduled: Cell::new(false), hardware_cursor_needs_render: Cell::new(false), + screencopies: Default::default(), }); self.state .add_output_scale(on.global.persistent.scale.get()); @@ -235,6 +236,9 @@ impl ConnectorHandler { for sc in screencasts { sc.do_destroy(); } + for (_, sc) in on.screencopies.lock().drain() { + sc.send_failed(); + } global.destroyed.set(true); self.state.root.outputs.remove(&self.id); self.state.root.update_extents(); diff --git a/src/tree/output.rs b/src/tree/output.rs index a188010e..00cf8459 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -8,6 +8,7 @@ use { ifs::{ jay_output::JayOutput, jay_screencast::JayScreencast, + wl_buffer::WlBufferStorage, wl_output::WlOutputGlobal, wl_seat::{ collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal, @@ -19,12 +20,14 @@ use { SurfaceSendPreferredTransformVisitor, }, zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP}, + zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, }, rect::Rect, renderer::Renderer, scale::Scale, state::State, text::{self, TextTexture}, + time::Time, tree::{ walker::NodeVisitor, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, WorkspaceNode, @@ -33,7 +36,7 @@ use { clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::LinkedList, scroller::Scroller, transform_ext::TransformExt, }, - wire::{JayOutputId, JayScreencastId}, + wire::{JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id}, }, ahash::AHashMap, jay_config::video::Transform, @@ -66,6 +69,7 @@ pub struct OutputNode { pub hardware_cursor_needs_render: Cell, pub update_render_data_scheduled: Cell, pub screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc>, + pub screencopies: CopyHashMap<(ClientId, ZwlrScreencopyFrameV1Id), Rc>, } pub async fn output_render_data(state: Rc) { @@ -81,6 +85,22 @@ pub async fn output_render_data(state: Rc) { } impl OutputNode { + pub fn add_screencast(&self, sc: &Rc) { + self.screencasts.set((sc.client.id, sc.id), sc.clone()); + self.screencast_changed(); + } + + pub fn remove_screencast(&self, sc: &JayScreencast) { + self.screencasts.remove(&(sc.client.id, sc.id)); + self.screencast_changed(); + } + + pub fn screencast_changed(&self) { + for ws in self.workspaces.iter() { + ws.update_has_captures(); + } + } + pub fn perform_screencopies( &self, tex: &Rc, @@ -90,17 +110,94 @@ impl OutputNode { size: Option<(i32, i32)>, ) { if let Some(workspace) = self.workspace.get() { - if !workspace.capture.get() { + if !workspace.may_capture.get() { return; } } - self.global - .perform_screencopies(tex, render_hardware_cursor, x_off, y_off, size); + self.perform_wlr_screencopies(tex, render_hardware_cursor, x_off, y_off, size); for sc in self.screencasts.lock().values() { sc.copy_texture(self, tex, render_hardware_cursor, x_off, y_off, size); } } + pub fn perform_wlr_screencopies( + &self, + tex: &Rc, + render_hardware_cursors: bool, + x_off: i32, + y_off: i32, + size: Option<(i32, i32)>, + ) { + if self.screencopies.is_empty() { + return; + } + let now = Time::now().unwrap(); + for (_, capture) in self.screencopies.lock().drain() { + let wl_buffer = match capture.buffer.take() { + Some(b) => b, + _ => { + log::warn!("Capture frame is pending but has no buffer attached"); + capture.send_failed(); + continue; + } + }; + if wl_buffer.destroyed() { + capture.send_failed(); + continue; + } + if let Some(WlBufferStorage::Shm { mem, stride }) = + wl_buffer.storage.borrow_mut().deref() + { + let res = self.state.perform_shm_screencopy( + tex, + self.global.pos.get(), + x_off, + y_off, + size, + &capture, + mem, + *stride, + wl_buffer.format, + Transform::None, + ); + if let Err(e) = res { + log::warn!("Could not perform shm screencopy: {}", ErrorFmt(e)); + capture.send_failed(); + continue; + } + } else { + let fb = match wl_buffer.famebuffer.get() { + Some(fb) => fb, + _ => { + log::warn!("Capture buffer has no framebuffer"); + capture.send_failed(); + continue; + } + }; + let res = self.state.perform_screencopy( + tex, + &fb, + self.global.pos.get(), + render_hardware_cursors, + x_off - capture.rect.x1(), + y_off - capture.rect.y1(), + size, + Transform::None, + ); + if let Err(e) = res { + log::warn!("Could not perform screencopy: {}", ErrorFmt(e)); + capture.send_failed(); + continue; + } + } + if capture.with_damage.get() { + capture.send_damage(); + } + capture.send_ready(now.0.tv_sec as _, now.0.tv_nsec as _); + } + self.screencast_changed(); + } + pub fn clear(&self) { self.global.clear(); self.workspace.set(None); @@ -111,6 +208,8 @@ impl OutputNode { self.render_data.borrow_mut().titles.clear(); self.lock_surface.take(); self.jay_outputs.clear(); + self.screencasts.clear(); + self.screencopies.clear(); } pub fn on_spaces_changed(self: &Rc) { @@ -228,13 +327,13 @@ impl OutputNode { if Some(ws.id) == active_id { rd.active_workspace = Some(OutputWorkspaceRenderData { rect, - captured: ws.capture.get(), + captured: ws.has_capture.get(), }); } else { if ws.attention_requests.active() { rd.attention_requested_workspaces.push(rect); } - if ws.capture.get() { + if ws.has_capture.get() { rd.captured_inactive_workspaces.push(rect); } else { rd.inactive_workspaces.push(rect); @@ -345,10 +444,12 @@ impl OutputNode { visible_on_desired_output: Cell::new(false), desired_output: CloneCell::new(self.global.output_id.clone()), jay_workspaces: Default::default(), - capture: self.state.default_workspace_capture.clone(), + may_capture: self.state.default_workspace_capture.clone(), + has_capture: Cell::new(false), title_texture: Default::default(), attention_requests: Default::default(), }); + ws.update_has_captures(); *ws.output_link.borrow_mut() = Some(self.workspaces.add_last(ws.clone())); self.state.workspaces.set(name.to_string(), ws.clone()); if self.workspace.is_none() { diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index f210c095..c7b2286f 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -49,7 +49,8 @@ pub struct WorkspaceNode { pub visible_on_desired_output: Cell, pub desired_output: CloneCell>, pub jay_workspaces: CopyHashMap<(ClientId, JayWorkspaceId), Rc>, - pub capture: Cell, + pub may_capture: Cell, + pub has_capture: Cell, pub title_texture: Cell>, pub attention_requests: ThresholdCounter, } @@ -62,11 +63,34 @@ impl WorkspaceNode { self.jay_workspaces.clear(); } + pub fn update_has_captures(&self) { + let mut has_capture = false; + let output = self.output.get(); + 'update: { + if !self.may_capture.get() { + break 'update; + } + for sc in output.screencasts.lock().values() { + if sc.shows_ws(self) { + has_capture = true; + break 'update; + } + } + if output.screencopies.is_not_empty() { + has_capture = true; + } + } + if self.has_capture.replace(has_capture) != has_capture { + output.schedule_update_render_data(); + } + } + pub fn set_output(&self, output: &Rc) { self.output.set(output.clone()); for jw in self.jay_workspaces.lock().values() { jw.send_output(output); } + self.update_has_captures(); struct OutputSetter<'a>(&'a Rc); impl NodeVisitorBase for OutputSetter<'_> { fn visit_surface(&mut self, node: &Rc) {