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 {
CmdSender(calloop::channel::Sender<Cmd>),
Workspaces(Vec<(HashSet<wl_output::WlOutput>, Workspace)>),
WorkspaceCapture(ZcosmicWorkspaceHandleV1, wl_output::WlOutput, CaptureImage),
WorkspaceCapture(ZcosmicWorkspaceHandleV1, CaptureImage),
NewToplevel(ZcosmicToplevelHandleV1, ToplevelInfo),
UpdateToplevel(ZcosmicToplevelHandleV1, ToplevelInfo),
CloseToplevel(ZcosmicToplevelHandleV1),

View file

@ -31,7 +31,7 @@ pub struct Buffer {
}
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?
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(
0,
width as i32,
@ -179,11 +178,15 @@ impl AppData {
pub fn create_buffer(&self, formats: &Formats) -> Buffer {
// XXX Handle other formats?
let format = u32::from(wl_shm::Format::Abgr8888);
let format = wl_shm::Format::Abgr8888;
#[cfg(not(feature = "force-shm-screencopy"))]
if let Some((_, modifiers)) = formats.dmabuf_formats.iter().find(|(f, _)| *f == format) {
match self.create_gbm_buffer(format, modifiers, formats.buffer_size, false) {
if let Some((_, modifiers)) = formats
.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)) => {
return buffer;
}

View file

@ -1,11 +1,6 @@
use cctk::{
cosmic_protocols::{
screencopy::v2::client::zcosmic_screencopy_session_v2,
toplevel_info::v1::client::zcosmic_toplevel_handle_v1,
workspace::v1::client::zcosmic_workspace_handle_v1,
},
screencopy::ScreencopyState,
wayland_client::{protocol::wl_output, Proxy, QueueHandle},
screencopy::{CaptureSession, CaptureSource, ScreencopyState},
wayland_client::QueueHandle,
};
use cosmic::cctk;
@ -13,15 +8,6 @@ use std::sync::{Arc, Mutex};
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 source: CaptureSource,
pub session: Mutex<Option<ScreencopySession>>,
@ -37,9 +23,7 @@ impl Capture {
// Returns `None` if capture is destroyed
// (or if `session` wasn't created with `SessionData`)
pub fn for_session(
session: &zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2,
) -> Option<Arc<Self>> {
pub fn for_session(session: &CaptureSession) -> Option<Arc<Self>> {
session.data::<SessionData>()?.capture.upgrade()
}

View file

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

View file

@ -26,7 +26,7 @@ impl ToplevelInfoHandler for AppData {
let info = self.toplevel_info_state.info(toplevel).unwrap();
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(
@ -47,7 +47,7 @@ impl ToplevelInfoHandler for AppData {
) {
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 {
workspaces.push((group.outputs.iter().cloned().collect(), workspace.clone()));
for output in &group.outputs {
self.add_capture_source(CaptureSource::Workspace(
workspace.handle.clone(),
output.clone(),
));
}
// TODO one capture per output on workspace?
self.add_capture_source(CaptureSource::CosmicWorkspace(workspace.handle.clone()));
}
}

View file

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

View file

@ -137,10 +137,10 @@ fn workspace_item_appearance(
fn workspace_item<'a>(
workspace: &'a Workspace,
output: &wl_output::WlOutput,
_output: &wl_output::WlOutput,
is_drop_target: bool,
) -> 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;
// TODO editable name?
widget::button::custom(