From 39f51c819128887273924c84ff3aceaebb6e4c97 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Mon, 18 Mar 2024 17:57:18 -0700 Subject: [PATCH] WIP cosmic-screencopy-v2 --- Cargo.lock | 5 +- Cargo.toml | 4 + src/wayland/buffer.rs | 82 +++++++++----------- src/wayland/capture.rs | 13 ++-- src/wayland/mod.rs | 4 +- src/wayland/screencopy.rs | 153 ++++++++++++++++++++++++-------------- 6 files changed, 149 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad675b6..941ab5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,9 +937,10 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=e65fa5e#e65fa5e2bb47e51656221657049bd3f88ae9dae5" +source = "git+https://github.com/pop-os/cosmic-protocols//?branch=ext-screencopy#bf41b27b83abe969d7ec6c2e891c035c310a6021" dependencies = [ "cosmic-protocols", + "libc", "smithay-client-toolkit", "wayland-client", ] @@ -983,7 +984,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols?rev=e65fa5e#e65fa5e2bb47e51656221657049bd3f88ae9dae5" +source = "git+https://github.com/pop-os/cosmic-protocols//?branch=ext-screencopy#bf41b27b83abe969d7ec6c2e891c035c310a6021" dependencies = [ "bitflags 2.4.2", "wayland-backend", diff --git a/Cargo.toml b/Cargo.toml index 7dcd272..1720a83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,3 +44,7 @@ opt-level = 1 # [patch."https://github.com/pop-os/libcosmic"] # libcosmic = { path = "../libcosmic" } # cosmic-config = { path = "../libcosmic/cosmic-config" } + +[patch."https://github.com/pop-os/cosmic-protocols"] +cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "ext-screencopy" } +cosmic-client-toolkit = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "ext-screencopy" } diff --git a/src/wayland/buffer.rs b/src/wayland/buffer.rs index ff0af03..8af08a5 100644 --- a/src/wayland/buffer.rs +++ b/src/wayland/buffer.rs @@ -1,6 +1,5 @@ use cctk::{ - cosmic_protocols::screencopy::v1::client::zcosmic_screencopy_session_v1::BufferType, - screencopy::BufferInfo, + screencopy::Formats, wayland_client::{ protocol::{wl_buffer, wl_shm, wl_shm_pool}, Connection, Dispatch, QueueHandle, WEnum, @@ -68,34 +67,30 @@ fn create_memfile() -> rustix::io::Result { pub struct Buffer { pub backing: Arc, pub buffer: wl_buffer::WlBuffer, - pub buffer_info: BufferInfo, node: Option, + pub size: (u32, u32), } impl AppData { - fn create_shm_buffer(&self, buffer_info: &BufferInfo) -> Buffer { + fn create_shm_buffer(&self, format: u32, (width, height): (u32, u32)) -> Buffer { let fd = create_memfile().unwrap(); // XXX? - rustix::fs::ftruncate(&fd, buffer_info.stride as u64 * buffer_info.height as u64).unwrap(); + rustix::fs::ftruncate(&fd, width as u64 * height as u64 * 4).unwrap(); let pool = self.shm_state.wl_shm().create_pool( fd.as_fd(), - buffer_info.stride as i32 * buffer_info.height as i32, + width as i32 * height as i32 * 4, &self.qh, (), ); pool.destroy(); - // XXX - let fd = rustix::fs::memfd_create("shm-buffer", rustix::fs::MemfdFlags::CLOEXEC).unwrap(); - rustix::fs::ftruncate(&fd, buffer_info.stride as u64 * buffer_info.height as u64).unwrap(); - - let format = wl_shm::Format::try_from(buffer_info.format).unwrap(); + let format = wl_shm::Format::try_from(format).unwrap(); let buffer = pool.create_buffer( 0, - buffer_info.width as i32, - buffer_info.height as i32, - buffer_info.stride as i32, + width as i32, + height as i32, + width as i32 * 4, format, &self.qh, (), @@ -106,23 +101,24 @@ impl AppData { Shmbuf { fd, offset: 0, - width: buffer_info.width as i32, - height: buffer_info.height as i32, - stride: buffer_info.stride as i32, + width: width as i32, + height: height as i32, + stride: width as i32 * 4, format, } .into(), ), buffer, - buffer_info: buffer_info.clone(), node: None, + size: (width, height), } } #[allow(dead_code)] fn create_gbm_buffer( &self, - buffer_info: &BufferInfo, + format: u32, + (width, height): (u32, u32), needs_linear: bool, ) -> anyhow::Result> { let (Some((node, gbm)), Some(feedback)) = @@ -138,7 +134,7 @@ impl AppData { .flat_map(|x| &x.formats) .filter_map(|x| formats.get(*x as usize)) .filter(|x| { - x.format == buffer_info.format + x.format == format && (!needs_linear || x.modifier == u64::from(gbm::Modifier::Linear)) }) .filter_map(|x| gbm::Modifier::try_from(x.modifier).ok()) @@ -147,21 +143,21 @@ impl AppData { if modifiers.is_empty() { return Ok(None); }; - let format = gbm::Format::try_from(buffer_info.format)?; + let gbm_format = gbm::Format::try_from(format)?; //dbg!(format, modifiers); let bo = if !modifiers.iter().all(|x| *x == gbm::Modifier::Invalid) { gbm.create_buffer_object_with_modifiers::<()>( - buffer_info.width, - buffer_info.height, - format, + width, + height, + gbm_format, modifiers.iter().copied(), )? } else { // TODO make sure this isn't used across different GPUs gbm.create_buffer_object::<()>( - buffer_info.width, - buffer_info.height, - format, + width, + height, + gbm_format, gbm::BufferObjectFlags::empty(), )? }; @@ -190,9 +186,9 @@ impl AppData { } let buffer = params .create_immed( - buffer_info.width as i32, - buffer_info.height as i32, - buffer_info.format, + width as i32, + height as i32, + format, zwp_linux_buffer_params_v1::Flags::empty(), &self.qh, ) @@ -201,29 +197,26 @@ impl AppData { Ok(Some(Buffer { backing: Arc::new( Dmabuf { - width: buffer_info.width as i32, - height: buffer_info.height as i32, + width: width as i32, + height: height as i32, planes, - format: buffer_info.format, + format, modifier: modifier.into(), } .into(), ), buffer, - buffer_info: buffer_info.clone(), node: Some(node.clone()), + size: (width, height), })) } - pub fn create_buffer(&self, buffer_infos: &[BufferInfo]) -> Buffer { + pub fn create_buffer(&self, formats: &Formats) -> Buffer { // XXX Handle other formats? - let format = wl_shm::Format::Abgr8888.into(); + let format = u32::from(wl_shm::Format::Abgr8888); - if let Some(buffer_info) = buffer_infos - .iter() - .find(|x| x.type_ == WEnum::Value(BufferType::Dmabuf) && x.format == format) - { - match self.create_gbm_buffer(buffer_info, false) { + if let Some((_, modifiers)) = formats.dmabuf_formats.iter().find(|(f, _)| *f == format) { + match self.create_gbm_buffer(format, formats.buffer_size, false) { Ok(Some(buffer)) => { return buffer; } @@ -234,11 +227,8 @@ impl AppData { // Fallback to shm buffer // Assume format is already known to be valid - let buffer_info = buffer_infos - .iter() - .find(|x| x.type_ == WEnum::Value(BufferType::WlShm) && x.format == format) - .unwrap(); - self.create_shm_buffer(buffer_info) + assert!(formats.shm_formats.contains(&format)); + self.create_shm_buffer(format, formats.buffer_size) } } diff --git a/src/wayland/capture.rs b/src/wayland/capture.rs index 5a45b64..4edf7b3 100644 --- a/src/wayland/capture.rs +++ b/src/wayland/capture.rs @@ -1,9 +1,10 @@ use cctk::{ cosmic_protocols::{ - screencopy::v1::client::{zcosmic_screencopy_manager_v1, zcosmic_screencopy_session_v1}, + screencopy::v2::client::{zcosmic_screencopy_manager_v2, 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}, }; use cosmic::cctk; @@ -43,20 +44,16 @@ impl Capture { // Returns `None` if capture is destroyed // (or if `session` wasn't created with `SessionData`) pub fn for_session( - session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, + session: &zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, ) -> Option> { session.data::()?.capture.upgrade() } // Start capturing frames - pub fn start( - self: &Arc, - manager: &zcosmic_screencopy_manager_v1::ZcosmicScreencopyManagerV1, - qh: &QueueHandle, - ) { + pub fn start(self: &Arc, screencopy_state: &ScreencopyState, qh: &QueueHandle) { let mut session = self.session.lock().unwrap(); if session.is_none() { - *session = Some(ScreencopySession::new(self, manager, qh)); + *session = Some(ScreencopySession::new(self, screencopy_state, qh)); } } diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 8888702..6468265 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -152,7 +152,7 @@ impl AppData { for (source, capture) in self.captures.borrow_mut().iter_mut() { let matches = self.matches_capture_filter(source); if matches { - capture.start(&self.screencopy_state.screencopy_manager, &self.qh); + capture.start(&self.screencopy_state, &self.qh); } else { capture.stop(); } @@ -167,7 +167,7 @@ impl AppData { let matches = self.matches_capture_filter(&source); let capture = Capture::new(source); if matches { - capture.start(&self.screencopy_state.screencopy_manager, &self.qh); + capture.start(&self.screencopy_state, &self.qh); } capture }); diff --git a/src/wayland/screencopy.rs b/src/wayland/screencopy.rs index 2daf1cd..9f21d88 100644 --- a/src/wayland/screencopy.rs +++ b/src/wayland/screencopy.rs @@ -1,13 +1,20 @@ use cosmic::cctk::{ self, - cosmic_protocols::screencopy::v1::client::{ - zcosmic_screencopy_manager_v1, zcosmic_screencopy_session_v1, + cosmic_protocols::{ + image_source::v1::client::{ + zcosmic_toplevel_image_source_manager_v1::ZcosmicToplevelImageSourceManagerV1, + zcosmic_workspace_image_source_manager_v1::ZcosmicWorkspaceImageSourceManagerV1, + }, + screencopy::v2::client::{ + zcosmic_screencopy_frame_v2, zcosmic_screencopy_manager_v2, + zcosmic_screencopy_session_v2, + }, }, screencopy::{ - BufferInfo, ScreencopyHandler, ScreencopySessionData, ScreencopySessionDataExt, - ScreencopyState, + capture, Formats, Frame, ScreencopyFrameData, ScreencopyFrameDataExt, ScreencopyHandler, + ScreencopySessionData, ScreencopySessionDataExt, ScreencopyState, }, - wayland_client::{Connection, QueueHandle, WEnum}, + wayland_client::{Connection, Proxy, QueueHandle, WEnum}, }; use cosmic::iced_sctk::subsurface_widget::{SubsurfaceBuffer, SubsurfaceBufferRelease}; use std::{ @@ -20,8 +27,7 @@ use super::{AppData, Buffer, Capture, CaptureImage, CaptureSource, Event}; pub struct ScreencopySession { // swapchain buffers buffers: Option<[Buffer; 2]>, - session: zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, - first_frame: bool, + session: zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, // Future signaled when buffer is signaled. // if triple buffer is used, will need more than one. release: Option, @@ -30,55 +36,69 @@ pub struct ScreencopySession { impl ScreencopySession { pub fn new( capture: &Arc, - manager: &zcosmic_screencopy_manager_v1::ZcosmicScreencopyManagerV1, + screencopy_state: &ScreencopyState, qh: &QueueHandle, ) -> 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, + // output, + qh, + (), + ), + }; + let udata = SessionData { session_data: Default::default(), capture: Arc::downgrade(capture), }; - let session = match &capture.source { - CaptureSource::Toplevel(toplevel) => manager.capture_toplevel( - toplevel, - zcosmic_screencopy_manager_v1::CursorMode::Hidden, - qh, - udata, - ), - CaptureSource::Workspace(workspace, output) => manager.capture_workspace( - workspace, - output, - zcosmic_screencopy_manager_v1::CursorMode::Hidden, - qh, - udata, - ), - }; + let session = screencopy_state.screencopy_manager.create_session( + &image_source, + zcosmic_screencopy_manager_v2::Options::empty(), + qh, + udata, + ); Self { buffers: None, session, - first_frame: true, release: None, } } - fn attach_buffer_and_commit(&mut self, _capture: &Capture, conn: &Connection) { + fn attach_buffer_and_commit( + &mut self, + _capture: &Capture, + conn: &Connection, + qh: &QueueHandle, + ) { let Some(back) = self.buffers.as_ref().map(|x| &x[1]) else { return; }; - let node = back.node().and_then(|x| x.to_str().map(|x| x.to_string())); - - self.session.attach_buffer(&back.buffer, node, 0); // XXX age? - if self.first_frame { - self.session - .commit(zcosmic_screencopy_session_v1::Options::empty()); - self.first_frame = false; - } else { - // TODO Not updating properly if `Options::OnDamage` is used - self.session - .commit(zcosmic_screencopy_session_v1::Options::empty()); - } + // TODO + // let node = back.node().and_then(|x| x.to_str().map(|x| x.to_string())); + + capture( + &self.session, + &back.buffer, + &[], + qh, + FrameData { + frame_data: Default::default(), + session: self.session.clone(), + }, + ); conn.flush().unwrap(); } } @@ -102,6 +122,17 @@ impl ScreencopySessionDataExt for SessionData { } } +struct FrameData { + frame_data: ScreencopyFrameData, + session: zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, +} + +impl ScreencopyFrameDataExt for FrameData { + fn screencopy_frame_data(&self) -> &ScreencopyFrameData { + &self.frame_data + } +} + impl ScreencopyHandler for AppData { fn screencopy_state(&mut self) -> &mut ScreencopyState { &mut self.screencopy_state @@ -111,8 +142,8 @@ impl ScreencopyHandler for AppData { &mut self, conn: &Connection, _qh: &QueueHandle, - session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, - buffer_infos: &[BufferInfo], + session: &zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, + formats: &Formats, ) { let Some(capture) = Capture::for_session(session) else { return; @@ -122,22 +153,23 @@ impl ScreencopyHandler for AppData { return; }; - // Create new buffer if none, or different format - if !session.buffers.as_ref().map_or(false, |buffers| { - buffer_infos.contains(&buffers[0].buffer_info) - }) { - session.buffers = Some(array::from_fn(|_| self.create_buffer(buffer_infos))); + // Create new buffer if none + // XXX What if formats have changed? + if session.buffers.is_none() { + session.buffers = Some(array::from_fn(|_| self.create_buffer(formats))); } - session.attach_buffer_and_commit(&capture, conn); + session.attach_buffer_and_commit(&capture, conn, &self.qh); } fn ready( &mut self, conn: &Connection, - _qh: &QueueHandle, - session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, + qh: &QueueHandle, + screencopy_frame: &zcosmic_screencopy_frame_v2::ZcosmicScreencopyFrameV2, + frame: Frame, ) { + let session = &screencopy_frame.data::().unwrap().session; let Some(capture) = Capture::for_session(session) else { return; }; @@ -158,6 +190,7 @@ impl ScreencopyHandler for AppData { let capture_clone = capture.clone(); let conn = conn.clone(); let release = session.release.take(); + let qh = qh.clone(); self.scheduler .schedule(async move { if let Some(release) = release { @@ -168,7 +201,7 @@ impl ScreencopyHandler for AppData { let Some(session) = session.as_mut() else { return; }; - session.attach_buffer_and_commit(&capture_clone, &conn); + session.attach_buffer_and_commit(&capture_clone, &conn, &qh); }) .unwrap(); @@ -177,11 +210,10 @@ impl ScreencopyHandler for AppData { session.release = Some(release); // let img = unsafe { front.to_image() }; // let image = CaptureImage { img }; - let buffer_info = &front.buffer_info; let image = CaptureImage { wl_buffer: buffer, - width: buffer_info.width, - height: buffer_info.height, + width: front.size.0, + height: front.size.1, }; match &capture.source { CaptureSource::Toplevel(toplevel) => { @@ -201,15 +233,28 @@ impl ScreencopyHandler for AppData { &mut self, _conn: &Connection, _qh: &QueueHandle, - session: &zcosmic_screencopy_session_v1::ZcosmicScreencopySessionV1, - _reason: WEnum, + screencopy_frame: &zcosmic_screencopy_frame_v2::ZcosmicScreencopyFrameV2, + _reason: WEnum, ) { // TODO log::error!("Screencopy failed"); + let session = &screencopy_frame.data::().unwrap().session; + if let Some(capture) = Capture::for_session(session) { + capture.stop(); + } + } + + fn stopped( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + session: &zcosmic_screencopy_session_v2::ZcosmicScreencopySessionV2, + ) { + // TODO if let Some(capture) = Capture::for_session(session) { capture.stop(); } } } -cctk::delegate_screencopy!(AppData, session: [SessionData]); +cctk::delegate_screencopy!(AppData, session: [SessionData], frame: [FrameData]);