Initial support for filtering what to capture to only what's needed

This commit is contained in:
Ian Douglas Scott 2023-02-10 10:12:26 -08:00
parent b29f1149a4
commit b5931cd240
5 changed files with 110 additions and 40 deletions

View file

@ -55,7 +55,7 @@ struct Workspace {
name: String, name: String,
img: Option<iced::widget::image::Handle>, img: Option<iced::widget::image::Handle>,
handle: zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, handle: zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
output_name: Option<String>, output_name: String,
is_active: bool, is_active: bool,
} }
@ -70,7 +70,7 @@ struct Toplevel {
struct Output { struct Output {
// Output, on the `iced_sctk` Wayland connection // Output, on the `iced_sctk` Wayland connection
handle: wl_output::WlOutput, handle: wl_output::WlOutput,
name: Option<String>, name: String,
width: i32, width: i32,
height: i32, height: i32,
} }
@ -78,7 +78,7 @@ struct Output {
struct LayerSurface { struct LayerSurface {
// Output, on the `iced_sctk` Wayland connection // Output, on the `iced_sctk` Wayland connection
output: wl_output::WlOutput, output: wl_output::WlOutput,
output_name: Option<String>, output_name: String,
// for transitions, would need windows in more than one workspace? But don't capture all of // for transitions, would need windows in more than one workspace? But don't capture all of
// them all the time every frame. // them all the time every frame.
} }
@ -128,7 +128,7 @@ impl App {
fn create_surface( fn create_surface(
&mut self, &mut self,
output: wl_output::WlOutput, output: wl_output::WlOutput,
output_name: Option<String>, output_name: String,
width: i32, width: i32,
height: i32, height: i32,
) -> Command<Msg> { ) -> Command<Msg> {
@ -177,14 +177,16 @@ impl App {
if !self.visible { if !self.visible {
self.visible = true; self.visible = true;
let outputs = self.outputs.clone(); let outputs = self.outputs.clone();
Command::batch( let cmd = Command::batch(
outputs outputs
.into_iter() .into_iter()
.map(|output| { .map(|output| {
self.create_surface(output.handle, output.name, output.width, output.height) self.create_surface(output.handle, output.name, output.width, output.height)
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
) );
self.update_capture_filter();
cmd
} else { } else {
Command::none() Command::none()
} }
@ -193,6 +195,7 @@ impl App {
// Close all shell surfaces // Close all shell surfaces
fn hide(&mut self) -> Command<Msg> { fn hide(&mut self) -> Command<Msg> {
self.visible = false; self.visible = false;
self.update_capture_filter();
Command::batch( Command::batch(
mem::take(&mut self.layer_surfaces) mem::take(&mut self.layer_surfaces)
.into_keys() .into_keys()
@ -200,6 +203,24 @@ impl App {
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
) )
} }
fn update_capture_filter(&self) {
if let Some(sender) = self.wayland_cmd_sender.as_ref() {
let mut capture_filter = wayland::CaptureFilter::default();
if self.visible {
// XXX handle on wrong connection
capture_filter.workspaces_on_outputs =
self.outputs.iter().map(|x| x.name.clone()).collect();
capture_filter.toplevels_on_workspaces = self
.workspaces
.iter()
.filter(|x| x.is_active)
.map(|x| x.handle.clone())
.collect();
}
let _ = sender.send(wayland::Cmd::CaptureFilter(capture_filter));
}
}
} }
impl Application for App { impl Application for App {
@ -225,20 +246,16 @@ impl Application for App {
Msg::WaylandEvent(evt) => match evt { Msg::WaylandEvent(evt) => match evt {
WaylandEvent::Output(evt, output) => match evt { WaylandEvent::Output(evt, output) => match evt {
OutputEvent::Created(Some(info)) => { OutputEvent::Created(Some(info)) => {
if let Some((width, height)) = info.logical_size { if let (Some((width, height)), Some(name)) = (info.logical_size, info.name)
{
self.outputs.push(Output { self.outputs.push(Output {
handle: output.clone(), handle: output.clone(),
name: info.name.clone(), name: name.clone(),
width, width,
height, height,
}); });
if self.visible { if self.visible {
return self.create_surface( return self.create_surface(output.clone(), name, width, height);
output.clone(),
info.name,
width,
height,
);
} }
} }
} }
@ -249,7 +266,9 @@ impl Application for App {
output.width = width; output.width = width;
output.height = height; output.height = height;
} }
output.name = info.name; if let Some(name) = info.name {
output.name = name;
}
// XXX re-create surface? // XXX re-create surface?
} }
} }
@ -300,6 +319,7 @@ impl Application for App {
is_active, is_active,
}); });
} }
self.update_capture_filter();
} }
wayland::Event::NewToplevel(handle, info) => { wayland::Event::NewToplevel(handle, info) => {
println!("New toplevel: {info:?}"); println!("New toplevel: {info:?}");

View file

@ -40,15 +40,16 @@ impl std::hash::Hash for CaptureSource {
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct CaptureFilter { pub struct CaptureFilter {
pub workspaces_on_outputs: Vec<wl_output::WlOutput>, // TODO: Use `WlOutput` when one Wayland connection is used
pub workspaces_on_outputs: Vec<String>,
pub toplevels_on_workspaces: Vec<zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1>, pub toplevels_on_workspaces: Vec<zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1>,
} }
pub struct Capture { pub struct Capture {
pub buffer: Mutex<Option<Buffer>>, pub buffer: Mutex<Option<Buffer>>,
pub source: CaptureSource, pub source: CaptureSource,
pub first_frame: AtomicBool, first_frame: AtomicBool,
pub cancelled: AtomicBool, running: AtomicBool,
} }
impl Capture { impl Capture {
@ -57,7 +58,7 @@ impl Capture {
buffer: Mutex::new(None), buffer: Mutex::new(None),
source, source,
first_frame: AtomicBool::new(true), first_frame: AtomicBool::new(true),
cancelled: AtomicBool::new(false), running: AtomicBool::new(false),
} }
} }
@ -67,8 +68,17 @@ impl Capture {
Some(&session.data::<SessionData>()?.capture) Some(&session.data::<SessionData>()?.capture)
} }
pub fn running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
pub fn first_frame(&self) -> bool {
self.first_frame.load(Ordering::SeqCst)
}
pub fn cancel(&self) { pub fn cancel(&self) {
self.cancelled.store(true, Ordering::SeqCst); self.running.store(false, Ordering::SeqCst);
*self.buffer.lock().unwrap() = None;
} }
pub fn capture( pub fn capture(
@ -76,6 +86,10 @@ impl Capture {
manager: &zcosmic_screencopy_manager_v1::ZcosmicScreencopyManagerV1, manager: &zcosmic_screencopy_manager_v1::ZcosmicScreencopyManagerV1,
qh: &QueueHandle<AppData>, qh: &QueueHandle<AppData>,
) { ) {
// Mark as running. If already running, this is not the first frame.
let already_running = self.running.swap(true, Ordering::SeqCst);
self.first_frame.store(!already_running, Ordering::SeqCst);
let udata = SessionData { let udata = SessionData {
session_data: Default::default(), session_data: Default::default(),
capture: self.clone(), capture: self.clone(),

View file

@ -58,7 +58,7 @@ pub enum Event {
ToplevelManager(zcosmic_toplevel_manager_v1::ZcosmicToplevelManagerV1), ToplevelManager(zcosmic_toplevel_manager_v1::ZcosmicToplevelManagerV1),
WorkspaceManager(zcosmic_workspace_manager_v1::ZcosmicWorkspaceManagerV1), WorkspaceManager(zcosmic_workspace_manager_v1::ZcosmicWorkspaceManagerV1),
// XXX Output name rather than `WlOutput` // XXX Output name rather than `WlOutput`
Workspaces(Vec<(Option<String>, cctk::workspace::Workspace)>), Workspaces(Vec<(String, cctk::workspace::Workspace)>),
WorkspaceCapture( WorkspaceCapture(
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
image::Handle, image::Handle,
@ -116,16 +116,52 @@ impl AppData {
} }
} }
fn invalidate_capture_filter(&mut self) { fn matches_capture_filter(&self, source: &CaptureSource) -> bool {
//for i in self.captures match source {
// XXX drain filter CaptureSource::Toplevel(toplevel) => {
// TODO cancel captures if needed, enable capture let info = self.toplevel_info_state.info(toplevel).unwrap();
if let Some(workspace) = &info.workspace {
self.capture_filter
.toplevels_on_workspaces
.contains(workspace)
} else {
false
}
}
CaptureSource::Workspace(_, output) => {
if let Some(name) = &self.output_state.info(&output).unwrap().name {
self.capture_filter.workspaces_on_outputs.contains(name)
} else {
false
}
}
}
}
fn invalidate_capture_filter(&self) {
for (source, capture) in self.captures.borrow_mut().iter_mut() {
let matches = self.matches_capture_filter(source);
let running = capture.running();
if running && !matches {
capture.cancel();
} else if !running & matches {
capture.capture(&self.screencopy_state.screencopy_manager, &self.qh);
}
}
} }
fn add_capture_source(&self, source: CaptureSource) { fn add_capture_source(&self, source: CaptureSource) {
let capture = Arc::new(Capture::new(source.clone())); self.captures
capture.capture(&self.screencopy_state.screencopy_manager, &self.qh); .borrow_mut()
self.captures.borrow_mut().insert(source, capture); .entry(source.clone())
.or_insert_with(|| {
let matches = self.matches_capture_filter(&source);
let capture = Arc::new(Capture::new(source));
if matches {
capture.capture(&self.screencopy_state.screencopy_manager, &self.qh);
}
capture
});
} }
fn remove_capture_source(&self, source: CaptureSource) { fn remove_capture_source(&self, source: CaptureSource) {

View file

@ -4,8 +4,6 @@ use cctk::{
wayland_client::{protocol::wl_shm, Connection, QueueHandle, WEnum}, wayland_client::{protocol::wl_shm, Connection, QueueHandle, WEnum},
}; };
use std::sync::atomic::Ordering;
use super::{AppData, Buffer, Capture, CaptureSource, Event}; use super::{AppData, Buffer, Capture, CaptureSource, Event};
impl ScreencopyHandler for AppData { impl ScreencopyHandler for AppData {
@ -21,7 +19,7 @@ impl ScreencopyHandler for AppData {
buffer_infos: &[BufferInfo], buffer_infos: &[BufferInfo],
) { ) {
let capture = Capture::for_session(session).unwrap(); let capture = Capture::for_session(session).unwrap();
if capture.cancelled.load(Ordering::SeqCst) { if !capture.running() {
session.destroy(); session.destroy();
return; return;
} }
@ -51,7 +49,7 @@ impl ScreencopyHandler for AppData {
let buffer = buffer.as_ref().unwrap(); let buffer = buffer.as_ref().unwrap();
session.attach_buffer(&buffer.buffer, None, 0); // XXX age? session.attach_buffer(&buffer.buffer, None, 0); // XXX age?
if capture.first_frame.load(Ordering::SeqCst) { if capture.first_frame() {
session.commit(zcosmic_screencopy_session_v1::Options::empty()); session.commit(zcosmic_screencopy_session_v1::Options::empty());
} else { } else {
session.commit(zcosmic_screencopy_session_v1::Options::OnDamage); session.commit(zcosmic_screencopy_session_v1::Options::OnDamage);
@ -66,7 +64,7 @@ impl ScreencopyHandler for AppData {
session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1,
) { ) {
let capture = Capture::for_session(session).unwrap(); let capture = Capture::for_session(session).unwrap();
if capture.cancelled.load(Ordering::SeqCst) { if !capture.running() {
session.destroy(); session.destroy();
return; return;
} }
@ -83,7 +81,6 @@ impl ScreencopyHandler for AppData {
session.destroy(); session.destroy();
// Capture again on damage // Capture again on damage
capture.first_frame.store(false, Ordering::SeqCst);
capture.capture(&self.screencopy_state.screencopy_manager, &self.qh); capture.capture(&self.screencopy_state.screencopy_manager, &self.qh);
} }

View file

@ -14,17 +14,20 @@ impl WorkspaceHandler for AppData {
let mut workspaces = Vec::new(); let mut workspaces = Vec::new();
// XXX remove capture source for removed workspaces // XXX remove capture source for removed workspaces
// Handle move to another output
for group in self.workspace_state.workspace_groups() { for group in self.workspace_state.workspace_groups() {
for workspace in &group.workspaces { for workspace in &group.workspaces {
if let Some(output) = group.output.as_ref() { if let Some(output) = group.output.as_ref() {
let output_name = self.output_names.get(&output.id()).unwrap().clone(); if let Some(output_name) = self.output_names.get(&output.id()).unwrap().clone()
workspaces.push((output_name, workspace.clone())); {
workspaces.push((output_name, workspace.clone()));
self.add_capture_source(CaptureSource::Workspace( self.add_capture_source(CaptureSource::Workspace(
workspace.handle.clone(), workspace.handle.clone(),
output.clone(), output.clone(),
)); ));
}
} }
} }
} }