From 1242a6c1e139de893352e7f4ac40ceda0a2bfccf Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 28 Apr 2022 19:49:51 +0200 Subject: [PATCH] autocommit 2022-04-28 19:49:51 CEST --- src/backends/metal.rs | 4 + src/backends/metal/video.rs | 85 +++++++- src/globals.rs | 2 + src/ifs.rs | 2 + src/ifs/ipc/wl_data_device_manager.rs | 5 +- ...zwp_primary_selection_device_manager_v1.rs | 15 +- .../ipc/zwp_primary_selection_device_v1.rs | 5 +- src/ifs/org_kde_kwin_server_decoration.rs | 5 +- .../org_kde_kwin_server_decoration_manager.rs | 5 +- src/ifs/wl_buffer.rs | 21 +- src/ifs/wl_drm.rs | 10 +- src/ifs/wl_output.rs | 7 +- src/ifs/wl_seat/wl_keyboard.rs | 6 +- src/ifs/wl_surface.rs | 5 +- .../wl_surface/xdg_surface/xdg_toplevel.rs | 5 +- src/ifs/xdg_positioner.rs | 11 +- src/ifs/xdg_wm_base.rs | 10 +- src/ifs/zwlr_screencopy_frame_v1.rs | 196 ++++++++++++++++++ src/ifs/zwlr_screencopy_manager_v1.rs | 163 +++++++++++++++ src/ifs/zwp_linux_buffer_params_v1.rs | 9 +- src/ifs/zwp_linux_dmabuf_v1.rs | 5 +- src/ifs/zxdg_output_manager_v1.rs | 5 +- src/ifs/zxdg_output_v1.rs | 6 +- src/ifs/zxdg_toplevel_decoration_v1.rs | 10 +- src/render/gl/sys.rs | 11 + src/render/renderer/framebuffer.rs | 57 ++++- src/render/renderer/image.rs | 15 +- src/render/renderer/texture.rs | 11 +- src/utils/linkedlist.rs | 9 + wire/zwlr_screencopy_frame_v1.txt | 51 +++++ wire/zwlr_screencopy_manager_v1.txt | 20 ++ 31 files changed, 707 insertions(+), 64 deletions(-) create mode 100644 src/ifs/zwlr_screencopy_frame_v1.rs create mode 100644 src/ifs/zwlr_screencopy_manager_v1.rs create mode 100644 wire/zwlr_screencopy_frame_v1.txt create mode 100644 wire/zwlr_screencopy_manager_v1.txt diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 89583cfd..448b31fa 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -85,6 +85,10 @@ pub enum MetalError { Framebuffer(#[source] DrmError), #[error("Could not import a framebuffer into EGL")] ImportFb(#[source] RenderError), + #[error("Could not import a texture into EGL")] + ImportTexture(#[source] RenderError), + #[error("Could not import an image into EGL")] + ImportImage(#[source] RenderError), #[error("Could not perform modeset")] Modeset(#[source] DrmError), #[error("Could not enable atomic modesetting")] diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 375d3ff4..0d0ddc1f 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -7,9 +7,13 @@ use { backends::metal::{DrmId, MetalBackend, MetalError}, edid::Descriptor, format::{Format, XRGB8888}, - ifs::wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC}, - render::{Framebuffer, RenderContext, RenderResult}, + ifs::{ + wl_buffer::WlBufferStorage, + wp_presentation_feedback::{KIND_HW_COMPLETION, KIND_VSYNC}, + }, + render::{Framebuffer, RenderContext, RenderResult, Texture}, state::State, + time::Time, utils::{ asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell, debug_fn::debug_fn, errorfmt::ErrorFmt, numcell::NumCell, oserror::OsError, @@ -33,6 +37,7 @@ use { cell::{Cell, RefCell}, ffi::CString, fmt::{Debug, Formatter}, + ops::Deref, rc::Rc, }, uapi::c, @@ -183,7 +188,7 @@ impl MetalConnector { let buffer = &buffers[self.next_buffer.fetch_add(1) % buffers.len()]; if let Some(node) = self.state.root.outputs.get(&self.connector_id) { let mut rr = self.render_result.borrow_mut(); - buffer.egl.render( + buffer.fb.render( &*node, &self.state, Some(node.global.pos.get()), @@ -194,6 +199,64 @@ impl MetalConnector { fr.send_done(); let _ = fr.client.remove_obj(&*fr); } + if !node.global.pending_captures.is_empty() { + let now = Time::now().unwrap(); + let mut captures = vec![]; + for capture in node.global.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; + } + let rect = capture.rect; + if let WlBufferStorage::Shm { mem, .. } = &wl_buffer.storage { + let res = mem.access(|mem| { + buffer.fb.copy_to_shm( + rect.x1(), + rect.y1(), + rect.width(), + rect.height(), + XRGB8888, + mem, + ); + }); + if let Err(e) = res { + capture.client.error(e); + } + // capture.send_flags(FLAGS_Y_INVERT); + } else { + let fb = match wl_buffer.famebuffer.get() { + Some(fb) => fb, + _ => { + log::warn!("Capture buffer has no framebuffer"); + capture.send_failed(); + continue; + } + }; + fb.copy_texture( + &self.state, + &buffer.tex, + -capture.rect.x1(), + -capture.rect.y1(), + ); + } + 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(); + } + } } let mut changes = self.master.change(); changes.change_object(plane.id, |c| { @@ -1025,14 +1088,23 @@ impl MetalBackend { Ok(fb) => Rc::new(fb), Err(e) => return Err(MetalError::Framebuffer(e)), }; - let egl_fb = match dev.dev.egl.dmabuf_fb(bo.dmabuf()) { + let egl_img = match dev.dev.egl.dmabuf_img(bo.dmabuf()) { + Ok(img) => img, + Err(e) => return Err(MetalError::ImportImage(e)), + }; + let egl_fb = match egl_img.to_framebuffer() { Ok(fb) => fb, Err(e) => return Err(MetalError::ImportFb(e)), }; + let egl_tex = match egl_img.to_texture() { + Ok(fb) => fb, + Err(e) => return Err(MetalError::ImportTexture(e)), + }; egl_fb.clear(); Ok(RenderBuffer { drm: drm_fb, - egl: egl_fb, + fb: egl_fb, + tex: egl_tex, }) } @@ -1158,7 +1230,8 @@ impl MetalBackend { #[derive(Debug)] pub struct RenderBuffer { drm: Rc, - egl: Rc, + fb: Rc, + tex: Rc, } fn modes_equal(a: &DrmModeInfo, b: &DrmModeInfo) -> bool { diff --git a/src/globals.rs b/src/globals.rs index cdb28abe..e0a22cb2 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -19,6 +19,7 @@ use { wp_presentation::WpPresentationGlobal, xdg_wm_base::XdgWmBaseGlobal, zwlr_layer_shell_v1::ZwlrLayerShellV1Global, + zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global, zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1Global, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global, zxdg_decoration_manager_v1::ZxdgDecorationManagerV1Global, @@ -132,6 +133,7 @@ impl Globals { add_singleton!(ZwlrLayerShellV1Global); add_singleton!(ZxdgOutputManagerV1Global); add_singleton!(JayCompositorGlobal); + add_singleton!(ZwlrScreencopyManagerV1Global); if backend.supports_idle() { add_singleton!(ZwpIdleInhibitManagerV1Global); diff --git a/src/ifs.rs b/src/ifs.rs index fbb2c738..11bc5afa 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -23,6 +23,8 @@ pub mod wp_presentation_feedback; pub mod xdg_positioner; pub mod xdg_wm_base; pub mod zwlr_layer_shell_v1; +pub mod zwlr_screencopy_frame_v1; +pub mod zwlr_screencopy_manager_v1; pub mod zwp_idle_inhibit_manager_v1; pub mod zwp_linux_buffer_params_v1; pub mod zwp_linux_dmabuf_v1; diff --git a/src/ifs/ipc/wl_data_device_manager.rs b/src/ifs/ipc/wl_data_device_manager.rs index 7f83ed5a..1871dd56 100644 --- a/src/ifs/ipc/wl_data_device_manager.rs +++ b/src/ifs/ipc/wl_data_device_manager.rs @@ -56,7 +56,10 @@ impl WlDataDeviceManagerGlobal { } impl WlDataDeviceManager { - fn create_data_source(&self, parser: MsgParser<'_, '_>) -> Result<(), WlDataDeviceManagerError> { + fn create_data_source( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), WlDataDeviceManagerError> { let req: CreateDataSource = self.client.parse(self, parser)?; let res = Rc::new(WlDataSource::new(req.id, &self.client)); track!(self.client, res); diff --git a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs index fcfb0501..86226e67 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs @@ -50,7 +50,10 @@ impl ZwpPrimarySelectionDeviceManagerV1Global { } impl ZwpPrimarySelectionDeviceManagerV1 { - fn create_source(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { + fn create_source( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { let req: CreateSource = self.client.parse(self, parser)?; let res = Rc::new(ZwpPrimarySelectionSourceV1::new(req.id, &self.client)); track!(self.client, res); @@ -58,7 +61,10 @@ impl ZwpPrimarySelectionDeviceManagerV1 { Ok(()) } - fn get_data_device(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { + fn get_data_device( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { let req: GetDevice = self.client.parse(&**self, parser)?; let seat = self.client.lookup(req.seat)?; let dev = Rc::new(ZwpPrimarySelectionDeviceV1::new(req.id, self, &seat)); @@ -68,7 +74,10 @@ impl ZwpPrimarySelectionDeviceManagerV1 { Ok(()) } - fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { + fn destroy( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpPrimarySelectionDeviceManagerV1Error> { let _req: Destroy = self.client.parse(self, parser)?; self.client.remove_obj(self)?; Ok(()) diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index 66e3e76f..3921bdb2 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -61,7 +61,10 @@ impl ZwpPrimarySelectionDeviceV1 { }) } - fn set_selection(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwpPrimarySelectionDeviceV1Error> { + fn set_selection( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpPrimarySelectionDeviceV1Error> { let req: SetSelection = self.manager.client.parse(self, parser)?; if !self.manager.client.valid_serial(req.serial) { log::warn!("Client tried to set_selection with an invalid serial"); diff --git a/src/ifs/org_kde_kwin_server_decoration.rs b/src/ifs/org_kde_kwin_server_decoration.rs index 57829527..910ec715 100644 --- a/src/ifs/org_kde_kwin_server_decoration.rs +++ b/src/ifs/org_kde_kwin_server_decoration.rs @@ -46,7 +46,10 @@ impl OrgKdeKwinServerDecoration { Ok(()) } - fn request_mode(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), OrgKdeKwinServerDecorationError> { + fn request_mode( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), OrgKdeKwinServerDecorationError> { let req: RequestMode = self.client.parse(&**self, parser)?; if req.mode > SERVER { return Err(OrgKdeKwinServerDecorationError::InvalidMode(req.mode)); diff --git a/src/ifs/org_kde_kwin_server_decoration_manager.rs b/src/ifs/org_kde_kwin_server_decoration_manager.rs index 2939044d..93488580 100644 --- a/src/ifs/org_kde_kwin_server_decoration_manager.rs +++ b/src/ifs/org_kde_kwin_server_decoration_manager.rs @@ -2,7 +2,9 @@ use { crate::{ client::{Client, ClientError}, globals::{Global, GlobalName}, - ifs::org_kde_kwin_server_decoration::OrgKdeKwinServerDecoration, + ifs::org_kde_kwin_server_decoration::{ + OrgKdeKwinServerDecoration, OrgKdeKwinServerDecorationError, + }, leaks::Tracker, object::Object, utils::buffd::{MsgParser, MsgParserError}, @@ -11,7 +13,6 @@ use { std::rc::Rc, thiserror::Error, }; -use crate::ifs::org_kde_kwin_server_decoration::OrgKdeKwinServerDecorationError; #[allow(dead_code)] const NONE: u32 = 0; diff --git a/src/ifs/wl_buffer.rs b/src/ifs/wl_buffer.rs index de3fe63d..915499f0 100644 --- a/src/ifs/wl_buffer.rs +++ b/src/ifs/wl_buffer.rs @@ -6,7 +6,7 @@ use { leaks::Tracker, object::Object, rect::Rect, - render::{Image, RenderError, Texture}, + render::{Framebuffer, Image, RenderError, Texture}, utils::{ buffd::{MsgParser, MsgParserError}, clonecell::CloneCell, @@ -28,8 +28,9 @@ pub struct WlBuffer { pub client: Rc, pub rect: Rect, pub format: &'static Format, - storage: WlBufferStorage, + pub storage: WlBufferStorage, pub texture: CloneCell>>, + pub famebuffer: CloneCell>>, width: i32, height: i32, pub tracker: Tracker, @@ -58,6 +59,7 @@ impl WlBuffer { width, height, texture: CloneCell::new(None), + famebuffer: Default::default(), storage: WlBufferStorage::Dmabuf(img.clone()), tracker: Default::default(), } @@ -95,6 +97,7 @@ impl WlBuffer { height, texture: CloneCell::new(None), tracker: Default::default(), + famebuffer: Default::default(), }) } @@ -118,6 +121,20 @@ impl WlBuffer { Ok(()) } + pub fn update_framebuffer(&self) -> Result<(), WlBufferError> { + match &self.storage { + WlBufferStorage::Shm { .. } => { + // nothing + } + WlBufferStorage::Dmabuf(img) => { + if self.famebuffer.get().is_none() { + self.famebuffer.set(Some(img.to_framebuffer()?)); + } + } + } + Ok(()) + } + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlBufferError> { let _req: Destroy = self.client.parse(self, parser)?; self.client.remove_obj(self)?; diff --git a/src/ifs/wl_drm.rs b/src/ifs/wl_drm.rs index 2730705f..6af8f443 100644 --- a/src/ifs/wl_drm.rs +++ b/src/ifs/wl_drm.rs @@ -102,18 +102,12 @@ impl WlDrm { Err(WlDrmError::Unsupported) } - fn create_planar_buffer( - self: &Rc, - parser: MsgParser<'_, '_>, - ) -> Result<(), WlDrmError> { + fn create_planar_buffer(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), WlDrmError> { let _req: CreatePlanarBuffer = self.client.parse(&**self, parser)?; Err(WlDrmError::Unsupported) } - fn create_prime_buffer( - self: &Rc, - parser: MsgParser<'_, '_>, - ) -> Result<(), WlDrmError> { + fn create_prime_buffer(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), WlDrmError> { let req: CreatePrimeBuffer = self.client.parse(&**self, parser)?; let ctx = match self.client.state.render_ctx.get() { Some(ctx) => ctx, diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index 9f847702..5c3088a7 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -3,7 +3,7 @@ use { backend, client::{Client, ClientError, ClientId}, globals::{Global, GlobalName}, - ifs::zxdg_output_v1::ZxdgOutputV1, + ifs::{zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, zxdg_output_v1::ZxdgOutputV1}, leaks::Tracker, object::Object, rect::Rect, @@ -13,6 +13,7 @@ use { buffd::{MsgParser, MsgParserError}, clonecell::CloneCell, copyhashmap::CopyHashMap, + linkedlist::LinkedList, }, wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id}, }, @@ -68,6 +69,8 @@ pub struct WlOutputGlobal { pub width_mm: i32, pub height_mm: i32, pub bindings: RefCell>>>, + pub unused_captures: LinkedList>, + pub pending_captures: LinkedList>, } impl WlOutputGlobal { @@ -92,6 +95,8 @@ impl WlOutputGlobal { width_mm, height_mm, bindings: Default::default(), + unused_captures: Default::default(), + pending_captures: Default::default(), } } diff --git a/src/ifs/wl_seat/wl_keyboard.rs b/src/ifs/wl_seat/wl_keyboard.rs index 2046adc4..80937845 100644 --- a/src/ifs/wl_seat/wl_keyboard.rs +++ b/src/ifs/wl_seat/wl_keyboard.rs @@ -4,14 +4,16 @@ use { ifs::wl_seat::WlSeat, leaks::Tracker, object::Object, - utils::buffd::{MsgParser, MsgParserError}, + utils::{ + buffd::{MsgParser, MsgParserError}, + oserror::OsError, + }, wire::{wl_keyboard::*, WlKeyboardId, WlSurfaceId}, }, std::rc::Rc, thiserror::Error, uapi::OwnedFd, }; -use crate::utils::oserror::OsError; pub const REPEAT_INFO_SINCE: u32 = 4; diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 81fa735d..82fe2771 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -539,10 +539,7 @@ impl WlSurface { Ok(()) } - fn set_buffer_transform( - &self, - parser: MsgParser<'_, '_>, - ) -> Result<(), WlSurfaceError> { + fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> { let _req: SetBufferTransform = self.parse(parser)?; Ok(()) } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index f3be0d3a..ac423c17 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -283,10 +283,7 @@ impl XdgToplevel { Ok(()) } - fn set_fullscreen( - self: &Rc, - parser: MsgParser<'_, '_>, - ) -> Result<(), XdgToplevelError> { + fn set_fullscreen(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), XdgToplevelError> { let client = &self.xdg.surface.client; let req: SetFullscreen = client.parse(self.deref(), parser)?; self.states.borrow_mut().insert(STATE_FULLSCREEN); diff --git a/src/ifs/xdg_positioner.rs b/src/ifs/xdg_positioner.rs index 70b4cd57..5c645464 100644 --- a/src/ifs/xdg_positioner.rs +++ b/src/ifs/xdg_positioner.rs @@ -221,11 +221,7 @@ impl XdgPositioner { let req: SetConstraintAdjustment = self.client.parse(self, parser)?; let ca = match CA::from_bits(req.constraint_adjustment) { Some(c) => c, - _ => { - return Err(XdgPositionerError::UnknownCa( - req.constraint_adjustment, - )) - } + _ => return Err(XdgPositionerError::UnknownCa(req.constraint_adjustment)), }; self.position.borrow_mut().ca = ca; Ok(()) @@ -261,10 +257,7 @@ impl XdgPositioner { Ok(()) } - fn set_parent_configure( - &self, - parser: MsgParser<'_, '_>, - ) -> Result<(), XdgPositionerError> { + fn set_parent_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), XdgPositionerError> { let req: SetParentConfigure = self.client.parse(self, parser)?; self.position.borrow_mut().parent_serial = req.serial; Ok(()) diff --git a/src/ifs/xdg_wm_base.rs b/src/ifs/xdg_wm_base.rs index 566b34e4..b4fd8160 100644 --- a/src/ifs/xdg_wm_base.rs +++ b/src/ifs/xdg_wm_base.rs @@ -84,10 +84,7 @@ impl XdgWmBase { Ok(()) } - fn create_positioner( - self: &Rc, - parser: MsgParser<'_, '_>, - ) -> Result<(), XdgWmBaseError> { + fn create_positioner(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), XdgWmBaseError> { let req: CreatePositioner = self.client.parse(&**self, parser)?; let pos = Rc::new(XdgPositioner::new(self, req.id, &self.client)); track!(self.client, pos); @@ -95,10 +92,7 @@ impl XdgWmBase { Ok(()) } - fn get_xdg_surface( - self: &Rc, - parser: MsgParser<'_, '_>, - ) -> Result<(), XdgWmBaseError> { + fn get_xdg_surface(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), XdgWmBaseError> { let req: GetXdgSurface = self.client.parse(&**self, parser)?; let surface = self.client.lookup(req.surface)?; let xdg_surface = Rc::new(XdgSurface::new(self, req.id, &surface)); diff --git a/src/ifs/zwlr_screencopy_frame_v1.rs b/src/ifs/zwlr_screencopy_frame_v1.rs new file mode 100644 index 00000000..9045089d --- /dev/null +++ b/src/ifs/zwlr_screencopy_frame_v1.rs @@ -0,0 +1,196 @@ +use { + crate::{ + client::{Client, ClientError}, + format::XRGB8888, + ifs::{ + wl_buffer::{WlBuffer, WlBufferError, WlBufferStorage}, + wl_output::WlOutputGlobal, + }, + leaks::Tracker, + object::Object, + rect::Rect, + utils::{ + buffd::{MsgParser, MsgParserError}, + linkedlist::LinkedNode, + }, + wire::{zwlr_screencopy_frame_v1::*, WlBufferId, ZwlrScreencopyFrameV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +#[allow(dead_code)] +pub const FLAGS_Y_INVERT: u32 = 1; + +pub struct ZwlrScreencopyFrameV1 { + pub id: ZwlrScreencopyFrameV1Id, + pub client: Rc, + pub tracker: Tracker, + pub output: Rc, + pub rect: Rect, + pub overlay_cursor: bool, + pub used: Cell, + pub with_damage: Cell, + pub output_link: Cell>>>, + pub buffer: Cell>>, + pub version: u32, +} + +impl ZwlrScreencopyFrameV1 { + pub fn send_ready(&self, tv_sec: u64, tv_nsec: u32) { + self.client.event(Ready { + self_id: self.id, + tv_sec_hi: (tv_sec >> 32) as u32, + tv_sec_lo: tv_sec as u32, + tv_nsec, + }); + } + + pub fn send_failed(&self) { + self.client.event(Failed { self_id: self.id }); + } + + pub fn send_damage(&self) { + self.client.event(Damage { + self_id: self.id, + x: 0, + y: 0, + width: self.rect.width() as _, + height: self.rect.height() as _, + }); + } + + pub fn send_buffer(&self) { + self.client.event(Buffer { + self_id: self.id, + format: XRGB8888.wl_id.unwrap(), + width: self.rect.width() as _, + height: self.rect.height() as _, + stride: self.rect.width() as u32 * 4, // TODO + }); + } + + pub fn send_linux_dmabuf(&self) { + self.client.event(LinuxDmabuf { + self_id: self.id, + format: XRGB8888.drm, + width: self.rect.width() as _, + height: self.rect.height() as _, + }); + } + + pub fn send_buffer_done(&self) { + self.client.event(BufferDone { self_id: self.id }) + } + + #[allow(dead_code)] + pub fn send_flags(&self, flags: u32) { + self.client.event(Flags { + self_id: self.id, + flags, + }) + } + + fn do_copy( + &self, + 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 buffer = self.client.lookup(buffer_id)?; + if (buffer.rect.width(), buffer.rect.height()) != (self.rect.width(), self.rect.height()) { + return Err(ZwlrScreencopyFrameV1Error::InvalidBufferSize); + } + if buffer.format != XRGB8888 { + return Err(ZwlrScreencopyFrameV1Error::InvalidBufferFormat); + } + buffer.update_framebuffer()?; + if let WlBufferStorage::Shm { stride, .. } = &buffer.storage { + if *stride != self.rect.width() * 4 { + return Err(ZwlrScreencopyFrameV1Error::InvalidBufferStride); + } + } + self.buffer.set(Some(buffer)); + if !with_damage { + 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)); + Ok(()) + } + + fn copy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrScreencopyFrameV1Error> { + let req: Copy = self.client.parse(self, parser)?; + self.do_copy(req.buffer, false) + } + + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrScreencopyFrameV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + self.client.remove_obj(self)?; + self.output_link.take(); + Ok(()) + } + + fn copy_with_damage( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwlrScreencopyFrameV1Error> { + let req: CopyWithDamage = self.client.parse(self, parser)?; + self.do_copy(req.buffer, true) + } +} + +object_base! { + ZwlrScreencopyFrameV1; + + COPY => copy, + DESTROY => destroy, + COPY_WITH_DAMAGE => copy_with_damage, +} + +simple_add_obj!(ZwlrScreencopyFrameV1); + +impl Object for ZwlrScreencopyFrameV1 { + fn num_requests(&self) -> u32 { + if self.version >= 2 { + COPY_WITH_DAMAGE + 1 + } else { + DESTROY + 1 + } + } + + fn break_loops(&self) { + self.output_link.take(); + } +} + +#[derive(Debug, Error)] +pub enum ZwlrScreencopyFrameV1Error { + #[error("This frame has already been used")] + AlreadyUsed, + #[error("The buffer has an invalid size for the frame")] + InvalidBufferSize, + #[error("The buffer has an invalid stride for the frame")] + InvalidBufferStride, + #[error("The buffer has an invalid format")] + InvalidBufferFormat, + #[error(transparent)] + WlBufferError(Box), + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + MsgParserError(Box), +} +efrom!(ZwlrScreencopyFrameV1Error, WlBufferError); +efrom!(ZwlrScreencopyFrameV1Error, ClientError); +efrom!(ZwlrScreencopyFrameV1Error, MsgParserError); diff --git a/src/ifs/zwlr_screencopy_manager_v1.rs b/src/ifs/zwlr_screencopy_manager_v1.rs new file mode 100644 index 00000000..89de8465 --- /dev/null +++ b/src/ifs/zwlr_screencopy_manager_v1.rs @@ -0,0 +1,163 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, + leaks::Tracker, + object::Object, + rect::Rect, + utils::buffd::{MsgParser, MsgParserError}, + wire::{ + zwlr_screencopy_manager_v1::*, WlOutputId, ZwlrScreencopyFrameV1Id, + ZwlrScreencopyManagerV1Id, + }, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct ZwlrScreencopyManagerV1Global { + pub name: GlobalName, +} + +impl ZwlrScreencopyManagerV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: ZwlrScreencopyManagerV1Id, + client: &Rc, + version: u32, + ) -> Result<(), ZwlrScreencopyManagerV1Error> { + let mgr = Rc::new(ZwlrScreencopyManagerV1 { + id, + client: client.clone(), + tracker: Default::default(), + version, + }); + track!(client, mgr); + client.add_client_obj(&mgr)?; + Ok(()) + } +} + +global_base!( + ZwlrScreencopyManagerV1Global, + ZwlrScreencopyManagerV1, + ZwlrScreencopyManagerV1Error +); + +simple_add_global!(ZwlrScreencopyManagerV1Global); + +impl Global for ZwlrScreencopyManagerV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 3 + } +} + +pub struct ZwlrScreencopyManagerV1 { + pub id: ZwlrScreencopyManagerV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: u32, +} + +impl ZwlrScreencopyManagerV1 { + fn capture_output( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwlrScreencopyManagerV1Error> { + let req: CaptureOutput = self.client.parse(self, parser)?; + self.do_capture_output(req.output, req.overlay_cursor != 0, req.frame, None) + } + + fn capture_output_region( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwlrScreencopyManagerV1Error> { + let req: CaptureOutputRegion = self.client.parse(self, parser)?; + let region = match Rect::new_sized(req.x, req.y, req.width, req.height) { + Some(r) => r, + _ => return Err(ZwlrScreencopyManagerV1Error::InvalidRegion), + }; + self.do_capture_output(req.output, req.overlay_cursor != 0, req.frame, Some(region)) + } + + fn do_capture_output( + &self, + output: WlOutputId, + overlay_cursor: bool, + frame: ZwlrScreencopyFrameV1Id, + region: Option, + ) -> Result<(), ZwlrScreencopyManagerV1Error> { + let output = self.client.lookup(output)?; + let mut rect = output.global.position().at_point(0, 0); + if let Some(region) = region { + rect = rect.intersect(region); + } + let frame = Rc::new(ZwlrScreencopyFrameV1 { + id: frame, + client: self.client.clone(), + tracker: Default::default(), + output: output.global.clone(), + rect, + overlay_cursor, + used: Cell::new(false), + with_damage: Cell::new(false), + output_link: Cell::new(None), + buffer: Cell::new(None), + version: self.version, + }); + track!(self.client, frame); + self.client.add_client_obj(&frame)?; + frame.send_buffer(); + if self.version >= 3 { + frame.send_linux_dmabuf(); + frame.send_buffer_done(); + } + frame + .output_link + .set(Some(output.global.unused_captures.add_last(frame.clone()))); + Ok(()) + } + + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), ZwlrScreencopyManagerV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + ZwlrScreencopyManagerV1; + + CAPTURE_OUTPUT => capture_output, + CAPTURE_OUTPUT_REGION => capture_output_region, + DESTROY => destroy, +} + +impl Object for ZwlrScreencopyManagerV1 { + fn num_requests(&self) -> u32 { + DESTROY + 1 + } +} + +simple_add_obj!(ZwlrScreencopyManagerV1); + +#[derive(Debug, Error)] +pub enum ZwlrScreencopyManagerV1Error { + #[error(transparent)] + ClientError(Box), + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error("The passed region is invalid")] + InvalidRegion, +} +efrom!(ZwlrScreencopyManagerV1Error, ClientError); +efrom!(ZwlrScreencopyManagerV1Error, MsgParserError); diff --git a/src/ifs/zwp_linux_buffer_params_v1.rs b/src/ifs/zwp_linux_buffer_params_v1.rs index ce2d7c8b..30e51b71 100644 --- a/src/ifs/zwp_linux_buffer_params_v1.rs +++ b/src/ifs/zwp_linux_buffer_params_v1.rs @@ -61,7 +61,10 @@ impl ZwpLinuxBufferParamsV1 { self.parent.client.event(Failed { self_id: self.id }) } - fn destroy(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), ZwpLinuxBufferParamsV1Error> { + fn destroy( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpLinuxBufferParamsV1Error> { let _req: Destroy = self.parent.client.parse(&**self, parser)?; self.parent.client.remove_obj(&**self)?; Ok(()) @@ -71,7 +74,9 @@ impl ZwpLinuxBufferParamsV1 { let req: Add = self.parent.client.parse(&**self, parser)?; let modifier = ((req.modifier_hi as u64) << 32) | req.modifier_lo as u64; match self.modifier.get() { - Some(m) if m != modifier => return Err(ZwpLinuxBufferParamsV1Error::MixedModifiers(modifier, m)), + Some(m) if m != modifier => { + return Err(ZwpLinuxBufferParamsV1Error::MixedModifiers(modifier, m)) + } _ => self.modifier.set(Some(modifier)), } let plane = req.plane_idx; diff --git a/src/ifs/zwp_linux_dmabuf_v1.rs b/src/ifs/zwp_linux_dmabuf_v1.rs index 52408e78..be59412a 100644 --- a/src/ifs/zwp_linux_dmabuf_v1.rs +++ b/src/ifs/zwp_linux_dmabuf_v1.rs @@ -100,7 +100,10 @@ impl ZwpLinuxDmabufV1 { Ok(()) } - fn create_params(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), ZwpLinuxDmabufV1Error> { + fn create_params( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZwpLinuxDmabufV1Error> { let req: CreateParams = self.client.parse(&**self, parser)?; let params = Rc::new(ZwpLinuxBufferParamsV1::new(req.params_id, self)); track!(self.client, params); diff --git a/src/ifs/zxdg_output_manager_v1.rs b/src/ifs/zxdg_output_manager_v1.rs index 11336253..cda300ec 100644 --- a/src/ifs/zxdg_output_manager_v1.rs +++ b/src/ifs/zxdg_output_manager_v1.rs @@ -53,7 +53,10 @@ impl ZxdgOutputManagerV1 { Ok(()) } - fn get_xdg_output(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), ZxdgOutputManagerV1Error> { + fn get_xdg_output( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZxdgOutputManagerV1Error> { let req: GetXdgOutput = self.client.parse(&**self, parser)?; let output = self.client.lookup(req.output)?; let xdg_output = Rc::new(ZxdgOutputV1 { diff --git a/src/ifs/zxdg_output_v1.rs b/src/ifs/zxdg_output_v1.rs index c52a8035..30d40f77 100644 --- a/src/ifs/zxdg_output_v1.rs +++ b/src/ifs/zxdg_output_v1.rs @@ -67,8 +67,10 @@ impl ZxdgOutputV1 { if self.version >= NAME_SINCE { self.send_name(&self.output.global.connector.name); } - if self.version >= NO_DONE_SINCE && self.output.version >= SEND_DONE_SINCE { - self.output.send_done(); + if self.version >= NO_DONE_SINCE { + if self.output.version >= SEND_DONE_SINCE { + self.output.send_done(); + } } else { self.send_done(); } diff --git a/src/ifs/zxdg_toplevel_decoration_v1.rs b/src/ifs/zxdg_toplevel_decoration_v1.rs index 18ae7edc..5b65738b 100644 --- a/src/ifs/zxdg_toplevel_decoration_v1.rs +++ b/src/ifs/zxdg_toplevel_decoration_v1.rs @@ -57,13 +57,19 @@ impl ZxdgToplevelDecorationV1 { Ok(()) } - fn set_mode(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), ZxdgToplevelDecorationV1Error> { + fn set_mode( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZxdgToplevelDecorationV1Error> { let _req: SetMode = self.client.parse(&**self, parser)?; self.do_send_configure(); Ok(()) } - fn unset_mode(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), ZxdgToplevelDecorationV1Error> { + fn unset_mode( + self: &Rc, + parser: MsgParser<'_, '_>, + ) -> Result<(), ZxdgToplevelDecorationV1Error> { let _req: UnsetMode = self.client.parse(&**self, parser)?; self.do_send_configure(); Ok(()) diff --git a/src/render/gl/sys.rs b/src/render/gl/sys.rs index 63bc3e6b..ac91f620 100644 --- a/src/render/gl/sys.rs +++ b/src/render/gl/sys.rs @@ -74,6 +74,17 @@ extern "C" { #[allow(dead_code)] pub fn glFlush(); + pub fn glReadnPixels( + x: GLint, + y: GLint, + width: GLsizei, + height: GLsizei, + format: GLenum, + ty: GLenum, + buf_size: GLsizei, + data: *mut c::c_void, + ); + pub fn glGenTextures(n: GLsizei, textures: *mut GLuint); pub fn glDeleteTextures(n: GLsizei, textures: *const GLuint); pub fn glBindTexture(target: GLenum, texture: GLuint); diff --git a/src/render/renderer/framebuffer.rs b/src/render/renderer/framebuffer.rs index 378cace0..f13e3050 100644 --- a/src/render/renderer/framebuffer.rs +++ b/src/render/renderer/framebuffer.rs @@ -1,5 +1,6 @@ use { crate::{ + format::{Format, XRGB8888}, rect::Rect, render::{ gl::{ @@ -10,13 +11,14 @@ use { }, }, renderer::{context::RenderContext, renderer::Renderer}, - sys::{glBlendFunc, glFlush, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, - RenderResult, + sys::{glBlendFunc, glFlush, glReadnPixels, GL_ONE, GL_ONE_MINUS_SRC_ALPHA}, + RenderResult, Texture, }, state::State, tree::Node, }, std::{ + cell::Cell, fmt::{Debug, Formatter}, rc::Rc, }, @@ -46,6 +48,57 @@ impl Framebuffer { }); } + pub fn copy_texture(&self, state: &State, texture: &Texture, x: i32, y: i32) { + let _ = self.ctx.ctx.with_current(|| { + unsafe { + glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo); + glViewport(0, 0, self.gl.width, self.gl.height); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + let mut renderer = Renderer { + ctx: &self.ctx, + fb: &self.gl, + state, + on_output: false, + result: &mut RenderResult::default(), + }; + renderer.render_texture(texture, x, y, XRGB8888); + unsafe { + glFlush(); + } + Ok(()) + }); + } + + pub fn copy_to_shm( + &self, + x: i32, + y: i32, + width: i32, + height: i32, + format: &Format, + shm: &[Cell], + ) { + let y = self.gl.height - y - height; + let _ = self.ctx.ctx.with_current(|| { + unsafe { + glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo); + glViewport(0, 0, self.gl.width, self.gl.height); + glReadnPixels( + x, + y, + width, + height, + format.gl_format as _, + format.gl_type as _, + shm.len() as _, + shm.as_ptr() as _, + ); + } + Ok(()) + }); + } + pub fn render( &self, node: &dyn Node, diff --git a/src/render/renderer/image.rs b/src/render/renderer/image.rs index a85fa903..6156fbea 100644 --- a/src/render/renderer/image.rs +++ b/src/render/renderer/image.rs @@ -1,6 +1,8 @@ use { crate::render::{ - egl::image::EglImage, gl::texture::GlTexture, RenderContext, RenderError, Texture, + egl::image::EglImage, + gl::{render_buffer::GlRenderBuffer, texture::GlTexture}, + Framebuffer, RenderContext, RenderError, Texture, }, std::rc::Rc, }; @@ -25,4 +27,15 @@ impl Image { gl: GlTexture::import_img(&self.ctx.ctx, &self.gl)?, })) } + + pub fn to_framebuffer(&self) -> Result, RenderError> { + self.ctx.ctx.with_current(|| unsafe { + let rb = GlRenderBuffer::from_image(&self.gl, &self.ctx.ctx)?; + let fb = rb.create_framebuffer()?; + Ok(Rc::new(Framebuffer { + ctx: self.ctx.clone(), + gl: fb, + })) + }) + } } diff --git a/src/render/renderer/texture.rs b/src/render/renderer/texture.rs index beb55252..9ac815f2 100644 --- a/src/render/renderer/texture.rs +++ b/src/render/renderer/texture.rs @@ -1,6 +1,9 @@ use { crate::render::{gl::texture::GlTexture, renderer::context::RenderContext}, - std::rc::Rc, + std::{ + fmt::{Debug, Formatter}, + rc::Rc, + }, }; pub struct Texture { @@ -8,6 +11,12 @@ pub struct Texture { pub(super) gl: GlTexture, } +impl Debug for Texture { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Texture").finish_non_exhaustive() + } +} + impl Texture { pub fn width(&self) -> i32 { self.gl.width diff --git a/src/utils/linkedlist.rs b/src/utils/linkedlist.rs index 9abecf6c..cf1b78e4 100644 --- a/src/utils/linkedlist.rs +++ b/src/utils/linkedlist.rs @@ -79,6 +79,15 @@ impl LinkedList { self.root.append(t) } + pub fn add_last_existing(&self, t: &NodeRef) { + self.root.prepend_existing(t) + } + + #[allow(dead_code)] + pub fn add_first_existing(&self, t: &NodeRef) { + self.root.append_existing(t) + } + pub fn iter(&self) -> LinkedListIter { unsafe { let root = self.root.data.as_ref(); diff --git a/wire/zwlr_screencopy_frame_v1.txt b/wire/zwlr_screencopy_frame_v1.txt new file mode 100644 index 00000000..0c60291a --- /dev/null +++ b/wire/zwlr_screencopy_frame_v1.txt @@ -0,0 +1,51 @@ +# requests + +msg copy = 0 { + buffer: id(wl_buffer), +} + +msg destroy = 1 { +} + +msg copy_with_damage = 2 { + buffer: id(wl_buffer), +} + +# events + +msg buffer = 0 { + format: u32, + width: u32, + height: u32, + stride: u32, +} + +msg flags = 1 { + flags: u32, +} + +msg ready = 2 { + tv_sec_hi: u32, + tv_sec_lo: u32, + tv_nsec: u32, +} + +msg failed = 3 { + +} + +msg damage = 4 { + x: u32, + y: u32, + width: u32, + height: u32, +} + +msg linux_dmabuf = 5 { + format: u32, + width: u32, + height: u32, +} + +msg buffer_done = 6 { +} diff --git a/wire/zwlr_screencopy_manager_v1.txt b/wire/zwlr_screencopy_manager_v1.txt new file mode 100644 index 00000000..3fb2756d --- /dev/null +++ b/wire/zwlr_screencopy_manager_v1.txt @@ -0,0 +1,20 @@ +# requests + +msg capture_output = 0 { + frame: id(zwlr_screencopy_frame_v1), + overlay_cursor: i32, + output: id(wl_output), +} + +msg capture_output_region = 1 { + frame: id(zwlr_screencopy_frame_v1), + overlay_cursor: i32, + output: id(wl_output), + x: i32, + y: i32, + width: i32, + height: i32, +} + +msg destroy = 2 { +}