protocols: Drop export-dmabuf
This commit is contained in:
parent
36cb2ac719
commit
ec7fd17b8f
2 changed files with 0 additions and 732 deletions
|
|
@ -1,424 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
|
||||||
|
|
||||||
use std::{cell::RefCell, time::Instant};
|
|
||||||
|
|
||||||
use smithay::{
|
|
||||||
backend::{
|
|
||||||
drm::{DrmNode, NodeType},
|
|
||||||
egl::EGLDevice,
|
|
||||||
renderer::{
|
|
||||||
gles2::{Gles2Error, Gles2Renderbuffer, Gles2Renderer},
|
|
||||||
utils::with_renderer_surface_state,
|
|
||||||
Bind, ExportDma, ImportAll, Offscreen, Renderer,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
desktop::{draw_window, draw_window_popups, space::RenderElement, Kind, Window},
|
|
||||||
input::pointer::CursorImageStatus,
|
|
||||||
output::Output,
|
|
||||||
reexports::wayland_server::{protocol::wl_output::WlOutput, DisplayHandle, Resource},
|
|
||||||
utils::{IsAlive, Size, Transform},
|
|
||||||
wayland::{
|
|
||||||
compositor::{get_children, with_states, SurfaceAttributes},
|
|
||||||
dmabuf::get_dmabuf,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
backend::render::{
|
|
||||||
cursor::draw_cursor, render_output, render_workspace, AsGles2Renderer, CustomElem,
|
|
||||||
},
|
|
||||||
state::{BackendData, ClientState, Common},
|
|
||||||
utils::prelude::*,
|
|
||||||
wayland::protocols::{
|
|
||||||
export_dmabuf::{delegate_export_dmabuf, Capture, CaptureError, ExportDmabufHandler},
|
|
||||||
workspace::WorkspaceHandle,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl ExportDmabufHandler for State {
|
|
||||||
fn capture_output(
|
|
||||||
&mut self,
|
|
||||||
_dh: &DisplayHandle,
|
|
||||||
output: WlOutput,
|
|
||||||
overlay_cursor: bool,
|
|
||||||
) -> Result<Capture, CaptureError> {
|
|
||||||
let output = Output::from_resource(&output)
|
|
||||||
.ok_or(CaptureError::Permanent(anyhow!("Output is gone").into()))?;
|
|
||||||
|
|
||||||
let renderer = match self.backend {
|
|
||||||
BackendData::Kms(ref mut kms) => {
|
|
||||||
// the kms backend just keeps its dmabufs easily accessible for capture.
|
|
||||||
return kms
|
|
||||||
.capture_output(&output)
|
|
||||||
.map(|(device, dmabuf, presentation_time)| Capture {
|
|
||||||
device,
|
|
||||||
dmabuf,
|
|
||||||
presentation_time,
|
|
||||||
})
|
|
||||||
.ok_or(CaptureError::Temporary(
|
|
||||||
anyhow!("Surface not initialized yet").into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
BackendData::Winit(ref mut winit) => winit.backend.renderer(),
|
|
||||||
BackendData::X11(ref mut x11) => &mut x11.renderer,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let device = device_from_renderer(renderer)
|
|
||||||
.context("Failed to find DrmNode")
|
|
||||||
.map_err(|err| CaptureError::Permanent(err.into()))?;
|
|
||||||
|
|
||||||
let size = output
|
|
||||||
.geometry()
|
|
||||||
.size
|
|
||||||
.to_f64()
|
|
||||||
.to_buffer(
|
|
||||||
output.current_scale().fractional_scale(),
|
|
||||||
output.current_transform().into(),
|
|
||||||
)
|
|
||||||
.to_i32_round();
|
|
||||||
let buffer = Offscreen::<Gles2Renderbuffer>::create_buffer(renderer, size)
|
|
||||||
.context("Failed to create render buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
renderer
|
|
||||||
.bind(buffer)
|
|
||||||
.context("Failed to bind render buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
render_output(
|
|
||||||
None,
|
|
||||||
renderer,
|
|
||||||
0,
|
|
||||||
&mut self.common,
|
|
||||||
&output,
|
|
||||||
!overlay_cursor,
|
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.context("Failed to render desktop for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
let dmabuf = renderer
|
|
||||||
.export_framebuffer(size)
|
|
||||||
.context("Failed to export buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
|
|
||||||
Ok(Capture {
|
|
||||||
device,
|
|
||||||
dmabuf,
|
|
||||||
presentation_time: Instant::now(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn capture_workspace(
|
|
||||||
&mut self,
|
|
||||||
_dh: &DisplayHandle,
|
|
||||||
workspace: WorkspaceHandle,
|
|
||||||
wl_output: WlOutput,
|
|
||||||
overlay_cursor: bool,
|
|
||||||
) -> Result<Capture, CaptureError> {
|
|
||||||
let output = Output::from_resource(&wl_output)
|
|
||||||
.ok_or(CaptureError::Permanent(anyhow!("Output is gone").into()))?;
|
|
||||||
let workspace = self
|
|
||||||
.common
|
|
||||||
.shell
|
|
||||||
.spaces
|
|
||||||
.iter()
|
|
||||||
.find(|w| w.handle == workspace)
|
|
||||||
.ok_or(CaptureError::Permanent(anyhow!("Workspace is gone").into()))?
|
|
||||||
.idx;
|
|
||||||
if self.common.shell.active_space(&output).idx == workspace {
|
|
||||||
self.capture_output(_dh, wl_output, overlay_cursor)
|
|
||||||
} else {
|
|
||||||
match self.backend {
|
|
||||||
BackendData::Winit(ref mut winit) => {
|
|
||||||
let device = device_from_renderer(winit.backend.renderer())
|
|
||||||
.context("Failed to find DrmNode")
|
|
||||||
.map_err(|err| CaptureError::Permanent(err.into()))?;
|
|
||||||
capture_workspace(
|
|
||||||
device,
|
|
||||||
winit.backend.renderer(),
|
|
||||||
&output,
|
|
||||||
workspace,
|
|
||||||
&mut self.common,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
BackendData::X11(ref mut x11) => {
|
|
||||||
let device = device_from_renderer(&x11.renderer)
|
|
||||||
.context("Failed to find DrmNode")
|
|
||||||
.map_err(|err| CaptureError::Permanent(err.into()))?;
|
|
||||||
capture_workspace(
|
|
||||||
device,
|
|
||||||
&mut x11.renderer,
|
|
||||||
&output,
|
|
||||||
workspace,
|
|
||||||
&mut self.common,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
BackendData::Kms(ref mut kms) => {
|
|
||||||
let node = kms
|
|
||||||
.target_node_for_output(&output)
|
|
||||||
.unwrap_or(kms.primary)
|
|
||||||
.node_with_type(NodeType::Render)
|
|
||||||
.with_context(|| "Unable to find node")
|
|
||||||
.map_err(|x| CaptureError::Permanent(x.into()))?
|
|
||||||
.map_err(|x| CaptureError::Permanent(x.into()))?;
|
|
||||||
let mut renderer = kms
|
|
||||||
.api
|
|
||||||
.renderer::<Gles2Renderbuffer>(&node, &node)
|
|
||||||
.with_context(|| format!("Failed to optain renderer for {:?}", node))
|
|
||||||
.map_err(|x| CaptureError::Permanent(x.into()))?;
|
|
||||||
capture_workspace(node, &mut renderer, &output, workspace, &mut self.common)
|
|
||||||
}
|
|
||||||
BackendData::Unset => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn capture_toplevel(
|
|
||||||
&mut self,
|
|
||||||
dh: &DisplayHandle,
|
|
||||||
window: Window,
|
|
||||||
overlay_cursor: bool,
|
|
||||||
) -> Result<Capture, CaptureError> {
|
|
||||||
let Kind::Xdg(xdg) = window.toplevel();
|
|
||||||
let surface = xdg.wl_surface();
|
|
||||||
let window_transform = with_states(surface, |states| {
|
|
||||||
states
|
|
||||||
.cached_state
|
|
||||||
.current::<SurfaceAttributes>()
|
|
||||||
.buffer_transform
|
|
||||||
.into()
|
|
||||||
});
|
|
||||||
|
|
||||||
let workspace = self.common.shell.space_for_window(surface);
|
|
||||||
let pointers = if overlay_cursor && workspace.is_some() {
|
|
||||||
self.common
|
|
||||||
.seats
|
|
||||||
.iter()
|
|
||||||
.filter_map(|seat| {
|
|
||||||
let cursor_status = seat
|
|
||||||
.user_data()
|
|
||||||
.get::<RefCell<CursorImageStatus>>()
|
|
||||||
.map(|cell| {
|
|
||||||
let mut cursor_status = cell.borrow_mut();
|
|
||||||
if let CursorImageStatus::Surface(ref surface) = *cursor_status {
|
|
||||||
if !surface.alive() {
|
|
||||||
*cursor_status = CursorImageStatus::Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor_status.clone()
|
|
||||||
})
|
|
||||||
.unwrap_or(CursorImageStatus::Default);
|
|
||||||
|
|
||||||
if cursor_status != CursorImageStatus::Hidden {
|
|
||||||
let workspace = workspace.as_deref()?;
|
|
||||||
let loc = seat.get_pointer().map(|ptr| ptr.current_location())?;
|
|
||||||
let output = seat.active_output();
|
|
||||||
|
|
||||||
if self.common.shell.active_space(&output).idx == workspace.idx {
|
|
||||||
let relative = self
|
|
||||||
.common
|
|
||||||
.shell
|
|
||||||
.space_relative_output_geometry(loc, &output);
|
|
||||||
// unwrap is safe, because we got this workspace from `space_for_window`. It has to contain the window.
|
|
||||||
let bbox = workspace.space.window_bbox(&window).unwrap();
|
|
||||||
bbox.contains(relative.to_i32_round())
|
|
||||||
.then_some((seat, (relative - bbox.loc.to_f64()).to_i32_round()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
} else {
|
|
||||||
Vec::with_capacity(0)
|
|
||||||
};
|
|
||||||
|
|
||||||
let device = match self.backend {
|
|
||||||
BackendData::Winit(ref mut winit) => device_from_renderer(winit.backend.renderer()),
|
|
||||||
BackendData::X11(ref x11) => device_from_renderer(&x11.renderer),
|
|
||||||
BackendData::Kms(ref kms) => Ok(dh
|
|
||||||
.get_client(window.toplevel().wl_surface().id())
|
|
||||||
.ok()
|
|
||||||
.with_context(|| "Unable to find matching wayland client")
|
|
||||||
.map_err(|x| CaptureError::Permanent(x.into()))?
|
|
||||||
.get_data::<ClientState>()
|
|
||||||
.unwrap()
|
|
||||||
.drm_node
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| kms.primary.clone())),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
.context("Failed to find DrmNode")
|
|
||||||
.map_err(|err| CaptureError::Permanent(err.into()))?;
|
|
||||||
|
|
||||||
// first lets check, if we can just send a dmabuf from the client directly
|
|
||||||
if pointers.is_empty()
|
|
||||||
&& window_transform == Transform::Normal
|
|
||||||
&& get_children(surface).is_empty()
|
|
||||||
&& self.common.shell.popups.find_popup(surface).is_none()
|
|
||||||
{
|
|
||||||
let dmabuf = with_renderer_surface_state(surface, |state| {
|
|
||||||
state.wl_buffer().and_then(|buf| get_dmabuf(buf).ok())
|
|
||||||
});
|
|
||||||
if let Some(dmabuf) = dmabuf {
|
|
||||||
return Ok(Capture {
|
|
||||||
device,
|
|
||||||
dmabuf,
|
|
||||||
presentation_time: std::time::Instant::now(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need to composite
|
|
||||||
let mut _tmp_multirenderer = None;
|
|
||||||
let renderer = match self.backend {
|
|
||||||
BackendData::Winit(ref mut winit) => winit.backend.renderer(),
|
|
||||||
BackendData::X11(ref mut x11) => &mut x11.renderer,
|
|
||||||
BackendData::Kms(ref mut kms) => {
|
|
||||||
_tmp_multirenderer = Some(
|
|
||||||
kms.api
|
|
||||||
.renderer::<Gles2Renderbuffer>(&device, &device)
|
|
||||||
.with_context(|| format!("Failed to optain renderer for {:?}", device))
|
|
||||||
.map_err(|x| CaptureError::Permanent(x.into()))?,
|
|
||||||
);
|
|
||||||
_tmp_multirenderer.as_mut().unwrap().as_gles2()
|
|
||||||
}
|
|
||||||
BackendData::Unset => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let bbox = window.bbox_with_popups();
|
|
||||||
let size = bbox.size + Size::from((-bbox.loc.x, -bbox.loc.y));
|
|
||||||
let buffer = Offscreen::<Gles2Renderbuffer>::create_buffer(
|
|
||||||
renderer,
|
|
||||||
size.to_buffer(1, window_transform),
|
|
||||||
)
|
|
||||||
.context("Failed to create render buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
renderer
|
|
||||||
.bind(buffer)
|
|
||||||
.context("Failed to bind render buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
renderer
|
|
||||||
.render(size.to_physical(1), Transform::Normal, |renderer, frame| {
|
|
||||||
let log = slog_scope::logger();
|
|
||||||
let damage = &[window.physical_bbox_with_popups((0.0, 0.0), 1.0)];
|
|
||||||
draw_window(renderer, frame, &window, 1.0, (0.0, 0.0), damage, &log)?;
|
|
||||||
draw_window_popups(renderer, frame, &window, 1.0, (0.0, 0.0), damage, &log)?;
|
|
||||||
for (seat, loc) in pointers.into_iter() {
|
|
||||||
if let Some(cursor_elem) = draw_cursor::<_, CustomElem>(
|
|
||||||
renderer,
|
|
||||||
seat,
|
|
||||||
loc,
|
|
||||||
&self.common.start_time,
|
|
||||||
true,
|
|
||||||
) {
|
|
||||||
let damage = RenderElement::<Gles2Renderer>::accumulated_damage(
|
|
||||||
&cursor_elem,
|
|
||||||
1.0,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
cursor_elem.draw(
|
|
||||||
renderer,
|
|
||||||
frame,
|
|
||||||
1.0,
|
|
||||||
loc.to_physical(1.0),
|
|
||||||
&damage,
|
|
||||||
&log,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Result::<(), Gles2Error>::Ok(())
|
|
||||||
})
|
|
||||||
.context("Failed to render window for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?
|
|
||||||
.context("Failed to render window for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
|
|
||||||
let dmabuf = renderer
|
|
||||||
.export_framebuffer(size.to_buffer(1, window_transform))
|
|
||||||
.context("Failed to export buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
|
|
||||||
Ok(Capture {
|
|
||||||
device,
|
|
||||||
dmabuf,
|
|
||||||
presentation_time: Instant::now(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_time(&mut self) -> Instant {
|
|
||||||
self.common.start_time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn capture_workspace<E, T, R>(
|
|
||||||
gpu: DrmNode,
|
|
||||||
renderer: &mut R,
|
|
||||||
output: &Output,
|
|
||||||
idx: u8,
|
|
||||||
state: &mut Common,
|
|
||||||
) -> Result<Capture, CaptureError>
|
|
||||||
where
|
|
||||||
E: std::error::Error + Send + Sync + 'static,
|
|
||||||
T: Clone + 'static,
|
|
||||||
R: Renderer<Error = E, TextureId = T>
|
|
||||||
+ ImportAll
|
|
||||||
+ AsGles2Renderer
|
|
||||||
+ Offscreen<Gles2Renderbuffer>
|
|
||||||
+ Bind<Gles2Renderbuffer>
|
|
||||||
+ ExportDma,
|
|
||||||
CustomElem: RenderElement<R>,
|
|
||||||
{
|
|
||||||
let size = output
|
|
||||||
.geometry()
|
|
||||||
.size
|
|
||||||
.to_f64()
|
|
||||||
.to_buffer(
|
|
||||||
output.current_scale().fractional_scale(),
|
|
||||||
output.current_transform().into(),
|
|
||||||
)
|
|
||||||
.to_i32_round();
|
|
||||||
let buffer = Offscreen::<Gles2Renderbuffer>::create_buffer(renderer, size)
|
|
||||||
.context("Failed to create render buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
renderer
|
|
||||||
.bind(buffer)
|
|
||||||
.context("Failed to bind render buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
render_workspace(
|
|
||||||
Some(&gpu),
|
|
||||||
renderer,
|
|
||||||
0,
|
|
||||||
state,
|
|
||||||
idx,
|
|
||||||
&output,
|
|
||||||
true,
|
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.map_err(|err| anyhow!("Failed to render desktop for offscreen capture: {:?}", err)) // meh..
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
let dmabuf = renderer
|
|
||||||
.export_framebuffer(size)
|
|
||||||
.context("Failed to export buffer for offscreen capture")
|
|
||||||
.map_err(|err| CaptureError::Temporary(err.into()))?;
|
|
||||||
|
|
||||||
Ok(Capture {
|
|
||||||
device: gpu,
|
|
||||||
dmabuf,
|
|
||||||
presentation_time: Instant::now(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn device_from_renderer(renderer: &Gles2Renderer) -> Result<DrmNode> {
|
|
||||||
EGLDevice::device_for_display(renderer.egl_context().display())?
|
|
||||||
.try_get_render_node()?
|
|
||||||
.ok_or(anyhow!(
|
|
||||||
"No node associated with context (software context?)"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate_export_dmabuf!(State);
|
|
||||||
|
|
@ -1,308 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
|
|
||||||
use smithay::{
|
|
||||||
backend::{
|
|
||||||
allocator::{dmabuf::Dmabuf, Buffer},
|
|
||||||
drm::DrmNode,
|
|
||||||
},
|
|
||||||
desktop::Window,
|
|
||||||
reexports::wayland_server::{
|
|
||||||
self, backend::GlobalId, protocol::wl_output::WlOutput, Client, Dispatch, DisplayHandle,
|
|
||||||
GlobalDispatch,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
fs::File,
|
|
||||||
io::{Seek, SeekFrom},
|
|
||||||
os::unix::io::{AsRawFd, FromRawFd, IntoRawFd},
|
|
||||||
time::Instant,
|
|
||||||
};
|
|
||||||
|
|
||||||
use cosmic_protocols::export_dmabuf::v1::server::{
|
|
||||||
zcosmic_export_dmabuf_frame_v1::{self, CancelReason, Flags, ZcosmicExportDmabufFrameV1},
|
|
||||||
zcosmic_export_dmabuf_manager_v1::{self, ZcosmicExportDmabufManagerV1},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::wayland::protocols::{
|
|
||||||
toplevel_info::{window_from_handle, ToplevelInfoHandler},
|
|
||||||
workspace::{WorkspaceHandle, WorkspaceHandler},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Export Dmabuf global state
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ExportDmabufState {
|
|
||||||
global: GlobalId,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ExportDmabufGlobalData {
|
|
||||||
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExportDmabufState {
|
|
||||||
/// Create a new dmabuf global
|
|
||||||
pub fn new<D, F>(display: &DisplayHandle, client_filter: F) -> ExportDmabufState
|
|
||||||
where
|
|
||||||
D: GlobalDispatch<ZcosmicExportDmabufManagerV1, ExportDmabufGlobalData>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufManagerV1, ()>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufFrameV1, ()>
|
|
||||||
+ ExportDmabufHandler
|
|
||||||
+ 'static,
|
|
||||||
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
ExportDmabufState {
|
|
||||||
global: display.create_global::<D, ZcosmicExportDmabufManagerV1, _>(
|
|
||||||
1,
|
|
||||||
ExportDmabufGlobalData {
|
|
||||||
filter: Box::new(client_filter),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the export dmabuf global.
|
|
||||||
pub fn global(&self) -> GlobalId {
|
|
||||||
self.global.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum CaptureError {
|
|
||||||
Temporary(Box<dyn std::error::Error>),
|
|
||||||
Permanent(Box<dyn std::error::Error>),
|
|
||||||
Resizing,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Capture {
|
|
||||||
pub device: DrmNode,
|
|
||||||
pub dmabuf: Dmabuf,
|
|
||||||
pub presentation_time: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ExportDmabufHandler {
|
|
||||||
fn capture_output(
|
|
||||||
&mut self,
|
|
||||||
dh: &DisplayHandle,
|
|
||||||
output: WlOutput,
|
|
||||||
overlay_cursor: bool,
|
|
||||||
) -> Result<Capture, CaptureError>;
|
|
||||||
fn capture_workspace(
|
|
||||||
&mut self,
|
|
||||||
dh: &DisplayHandle,
|
|
||||||
workspace: WorkspaceHandle,
|
|
||||||
output: WlOutput,
|
|
||||||
overlay_cursor: bool,
|
|
||||||
) -> Result<Capture, CaptureError>;
|
|
||||||
fn capture_toplevel(
|
|
||||||
&mut self,
|
|
||||||
dh: &DisplayHandle,
|
|
||||||
toplevel: Window,
|
|
||||||
overlay_cursor: bool,
|
|
||||||
) -> Result<Capture, CaptureError>;
|
|
||||||
fn start_time(&mut self) -> Instant;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D> GlobalDispatch<ZcosmicExportDmabufManagerV1, ExportDmabufGlobalData, D>
|
|
||||||
for ExportDmabufState
|
|
||||||
where
|
|
||||||
D: GlobalDispatch<ZcosmicExportDmabufManagerV1, ExportDmabufGlobalData>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufManagerV1, ()>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufFrameV1, ()>
|
|
||||||
+ ExportDmabufHandler,
|
|
||||||
{
|
|
||||||
fn bind(
|
|
||||||
_state: &mut D,
|
|
||||||
_handle: &DisplayHandle,
|
|
||||||
_client: &Client,
|
|
||||||
resource: wayland_server::New<ZcosmicExportDmabufManagerV1>,
|
|
||||||
_global_data: &ExportDmabufGlobalData,
|
|
||||||
data_init: &mut wayland_server::DataInit<'_, D>,
|
|
||||||
) {
|
|
||||||
data_init.init(resource, ());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_view(client: Client, global_data: &ExportDmabufGlobalData) -> bool {
|
|
||||||
(global_data.filter)(&client)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D> Dispatch<ZcosmicExportDmabufManagerV1, (), D> for ExportDmabufState
|
|
||||||
where
|
|
||||||
D: GlobalDispatch<ZcosmicExportDmabufManagerV1, ExportDmabufGlobalData>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufManagerV1, ()>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufFrameV1, ()>
|
|
||||||
+ ExportDmabufHandler
|
|
||||||
+ WorkspaceHandler
|
|
||||||
+ ToplevelInfoHandler,
|
|
||||||
{
|
|
||||||
fn request(
|
|
||||||
state: &mut D,
|
|
||||||
_client: &wayland_server::Client,
|
|
||||||
_resource: &ZcosmicExportDmabufManagerV1,
|
|
||||||
request: <ZcosmicExportDmabufManagerV1 as wayland_server::Resource>::Request,
|
|
||||||
_data: &(),
|
|
||||||
dhandle: &DisplayHandle,
|
|
||||||
data_init: &mut wayland_server::DataInit<'_, D>,
|
|
||||||
) {
|
|
||||||
let start_time = state.start_time();
|
|
||||||
match request {
|
|
||||||
zcosmic_export_dmabuf_manager_v1::Request::CaptureOutput {
|
|
||||||
frame,
|
|
||||||
overlay_cursor,
|
|
||||||
output,
|
|
||||||
} => {
|
|
||||||
let frame = data_init.init(frame, ());
|
|
||||||
match state.capture_output(dhandle, output, overlay_cursor != 0) {
|
|
||||||
Ok(capture) => handle_capture(capture, frame, start_time),
|
|
||||||
Err(err) => frame.cancel(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zcosmic_export_dmabuf_manager_v1::Request::CaptureWorkspace {
|
|
||||||
frame,
|
|
||||||
overlay_cursor,
|
|
||||||
workspace,
|
|
||||||
output,
|
|
||||||
} => {
|
|
||||||
let frame = data_init.init(frame, ());
|
|
||||||
match state.workspace_state().workspace_handle(&workspace) {
|
|
||||||
Some(workspace) => {
|
|
||||||
match state.capture_workspace(
|
|
||||||
dhandle,
|
|
||||||
workspace,
|
|
||||||
output,
|
|
||||||
overlay_cursor != 0,
|
|
||||||
) {
|
|
||||||
Ok(capture) => handle_capture(capture, frame, start_time),
|
|
||||||
Err(err) => frame.cancel(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => frame.cancel(CancelReason::Permanent),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zcosmic_export_dmabuf_manager_v1::Request::CaptureToplevel {
|
|
||||||
frame,
|
|
||||||
overlay_cursor,
|
|
||||||
toplevel,
|
|
||||||
} => {
|
|
||||||
let frame = data_init.init(frame, ());
|
|
||||||
match window_from_handle(toplevel) {
|
|
||||||
Some(window) => {
|
|
||||||
match state.capture_toplevel(dhandle, window, overlay_cursor != 0) {
|
|
||||||
Ok(capture) => handle_capture(capture, frame, start_time),
|
|
||||||
Err(err) => frame.cancel(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => frame.cancel(CancelReason::Permanent),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zcosmic_export_dmabuf_manager_v1::Request::Destroy => {}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CaptureError> for CancelReason {
|
|
||||||
fn from(err: CaptureError) -> Self {
|
|
||||||
match err {
|
|
||||||
CaptureError::Temporary(err) => {
|
|
||||||
slog_scope::debug!("Temporary Capture Error: {}", err);
|
|
||||||
CancelReason::Temporary
|
|
||||||
}
|
|
||||||
CaptureError::Permanent(err) => {
|
|
||||||
slog_scope::warn!("Permanent Capture Error: {}", err);
|
|
||||||
CancelReason::Permanent
|
|
||||||
}
|
|
||||||
CaptureError::Resizing => CancelReason::Resizing,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_capture(capture: Capture, frame: ZcosmicExportDmabufFrameV1, start_time: Instant) {
|
|
||||||
let Capture {
|
|
||||||
device,
|
|
||||||
dmabuf,
|
|
||||||
presentation_time,
|
|
||||||
} = capture;
|
|
||||||
let format = dmabuf.format();
|
|
||||||
let modifier: u64 = format.modifier.into();
|
|
||||||
|
|
||||||
frame.device(Vec::from(device.dev_id().to_ne_bytes()));
|
|
||||||
frame.frame(
|
|
||||||
dmabuf.width(),
|
|
||||||
dmabuf.height(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
if dmabuf.y_inverted() { 1 } else { 0 },
|
|
||||||
Flags::Transient,
|
|
||||||
format.code as u32,
|
|
||||||
(modifier >> 32) as u32,
|
|
||||||
(modifier & 0xFFFFFFFF) as u32,
|
|
||||||
dmabuf.num_planes() as u32,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (i, (handle, (offset, stride))) in dmabuf
|
|
||||||
.handles()
|
|
||||||
.zip(dmabuf.offsets().zip(dmabuf.strides()))
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
// SAFETY: BorrowedFd is used for seeking
|
|
||||||
let mut file = unsafe { File::from_raw_fd(handle.as_raw_fd()) };
|
|
||||||
let size = match file.seek(SeekFrom::End(0)) {
|
|
||||||
Ok(size) => size,
|
|
||||||
Err(err) => {
|
|
||||||
slog_scope::debug!("Temporary Capture Error: {}", err);
|
|
||||||
frame.cancel(zcosmic_export_dmabuf_frame_v1::CancelReason::Temporary);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Err(err) = file.rewind() {
|
|
||||||
slog_scope::debug!("Temporary Capture Error: {}", err);
|
|
||||||
frame.cancel(zcosmic_export_dmabuf_frame_v1::CancelReason::Temporary);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// SAFETY: Converted back to raw_fd, no chance in ownership
|
|
||||||
let handle = file.into_raw_fd();
|
|
||||||
// FDs are dup'ed by wayland-rs before sending them
|
|
||||||
frame.object(i as u32, handle, size as u32, offset, stride, i as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
let duration = presentation_time.duration_since(start_time);
|
|
||||||
let (tv_sec, tv_nsec) = (duration.as_secs(), duration.subsec_nanos());
|
|
||||||
frame.ready((tv_sec >> 32) as u32, (tv_sec & 0xFFFFFFFF) as u32, tv_nsec);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D> Dispatch<ZcosmicExportDmabufFrameV1, (), D> for ExportDmabufState
|
|
||||||
where
|
|
||||||
D: GlobalDispatch<ZcosmicExportDmabufManagerV1, ExportDmabufGlobalData>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufManagerV1, ()>
|
|
||||||
+ Dispatch<ZcosmicExportDmabufFrameV1, ()>
|
|
||||||
+ ExportDmabufHandler,
|
|
||||||
{
|
|
||||||
fn request(
|
|
||||||
_state: &mut D,
|
|
||||||
_client: &wayland_server::Client,
|
|
||||||
_resource: &ZcosmicExportDmabufFrameV1,
|
|
||||||
request: <ZcosmicExportDmabufFrameV1 as wayland_server::Resource>::Request,
|
|
||||||
_data: &(),
|
|
||||||
_dhandle: &DisplayHandle,
|
|
||||||
_data_init: &mut wayland_server::DataInit<'_, D>,
|
|
||||||
) {
|
|
||||||
match request {
|
|
||||||
zcosmic_export_dmabuf_frame_v1::Request::Destroy => {}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! delegate_export_dmabuf {
|
|
||||||
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
|
|
||||||
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
|
||||||
cosmic_protocols::export_dmabuf::v1::server::zcosmic_export_dmabuf_manager_v1::ZcosmicExportDmabufManagerV1: $crate::wayland::protocols::export_dmabuf::ExportDmabufGlobalData
|
|
||||||
] => $crate::wayland::protocols::export_dmabuf::ExportDmabufState);
|
|
||||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
|
||||||
cosmic_protocols::export_dmabuf::v1::server::zcosmic_export_dmabuf_manager_v1::ZcosmicExportDmabufManagerV1: ()
|
|
||||||
] => $crate::wayland::protocols::export_dmabuf::ExportDmabufState);
|
|
||||||
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
|
||||||
cosmic_protocols::export_dmabuf::v1::server::zcosmic_export_dmabuf_frame_v1::ZcosmicExportDmabufFrameV1: ()
|
|
||||||
] => $crate::wayland::protocols::export_dmabuf::ExportDmabufState);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use delegate_export_dmabuf;
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue