Merge pull request #290 from mahkoh/jorth/ext-image-copy
screencapture: implement ext_image_copy_capture_manager_v1
This commit is contained in:
commit
6e5d8f530c
44 changed files with 1624 additions and 118 deletions
|
|
@ -138,50 +138,54 @@ Jay uses frame scheduling to achieve input latency as low as 1.5 ms.
|
|||
|
||||
Jay supports the following wayland protocols:
|
||||
|
||||
| Global | Version | Privileged |
|
||||
|-----------------------------------------|:----------------|---------------|
|
||||
| ext_foreign_toplevel_list_v1 | 1 | Yes |
|
||||
| ext_idle_notifier_v1 | 1 | Yes |
|
||||
| ext_session_lock_manager_v1 | 1 | Yes |
|
||||
| ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes |
|
||||
| org_kde_kwin_server_decoration_manager | 1 | |
|
||||
| wl_compositor | 6 | |
|
||||
| wl_data_device_manager | 3 | |
|
||||
| wl_drm | 2 | |
|
||||
| wl_output | 4 | |
|
||||
| wl_seat | 9 | |
|
||||
| wl_shm | 2 | |
|
||||
| wl_subcompositor | 1 | |
|
||||
| wp_alpha_modifier_v1 | 1 | |
|
||||
| wp_content_type_manager_v1 | 1 | |
|
||||
| wp_cursor_shape_manager_v1 | 1 | |
|
||||
| wp_drm_lease_device_v1 | 1 | |
|
||||
| wp_fractional_scale_manager_v1 | 1 | |
|
||||
| wp_linux_drm_syncobj_manager_v1 | 1 | |
|
||||
| wp_presentation | 1 | |
|
||||
| wp_security_context_manager_v1 | 1 | |
|
||||
| wp_single_pixel_buffer_manager_v1 | 1 | |
|
||||
| wp_tearing_control_manager_v1 | 1 | |
|
||||
| wp_viewporter | 1 | |
|
||||
| xdg_activation_v1 | 1 | |
|
||||
| xdg_toplevel_drag_manager_v1 | 1 | |
|
||||
| xdg_wm_base | 6 | |
|
||||
| xdg_wm_dialog_v1 | 1 | |
|
||||
| zwlr_data_control_manager_v1 | 2 | Yes |
|
||||
| zwlr_layer_shell_v1 | 5 | No[^lsaccess] |
|
||||
| zwlr_screencopy_manager_v1 | 3 | Yes |
|
||||
| zwp_idle_inhibit_manager_v1 | 1 | |
|
||||
| zwp_input_method_manager_v2 | 1 | Yes |
|
||||
| zwp_linux_dmabuf_v1 | 5 | |
|
||||
| zwp_pointer_constraints_v1 | 1 | |
|
||||
| zwp_pointer_gestures_v1 | 3 | |
|
||||
| zwp_primary_selection_device_manager_v1 | 1 | |
|
||||
| zwp_relative_pointer_manager_v1 | 1 | |
|
||||
| zwp_tablet_manager_v2 | 1 | |
|
||||
| zwp_text_input_manager_v3 | 1 | |
|
||||
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
|
||||
| zxdg_decoration_manager_v1 | 1 | |
|
||||
| zxdg_output_manager_v1 | 3 | |
|
||||
| Global | Version | Privileged |
|
||||
|------------------------------------------------------|:----------------|---------------|
|
||||
| ext_foreign_toplevel_image_capture_source_manager_v1 | 1 | |
|
||||
| ext_foreign_toplevel_list_v1 | 1 | Yes |
|
||||
| ext_idle_notifier_v1 | 1 | Yes |
|
||||
| ext_image_copy_capture_manager_v1 | 1[^composited] | Yes |
|
||||
| ext_output_image_capture_source_manager_v1 | 1 | |
|
||||
| ext_session_lock_manager_v1 | 1 | Yes |
|
||||
| ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes |
|
||||
| org_kde_kwin_server_decoration_manager | 1 | |
|
||||
| wl_compositor | 6 | |
|
||||
| wl_data_device_manager | 3 | |
|
||||
| wl_drm | 2 | |
|
||||
| wl_output | 4 | |
|
||||
| wl_seat | 9 | |
|
||||
| wl_shm | 2 | |
|
||||
| wl_subcompositor | 1 | |
|
||||
| wp_alpha_modifier_v1 | 1 | |
|
||||
| wp_content_type_manager_v1 | 1 | |
|
||||
| wp_cursor_shape_manager_v1 | 1 | |
|
||||
| wp_drm_lease_device_v1 | 1 | |
|
||||
| wp_fractional_scale_manager_v1 | 1 | |
|
||||
| wp_linux_drm_syncobj_manager_v1 | 1 | |
|
||||
| wp_presentation | 1 | |
|
||||
| wp_security_context_manager_v1 | 1 | |
|
||||
| wp_single_pixel_buffer_manager_v1 | 1 | |
|
||||
| wp_tearing_control_manager_v1 | 1 | |
|
||||
| wp_viewporter | 1 | |
|
||||
| xdg_activation_v1 | 1 | |
|
||||
| xdg_toplevel_drag_manager_v1 | 1 | |
|
||||
| xdg_wm_base | 6 | |
|
||||
| xdg_wm_dialog_v1 | 1 | |
|
||||
| zwlr_data_control_manager_v1 | 2 | Yes |
|
||||
| zwlr_layer_shell_v1 | 5 | No[^lsaccess] |
|
||||
| zwlr_screencopy_manager_v1 | 3 | Yes |
|
||||
| zwp_idle_inhibit_manager_v1 | 1 | |
|
||||
| zwp_input_method_manager_v2 | 1 | Yes |
|
||||
| zwp_linux_dmabuf_v1 | 5 | |
|
||||
| zwp_pointer_constraints_v1 | 1 | |
|
||||
| zwp_pointer_gestures_v1 | 3 | |
|
||||
| zwp_primary_selection_device_manager_v1 | 1 | |
|
||||
| zwp_relative_pointer_manager_v1 | 1 | |
|
||||
| zwp_tablet_manager_v2 | 1 | |
|
||||
| zwp_text_input_manager_v3 | 1 | |
|
||||
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
|
||||
| zxdg_decoration_manager_v1 | 1 | |
|
||||
| zxdg_output_manager_v1 | 3 | |
|
||||
|
||||
[^lsaccess]: Sandboxes can restrict access to this protocol.
|
||||
[^ts_rejected]: Seat creation is always rejected.
|
||||
[^composited]: Cursors are always composited.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
- Vulkan is now the default renderer.
|
||||
- Emulate vblank events on the nvidia driver.
|
||||
- Allow X windows to scale themselves.
|
||||
- Implement ext-image-capture-source-v1.
|
||||
- Implement ext-image-copy-capture-v1.
|
||||
|
||||
# 1.6.0 (2024-09-25)
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,13 @@ impl MetalBackend {
|
|||
return;
|
||||
}
|
||||
};
|
||||
let master = Rc::new(DrmMaster::new(&slf.state.ring, res.fd.clone()));
|
||||
let master = match DrmMaster::new(&slf.state.ring, res.fd.clone()) {
|
||||
Ok(m) => Rc::new(m),
|
||||
Err(e) => {
|
||||
log::error!("Could not open the drm device: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dev = match slf.create_drm_device(dev, &master) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ use {
|
|||
rc::Rc,
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::{c::dev_t, Errno},
|
||||
uapi::c::dev_t,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -114,8 +114,6 @@ pub enum XBackendError {
|
|||
MapWindow(#[source] XconError),
|
||||
#[error("Could not query device")]
|
||||
QueryDevice(#[source] XconError),
|
||||
#[error("Could not fstat the drm device")]
|
||||
DrmDeviceFstat(#[source] Errno),
|
||||
#[error("Render device does not support XRGB8888 format")]
|
||||
XRGB8888,
|
||||
}
|
||||
|
|
@ -174,10 +172,7 @@ pub async fn create(state: &Rc<State>) -> Result<Rc<XBackend>, XBackendError> {
|
|||
Err(e) => return Err(XBackendError::DriOpen(e)),
|
||||
}
|
||||
};
|
||||
let drm_dev = match uapi::fstat(drm.raw()) {
|
||||
Ok(s) => s.st_rdev,
|
||||
Err(e) => return Err(XBackendError::DrmDeviceFstat(e)),
|
||||
};
|
||||
let drm_dev = drm.dev();
|
||||
let gbm = GbmDevice::new(&drm)?;
|
||||
let ctx = match state.create_gfx_context(&drm, None) {
|
||||
Ok(r) => r,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ use {
|
|||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||
ext_image_capture_source_v1::ExtImageCaptureSourceV1,
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
ipc::{
|
||||
wl_data_source::WlDataSource, zwlr_data_control_source_v1::ZwlrDataControlSourceV1,
|
||||
zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1,
|
||||
|
|
@ -31,11 +34,13 @@ use {
|
|||
copyhashmap::{CopyHashMap, Locked},
|
||||
},
|
||||
wire::{
|
||||
JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId,
|
||||
WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId,
|
||||
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId,
|
||||
XdgPositionerId, XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
|
||||
ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id,
|
||||
ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id,
|
||||
ExtImageCopyCaptureSessionV1Id, JayOutputId, JayScreencastId, JayToplevelId,
|
||||
JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId,
|
||||
WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id,
|
||||
WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId, XdgSurfaceId,
|
||||
XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, ZwpPrimarySelectionSourceV1Id,
|
||||
ZwpTabletToolV2Id,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, mem, rc::Rc},
|
||||
|
|
@ -67,6 +72,11 @@ pub struct Objects {
|
|||
pub drm_lease_outputs: CopyHashMap<WpDrmLeaseConnectorV1Id, Rc<WpDrmLeaseConnectorV1>>,
|
||||
pub tablet_tools: CopyHashMap<ZwpTabletToolV2Id, Rc<ZwpTabletToolV2>>,
|
||||
pub xdg_popups: CopyHashMap<XdgPopupId, Rc<XdgPopup>>,
|
||||
pub image_capture_sources: CopyHashMap<ExtImageCaptureSourceV1Id, Rc<ExtImageCaptureSourceV1>>,
|
||||
pub foreign_toplevel_handles:
|
||||
CopyHashMap<ExtForeignToplevelHandleV1Id, Rc<ExtForeignToplevelHandleV1>>,
|
||||
pub ext_copy_sessions:
|
||||
CopyHashMap<ExtImageCopyCaptureSessionV1Id, Rc<ExtImageCopyCaptureSessionV1>>,
|
||||
ids: RefCell<Vec<usize>>,
|
||||
}
|
||||
|
||||
|
|
@ -100,6 +110,9 @@ impl Objects {
|
|||
drm_lease_outputs: Default::default(),
|
||||
tablet_tools: Default::default(),
|
||||
xdg_popups: Default::default(),
|
||||
image_capture_sources: Default::default(),
|
||||
foreign_toplevel_handles: Default::default(),
|
||||
ext_copy_sessions: Default::default(),
|
||||
ids: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
|
@ -137,6 +150,9 @@ impl Objects {
|
|||
self.drm_lease_outputs.clear();
|
||||
self.tablet_tools.clear();
|
||||
self.xdg_popups.clear();
|
||||
self.image_capture_sources.clear();
|
||||
self.foreign_toplevel_handles.clear();
|
||||
self.ext_copy_sessions.clear();
|
||||
}
|
||||
|
||||
pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError>
|
||||
|
|
|
|||
|
|
@ -576,6 +576,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
latch_event: Default::default(),
|
||||
presentation_event: Default::default(),
|
||||
flip_margin_ns: Default::default(),
|
||||
ext_copy_sessions: Default::default(),
|
||||
});
|
||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||
id: state.node_ids.next(),
|
||||
|
|
|
|||
|
|
@ -36,11 +36,10 @@ impl DrmFeedback {
|
|||
ids: &DrmFeedbackIds,
|
||||
render_ctx: &dyn GfxContext,
|
||||
) -> Result<Self, DrmFeedbackError> {
|
||||
let drm = match render_ctx.allocator().drm() {
|
||||
Some(drm) => drm.raw(),
|
||||
let main_device = match render_ctx.allocator().drm() {
|
||||
Some(drm) => drm.dev(),
|
||||
_ => return Err(DrmFeedbackError::NoDrmDevice),
|
||||
};
|
||||
let main_device = uapi::fstat(drm).map_err(OsError::from)?.st_rdev;
|
||||
let (data, index_map) = create_fd_data(render_ctx);
|
||||
let mut memfd =
|
||||
uapi::memfd_create("drm_feedback", c::MFD_CLOEXEC | c::MFD_ALLOW_SEALING).unwrap();
|
||||
|
|
|
|||
|
|
@ -270,6 +270,7 @@ pub trait GfxFramebuffer: Debug {
|
|||
|
||||
pub trait GfxInternalFramebuffer: GfxFramebuffer {
|
||||
fn into_fb(self: Rc<Self>) -> Rc<dyn GfxFramebuffer>;
|
||||
fn stride(&self) -> i32;
|
||||
|
||||
fn staging_size(&self) -> usize;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub struct GlRenderBuffer {
|
|||
pub ctx: Rc<EglContext>,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub stride: i32,
|
||||
pub format: &'static Format,
|
||||
rbo: GLuint,
|
||||
}
|
||||
|
|
@ -30,6 +31,7 @@ impl GlRenderBuffer {
|
|||
ctx: &Rc<EglContext>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<GlRenderBuffer>, RenderError> {
|
||||
let Some(shm_info) = &format.shm_info else {
|
||||
|
|
@ -46,6 +48,7 @@ impl GlRenderBuffer {
|
|||
ctx: ctx.clone(),
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
format,
|
||||
rbo,
|
||||
}))
|
||||
|
|
@ -71,6 +74,7 @@ impl GlRenderBuffer {
|
|||
ctx: ctx.clone(),
|
||||
width: img.dmabuf.width,
|
||||
height: img.dmabuf.height,
|
||||
stride: 0,
|
||||
format: img.dmabuf.format,
|
||||
rbo,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -321,11 +321,11 @@ impl GfxContext for GlRenderContext {
|
|||
_cpu_worker: &Rc<CpuWorker>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
_stride: i32,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<dyn GfxInternalFramebuffer>, GfxError> {
|
||||
let fb = self.ctx.with_current(|| unsafe {
|
||||
GlRenderBuffer::new(&self.ctx, width, height, format)?.create_framebuffer()
|
||||
GlRenderBuffer::new(&self.ctx, width, height, stride, format)?.create_framebuffer()
|
||||
})?;
|
||||
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ impl GfxInternalFramebuffer for Framebuffer {
|
|||
self
|
||||
}
|
||||
|
||||
fn stride(&self) -> i32 {
|
||||
self.gl.rb.stride
|
||||
}
|
||||
|
||||
fn staging_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,8 +91,6 @@ pub enum VulkanError {
|
|||
CreateInstance(#[source] vk::Result),
|
||||
#[error("Could not create a debug-utils messenger")]
|
||||
Messenger(#[source] vk::Result),
|
||||
#[error("Could not fstat the DRM FD")]
|
||||
Fstat(#[source] OsError),
|
||||
#[error("Could not enumerate the physical devices")]
|
||||
EnumeratePhysicalDevices(#[source] vk::Result),
|
||||
#[error("Could not find a vulkan device that matches dev_t {0}")]
|
||||
|
|
|
|||
|
|
@ -104,11 +104,7 @@ impl VulkanInstance {
|
|||
}
|
||||
|
||||
fn find_dev(&self, drm: &Drm) -> Result<PhysicalDevice, VulkanError> {
|
||||
let stat = match uapi::fstat(drm.raw()) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(VulkanError::Fstat(e.into())),
|
||||
};
|
||||
let dev = stat.st_rdev;
|
||||
let dev = drm.dev();
|
||||
log::log!(
|
||||
self.log_level,
|
||||
"Searching for vulkan device with devnum {}:{}",
|
||||
|
|
|
|||
|
|
@ -520,6 +520,13 @@ impl GfxInternalFramebuffer for VulkanImage {
|
|||
self
|
||||
}
|
||||
|
||||
fn stride(&self) -> i32 {
|
||||
let VulkanImageMemory::Internal(shm) = &self.ty else {
|
||||
unreachable!();
|
||||
};
|
||||
shm.stride as _
|
||||
}
|
||||
|
||||
fn staging_size(&self) -> usize {
|
||||
let VulkanImageMemory::Internal(shm) = &self.ty else {
|
||||
unreachable!();
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ use {
|
|||
backend::Backend,
|
||||
client::{Client, ClientCaps},
|
||||
ifs::{
|
||||
ext_foreign_toplevel_image_capture_source_manager_v1::ExtForeignToplevelImageCaptureSourceManagerV1Global,
|
||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1Global,
|
||||
ext_idle_notifier_v1::ExtIdleNotifierV1Global,
|
||||
ext_image_copy::ext_image_copy_capture_manager_v1::ExtImageCopyCaptureManagerV1Global,
|
||||
ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1Global,
|
||||
ext_session_lock_manager_v1::ExtSessionLockManagerV1Global,
|
||||
ipc::{
|
||||
wl_data_device_manager::WlDataDeviceManagerGlobal,
|
||||
|
|
@ -197,6 +200,9 @@ impl Globals {
|
|||
add_singleton!(ZwpPointerGesturesV1Global);
|
||||
add_singleton!(ZwpTabletManagerV2Global);
|
||||
add_singleton!(JayDamageTrackingGlobal);
|
||||
add_singleton!(ExtOutputImageCaptureSourceManagerV1Global);
|
||||
add_singleton!(ExtForeignToplevelImageCaptureSourceManagerV1Global);
|
||||
add_singleton!(ExtImageCopyCaptureManagerV1Global);
|
||||
}
|
||||
|
||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
pub mod ext_foreign_toplevel_handle_v1;
|
||||
pub mod ext_foreign_toplevel_image_capture_source_manager_v1;
|
||||
pub mod ext_foreign_toplevel_list_v1;
|
||||
pub mod ext_idle_notification_v1;
|
||||
pub mod ext_idle_notifier_v1;
|
||||
pub mod ext_image_capture_source_v1;
|
||||
pub mod ext_image_copy;
|
||||
pub mod ext_output_image_capture_source_manager_v1;
|
||||
pub mod ext_session_lock_manager_v1;
|
||||
pub mod ext_session_lock_v1;
|
||||
pub mod ipc;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
client::{Client, ClientError},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::ToplevelNode,
|
||||
tree::ToplevelOpt,
|
||||
wire::{ext_foreign_toplevel_handle_v1::*, ExtForeignToplevelHandleV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
|
|
@ -14,16 +14,15 @@ pub struct ExtForeignToplevelHandleV1 {
|
|||
pub id: ExtForeignToplevelHandleV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub toplevel: Rc<dyn ToplevelNode>,
|
||||
pub toplevel: ToplevelOpt,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl ExtForeignToplevelHandleV1 {
|
||||
fn detach(&self) {
|
||||
self.toplevel
|
||||
.tl_data()
|
||||
.handles
|
||||
.remove(&(self.client.id, self.id));
|
||||
if let Some(tl) = self.toplevel.get() {
|
||||
tl.tl_data().handles.remove(&(self.client.id, self.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +78,11 @@ impl Object for ExtForeignToplevelHandleV1 {
|
|||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ExtForeignToplevelHandleV1);
|
||||
dedicated_add_obj!(
|
||||
ExtForeignToplevelHandleV1,
|
||||
ExtForeignToplevelHandleV1Id,
|
||||
foreign_toplevel_handles
|
||||
);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtForeignToplevelHandleV1Error {
|
||||
|
|
|
|||
110
src/ifs/ext_foreign_toplevel_image_capture_source_manager_v1.rs
Normal file
110
src/ifs/ext_foreign_toplevel_image_capture_source_manager_v1.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::ext_image_capture_source_v1::{ExtImageCaptureSourceV1, ImageCaptureSource},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{
|
||||
ext_foreign_toplevel_image_capture_source_manager_v1::*,
|
||||
ExtForeignToplevelImageCaptureSourceManagerV1Id,
|
||||
},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ExtForeignToplevelImageCaptureSourceManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
impl ExtForeignToplevelImageCaptureSourceManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: ExtForeignToplevelImageCaptureSourceManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), ExtForeignToplevelImageCaptureSourceManagerV1Error> {
|
||||
let obj = Rc::new(ExtForeignToplevelImageCaptureSourceManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtForeignToplevelImageCaptureSourceManagerV1 {
|
||||
pub id: ExtForeignToplevelImageCaptureSourceManagerV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl ExtForeignToplevelImageCaptureSourceManagerV1RequestHandler
|
||||
for ExtForeignToplevelImageCaptureSourceManagerV1
|
||||
{
|
||||
type Error = ExtForeignToplevelImageCaptureSourceManagerV1Error;
|
||||
|
||||
fn create_source(&self, req: CreateSource, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let handle = self.client.lookup(req.toplevel_handle)?;
|
||||
let obj = Rc::new(ExtImageCaptureSourceV1 {
|
||||
id: req.source,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
ty: ImageCaptureSource::Toplevel(handle.toplevel.clone()),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
ExtForeignToplevelImageCaptureSourceManagerV1Global,
|
||||
ExtForeignToplevelImageCaptureSourceManagerV1,
|
||||
ExtForeignToplevelImageCaptureSourceManagerV1Error
|
||||
);
|
||||
|
||||
impl Global for ExtForeignToplevelImageCaptureSourceManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(ExtForeignToplevelImageCaptureSourceManagerV1Global);
|
||||
|
||||
object_base! {
|
||||
self = ExtForeignToplevelImageCaptureSourceManagerV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtForeignToplevelImageCaptureSourceManagerV1 {}
|
||||
|
||||
simple_add_obj!(ExtForeignToplevelImageCaptureSourceManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtForeignToplevelImageCaptureSourceManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(
|
||||
ExtForeignToplevelImageCaptureSourceManagerV1Error,
|
||||
ClientError
|
||||
);
|
||||
|
|
@ -8,7 +8,7 @@ use {
|
|||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::{NodeVisitorBase, ToplevelNode},
|
||||
tree::{NodeVisitorBase, ToplevelOpt},
|
||||
wire::{
|
||||
ext_foreign_toplevel_list_v1::*, ExtForeignToplevelHandleV1Id,
|
||||
ExtForeignToplevelListV1Id,
|
||||
|
|
@ -105,10 +105,7 @@ impl ExtForeignToplevelListV1 {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn publish_toplevel(
|
||||
&self,
|
||||
tl: &Rc<dyn ToplevelNode>,
|
||||
) -> Option<Rc<ExtForeignToplevelHandleV1>> {
|
||||
pub fn publish_toplevel(&self, tl: ToplevelOpt) -> Option<Rc<ExtForeignToplevelHandleV1>> {
|
||||
let id: ExtForeignToplevelHandleV1Id = match self.client.new_id() {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
|
|
@ -120,7 +117,7 @@ impl ExtForeignToplevelListV1 {
|
|||
id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
toplevel: tl.clone(),
|
||||
toplevel: tl,
|
||||
version: self.version,
|
||||
});
|
||||
track!(self.client, handle);
|
||||
|
|
|
|||
54
src/ifs/ext_image_capture_source_v1.rs
Normal file
54
src/ifs/ext_image_capture_source_v1.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::wl_output::OutputGlobalOpt,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::ToplevelOpt,
|
||||
wire::{ext_image_capture_source_v1::*, ExtImageCaptureSourceV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ImageCaptureSource {
|
||||
Output(Rc<OutputGlobalOpt>),
|
||||
Toplevel(ToplevelOpt),
|
||||
}
|
||||
|
||||
pub struct ExtImageCaptureSourceV1 {
|
||||
pub id: ExtImageCaptureSourceV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub ty: ImageCaptureSource,
|
||||
}
|
||||
|
||||
impl ExtImageCaptureSourceV1RequestHandler for ExtImageCaptureSourceV1 {
|
||||
type Error = ExtImageCaptureSourceError;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ExtImageCaptureSourceV1;
|
||||
version = Version(1);
|
||||
}
|
||||
|
||||
impl Object for ExtImageCaptureSourceV1 {}
|
||||
|
||||
dedicated_add_obj!(
|
||||
ExtImageCaptureSourceV1,
|
||||
ExtImageCaptureSourceV1Id,
|
||||
image_capture_sources
|
||||
);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtImageCaptureSourceError {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ExtImageCaptureSourceError, ClientError);
|
||||
4
src/ifs/ext_image_copy.rs
Normal file
4
src/ifs/ext_image_copy.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod ext_image_copy_capture_cursor_session_v1;
|
||||
pub mod ext_image_copy_capture_frame_v1;
|
||||
pub mod ext_image_copy_capture_manager_v1;
|
||||
pub mod ext_image_copy_capture_session_v1;
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
ext_image_capture_source_v1::ImageCaptureSource,
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{ext_image_copy_capture_cursor_session_v1::*, ExtImageCopyCaptureCursorSessionV1Id},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ExtImageCopyCaptureCursorSessionV1 {
|
||||
pub(super) id: ExtImageCopyCaptureCursorSessionV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
pub(super) have_session: Cell<bool>,
|
||||
pub(super) source: ImageCaptureSource,
|
||||
}
|
||||
|
||||
impl ExtImageCopyCaptureCursorSessionV1RequestHandler for ExtImageCopyCaptureCursorSessionV1 {
|
||||
type Error = ExtImageCopyCaptureCursorSessionV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_capture_session(
|
||||
&self,
|
||||
req: GetCaptureSession,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
if self.have_session.replace(true) {
|
||||
return Err(ExtImageCopyCaptureCursorSessionV1Error::HaveSession);
|
||||
}
|
||||
let obj = Rc::new_cyclic(|slf| {
|
||||
ExtImageCopyCaptureSessionV1::new(
|
||||
req.session,
|
||||
&self.client,
|
||||
self.version,
|
||||
&self.source,
|
||||
slf,
|
||||
)
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
obj.send_shm_formats();
|
||||
obj.send_buffer_size(1, 1);
|
||||
obj.send_done();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ExtImageCopyCaptureCursorSessionV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtImageCopyCaptureCursorSessionV1 {}
|
||||
|
||||
simple_add_obj!(ExtImageCopyCaptureCursorSessionV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtImageCopyCaptureCursorSessionV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("The session has already been created")]
|
||||
HaveSession,
|
||||
}
|
||||
efrom!(ExtImageCopyCaptureCursorSessionV1Error, ClientError);
|
||||
371
src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs
Normal file
371
src/ifs/ext_image_copy/ext_image_copy_capture_frame_v1.rs
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
gfx_api::{
|
||||
AcquireSync, AsyncShmGfxTextureCallback, BufferResv, GfxError, GfxFramebuffer,
|
||||
GfxTexture, ReleaseSync, SyncFile, STAGING_DOWNLOAD,
|
||||
},
|
||||
ifs::{
|
||||
ext_image_capture_source_v1::ImageCaptureSource,
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
wl_buffer::WlBufferStorage,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
rect::Region,
|
||||
tree::{Node, OutputNode},
|
||||
utils::{cell_ext::CellExt, errorfmt::ErrorFmt, transform_ext::TransformExt},
|
||||
wire::{ext_image_copy_capture_frame_v1::*, ExtImageCopyCaptureFrameV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub(super) enum FrameStatus {
|
||||
Unused,
|
||||
Capturing,
|
||||
Captured,
|
||||
Ready,
|
||||
Failed,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub(super) enum FrameFailureReason {
|
||||
Unknown,
|
||||
BufferConstraints,
|
||||
Stopped,
|
||||
}
|
||||
|
||||
pub struct ExtImageCopyCaptureFrameV1 {
|
||||
pub(super) id: ExtImageCopyCaptureFrameV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) session: Rc<ExtImageCopyCaptureSessionV1>,
|
||||
}
|
||||
|
||||
impl ExtImageCopyCaptureFrameV1 {
|
||||
fn ensure_unused(&self) -> Result<(), ExtImageCopyCaptureFrameV1Error> {
|
||||
if self.session.status.get() != FrameStatus::Unused {
|
||||
return Err(ExtImageCopyCaptureFrameV1Error::AlreadyCaptured);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn fail(&self, reason: FrameFailureReason) {
|
||||
let reason = match reason {
|
||||
FrameFailureReason::Unknown => 0,
|
||||
FrameFailureReason::BufferConstraints => 1,
|
||||
FrameFailureReason::Stopped => 2,
|
||||
};
|
||||
self.client.event(Failed {
|
||||
self_id: self.id,
|
||||
reason,
|
||||
});
|
||||
self.session.status.set(FrameStatus::Failed);
|
||||
self.session.presentation_listener.detach();
|
||||
self.session.buffer.take();
|
||||
self.session.pending_download.take();
|
||||
self.session.force_capture.set(true);
|
||||
}
|
||||
|
||||
fn try_copy(
|
||||
self: &Rc<Self>,
|
||||
on: &OutputNode,
|
||||
size: (i32, i32),
|
||||
f: impl FnOnce(
|
||||
Rc<dyn GfxFramebuffer>,
|
||||
AcquireSync,
|
||||
ReleaseSync,
|
||||
) -> Result<Option<SyncFile>, GfxError>,
|
||||
) -> Result<(), FrameFailureReason> {
|
||||
let Some(ctx) = self.client.state.render_ctx.get() else {
|
||||
return Err(FrameFailureReason::BufferConstraints);
|
||||
};
|
||||
let buffer = self.session.buffer.get().unwrap();
|
||||
if size != buffer.rect.size() {
|
||||
self.session.buffer_size_changed();
|
||||
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/222
|
||||
// self.fail(FrameFailureReason::BufferConstraints);
|
||||
// return;
|
||||
}
|
||||
if let Err(e) = buffer.update_framebuffer() {
|
||||
log::error!("Could not import buffer: {}", ErrorFmt(e));
|
||||
return Err(FrameFailureReason::BufferConstraints);
|
||||
}
|
||||
let storage = &*buffer.storage.borrow();
|
||||
let Some(storage) = storage else {
|
||||
return Err(FrameFailureReason::BufferConstraints);
|
||||
};
|
||||
let mut shm_bridge = self.session.shm_bridge.take();
|
||||
let mut shm_staging = self.session.shm_staging.take();
|
||||
match storage {
|
||||
WlBufferStorage::Shm { mem, stride } => {
|
||||
if let Some(b) = &shm_bridge {
|
||||
if b.physical_size() != buffer.rect.size()
|
||||
|| b.format() != buffer.format
|
||||
|| b.stride() != *stride
|
||||
{
|
||||
shm_bridge = None;
|
||||
}
|
||||
}
|
||||
let bridge = match shm_bridge {
|
||||
Some(b) => b,
|
||||
_ => {
|
||||
let res = ctx.clone().create_internal_fb(
|
||||
&self.client.state.cpu_worker,
|
||||
buffer.rect.width(),
|
||||
buffer.rect.height(),
|
||||
*stride,
|
||||
buffer.format,
|
||||
);
|
||||
match res {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
log::error!("Could not allocate staging fb: {}", ErrorFmt(e));
|
||||
return Err(FrameFailureReason::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(s) = &shm_staging {
|
||||
if s.size() != bridge.staging_size() {
|
||||
shm_staging = None;
|
||||
}
|
||||
}
|
||||
let staging = match shm_staging {
|
||||
Some(s) => s,
|
||||
_ => ctx.create_staging_buffer(bridge.staging_size(), STAGING_DOWNLOAD),
|
||||
};
|
||||
let res = f(
|
||||
bridge.clone().into_fb(),
|
||||
AcquireSync::Unnecessary,
|
||||
ReleaseSync::None,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not copy frame to staging texture: {}", ErrorFmt(e));
|
||||
return Err(FrameFailureReason::Unknown);
|
||||
}
|
||||
let res = bridge.clone().download(
|
||||
&staging,
|
||||
self.clone(),
|
||||
mem.clone(),
|
||||
Region::new2(buffer.rect),
|
||||
);
|
||||
match res {
|
||||
Ok(d) => self.session.pending_download.set(d),
|
||||
Err(e) => {
|
||||
log::error!("Could not initiate bridge download: {}", ErrorFmt(e));
|
||||
return Err(FrameFailureReason::Unknown);
|
||||
}
|
||||
}
|
||||
self.session.shm_bridge.set(Some(bridge));
|
||||
self.session.shm_staging.set(Some(staging));
|
||||
}
|
||||
WlBufferStorage::Dmabuf { fb, .. } => {
|
||||
let Some(fb) = fb else {
|
||||
return Err(FrameFailureReason::BufferConstraints);
|
||||
};
|
||||
let res = f(fb.clone(), AcquireSync::Implicit, ReleaseSync::Implicit);
|
||||
if let Err(e) = res {
|
||||
log::error!("Could not copy frame to client fb: {}", ErrorFmt(e));
|
||||
return Err(FrameFailureReason::Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.session
|
||||
.presentation_listener
|
||||
.attach(&on.presentation_event);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy(
|
||||
self: &Rc<Self>,
|
||||
on: &OutputNode,
|
||||
size: (i32, i32),
|
||||
f: impl FnOnce(
|
||||
Rc<dyn GfxFramebuffer>,
|
||||
AcquireSync,
|
||||
ReleaseSync,
|
||||
) -> Result<Option<SyncFile>, GfxError>,
|
||||
) {
|
||||
match self.try_copy(on, size, f) {
|
||||
Ok(()) => self.session.status.set(FrameStatus::Captured),
|
||||
Err(e) => self.fail(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn copy_texture(
|
||||
self: &Rc<Self>,
|
||||
on: &OutputNode,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
render_hardware_cursors: bool,
|
||||
x_off: i32,
|
||||
y_off: i32,
|
||||
size: Option<(i32, i32)>,
|
||||
) {
|
||||
let transform = on.global.persistent.transform.get();
|
||||
let req_size = size.unwrap_or(transform.maybe_swap(texture.size()));
|
||||
self.copy(on, req_size, |fb, aq, re| {
|
||||
self.client.state.perform_screencopy(
|
||||
texture,
|
||||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
&fb,
|
||||
aq,
|
||||
re,
|
||||
jay_config::video::Transform::None,
|
||||
on.global.pos.get(),
|
||||
render_hardware_cursors,
|
||||
x_off,
|
||||
y_off,
|
||||
size,
|
||||
transform,
|
||||
on.global.persistent.scale.get(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn copy_node(self: &Rc<Self>, on: &OutputNode, node: &dyn Node, size: (i32, i32)) {
|
||||
let scale = on.global.persistent.scale.get();
|
||||
self.copy(on, size, |fb, aq, re| {
|
||||
fb.render_node(
|
||||
aq,
|
||||
re,
|
||||
node,
|
||||
&self.client.state,
|
||||
Some(node.node_absolute_position()),
|
||||
scale,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
jay_config::video::Transform::None,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn maybe_ready(&self) {
|
||||
if self.session.pending_download.is_some() {
|
||||
return;
|
||||
}
|
||||
let Some((tv_sec, tv_nsec)) = self.session.presented.get() else {
|
||||
return;
|
||||
};
|
||||
if let Some(buffer) = self.session.buffer.get() {
|
||||
self.client.event(Damage {
|
||||
self_id: self.id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: buffer.rect.width(),
|
||||
height: buffer.rect.height(),
|
||||
});
|
||||
}
|
||||
self.client.event(PresentationTime {
|
||||
self_id: self.id,
|
||||
tv_sec_hi: (tv_sec >> 32) as u32,
|
||||
tv_sec_lo: tv_sec as u32,
|
||||
tv_nsec,
|
||||
});
|
||||
self.client.event(Ready { self_id: self.id });
|
||||
self.session.status.set(FrameStatus::Ready);
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtImageCopyCaptureFrameV1RequestHandler for ExtImageCopyCaptureFrameV1 {
|
||||
type Error = ExtImageCopyCaptureFrameV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if self.session.status.get() == FrameStatus::Captured {
|
||||
self.session.shm_staging.take();
|
||||
self.session.shm_bridge.take();
|
||||
}
|
||||
self.session.frame.take();
|
||||
self.session.presentation_listener.detach();
|
||||
self.session.presented.take();
|
||||
self.session.pending_download.take();
|
||||
self.session.status.set(FrameStatus::Unused);
|
||||
self.session.buffer.take();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn attach_buffer(&self, req: AttachBuffer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.ensure_unused()?;
|
||||
let buffer = self.client.lookup(req.buffer)?;
|
||||
self.session.buffer.set(Some(buffer));
|
||||
self.session.size_debounce.set(false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn damage_buffer(&self, _req: DamageBuffer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.ensure_unused()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn capture(&self, _req: Capture, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.ensure_unused()?;
|
||||
if self.session.buffer.is_none() {
|
||||
return Err(ExtImageCopyCaptureFrameV1Error::NoBuffer);
|
||||
}
|
||||
if self.session.stopped.get() {
|
||||
self.fail(FrameFailureReason::Stopped);
|
||||
return Ok(());
|
||||
}
|
||||
self.session.status.set(FrameStatus::Capturing);
|
||||
if self.session.force_capture.get() {
|
||||
self.session.force_capture.set(false);
|
||||
match &self.session.source {
|
||||
ImageCaptureSource::Output(o) => {
|
||||
if let Some(node) = o.node.get() {
|
||||
node.global.connector.damage();
|
||||
}
|
||||
}
|
||||
ImageCaptureSource::Toplevel(tl) => {
|
||||
if let Some(tl) = tl.get() {
|
||||
tl.tl_data().output().global.connector.damage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncShmGfxTextureCallback for ExtImageCopyCaptureFrameV1 {
|
||||
fn completed(self: Rc<Self>, res: Result<(), GfxError>) {
|
||||
self.session.pending_download.take();
|
||||
if self.session.status.get() != FrameStatus::Captured {
|
||||
return;
|
||||
}
|
||||
if let Err(e) = res {
|
||||
log::error!("Bridge download failed: {}", ErrorFmt(e));
|
||||
self.fail(FrameFailureReason::Unknown);
|
||||
return;
|
||||
}
|
||||
self.maybe_ready();
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ExtImageCopyCaptureFrameV1;
|
||||
version = self.session.version;
|
||||
}
|
||||
|
||||
impl Object for ExtImageCopyCaptureFrameV1 {}
|
||||
|
||||
simple_add_obj!(ExtImageCopyCaptureFrameV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtImageCopyCaptureFrameV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("The frame has already been captured")]
|
||||
AlreadyCaptured,
|
||||
#[error("The frame does not have a buffer attached")]
|
||||
NoBuffer,
|
||||
}
|
||||
efrom!(ExtImageCopyCaptureFrameV1Error, ClientError);
|
||||
174
src/ifs/ext_image_copy/ext_image_copy_capture_manager_v1.rs
Normal file
174
src/ifs/ext_image_copy/ext_image_copy_capture_manager_v1.rs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientCaps, ClientError, CAP_SCREENCOPY_MANAGER},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
ext_image_capture_source_v1::ImageCaptureSource,
|
||||
ext_image_copy::{
|
||||
ext_image_copy_capture_cursor_session_v1::ExtImageCopyCaptureCursorSessionV1,
|
||||
ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
},
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{ext_image_copy_capture_manager_v1::*, ExtImageCopyCaptureManagerV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ExtImageCopyCaptureManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
impl ExtImageCopyCaptureManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: ExtImageCopyCaptureManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), ExtImageCopyCaptureManagerV1Error> {
|
||||
let obj = Rc::new(ExtImageCopyCaptureManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtImageCopyCaptureManagerV1 {
|
||||
pub(super) id: ExtImageCopyCaptureManagerV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
}
|
||||
|
||||
impl ExtImageCopyCaptureManagerV1RequestHandler for ExtImageCopyCaptureManagerV1 {
|
||||
type Error = ExtImageCopyCaptureManagerV1Error;
|
||||
|
||||
fn create_session(&self, req: CreateSession, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let source = self.client.lookup(req.source)?;
|
||||
let obj = Rc::new_cyclic(|slf| {
|
||||
ExtImageCopyCaptureSessionV1::new(
|
||||
req.session,
|
||||
&self.client,
|
||||
self.version,
|
||||
&source.ty,
|
||||
slf,
|
||||
)
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
'send_constraints: {
|
||||
let id = (self.client.id, obj.id);
|
||||
match &source.ty {
|
||||
ImageCaptureSource::Output(o) => {
|
||||
let Some(node) = o.node() else {
|
||||
obj.send_stopped();
|
||||
break 'send_constraints;
|
||||
};
|
||||
node.ext_copy_sessions.set(id, obj.clone());
|
||||
}
|
||||
ImageCaptureSource::Toplevel(tl) => {
|
||||
let Some(node) = tl.get() else {
|
||||
obj.send_stopped();
|
||||
break 'send_constraints;
|
||||
};
|
||||
let data = node.tl_data();
|
||||
data.ext_copy_sessions.set(id, obj.clone());
|
||||
if data.visible.get() {
|
||||
obj.latch_listener.attach(&data.output().latch_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(ctx) = self.client.state.render_ctx.get() else {
|
||||
obj.send_stopped();
|
||||
break 'send_constraints;
|
||||
};
|
||||
obj.send_current_buffer_size();
|
||||
obj.send_shm_formats();
|
||||
if let Some(drm) = ctx.allocator().drm() {
|
||||
obj.send_dmabuf_device(drm.dev());
|
||||
for format in ctx.formats().values() {
|
||||
if format.write_modifiers.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let modifiers: Vec<_> = format.write_modifiers.keys().copied().collect();
|
||||
obj.send_dmabuf_format(format.format, &modifiers);
|
||||
}
|
||||
}
|
||||
obj.send_done();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_pointer_cursor_session(
|
||||
&self,
|
||||
req: CreatePointerCursorSession,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
let source = self.client.lookup(req.source)?;
|
||||
let obj = Rc::new(ExtImageCopyCaptureCursorSessionV1 {
|
||||
id: req.session,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
source: source.ty.clone(),
|
||||
have_session: Default::default(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
ExtImageCopyCaptureManagerV1Global,
|
||||
ExtImageCopyCaptureManagerV1,
|
||||
ExtImageCopyCaptureManagerV1Error
|
||||
);
|
||||
|
||||
impl Global for ExtImageCopyCaptureManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_SCREENCOPY_MANAGER
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(ExtImageCopyCaptureManagerV1Global);
|
||||
|
||||
object_base! {
|
||||
self = ExtImageCopyCaptureManagerV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtImageCopyCaptureManagerV1 {}
|
||||
|
||||
simple_add_obj!(ExtImageCopyCaptureManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtImageCopyCaptureManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ExtImageCopyCaptureManagerV1Error, ClientError);
|
||||
341
src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs
Normal file
341
src/ifs/ext_image_copy/ext_image_copy_capture_session_v1.rs
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
format::{Format, FORMATS},
|
||||
gfx_api::{
|
||||
AcquireSync, BufferResv, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture,
|
||||
PendingShmTransfer, ReleaseSync,
|
||||
},
|
||||
ifs::{
|
||||
ext_image_capture_source_v1::ImageCaptureSource,
|
||||
ext_image_copy::ext_image_copy_capture_frame_v1::{
|
||||
ExtImageCopyCaptureFrameV1, FrameFailureReason, FrameStatus,
|
||||
},
|
||||
wl_buffer::WlBuffer,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
time::Time,
|
||||
tree::{LatchListener, OutputNode, PresentationListener},
|
||||
utils::{cell_ext::CellExt, clonecell::CloneCell, event_listener::EventListener},
|
||||
video::Modifier,
|
||||
wire::{ext_image_copy_capture_session_v1::*, ExtImageCopyCaptureSessionV1Id},
|
||||
},
|
||||
std::{
|
||||
cell::Cell,
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
thiserror::Error,
|
||||
uapi::c,
|
||||
};
|
||||
|
||||
pub struct ExtImageCopyCaptureSessionV1 {
|
||||
pub(super) id: ExtImageCopyCaptureSessionV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
pub(super) frame: CloneCell<Option<Rc<ExtImageCopyCaptureFrameV1>>>,
|
||||
pub(super) shm_bridge: CloneCell<Option<Rc<dyn GfxInternalFramebuffer>>>,
|
||||
pub(super) shm_staging: CloneCell<Option<Rc<dyn GfxStagingBuffer>>>,
|
||||
pub(super) source: ImageCaptureSource,
|
||||
pub(super) force_capture: Cell<bool>,
|
||||
pub(super) stopped: Cell<bool>,
|
||||
pub(super) latch_listener: EventListener<dyn LatchListener>,
|
||||
pub(super) presentation_listener: EventListener<dyn PresentationListener>,
|
||||
pub(super) size_debounce: Cell<bool>,
|
||||
pub(super) status: Cell<FrameStatus>,
|
||||
pub(super) buffer: CloneCell<Option<Rc<WlBuffer>>>,
|
||||
pub(super) pending_download: Cell<Option<PendingShmTransfer>>,
|
||||
pub(super) presented: Cell<Option<(u64, u32)>>,
|
||||
}
|
||||
|
||||
impl ExtImageCopyCaptureSessionV1 {
|
||||
pub(super) fn new(
|
||||
id: ExtImageCopyCaptureSessionV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
source: &ImageCaptureSource,
|
||||
slf: &Weak<Self>,
|
||||
) -> Self {
|
||||
ExtImageCopyCaptureSessionV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
frame: Default::default(),
|
||||
shm_bridge: Default::default(),
|
||||
shm_staging: Default::default(),
|
||||
source: source.clone(),
|
||||
force_capture: Cell::new(true),
|
||||
stopped: Default::default(),
|
||||
latch_listener: EventListener::new(slf.clone()),
|
||||
presentation_listener: EventListener::new(slf.clone()),
|
||||
size_debounce: Default::default(),
|
||||
status: Cell::new(FrameStatus::Unused),
|
||||
buffer: Default::default(),
|
||||
pending_download: Default::default(),
|
||||
presented: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer_size_changed(&self) {
|
||||
if self.size_debounce.replace(true) {
|
||||
return;
|
||||
}
|
||||
self.force_capture.set(true);
|
||||
self.send_current_buffer_size();
|
||||
self.send_done();
|
||||
}
|
||||
|
||||
pub(super) fn send_current_buffer_size(&self) {
|
||||
let (width, height) = match &self.source {
|
||||
ImageCaptureSource::Output(o) => {
|
||||
let Some(node) = o.node() else {
|
||||
return;
|
||||
};
|
||||
node.global.pixel_size()
|
||||
}
|
||||
ImageCaptureSource::Toplevel(o) => {
|
||||
let Some(node) = o.get() else {
|
||||
return;
|
||||
};
|
||||
node.tl_data().desired_pixel_size()
|
||||
}
|
||||
};
|
||||
self.send_buffer_size(width, height);
|
||||
}
|
||||
|
||||
pub(super) fn send_buffer_size(&self, width: i32, height: i32) {
|
||||
self.client.event(BufferSize {
|
||||
self_id: self.id,
|
||||
width: width as _,
|
||||
height: height as _,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_done(&self) {
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn stop(&self) {
|
||||
self.stopped.set(true);
|
||||
self.send_stopped();
|
||||
self.stop_pending_frame();
|
||||
}
|
||||
|
||||
fn stop_pending_frame(&self) {
|
||||
if let Some(frame) = self.frame.get() {
|
||||
if let FrameStatus::Capturing | FrameStatus::Captured = self.status.get() {
|
||||
frame.fail(FrameFailureReason::Stopped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn send_stopped(&self) {
|
||||
self.client.event(Stopped { self_id: self.id });
|
||||
}
|
||||
|
||||
pub(super) fn send_shm_formats(&self) {
|
||||
for format in FORMATS {
|
||||
if format.shm_info.is_some() {
|
||||
self.client.event(ShmFormat {
|
||||
self_id: self.id,
|
||||
format: format.wl_id.unwrap_or(format.drm),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn send_dmabuf_device(&self, device: c::dev_t) {
|
||||
self.client.event(DmabufDevice {
|
||||
self_id: self.id,
|
||||
device,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_dmabuf_format(&self, format: &Format, modifiers: &[Modifier]) {
|
||||
self.client.event(DmabufFormat {
|
||||
self_id: self.id,
|
||||
format: format.drm,
|
||||
modifiers: uapi::as_bytes(modifiers),
|
||||
});
|
||||
}
|
||||
|
||||
fn detach(&self) {
|
||||
let id = (self.client.id, self.id);
|
||||
match &self.source {
|
||||
ImageCaptureSource::Output(o) => {
|
||||
if let Some(n) = o.node() {
|
||||
n.ext_copy_sessions.remove(&id);
|
||||
}
|
||||
}
|
||||
ImageCaptureSource::Toplevel(tl) => {
|
||||
if let Some(n) = tl.get() {
|
||||
n.tl_data().ext_copy_sessions.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.frame.take();
|
||||
self.shm_bridge.take();
|
||||
self.shm_staging.take();
|
||||
self.latch_listener.detach();
|
||||
self.presentation_listener.detach();
|
||||
self.buffer.take();
|
||||
self.pending_download.take();
|
||||
self.presented.take();
|
||||
}
|
||||
|
||||
pub fn update_latch_listener(&self) {
|
||||
let ImageCaptureSource::Toplevel(tl) = &self.source else {
|
||||
return;
|
||||
};
|
||||
let Some(tl) = tl.get() else {
|
||||
return;
|
||||
};
|
||||
let data = tl.tl_data();
|
||||
if data.visible.get() {
|
||||
self.latch_listener.attach(&data.output().latch_event);
|
||||
} else {
|
||||
self.latch_listener.detach();
|
||||
}
|
||||
if self.status.get() == FrameStatus::Captured && self.presented.is_none() {
|
||||
self.presentation_listener.detach();
|
||||
let now = Time::now_unchecked();
|
||||
self.presented
|
||||
.set(Some((now.0.tv_sec as _, now.0.tv_nsec as _)));
|
||||
if let Some(frame) = self.frame.get() {
|
||||
frame.maybe_ready();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_texture(
|
||||
self: &Rc<Self>,
|
||||
on: &OutputNode,
|
||||
texture: &Rc<dyn GfxTexture>,
|
||||
resv: Option<&Rc<dyn BufferResv>>,
|
||||
acquire_sync: &AcquireSync,
|
||||
release_sync: ReleaseSync,
|
||||
render_hardware_cursors: bool,
|
||||
x_off: i32,
|
||||
y_off: i32,
|
||||
size: Option<(i32, i32)>,
|
||||
) {
|
||||
if self.status.get() == FrameStatus::Capturing {
|
||||
if let Some(frame) = self.frame.get() {
|
||||
frame.copy_texture(
|
||||
on,
|
||||
texture,
|
||||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
render_hardware_cursors,
|
||||
x_off,
|
||||
y_off,
|
||||
size,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.force_capture.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtImageCopyCaptureSessionV1RequestHandler for ExtImageCopyCaptureSessionV1 {
|
||||
type Error = ExtImageCopyCaptureSessionV1Error;
|
||||
|
||||
fn create_frame(&self, req: CreateFrame, slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if self.frame.is_some() {
|
||||
return Err(ExtImageCopyCaptureSessionV1Error::HaveFrame);
|
||||
}
|
||||
let obj = Rc::new(ExtImageCopyCaptureFrameV1 {
|
||||
id: req.frame,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
session: slf.clone(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
self.frame.set(Some(obj));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.stop_pending_frame();
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LatchListener for ExtImageCopyCaptureSessionV1 {
|
||||
fn after_latch(self: Rc<Self>, on: &OutputNode) {
|
||||
let ImageCaptureSource::Toplevel(tl) = &self.source else {
|
||||
return;
|
||||
};
|
||||
let Some(tl) = tl.get() else {
|
||||
return;
|
||||
};
|
||||
let data = tl.tl_data();
|
||||
if !data.visible.get() {
|
||||
return;
|
||||
}
|
||||
let Some(frame) = self.frame.get() else {
|
||||
self.force_capture.set(true);
|
||||
return;
|
||||
};
|
||||
if self.status.get() != FrameStatus::Capturing {
|
||||
self.force_capture.set(true);
|
||||
return;
|
||||
}
|
||||
frame.copy_node(on, tl.tl_as_node(), data.desired_pixel_size());
|
||||
}
|
||||
}
|
||||
|
||||
impl PresentationListener for ExtImageCopyCaptureSessionV1 {
|
||||
fn presented(
|
||||
self: Rc<Self>,
|
||||
_output: &OutputNode,
|
||||
tv_sec: u64,
|
||||
tv_nsec: u32,
|
||||
_refresh: u32,
|
||||
_seq: u64,
|
||||
_flags: u32,
|
||||
) {
|
||||
self.presentation_listener.detach();
|
||||
let Some(frame) = self.frame.get() else {
|
||||
return;
|
||||
};
|
||||
if self.status.get() != FrameStatus::Captured {
|
||||
return;
|
||||
};
|
||||
self.presented.set(Some((tv_sec, tv_nsec)));
|
||||
frame.maybe_ready();
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ExtImageCopyCaptureSessionV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtImageCopyCaptureSessionV1 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
dedicated_add_obj!(
|
||||
ExtImageCopyCaptureSessionV1,
|
||||
ExtImageCopyCaptureSessionV1Id,
|
||||
ext_copy_sessions
|
||||
);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtImageCopyCaptureSessionV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("There already is a pending frame")]
|
||||
HaveFrame,
|
||||
}
|
||||
efrom!(ExtImageCopyCaptureSessionV1Error, ClientError);
|
||||
104
src/ifs/ext_output_image_capture_source_manager_v1.rs
Normal file
104
src/ifs/ext_output_image_capture_source_manager_v1.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::ext_image_capture_source_v1::{ExtImageCaptureSourceV1, ImageCaptureSource},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{
|
||||
ext_output_image_capture_source_manager_v1::*, ExtOutputImageCaptureSourceManagerV1Id,
|
||||
},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ExtOutputImageCaptureSourceManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
impl ExtOutputImageCaptureSourceManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: ExtOutputImageCaptureSourceManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), ExtOutputImageCaptureSourceManagerV1Error> {
|
||||
let obj = Rc::new(ExtOutputImageCaptureSourceManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtOutputImageCaptureSourceManagerV1 {
|
||||
pub id: ExtOutputImageCaptureSourceManagerV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
}
|
||||
|
||||
impl ExtOutputImageCaptureSourceManagerV1RequestHandler for ExtOutputImageCaptureSourceManagerV1 {
|
||||
type Error = ExtOutputImageCaptureSourceManagerV1Error;
|
||||
|
||||
fn create_source(&self, req: CreateSource, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let output = self.client.lookup(req.output)?;
|
||||
let obj = Rc::new(ExtImageCaptureSourceV1 {
|
||||
id: req.source,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
ty: ImageCaptureSource::Output(output.global.clone()),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
ExtOutputImageCaptureSourceManagerV1Global,
|
||||
ExtOutputImageCaptureSourceManagerV1,
|
||||
ExtOutputImageCaptureSourceManagerV1Error
|
||||
);
|
||||
|
||||
impl Global for ExtOutputImageCaptureSourceManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(ExtOutputImageCaptureSourceManagerV1Global);
|
||||
|
||||
object_base! {
|
||||
self = ExtOutputImageCaptureSourceManagerV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtOutputImageCaptureSourceManagerV1 {}
|
||||
|
||||
simple_add_obj!(ExtOutputImageCaptureSourceManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtOutputImageCaptureSourceManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ExtOutputImageCaptureSourceManagerV1Error, ClientError);
|
||||
|
|
@ -89,7 +89,7 @@ enum Target {
|
|||
}
|
||||
|
||||
impl LatchListener for JayScreencast {
|
||||
fn after_latch(self: Rc<Self>) {
|
||||
fn after_latch(self: Rc<Self>, _on: &OutputNode) {
|
||||
self.schedule_toplevel_screencast();
|
||||
}
|
||||
}
|
||||
|
|
@ -780,17 +780,10 @@ efrom!(JayScreencastError, ClientError);
|
|||
|
||||
fn target_size(target: Option<&Target>) -> (i32, i32) {
|
||||
if let Some(target) = target {
|
||||
match target {
|
||||
Target::Output(o) => return o.global.pixel_size(),
|
||||
Target::Toplevel(t) => {
|
||||
let data = t.tl_data();
|
||||
let (dw, dh) = data.desired_extents.get().size();
|
||||
if let Some(ws) = data.workspace.get() {
|
||||
let scale = ws.output.get().global.persistent.scale.get();
|
||||
return scale.pixel_size(dw, dh);
|
||||
};
|
||||
}
|
||||
}
|
||||
return match target {
|
||||
Target::Output(o) => o.global.pixel_size(),
|
||||
Target::Toplevel(t) => t.tl_data().desired_pixel_size(),
|
||||
};
|
||||
}
|
||||
(0, 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,9 +143,13 @@ pub struct SurfaceSendPreferredScaleVisitor;
|
|||
|
||||
impl SurfaceSendPreferredScaleVisitor {
|
||||
fn schedule_realloc(&self, tl: &impl ToplevelNode) {
|
||||
for sc in tl.tl_data().jay_screencasts.lock().values() {
|
||||
let data = tl.tl_data();
|
||||
for sc in data.jay_screencasts.lock().values() {
|
||||
sc.schedule_realloc_or_reconfigure();
|
||||
}
|
||||
for sc in data.ext_copy_sessions.lock().values() {
|
||||
sc.buffer_size_changed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2102,7 +2106,7 @@ impl VblankListener for WlSurface {
|
|||
}
|
||||
|
||||
impl LatchListener for WlSurface {
|
||||
fn after_latch(self: Rc<Self>) {
|
||||
fn after_latch(self: Rc<Self>, _on: &OutputNode) {
|
||||
if self.visible.get() {
|
||||
if self.latched_commit_version.get() < self.commit_version.get() {
|
||||
let latched = &mut *self.latched_presentation_feedback.borrow_mut();
|
||||
|
|
|
|||
|
|
@ -210,6 +210,8 @@ efrom!(WpDrmLeaseDeviceV1Error, ClientError);
|
|||
enum ReopenError {
|
||||
#[error("Could not open the dev node")]
|
||||
OpenNode(#[source] OsError),
|
||||
#[error("Could not create the DRM device")]
|
||||
CreateDrm(#[source] DrmError),
|
||||
#[error("Could not drop DRM master")]
|
||||
DropMaster(#[source] DrmError),
|
||||
}
|
||||
|
|
@ -218,7 +220,7 @@ fn reopen_card(devnode: &str) -> Result<Rc<OwnedFd>, ReopenError> {
|
|||
let fd = uapi::open(devnode, c::O_RDWR | c::O_CLOEXEC, 0)
|
||||
.map_err(|e| ReopenError::OpenNode(e.into()))?;
|
||||
let fd = Rc::new(fd);
|
||||
let drm = Drm::open_existing(fd.clone());
|
||||
let drm = Drm::open_existing(fd.clone()).map_err(ReopenError::CreateDrm)?;
|
||||
if drm.is_master() {
|
||||
drm.drop_master().map_err(ReopenError::DropMaster)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use {
|
|||
on_change::OnChange, oserror::OsError, syncqueue::SyncQueue,
|
||||
},
|
||||
video::{
|
||||
drm::{ConnectorType, Drm},
|
||||
drm::{ConnectorType, Drm, DrmError},
|
||||
gbm::{GbmDevice, GbmError},
|
||||
},
|
||||
},
|
||||
|
|
@ -42,6 +42,8 @@ pub enum TestBackendError {
|
|||
NoDrmNode,
|
||||
#[error("Could not open drm node {0}")]
|
||||
OpenDrmNode(String, #[source] OsError),
|
||||
#[error("Could not open the drm device")]
|
||||
OpenDrmDevice(#[source] DrmError),
|
||||
#[error("Could not create a render context")]
|
||||
RenderContext(#[source] GfxError),
|
||||
#[error("Could not create a gbm device")]
|
||||
|
|
@ -264,7 +266,7 @@ where
|
|||
))
|
||||
}
|
||||
};
|
||||
let drm = Drm::open_existing(file);
|
||||
let drm = Drm::open_existing(file).map_err(TestBackendError::OpenDrmDevice)?;
|
||||
f(drm)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -559,6 +559,13 @@ impl GfxInternalFramebuffer for TestGfxFb {
|
|||
self
|
||||
}
|
||||
|
||||
fn stride(&self) -> i32 {
|
||||
let TestGfxImage::Shm(shm) = &*self.img else {
|
||||
unreachable!();
|
||||
};
|
||||
shm.stride
|
||||
}
|
||||
|
||||
fn staging_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,13 +162,14 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
|
|||
|
||||
fn device(&self, fd: Rc<OwnedFd>, server_formats: Option<AHashMap<u32, GfxFormat>>) {
|
||||
self.render_ctx.take();
|
||||
let dev_id = match uapi::fstat(fd.raw()) {
|
||||
Ok(s) => s.st_rdev,
|
||||
let drm = match Drm::open_existing(fd) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
log::error!("Could not fstat display device: {}", ErrorFmt(e));
|
||||
log::error!("Could not open the drm device: {}", ErrorFmt(e));
|
||||
return;
|
||||
}
|
||||
};
|
||||
let dev_id = drm.dev();
|
||||
let mut render_ctx = None;
|
||||
if let Some(ctx) = self.state.render_ctxs.get(&dev_id) {
|
||||
if let Some(ctx) = ctx.upgrade() {
|
||||
|
|
@ -176,7 +177,6 @@ impl UsrJayRenderCtxOwner for PortalDisplay {
|
|||
}
|
||||
}
|
||||
if render_ctx.is_none() {
|
||||
let drm = Drm::open_existing(fd);
|
||||
let ctx =
|
||||
match create_gfx_context(&self.state.eng, &self.state.ring, &drm, GfxApi::OpenGl) {
|
||||
Ok(c) => c,
|
||||
|
|
|
|||
|
|
@ -555,6 +555,9 @@ impl State {
|
|||
for sc in client.data.objects.screencasts.lock().values() {
|
||||
scs.push(sc.clone());
|
||||
}
|
||||
for sc in client.data.objects.ext_copy_sessions.lock().values() {
|
||||
sc.stop();
|
||||
}
|
||||
}
|
||||
for sc in scs {
|
||||
sc.do_destroy();
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ impl ConnectorHandler {
|
|||
vblank_event: Default::default(),
|
||||
presentation_event: Default::default(),
|
||||
flip_margin_ns: Default::default(),
|
||||
ext_copy_sessions: Default::default(),
|
||||
});
|
||||
on.update_visible();
|
||||
on.update_rects();
|
||||
|
|
@ -284,6 +285,9 @@ impl ConnectorHandler {
|
|||
for sc in on.screencopies.lock().drain_values() {
|
||||
sc.send_failed();
|
||||
}
|
||||
for sc in on.ext_copy_sessions.lock().drain_values() {
|
||||
sc.stop();
|
||||
}
|
||||
global.destroyed.set(true);
|
||||
self.state.root.outputs.remove(&self.id);
|
||||
self.state.output_extents_changed();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use {
|
|||
fixed::Fixed,
|
||||
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
|
||||
ifs::{
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
jay_output::JayOutput,
|
||||
jay_screencast::JayScreencast,
|
||||
wl_buffer::WlBufferStorage,
|
||||
|
|
@ -42,7 +43,9 @@ use {
|
|||
linkedlist::LinkedList, on_drop_event::OnDropEvent, scroller::Scroller,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
wire::{JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id},
|
||||
wire::{
|
||||
ExtImageCopyCaptureSessionV1Id, JayOutputId, JayScreencastId, ZwlrScreencopyFrameV1Id,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::{TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode},
|
||||
|
|
@ -87,10 +90,12 @@ pub struct OutputNode {
|
|||
pub vblank_event: EventSource<dyn VblankListener>,
|
||||
pub presentation_event: EventSource<dyn PresentationListener>,
|
||||
pub flip_margin_ns: Cell<Option<u64>>,
|
||||
pub ext_copy_sessions:
|
||||
CopyHashMap<(ClientId, ExtImageCopyCaptureSessionV1Id), Rc<ExtImageCopyCaptureSessionV1>>,
|
||||
}
|
||||
|
||||
pub trait LatchListener {
|
||||
fn after_latch(self: Rc<Self>);
|
||||
fn after_latch(self: Rc<Self>, on: &OutputNode);
|
||||
}
|
||||
|
||||
pub trait VblankListener {
|
||||
|
|
@ -133,7 +138,7 @@ impl OutputNode {
|
|||
pub fn latched(&self) {
|
||||
self.schedule.latched();
|
||||
for listener in self.latch_event.iter() {
|
||||
listener.after_latch();
|
||||
listener.after_latch(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +242,19 @@ impl OutputNode {
|
|||
size,
|
||||
);
|
||||
}
|
||||
for sc in self.ext_copy_sessions.lock().values() {
|
||||
sc.copy_texture(
|
||||
self,
|
||||
tex,
|
||||
resv,
|
||||
acquire_sync,
|
||||
release_sync,
|
||||
render_hardware_cursor,
|
||||
x_off,
|
||||
y_off,
|
||||
size,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perform_wlr_screencopies(
|
||||
|
|
@ -353,6 +371,7 @@ impl OutputNode {
|
|||
self.jay_outputs.clear();
|
||||
self.screencasts.clear();
|
||||
self.screencopies.clear();
|
||||
self.ext_copy_sessions.clear();
|
||||
}
|
||||
|
||||
pub fn on_spaces_changed(self: &Rc<Self>) {
|
||||
|
|
@ -692,6 +711,9 @@ impl OutputNode {
|
|||
for sc in self.screencasts.lock().values() {
|
||||
sc.schedule_realloc_or_reconfigure();
|
||||
}
|
||||
for sc in self.ext_copy_sessions.lock().values() {
|
||||
sc.buffer_size_changed();
|
||||
}
|
||||
}
|
||||
|
||||
if transform != old_transform {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use {
|
|||
ifs::{
|
||||
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
jay_screencast::JayScreencast,
|
||||
jay_toplevel::JayToplevel,
|
||||
wl_seat::{collect_kb_foci, collect_kb_foci2, NodeSeatState, SeatId},
|
||||
|
|
@ -24,12 +25,15 @@ use {
|
|||
threshold_counter::ThresholdCounter,
|
||||
toplevel_identifier::{toplevel_identifier, ToplevelIdentifier},
|
||||
},
|
||||
wire::{ExtForeignToplevelHandleV1Id, JayScreencastId, JayToplevelId},
|
||||
wire::{
|
||||
ExtForeignToplevelHandleV1Id, ExtImageCopyCaptureSessionV1Id, JayScreencastId,
|
||||
JayToplevelId,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -130,6 +134,9 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
for sc in data.jay_screencasts.lock().values() {
|
||||
sc.update_latch_listener();
|
||||
}
|
||||
for sc in data.ext_copy_sessions.lock().values() {
|
||||
sc.update_latch_listener();
|
||||
}
|
||||
}
|
||||
|
||||
fn tl_change_extents(self: Rc<Self>, rect: &Rect) {
|
||||
|
|
@ -139,6 +146,9 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
|||
for sc in data.jay_screencasts.lock().values() {
|
||||
sc.schedule_realloc_or_reconfigure();
|
||||
}
|
||||
for sc in data.ext_copy_sessions.lock().values() {
|
||||
sc.buffer_size_changed();
|
||||
}
|
||||
}
|
||||
if data.is_floating.get() {
|
||||
data.float_width.set(rect.width());
|
||||
|
|
@ -226,6 +236,23 @@ pub struct FullscreenedData {
|
|||
pub workspace: Rc<WorkspaceNode>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToplevelOpt {
|
||||
toplevel: Weak<dyn ToplevelNode>,
|
||||
identifier: ToplevelIdentifier,
|
||||
}
|
||||
|
||||
impl ToplevelOpt {
|
||||
pub fn get(&self) -> Option<Rc<dyn ToplevelNode>> {
|
||||
let tl = self.toplevel.upgrade()?;
|
||||
if tl.tl_data().identifier.get() == self.identifier {
|
||||
Some(tl)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ToplevelData {
|
||||
pub self_active: Cell<bool>,
|
||||
pub client: Option<Rc<Client>>,
|
||||
|
|
@ -253,6 +280,8 @@ pub struct ToplevelData {
|
|||
pub render_highlight: NumCell<u32>,
|
||||
pub jay_toplevels: CopyHashMap<(ClientId, JayToplevelId), Rc<JayToplevel>>,
|
||||
pub jay_screencasts: CopyHashMap<(ClientId, JayScreencastId), Rc<JayScreencast>>,
|
||||
pub ext_copy_sessions:
|
||||
CopyHashMap<(ClientId, ExtImageCopyCaptureSessionV1Id), Rc<ExtImageCopyCaptureSessionV1>>,
|
||||
}
|
||||
|
||||
impl ToplevelData {
|
||||
|
|
@ -283,6 +312,7 @@ impl ToplevelData {
|
|||
render_highlight: Default::default(),
|
||||
jay_toplevels: Default::default(),
|
||||
jay_screencasts: Default::default(),
|
||||
ext_copy_sessions: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -326,6 +356,9 @@ impl ToplevelData {
|
|||
for screencast in self.jay_screencasts.lock().drain_values() {
|
||||
screencast.do_destroy();
|
||||
}
|
||||
for screencast in self.ext_copy_sessions.lock().drain_values() {
|
||||
screencast.stop();
|
||||
}
|
||||
self.identifier.set(toplevel_identifier());
|
||||
{
|
||||
let mut handles = self.handles.lock();
|
||||
|
|
@ -372,7 +405,11 @@ impl ToplevelData {
|
|||
title: &str,
|
||||
app_id: &str,
|
||||
) {
|
||||
let handle = match list.publish_toplevel(toplevel) {
|
||||
let opt = ToplevelOpt {
|
||||
toplevel: Rc::downgrade(toplevel),
|
||||
identifier: self.identifier.get(),
|
||||
};
|
||||
let handle = match list.publish_toplevel(opt) {
|
||||
None => return,
|
||||
Some(handle) => handle,
|
||||
};
|
||||
|
|
@ -517,6 +554,9 @@ impl ToplevelData {
|
|||
for sc in self.jay_screencasts.lock().values() {
|
||||
sc.update_latch_listener();
|
||||
}
|
||||
for sc in self.ext_copy_sessions.lock().values() {
|
||||
sc.update_latch_listener();
|
||||
}
|
||||
if !visible {
|
||||
return;
|
||||
}
|
||||
|
|
@ -548,6 +588,15 @@ impl ToplevelData {
|
|||
Some(ws) => ws.output.get(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn desired_pixel_size(&self) -> (i32, i32) {
|
||||
let (dw, dh) = self.desired_extents.get().size();
|
||||
if let Some(ws) = self.workspace.get() {
|
||||
let scale = ws.output.get().global.persistent.scale.get();
|
||||
return scale.pixel_size(dw, dh);
|
||||
};
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TileDragDestination {
|
||||
|
|
|
|||
|
|
@ -144,6 +144,8 @@ pub enum DrmError {
|
|||
DropMaster(#[source] OsError),
|
||||
#[error("Could not queue a CRTC sequence")]
|
||||
QueueSequence(#[source] OsError),
|
||||
#[error("Could not stat the DRM fd")]
|
||||
Stat(#[source] OsError),
|
||||
}
|
||||
|
||||
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||
|
|
@ -177,17 +179,24 @@ fn reopen(fd: c::c_int, need_primary: bool) -> Result<Rc<OwnedFd>, DrmError> {
|
|||
|
||||
pub struct Drm {
|
||||
fd: Rc<OwnedFd>,
|
||||
dev: c::dev_t,
|
||||
}
|
||||
|
||||
impl Drm {
|
||||
pub fn open_existing(fd: Rc<OwnedFd>) -> Self {
|
||||
Self { fd }
|
||||
pub fn open_existing(fd: Rc<OwnedFd>) -> Result<Self, DrmError> {
|
||||
let stat = uapi::fstat(fd.raw()).map_err(|e| DrmError::Stat(e.into()))?;
|
||||
Ok(Self {
|
||||
fd,
|
||||
dev: stat.st_rdev,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reopen(fd: c::c_int, need_primary: bool) -> Result<Self, DrmError> {
|
||||
Ok(Self {
|
||||
fd: reopen(fd, need_primary)?,
|
||||
})
|
||||
Self::open_existing(reopen(fd, need_primary)?)
|
||||
}
|
||||
|
||||
pub fn dev(&self) -> c::dev_t {
|
||||
self.dev
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> &Rc<OwnedFd> {
|
||||
|
|
@ -291,16 +300,16 @@ impl DrmLease {
|
|||
}
|
||||
|
||||
impl DrmMaster {
|
||||
pub fn new(ring: &Rc<IoUring>, fd: Rc<OwnedFd>) -> Self {
|
||||
Self {
|
||||
drm: Drm { fd },
|
||||
pub fn new(ring: &Rc<IoUring>, fd: Rc<OwnedFd>) -> Result<Self, DrmError> {
|
||||
Ok(Self {
|
||||
drm: Drm::open_existing(fd)?,
|
||||
u32_bufs: Default::default(),
|
||||
u64_bufs: Default::default(),
|
||||
gem_handles: Default::default(),
|
||||
events: Default::default(),
|
||||
ring: ring.clone(),
|
||||
buf: RefCell::new(Buf::new(1024)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> c::c_int {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
request create_source {
|
||||
source: id(ext_image_capture_source_v1),
|
||||
toplevel_handle: id(ext_foreign_toplevel_handle_v1),
|
||||
}
|
||||
|
||||
request destroy {
|
||||
|
||||
}
|
||||
3
wire/ext_image_capture_source_v1.txt
Normal file
3
wire/ext_image_capture_source_v1.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
request destroy {
|
||||
|
||||
}
|
||||
25
wire/ext_image_copy_capture_cursor_session_v1.txt
Normal file
25
wire/ext_image_copy_capture_cursor_session_v1.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
request destroy {
|
||||
|
||||
}
|
||||
|
||||
request get_capture_session {
|
||||
session: id(ext_image_copy_capture_session_v1),
|
||||
}
|
||||
|
||||
event enter {
|
||||
|
||||
}
|
||||
|
||||
event leave {
|
||||
|
||||
}
|
||||
|
||||
event position {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
event hotspot {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
43
wire/ext_image_copy_capture_frame_v1.txt
Normal file
43
wire/ext_image_copy_capture_frame_v1.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
request destroy {
|
||||
|
||||
}
|
||||
|
||||
request attach_buffer {
|
||||
buffer: id(wl_buffer),
|
||||
}
|
||||
|
||||
request damage_buffer {
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
|
||||
request capture {
|
||||
|
||||
}
|
||||
|
||||
event transform {
|
||||
transform: u32,
|
||||
}
|
||||
|
||||
event damage {
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
|
||||
event presentation_time {
|
||||
tv_sec_hi: u32,
|
||||
tv_sec_lo: u32,
|
||||
tv_nsec: u32,
|
||||
}
|
||||
|
||||
event ready {
|
||||
|
||||
}
|
||||
|
||||
event failed {
|
||||
reason: i32,
|
||||
}
|
||||
15
wire/ext_image_copy_capture_manager_v1.txt
Normal file
15
wire/ext_image_copy_capture_manager_v1.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
request create_session {
|
||||
session: id(ext_image_copy_capture_session_v1),
|
||||
source: id(ext_image_capture_source_v1),
|
||||
options: u32,
|
||||
}
|
||||
|
||||
request create_pointer_cursor_session {
|
||||
session: id(ext_image_copy_capture_cursor_session_v1),
|
||||
source: id(ext_image_capture_source_v1),
|
||||
pointer: id(wl_pointer),
|
||||
}
|
||||
|
||||
request destroy {
|
||||
|
||||
}
|
||||
33
wire/ext_image_copy_capture_session_v1.txt
Normal file
33
wire/ext_image_copy_capture_session_v1.txt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
request create_frame {
|
||||
frame: id(ext_image_copy_capture_frame_v1),
|
||||
}
|
||||
|
||||
request destroy {
|
||||
|
||||
}
|
||||
|
||||
event buffer_size {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
event shm_format {
|
||||
format: u32,
|
||||
}
|
||||
|
||||
event dmabuf_device {
|
||||
device: pod(uapi::c::dev_t),
|
||||
}
|
||||
|
||||
event dmabuf_format {
|
||||
format: u32,
|
||||
modifiers: array(pod(u8)),
|
||||
}
|
||||
|
||||
event done {
|
||||
|
||||
}
|
||||
|
||||
event stopped {
|
||||
|
||||
}
|
||||
8
wire/ext_output_image_capture_source_manager_v1.txt
Normal file
8
wire/ext_output_image_capture_source_manager_v1.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
request create_source {
|
||||
source: id(ext_image_capture_source_v1),
|
||||
output: id(wl_output),
|
||||
}
|
||||
|
||||
request destroy {
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue