Use updated screencopy abstraction

Requires https://github.com/pop-os/cosmic-protocols/pull/46.
This commit is contained in:
Ian Douglas Scott 2025-01-29 15:09:07 -08:00 committed by Ian Douglas Scott
parent ba52d69421
commit a977667ac5
10 changed files with 453 additions and 532 deletions

806
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -58,7 +58,7 @@ pub struct CaptureImage {
pub enum Event { pub enum Event {
CmdSender(calloop::channel::Sender<Cmd>), CmdSender(calloop::channel::Sender<Cmd>),
Workspaces(Vec<(HashSet<wl_output::WlOutput>, Workspace)>), Workspaces(Vec<(HashSet<wl_output::WlOutput>, Workspace)>),
WorkspaceCapture(ZcosmicWorkspaceHandleV1, wl_output::WlOutput, CaptureImage), WorkspaceCapture(ZcosmicWorkspaceHandleV1, CaptureImage),
NewToplevel(ZcosmicToplevelHandleV1, ToplevelInfo), NewToplevel(ZcosmicToplevelHandleV1, ToplevelInfo),
UpdateToplevel(ZcosmicToplevelHandleV1, ToplevelInfo), UpdateToplevel(ZcosmicToplevelHandleV1, ToplevelInfo),
CloseToplevel(ZcosmicToplevelHandleV1), CloseToplevel(ZcosmicToplevelHandleV1),

View file

@ -31,7 +31,7 @@ pub struct Buffer {
} }
impl AppData { impl AppData {
fn create_shm_buffer(&self, format: u32, (width, height): (u32, u32)) -> Buffer { fn create_shm_buffer(&self, format: wl_shm::Format, (width, height): (u32, u32)) -> Buffer {
let fd = utils::create_memfile().unwrap(); // XXX? let fd = utils::create_memfile().unwrap(); // XXX?
rustix::fs::ftruncate(&fd, width as u64 * height as u64 * 4).unwrap(); rustix::fs::ftruncate(&fd, width as u64 * height as u64 * 4).unwrap();
@ -42,7 +42,6 @@ impl AppData {
(), (),
); );
let format = wl_shm::Format::try_from(format).unwrap();
let buffer = pool.create_buffer( let buffer = pool.create_buffer(
0, 0,
width as i32, width as i32,
@ -179,11 +178,15 @@ impl AppData {
pub fn create_buffer(&self, formats: &Formats) -> Buffer { pub fn create_buffer(&self, formats: &Formats) -> Buffer {
// XXX Handle other formats? // XXX Handle other formats?
let format = u32::from(wl_shm::Format::Abgr8888); let format = wl_shm::Format::Abgr8888;
#[cfg(not(feature = "force-shm-screencopy"))] #[cfg(not(feature = "force-shm-screencopy"))]
if let Some((_, modifiers)) = formats.dmabuf_formats.iter().find(|(f, _)| *f == format) { if let Some((_, modifiers)) = formats
match self.create_gbm_buffer(format, modifiers, formats.buffer_size, false) { .dmabuf_formats
.iter()
.find(|(f, _)| *f == u32::from(format))
{
match self.create_gbm_buffer(u32::from(format), modifiers, formats.buffer_size, false) {
Ok(Some(buffer)) => { Ok(Some(buffer)) => {
return buffer; return buffer;
} }

View file

@ -1,11 +1,6 @@
use cctk::{ use cctk::{
cosmic_protocols::{ screencopy::{CaptureSession, CaptureSource, ScreencopyState},
screencopy::v2::client::zcosmic_screencopy_session_v2, wayland_client::QueueHandle,
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
workspace::v1::client::zcosmic_workspace_handle_v1,
},
screencopy::ScreencopyState,
wayland_client::{protocol::wl_output, Proxy, QueueHandle},
}; };
use cosmic::cctk; use cosmic::cctk;
@ -13,15 +8,6 @@ use std::sync::{Arc, Mutex};
use super::{AppData, ScreencopySession, SessionData}; use super::{AppData, ScreencopySession, SessionData};
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum CaptureSource {
Toplevel(zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1),
Workspace(
zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
wl_output::WlOutput,
),
}
pub struct Capture { pub struct Capture {
pub source: CaptureSource, pub source: CaptureSource,
pub session: Mutex<Option<ScreencopySession>>, pub session: Mutex<Option<ScreencopySession>>,
@ -37,9 +23,7 @@ impl Capture {
// Returns `None` if capture is destroyed // Returns `None` if capture is destroyed
// (or if `session` wasn't created with `SessionData`) // (or if `session` wasn't created with `SessionData`)
pub fn for_session( pub fn for_session(session: &CaptureSession) -> Option<Arc<Self>> {
session: &zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2,
) -> Option<Arc<Self>> {
session.data::<SessionData>()?.capture.upgrade() session.data::<SessionData>()?.capture.upgrade()
} }

View file

@ -3,7 +3,7 @@
use calloop_wayland_source::WaylandSource; use calloop_wayland_source::WaylandSource;
use cctk::{ use cctk::{
screencopy::ScreencopyState, screencopy::{CaptureSource, ScreencopyState},
sctk::{ sctk::{
self, self,
dmabuf::{DmabufFeedback, DmabufState}, dmabuf::{DmabufFeedback, DmabufState},
@ -31,7 +31,7 @@ use std::{cell::RefCell, collections::HashMap, fs, path::PathBuf, sync::Arc, thr
mod buffer; mod buffer;
use buffer::Buffer; use buffer::Buffer;
mod capture; mod capture;
use capture::{Capture, CaptureSource}; use capture::Capture;
mod dmabuf; mod dmabuf;
mod screencopy; mod screencopy;
use screencopy::{ScreencopySession, SessionData}; use screencopy::{ScreencopySession, SessionData};
@ -104,7 +104,7 @@ impl AppData {
fn matches_capture_filter(&self, source: &CaptureSource) -> bool { fn matches_capture_filter(&self, source: &CaptureSource) -> bool {
match source { match source {
CaptureSource::Toplevel(toplevel) => { CaptureSource::CosmicToplevel(toplevel) => {
let info = self.toplevel_info_state.info(toplevel).unwrap(); let info = self.toplevel_info_state.info(toplevel).unwrap();
info.workspace.iter().any(|workspace| { info.workspace.iter().any(|workspace| {
self.capture_filter self.capture_filter
@ -112,9 +112,19 @@ impl AppData {
.contains(workspace) .contains(workspace)
}) })
} }
CaptureSource::Workspace(_, output) => { CaptureSource::CosmicWorkspace(workspace) => self
self.capture_filter.workspaces_on_outputs.contains(output) .workspace_state
} .workspace_groups()
.iter()
.find(|g| g.workspaces.iter().any(|w| w.handle == *workspace))
.map_or(false, |group| {
self.capture_filter
.workspaces_on_outputs
.iter()
.any(|o| group.outputs.contains(o))
}),
CaptureSource::Toplevel(_) => false,
CaptureSource::Output(_) => false,
} }
} }

View file

@ -1,15 +1,12 @@
use cosmic::{ use cosmic::{
cctk::{ cctk::{
self, self,
cosmic_protocols::screencopy::v2::client::{
zcosmic_screencopy_frame_v2, zcosmic_screencopy_manager_v2,
zcosmic_screencopy_session_v2,
},
screencopy::{ screencopy::{
capture, Formats, Frame, ScreencopyFrameData, ScreencopyFrameDataExt, CaptureFrame, CaptureOptions, CaptureSession, CaptureSource, FailureReason, Formats,
ScreencopyHandler, ScreencopySessionData, ScreencopySessionDataExt, ScreencopyState, Frame, ScreencopyFrameData, ScreencopyFrameDataExt, ScreencopyHandler,
ScreencopySessionData, ScreencopySessionDataExt, ScreencopyState,
}, },
wayland_client::{Connection, Proxy, QueueHandle, WEnum}, wayland_client::{Connection, QueueHandle, WEnum},
}, },
iced_winit::platform_specific::wayland::subsurface_widget::{ iced_winit::platform_specific::wayland::subsurface_widget::{
SubsurfaceBuffer, SubsurfaceBufferRelease, SubsurfaceBuffer, SubsurfaceBufferRelease,
@ -20,12 +17,12 @@ use std::{
sync::{Arc, Weak}, sync::{Arc, Weak},
}; };
use super::{AppData, Buffer, Capture, CaptureImage, CaptureSource, Event}; use super::{AppData, Buffer, Capture, CaptureImage, Event};
pub struct ScreencopySession { pub struct ScreencopySession {
// swapchain buffers // swapchain buffers
buffers: Option<[Buffer; 2]>, buffers: Option<[Buffer; 2]>,
session: zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, session: CaptureSession,
// Future signaled when buffer is signaled. // Future signaled when buffer is signaled.
// if triple buffer is used, will need more than one. // if triple buffer is used, will need more than one.
release: Option<SubsurfaceBufferRelease>, release: Option<SubsurfaceBufferRelease>,
@ -37,32 +34,15 @@ impl ScreencopySession {
screencopy_state: &ScreencopyState, screencopy_state: &ScreencopyState,
qh: &QueueHandle<AppData>, qh: &QueueHandle<AppData>,
) -> Self { ) -> Self {
let image_source = match &capture.source {
CaptureSource::Toplevel(toplevel) => screencopy_state
.toplevel_source_manager
.as_ref()
.unwrap()
.create_source(toplevel, qh, ()),
CaptureSource::Workspace(workspace, _output) => screencopy_state
.workspace_source_manager
.as_ref()
.unwrap()
.create_source(workspace, qh, ()),
};
let udata = SessionData { let udata = SessionData {
session_data: Default::default(), session_data: Default::default(),
capture: Arc::downgrade(capture), capture: Arc::downgrade(capture),
}; };
let session = screencopy_state.screencopy_manager.create_session( let session = screencopy_state
&image_source, .capturer()
zcosmic_screencopy_manager_v2::Options::empty(), .create_session(&capture.source, CaptureOptions::empty(), qh, udata)
qh, .unwrap();
udata,
);
image_source.destroy();
Self { Self {
buffers: None, buffers: None,
@ -73,7 +53,7 @@ impl ScreencopySession {
fn attach_buffer_and_commit( fn attach_buffer_and_commit(
&mut self, &mut self,
_capture: &Capture, capture: &Arc<Capture>,
conn: &Connection, conn: &Connection,
qh: &QueueHandle<AppData>, qh: &QueueHandle<AppData>,
) { ) {
@ -84,26 +64,19 @@ impl ScreencopySession {
// TODO // TODO
// let node = back.node().and_then(|x| x.to_str().map(|x| x.to_string())); // let node = back.node().and_then(|x| x.to_str().map(|x| x.to_string()));
capture( self.session.capture(
&self.session,
&back.buffer, &back.buffer,
&[], &[],
qh, qh,
FrameData { FrameData {
frame_data: Default::default(), frame_data: Default::default(),
session: self.session.clone(), capture: Arc::downgrade(&capture),
}, },
); );
conn.flush().unwrap(); conn.flush().unwrap();
} }
} }
impl Drop for ScreencopySession {
fn drop(&mut self) {
self.session.destroy();
}
}
pub struct SessionData { pub struct SessionData {
session_data: ScreencopySessionData, session_data: ScreencopySessionData,
// Weak reference so session can be destroyed when all strong references // Weak reference so session can be destroyed when all strong references
@ -119,7 +92,7 @@ impl ScreencopySessionDataExt for SessionData {
struct FrameData { struct FrameData {
frame_data: ScreencopyFrameData, frame_data: ScreencopyFrameData,
session: zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, capture: Weak<Capture>,
} }
impl ScreencopyFrameDataExt for FrameData { impl ScreencopyFrameDataExt for FrameData {
@ -137,7 +110,7 @@ impl ScreencopyHandler for AppData {
&mut self, &mut self,
conn: &Connection, conn: &Connection,
_qh: &QueueHandle<Self>, _qh: &QueueHandle<Self>,
session: &zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, session: &CaptureSession,
formats: &Formats, formats: &Formats,
) { ) {
let Some(capture) = Capture::for_session(session) else { let Some(capture) = Capture::for_session(session) else {
@ -161,11 +134,11 @@ impl ScreencopyHandler for AppData {
&mut self, &mut self,
conn: &Connection, conn: &Connection,
qh: &QueueHandle<Self>, qh: &QueueHandle<Self>,
screencopy_frame: &zcosmic_screencopy_frame_v2::ZcosmicScreencopyFrameV2, capture_frame: &CaptureFrame,
frame: Frame, frame: Frame,
) { ) {
let session = &screencopy_frame.data::<FrameData>().unwrap().session; let capture = &capture_frame.data::<FrameData>().unwrap().capture;
let Some(capture) = Capture::for_session(session) else { let Some(capture) = capture.upgrade() else {
return; return;
}; };
let mut session = capture.session.lock().unwrap(); let mut session = capture.session.lock().unwrap();
@ -219,15 +192,14 @@ impl ScreencopyHandler for AppData {
), ),
}; };
match &capture.source { match &capture.source {
CaptureSource::Toplevel(toplevel) => { CaptureSource::CosmicToplevel(toplevel) => {
self.send_event(Event::ToplevelCapture(toplevel.clone(), image)) self.send_event(Event::ToplevelCapture(toplevel.clone(), image))
} }
CaptureSource::Workspace(workspace, output) => { CaptureSource::CosmicWorkspace(workspace) => {
self.send_event(Event::WorkspaceCapture( self.send_event(Event::WorkspaceCapture(workspace.clone(), image));
workspace.clone(), }
output.clone(), CaptureSource::Output(_) | CaptureSource::Toplevel(_) => {
image, unreachable!()
));
} }
}; };
} }
@ -236,23 +208,18 @@ impl ScreencopyHandler for AppData {
&mut self, &mut self,
_conn: &Connection, _conn: &Connection,
_qh: &QueueHandle<Self>, _qh: &QueueHandle<Self>,
screencopy_frame: &zcosmic_screencopy_frame_v2::ZcosmicScreencopyFrameV2, capture_frame: &CaptureFrame,
reason: WEnum<zcosmic_screencopy_frame_v2::FailureReason>, reason: WEnum<FailureReason>,
) { ) {
// TODO // TODO
log::error!("Screencopy failed: {:?}", reason); log::error!("Screencopy failed: {:?}", reason);
let session = &screencopy_frame.data::<FrameData>().unwrap().session; let capture = &capture_frame.data::<FrameData>().unwrap().capture;
if let Some(capture) = Capture::for_session(session) { if let Some(capture) = capture.upgrade() {
capture.stop(); capture.stop();
} }
} }
fn stopped( fn stopped(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, session: &CaptureSession) {
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
session: &zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2,
) {
// TODO // TODO
if let Some(capture) = Capture::for_session(session) { if let Some(capture) = Capture::for_session(session) {
capture.stop(); capture.stop();

View file

@ -26,7 +26,7 @@ impl ToplevelInfoHandler for AppData {
let info = self.toplevel_info_state.info(toplevel).unwrap(); let info = self.toplevel_info_state.info(toplevel).unwrap();
self.send_event(Event::NewToplevel(toplevel.clone(), info.clone())); self.send_event(Event::NewToplevel(toplevel.clone(), info.clone()));
self.add_capture_source(CaptureSource::Toplevel(toplevel.clone())); self.add_capture_source(CaptureSource::CosmicToplevel(toplevel.clone()));
} }
fn update_toplevel( fn update_toplevel(
@ -47,7 +47,7 @@ impl ToplevelInfoHandler for AppData {
) { ) {
self.send_event(Event::CloseToplevel(toplevel.clone())); self.send_event(Event::CloseToplevel(toplevel.clone()));
self.remove_capture_source(CaptureSource::Toplevel(toplevel.clone())); self.remove_capture_source(CaptureSource::CosmicToplevel(toplevel.clone()));
} }
} }

View file

@ -18,12 +18,8 @@ impl WorkspaceHandler for AppData {
for workspace in &group.workspaces { for workspace in &group.workspaces {
workspaces.push((group.outputs.iter().cloned().collect(), workspace.clone())); workspaces.push((group.outputs.iter().cloned().collect(), workspace.clone()));
for output in &group.outputs { // TODO one capture per output on workspace?
self.add_capture_source(CaptureSource::Workspace( self.add_capture_source(CaptureSource::CosmicWorkspace(workspace.handle.clone()));
workspace.handle.clone(),
output.clone(),
));
}
} }
} }

View file

@ -104,7 +104,8 @@ enum Msg {
#[derive(Debug)] #[derive(Debug)]
struct Workspace { struct Workspace {
name: String, name: String,
img_for_output: HashMap<wl_output::WlOutput, backend::CaptureImage>, // img_for_output: HashMap<wl_output::WlOutput, backend::CaptureImage>,
img: Option<backend::CaptureImage>,
handle: ZcosmicWorkspaceHandleV1, handle: ZcosmicWorkspaceHandleV1,
outputs: HashSet<wl_output::WlOutput>, outputs: HashSet<wl_output::WlOutput>,
is_active: bool, is_active: bool,
@ -373,17 +374,17 @@ impl Application for App {
// XXX efficiency // XXX efficiency
#[allow(clippy::mutable_key_type)] #[allow(clippy::mutable_key_type)]
let img_for_output = old_workspaces let img = old_workspaces
.iter() .iter()
.find(|i| i.handle == workspace.handle) .find(|i| i.handle == workspace.handle)
.map(|i| i.img_for_output.clone()) .map(|i| i.img.clone())
.unwrap_or_default(); .unwrap_or_default();
self.workspaces.push(Workspace { self.workspaces.push(Workspace {
name: workspace.name, name: workspace.name,
handle: workspace.handle, handle: workspace.handle,
outputs, outputs,
img_for_output, img,
is_active, is_active,
}); });
} }
@ -432,10 +433,10 @@ impl Application for App {
self.toplevels.remove(idx); self.toplevels.remove(idx);
} }
} }
backend::Event::WorkspaceCapture(handle, output_name, image) => { backend::Event::WorkspaceCapture(handle, image) => {
//println!("Workspace capture"); //println!("Workspace capture");
if let Some(workspace) = self.workspace_for_handle_mut(&handle) { if let Some(workspace) = self.workspace_for_handle_mut(&handle) {
workspace.img_for_output.insert(output_name, image); workspace.img = Some(image);
} }
} }
backend::Event::ToplevelCapture(handle, image) => { backend::Event::ToplevelCapture(handle, image) => {

View file

@ -137,10 +137,10 @@ fn workspace_item_appearance(
fn workspace_item<'a>( fn workspace_item<'a>(
workspace: &'a Workspace, workspace: &'a Workspace,
output: &wl_output::WlOutput, _output: &wl_output::WlOutput,
is_drop_target: bool, is_drop_target: bool,
) -> cosmic::Element<'a, Msg> { ) -> cosmic::Element<'a, Msg> {
let image = capture_image(workspace.img_for_output.get(output), 1.0); let image = capture_image(workspace.img.as_ref(), 1.0);
let is_active = workspace.is_active; let is_active = workspace.is_active;
// TODO editable name? // TODO editable name?
widget::button::custom( widget::button::custom(