cosmic-screencopy-v2

This commit is contained in:
Victoria Brekenfeld 2024-03-12 19:42:48 +01:00 committed by Victoria Brekenfeld
parent 973cfed87b
commit b40d153809
27 changed files with 3647 additions and 2743 deletions

View file

@ -1,9 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
shell::grabs::SeatMoveGrabState, state::ClientState, utils::prelude::*,
wayland::protocols::screencopy::SessionType,
};
use crate::{shell::grabs::SeatMoveGrabState, state::ClientState, utils::prelude::*};
use calloop::Interest;
use smithay::{
backend::renderer::utils::{on_commit_buffer_handler, with_renderer_surface_state},
@ -29,8 +26,6 @@ use smithay::{
};
use std::sync::Mutex;
use super::screencopy::PendingScreencopyBuffers;
impl State {
fn toplevel_ensure_initial_configure(&mut self, toplevel: &ToplevelSurface) -> bool {
// send the initial configure if relevant
@ -251,14 +246,11 @@ impl CompositorHandler for State {
workspace.commit(surface);
}
}
//handle window screencopy sessions
self.schedule_window_session(surface);
// and refresh smithays internal state
self.common.shell.popups.commit(surface);
}
// and refresh smithays internal state
self.common.shell.popups.commit(surface);
// re-arrange layer-surfaces (commits may change size and positioning)
let layer_output = self
.common
@ -277,34 +269,10 @@ impl CompositorHandler for State {
}
}
let mut scheduled_sessions = self.schedule_workspace_sessions(surface);
// schedule a new render
if let Some(output) = self.common.shell.visible_output_for_surface(surface) {
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
scheduled_sessions
.get_or_insert_with(Vec::new)
.extend(sessions.borrow_mut().drain(..));
}
self.backend.schedule_render(
&self.common.event_loop_handle,
&output,
scheduled_sessions.as_ref().map(|sessions| {
sessions
.iter()
.filter(|(s, _)| match s.session_type() {
SessionType::Output(o) | SessionType::Workspace(o, _)
if &o == output =>
{
true
}
_ => false,
})
.cloned()
.collect::<Vec<_>>()
}),
);
self.backend
.schedule_render(&self.common.event_loop_handle, &output);
}
}
}

View file

@ -0,0 +1,4 @@
use crate::state::State;
use crate::wayland::protocols::image_source::delegate_image_source;
delegate_image_source!(State);

View file

@ -14,8 +14,6 @@ use smithay::{
},
};
use super::screencopy::PendingScreencopyBuffers;
impl WlrLayerShellHandler for State {
fn shell_state(&mut self) -> &mut WlrLayerShellState {
&mut self.common.shell.layer_shell_state
@ -76,19 +74,8 @@ impl WlrLayerShellHandler for State {
self.common.shell.workspaces.recalculate();
// collect screencopy sessions needing an update
let mut scheduled_sessions = self.schedule_workspace_sessions(surface.wl_surface());
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
scheduled_sessions
.get_or_insert_with(Vec::new)
.extend(sessions.borrow_mut().drain(..));
}
self.backend.schedule_render(
&self.common.event_loop_handle,
&output,
scheduled_sessions,
);
self.backend
.schedule_render(&self.common.event_loop_handle, &output);
}
}
}

View file

@ -9,6 +9,7 @@ pub mod dmabuf;
pub mod drm;
pub mod drm_lease;
pub mod fractional_scale;
pub mod image_source;
pub mod input_method;
pub mod keyboard_shortcuts_inhibit;
pub mod layer_shell;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,415 @@
use std::{borrow::Borrow, cell::RefCell, collections::HashMap};
use smithay::{
backend::{
allocator::{Fourcc, Modifier},
egl::EGLDevice,
renderer::{
damage::OutputDamageTracker,
gles::{Capability, GlesRenderer},
glow::GlowRenderer,
utils::with_renderer_surface_state,
},
},
desktop::space::SpaceElement,
output::Output,
reexports::wayland_server::protocol::wl_shm::Format as ShmFormat,
utils::{Buffer as BufferCoords, Point, Size, Transform},
wayland::{dmabuf::get_dmabuf, seat::WaylandFocus},
};
use crate::{
shell::CosmicSurface,
state::{BackendData, State},
utils::prelude::{
OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt,
},
wayland::protocols::{
image_source::ImageSourceData,
screencopy::{
delegate_screencopy, BufferConstraints, CursorSession, DmabufConstraints, Frame,
ScreencopyHandler, ScreencopyState, Session,
},
},
};
mod render;
mod user_data;
pub use self::render::*;
use self::user_data::*;
pub use self::user_data::{FrameHolder, ScreencopySessions, SessionData, SessionHolder};
pub const WORKSPACE_OVERVIEW_NAMESPACE: &str = "cosmic-workspace-overview";
impl ScreencopyHandler for State {
fn screencopy_state(&mut self) -> &mut ScreencopyState {
&mut self.common.screencopy_state
}
fn capture_source(&mut self, source: &ImageSourceData) -> Option<BufferConstraints> {
match source {
ImageSourceData::Output(weak) => weak
.upgrade()
.and_then(|output| constraints_for_output(&output, &mut self.backend)),
ImageSourceData::Workspace(handle) => {
let workspace = self.common.shell.workspaces.space_for_handle(&handle)?;
constraints_for_output(workspace.output(), &mut self.backend)
}
ImageSourceData::Toplevel(window) => {
constraints_for_toplevel(window, &mut self.backend)
}
_ => None,
}
}
fn capture_cursor_source(&mut self, _source: &ImageSourceData) -> Option<BufferConstraints> {
let size = if let Some((geometry, _)) = self
.common
.last_active_seat()
.cursor_geometry((0.0, 0.0), self.common.clock.now())
{
geometry.size
} else {
Size::from((64, 64))
};
Some(BufferConstraints {
size,
shm: vec![ShmFormat::Argb8888],
dma: None,
})
}
fn new_session(&mut self, session: Session) {
match session.source() {
ImageSourceData::Output(weak) => {
let Some(mut output) = weak.upgrade() else {
session.stop();
return;
};
session.user_data().insert_if_missing(|| {
RefCell::new(SessionUserData::new(OutputDamageTracker::from_output(
&output,
)))
});
output.add_session(session);
}
ImageSourceData::Workspace(handle) => {
let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
else {
session.stop();
return;
};
session.user_data().insert_if_missing(|| {
RefCell::new(SessionUserData::new(OutputDamageTracker::from_output(
workspace.output(),
)))
});
workspace.add_session(session);
}
ImageSourceData::Toplevel(mut toplevel) => {
let size = toplevel.geometry().size.to_physical(1);
session.user_data().insert_if_missing(|| {
RefCell::new(SessionUserData::new(OutputDamageTracker::new(
size,
1.0,
Transform::Normal,
)))
});
toplevel.add_session(session);
}
ImageSourceData::Destroyed => unreachable!(),
}
}
fn new_cursor_session(&mut self, session: CursorSession) {
let (pointer_loc, pointer_size, hotspot) = {
let seat = self.common.last_active_seat();
let pointer = seat.get_pointer().unwrap();
let pointer_loc = pointer.current_location().to_i32_round().as_global();
let (pointer_size, hotspot) = if let Some((geometry, hotspot)) =
seat.cursor_geometry((0.0, 0.0), self.common.clock.now())
{
(geometry.size, hotspot)
} else {
(Size::from((64, 64)), Point::from((0, 0)))
};
(pointer_loc, pointer_size, hotspot)
};
session.user_data().insert_if_missing(|| {
RefCell::new(SessionUserData::new(OutputDamageTracker::new(
pointer_size.to_logical(1, Transform::Normal).to_physical(1),
1.0,
Transform::Normal,
)))
});
match session.source() {
ImageSourceData::Output(weak) => {
let Some(mut output) = weak.upgrade() else {
session.stop();
return;
};
if output.geometry().contains(pointer_loc) {
let buffer_pos = pointer_loc
.to_local(&output)
.as_logical()
.to_f64()
.to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output
.current_mode()
.map(|mode| {
mode.size
.to_f64()
.to_logical(output.current_scale().fractional_scale())
})
.unwrap_or(Size::from((0.0, 0.0))),
)
.to_i32_round();
session.set_cursor_hotspot(hotspot);
session.set_cursor_pos(Some(buffer_pos));
}
output.add_cursor_session(session);
}
ImageSourceData::Workspace(handle) => {
let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
else {
session.stop();
return;
};
let output = workspace.output().clone();
if output.geometry().contains(pointer_loc) {
let buffer_pos = pointer_loc
.to_local(&output)
.as_logical()
.to_f64()
.to_buffer(
output.current_scale().fractional_scale(),
output.current_transform(),
&output
.current_mode()
.map(|mode| {
mode.size
.to_f64()
.to_logical(output.current_scale().fractional_scale())
})
.unwrap_or(Size::from((0.0, 0.0))),
)
.to_i32_round();
session.set_cursor_hotspot(hotspot);
session.set_cursor_pos(Some(buffer_pos));
}
workspace.add_cursor_session(session);
}
ImageSourceData::Toplevel(mut toplevel) => {
if let Some(element) = self.common.shell.element_for_surface(&toplevel) {
if element.has_active_window(&toplevel) {
if let Some(workspace) = self.common.shell.space_for(element) {
if let Some(geometry) = workspace.element_geometry(element) {
let mut surface_geo = element.active_window_geometry().as_local();
surface_geo.loc += geometry.loc;
let global_geo = surface_geo.to_global(workspace.output());
if global_geo.contains(pointer_loc) {
let buffer_pos = (pointer_loc - global_geo.loc)
.as_logical()
.to_buffer(1, Transform::Normal, &toplevel.geometry().size);
session.set_cursor_hotspot(hotspot);
session.set_cursor_pos(Some(buffer_pos));
}
}
}
}
}
toplevel.add_cursor_session(session);
}
ImageSourceData::Destroyed => unreachable!(),
}
}
fn frame(&mut self, session: Session, frame: Frame) {
match session.source() {
ImageSourceData::Output(weak) => {
let Some(mut output) = weak.upgrade() else {
session.stop(); // will fail the frame as well
return;
};
output.add_frame(session, frame);
self.backend
.schedule_render(&self.common.event_loop_handle, &output);
}
ImageSourceData::Workspace(handle) => {
render_workspace_to_buffer(self, session, frame, handle)
}
ImageSourceData::Toplevel(toplevel) => {
render_window_to_buffer(self, session, frame, &toplevel)
}
ImageSourceData::Destroyed => unreachable!(),
}
}
fn cursor_frame(&mut self, session: CursorSession, frame: Frame) {
if !session.has_cursor() {
frame.success(Transform::Normal, Vec::new(), self.common.clock.now());
return;
}
let seat = self.common.last_active_seat().clone();
render_cursor_to_buffer(self, &session, frame, &seat);
}
fn frame_aborted(&mut self, frame: Frame) {
for mut output in self.common.shell.outputs().cloned() {
output.remove_frame(&frame)
}
}
fn session_destroyed(&mut self, session: Session) {
match session.source() {
ImageSourceData::Output(weak) => {
if let Some(mut output) = weak.upgrade() {
output.remove_session(session);
}
}
ImageSourceData::Workspace(handle) => {
if let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
{
workspace.remove_session(session)
}
}
ImageSourceData::Toplevel(mut toplevel) => toplevel.remove_session(session),
ImageSourceData::Destroyed => unreachable!(),
}
}
fn cursor_session_destroyed(&mut self, session: CursorSession) {
match session.source() {
ImageSourceData::Output(weak) => {
if let Some(mut output) = weak.upgrade() {
output.remove_cursor_session(session);
}
}
ImageSourceData::Workspace(handle) => {
if let Some(workspace) = self.common.shell.workspaces.space_for_handle_mut(&handle)
{
workspace.remove_cursor_session(session)
}
}
ImageSourceData::Toplevel(mut toplevel) => toplevel.remove_cursor_session(session),
ImageSourceData::Destroyed => unreachable!(),
}
}
}
fn constraints_for_output(output: &Output, backend: &mut BackendData) -> Option<BufferConstraints> {
let mode = match output.current_mode() {
Some(mode) => mode.size.to_logical(1).to_buffer(1, Transform::Normal),
None => {
return None;
}
};
let mut _kms_renderer = None;
let renderer = match backend {
BackendData::Kms(ref mut kms) => {
let node = kms
.target_node_for_output(&output)
.unwrap_or(kms.primary_node);
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
_kms_renderer.as_mut().unwrap().as_mut()
}
BackendData::Winit(ref mut winit) => winit.backend.renderer(),
BackendData::X11(ref mut x11) => &mut x11.renderer,
_ => unreachable!(),
};
Some(constraints_for_renderer(mode, renderer))
}
fn constraints_for_toplevel(
surface: &CosmicSurface,
backend: &mut BackendData,
) -> Option<BufferConstraints> {
let size = surface.geometry().size.to_buffer(1, Transform::Normal);
let wl_surface = surface.wl_surface()?;
let mut _kms_renderer = None;
let renderer = match backend {
BackendData::Kms(ref mut kms) => {
let dma_node = with_renderer_surface_state(&wl_surface, |state| {
let buffer = state.buffer()?;
let dmabuf = get_dmabuf(&*buffer).ok()?;
dmabuf.node()
})
.flatten();
let node = dma_node.unwrap_or(kms.primary_node);
_kms_renderer = Some(kms.api.single_renderer(&node).unwrap());
_kms_renderer.as_mut().unwrap().as_mut()
}
BackendData::Winit(ref mut winit) => winit.backend.renderer(),
BackendData::X11(ref mut x11) => &mut x11.renderer,
_ => unreachable!(),
};
Some(constraints_for_renderer(size, renderer))
}
fn constraints_for_renderer(
size: Size<i32, BufferCoords>,
renderer: &mut GlowRenderer,
) -> BufferConstraints {
let mut constraints = BufferConstraints {
size,
shm: vec![ShmFormat::Abgr8888, ShmFormat::Xbgr8888],
dma: None,
};
if (renderer as &dyn Borrow<GlesRenderer>)
.borrow()
.capabilities()
.contains(&Capability::ColorTransformations)
{
constraints
.shm
.extend([ShmFormat::Abgr2101010, ShmFormat::Xbgr2101010]);
}
if let Some(node) = EGLDevice::device_for_display(renderer.egl_context().display())
.ok()
.and_then(|device| device.try_get_render_node().ok().flatten())
{
constraints.dma = Some(DmabufConstraints {
node,
formats: renderer
.egl_context()
.dmabuf_render_formats()
.iter()
.fold(
HashMap::<Fourcc, Vec<Modifier>>::new(),
|mut map, format| {
map.entry(format.code).or_default().push(format.modifier);
map
},
)
.into_iter()
.collect::<Vec<_>>(),
});
}
constraints
}
delegate_screencopy!(State);

View file

@ -0,0 +1,864 @@
use smithay::{
backend::{
allocator::{dmabuf::Dmabuf, format::get_transparent, Buffer, Fourcc},
renderer::{
buffer_dimensions, buffer_type,
damage::{Error as DTError, OutputDamageTracker, RenderOutputResult},
element::{
surface::WaylandSurfaceRenderElement,
utils::{Relocate, RelocateRenderElement},
AsRenderElements, RenderElement,
},
gles::{GlesError, GlesRenderbuffer},
sync::SyncPoint,
utils::with_renderer_surface_state,
Bind, Blit, BufferType, ExportMem, ImportAll, ImportMem, Offscreen, Renderer,
},
},
desktop::space::SpaceElement,
input::Seat,
output::{Output, OutputNoMode},
reexports::wayland_server::protocol::{wl_buffer::WlBuffer, wl_shm::Format as ShmFormat},
utils::{
Buffer as BufferCoords, IsAlive, Logical, Physical, Point, Rectangle, Scale, Size,
Transform,
},
wayland::{
dmabuf::get_dmabuf,
seat::WaylandFocus,
shm::{shm_format_to_fourcc, with_buffer_contents, with_buffer_contents_mut},
},
};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tracing::warn;
use crate::{
backend::render::{
cursor,
element::{AsGlowRenderer, CosmicElement, DamageElement},
render_workspace, CursorMode, CLEAR_COLOR,
},
shell::{CosmicMappedRenderElement, CosmicSurface, WorkspaceRenderElement},
state::{BackendData, Common, State},
utils::prelude::SeatExt,
wayland::{
handlers::screencopy::{
constraints_for_output, constraints_for_toplevel, SessionData, SessionUserData,
},
protocols::{
screencopy::{BufferConstraints, CursorSession, FailureReason, Frame, Session},
workspace::WorkspaceHandle,
},
},
};
use super::super::data_device::get_dnd_icon;
pub fn submit_buffer<R>(
frame: Frame,
renderer: &mut R,
transform: Transform,
damage: Option<Vec<Rectangle<i32, Physical>>>,
sync: SyncPoint,
) -> Result<Option<(Frame, Vec<Rectangle<i32, BufferCoords>>)>, <R as Renderer>::Error>
where
R: ExportMem,
{
let Some(damage) = damage else {
frame.success(
transform,
None,
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::ZERO),
);
return Ok(None);
};
let buffer = frame.buffer();
if matches!(buffer_type(&buffer), Some(BufferType::Shm)) {
let buffer_size = buffer_dimensions(&buffer).unwrap();
if let Err(err) = with_buffer_contents_mut(&buffer, |ptr, len, data| {
let offset = data.offset as i32;
let width = data.width as i32;
let height = data.height as i32;
let stride = data.stride as i32;
let format = shm_format_to_fourcc(data.format)
.expect("We should be able to convert all hardcoded shm screencopy formats");
// number of bytes per pixel
// TODO: compute from data.format
let pixelsize = 4i32;
// ensure consistency, the SHM handler of smithay should ensure this
assert!((offset + (height - 1) * stride + width * pixelsize) as usize <= len);
// ensure rendering is done
renderer.wait(&sync)?;
let format = get_transparent(format).unwrap_or(format);
let mapping = renderer
.copy_framebuffer(Rectangle::from_loc_and_size((0, 0), buffer_size), format)?;
let gl_data = renderer.map_texture(&mapping)?;
assert!((width * height * pixelsize) as usize <= gl_data.len());
for i in 0..height {
unsafe {
std::ptr::copy_nonoverlapping::<u8>(
gl_data.as_ptr().offset((width * pixelsize * i) as isize),
ptr.offset((offset + stride * i) as isize),
(width * pixelsize) as usize,
);
}
}
Ok(())
})
.unwrap()
{
frame.fail(FailureReason::Unknown);
return Err(err);
}
}
Ok(Some((
frame,
damage
.into_iter()
.map(|rect| {
let logical = rect.to_logical(1);
logical.to_buffer(1, transform, &logical.size)
})
.collect(),
)))
}
pub fn render_session<F, R>(
renderer: &mut R,
session: &SessionData,
frame: Frame,
transform: Transform,
render_fn: F,
) -> Result<Option<(Frame, Vec<Rectangle<i32, BufferCoords>>)>, DTError<R>>
where
R: ExportMem,
F: FnOnce(
&WlBuffer,
&mut R,
&mut OutputDamageTracker,
usize,
Vec<Rectangle<i32, BufferCoords>>,
) -> Result<RenderOutputResult, DTError<R>>,
{
#[cfg(feature = "debug")]
puffin::profile_function!();
let mut session_damage_tracking = session.borrow_mut();
let buffer = frame.buffer();
let age = session_damage_tracking.age_for_buffer(&buffer);
let res = render_fn(
&frame.buffer(),
renderer,
&mut session_damage_tracking.dt,
age,
frame.damage(),
);
match res {
Ok(result) => submit_buffer(frame, renderer, transform, result.damage, result.sync)
.map_err(DTError::Rendering),
Err(err) => {
frame.fail(FailureReason::Unknown);
Err(err)
}
}
}
pub fn render_workspace_to_buffer(
state: &mut State,
session: Session,
frame: Frame,
handle: WorkspaceHandle,
) {
#[cfg(feature = "debug")]
puffin::profile_function!();
let Some(workspace) = state.common.shell.workspaces.space_for_handle(&handle) else {
session.stop();
return;
};
let output = workspace.output().clone();
let idx = state
.common
.shell
.workspaces
.idx_for_handle(&output, &handle)
.unwrap();
let mode = output
.current_mode()
.map(|mode| mode.size.to_logical(1).to_buffer(1, Transform::Normal));
let buffer = frame.buffer();
let buffer_size = buffer_dimensions(&buffer).unwrap();
if mode != Some(buffer_size) {
let Some(constraints) = constraints_for_output(&output, &mut state.backend) else {
session.stop();
return;
};
session.update_constraints(constraints);
if let Some(data) = session.user_data().get::<SessionData>() {
*data.borrow_mut() = SessionUserData::new(OutputDamageTracker::from_output(&output));
}
frame.fail(FailureReason::BufferConstraints);
return;
}
fn render_fn<R>(
buffer: &WlBuffer,
renderer: &mut R,
dt: &mut OutputDamageTracker,
age: usize,
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
draw_cursor: bool,
common: &mut Common,
output: &Output,
handle: (WorkspaceHandle, usize),
) -> Result<RenderOutputResult, DTError<R>>
where
R: Renderer
+ ImportAll
+ ImportMem
+ ExportMem
+ Bind<Dmabuf>
+ Offscreen<GlesRenderbuffer>
+ Blit<Dmabuf>
+ AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
<R as Renderer>::Error: From<GlesError>,
CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>,
{
let cursor_mode = if draw_cursor {
CursorMode::All
} else {
CursorMode::None
};
let area = output
.current_mode()
.ok_or(DTError::OutputNoMode(OutputNoMode))
.map(
|mode| {
mode.size
.to_logical(1)
.to_buffer(1, Transform::Normal)
.to_f64()
}, /* TODO: Mode is Buffer..., why is this Physical in the first place */
)?;
let additional_damage = (!additional_damage.is_empty()).then(|| {
additional_damage
.into_iter()
.map(|rect| {
rect.to_f64()
.to_logical(
output.current_scale().fractional_scale(),
output.current_transform(),
&area,
)
.to_i32_round()
})
.collect()
});
if let Ok(dmabuf) = get_dmabuf(buffer) {
render_workspace::<_, _, GlesRenderbuffer>(
None,
renderer,
dmabuf,
dt,
age,
additional_damage,
common,
&output,
None,
handle,
cursor_mode,
None,
true,
)
.map(|res| res.0)
} else {
let size = buffer_dimensions(buffer).unwrap();
let format =
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
.map_err(|_| DTError::OutputNoMode(OutputNoMode))? // eh, we have to do some error
.expect("We should be able to convert all hardcoded shm screencopy formats");
let render_buffer =
Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
.map_err(DTError::Rendering)?;
render_workspace::<_, _, GlesRenderbuffer>(
None,
renderer,
render_buffer,
dt,
age,
additional_damage,
common,
&output,
None,
handle,
cursor_mode,
None,
true,
)
.map(|res| res.0)
}
}
let draw_cursor = session.draw_cursor();
let transform = output.current_transform();
let common = &mut state.common;
let result = match &mut state.backend {
BackendData::Kms(kms) => {
let render_node = kms
.target_node_for_output(&output)
.unwrap_or(kms.primary_node);
let target_node = get_dmabuf(&buffer)
.ok()
.and_then(|dma| dma.node())
.unwrap_or(render_node);
let buffer_format = match buffer_type(&buffer) {
Some(BufferType::Dma) => Some(get_dmabuf(&buffer).unwrap().format().code),
Some(BufferType::Shm) => {
with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format))
.unwrap()
}
_ => None,
};
let mut multirenderer = match kms.api.renderer(
&render_node,
&target_node,
buffer_format.unwrap_or(Fourcc::Abgr8888),
) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Couldn't use nodes for screencopy");
frame.fail(FailureReason::Unknown);
return;
}
};
match render_session::<_, _>(
&mut multirenderer,
session.user_data().get::<SessionData>().unwrap(),
frame,
transform,
|buffer, renderer, dt, age, additional_damage| {
render_fn(
buffer,
renderer,
dt,
age,
additional_damage,
draw_cursor,
common,
&output,
(handle, idx),
)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
}
}
BackendData::Winit(winit) => match render_session::<_, _>(
winit.backend.renderer(),
session.user_data().get::<SessionData>().unwrap(),
frame,
transform,
|buffer, renderer, dt, age, additional_damage| {
render_fn(
buffer,
renderer,
dt,
age,
additional_damage,
draw_cursor,
common,
&output,
(handle, idx),
)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
},
BackendData::X11(x11) => match render_session::<_, _>(
&mut x11.renderer,
session.user_data().get::<SessionData>().unwrap(),
frame,
transform,
|buffer, renderer, dt, age, additional_damage| {
render_fn(
buffer,
renderer,
dt,
age,
additional_damage,
draw_cursor,
common,
&output,
(handle, idx),
)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
},
_ => unreachable!(),
};
if let Some((frame, damage)) = result {
frame.success(transform, damage, common.clock.now())
}
}
smithay::render_elements! {
pub WindowCaptureElement<R> where R: ImportAll + ImportMem;
WaylandElement=WaylandSurfaceRenderElement<R>,
CursorElement=RelocateRenderElement<cursor::CursorRenderElement<R>>,
AdditionalDamage=DamageElement,
}
pub fn render_window_to_buffer(
state: &mut State,
session: Session,
frame: Frame,
toplevel: &CosmicSurface,
) {
#[cfg(feature = "debug")]
puffin::profile_function!();
if !toplevel.alive() {
session.stop();
return;
}
let buffer = frame.buffer();
let geometry = toplevel.geometry();
let buffer_size = buffer_dimensions(&buffer).unwrap();
if buffer_size != geometry.size.to_buffer(1, Transform::Normal) {
let Some(constraints) = constraints_for_toplevel(toplevel, &mut state.backend) else {
session.stop();
return;
};
session.update_constraints(constraints);
if let Some(data) = session.user_data().get::<SessionData>() {
let size = geometry.size.to_physical(1);
*data.borrow_mut() =
SessionUserData::new(OutputDamageTracker::new(size, 1.0, Transform::Normal));
}
frame.fail(FailureReason::BufferConstraints);
return;
}
fn render_fn<R>(
buffer: &WlBuffer,
renderer: &mut R,
dt: &mut OutputDamageTracker,
age: usize,
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
draw_cursor: bool,
common: &mut Common,
window: &CosmicSurface,
geometry: Rectangle<i32, Logical>,
) -> Result<RenderOutputResult, DTError<R>>
where
R: Renderer
+ ImportAll
+ ImportMem
+ ExportMem
+ Bind<Dmabuf>
+ Offscreen<GlesRenderbuffer>
+ Blit<Dmabuf>
+ AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
<R as Renderer>::Error: From<GlesError>,
CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
let mut elements = AsRenderElements::<R>::render_elements::<WindowCaptureElement<R>>(
window,
renderer,
(-geometry.loc.x, -geometry.loc.y).into(),
Scale::from(1.0),
1.0,
);
elements.extend(
additional_damage
.into_iter()
.filter_map(|rect| {
let logical_rect = rect.to_logical(
1,
Transform::Normal,
&geometry.size.to_buffer(1, Transform::Normal),
);
logical_rect.intersection(Rectangle::from_loc_and_size((0, 0), geometry.size))
})
.map(DamageElement::new)
.map(Into::<WindowCaptureElement<R>>::into),
);
let seat = common.last_active_seat().clone();
if let Some(location) = {
if let Some(mapped) = common.shell.element_for_surface(window) {
mapped.cursor_position(&seat).and_then(|mut p| {
p -= mapped.active_window_offset().to_f64();
if p.x < 0. || p.y < 0. {
None
} else {
Some(p)
}
})
} else {
None
}
} {
if draw_cursor {
elements.extend(
cursor::draw_cursor(
renderer,
&seat,
location,
1.0.into(),
common.clock.now(),
true,
)
.into_iter()
.map(|(elem, hotspot)| {
WindowCaptureElement::CursorElement(RelocateRenderElement::from_element(
elem,
Point::from((-hotspot.x, -hotspot.y)),
Relocate::Relative,
))
}),
);
}
if let Some(wl_surface) = get_dnd_icon(&seat) {
elements.extend(
cursor::draw_dnd_icon(renderer, &wl_surface, location.to_i32_round(), 1.0)
.into_iter()
.map(WindowCaptureElement::from),
);
}
}
if let Ok(dmabuf) = get_dmabuf(buffer) {
renderer.bind(dmabuf).map_err(DTError::Rendering)?;
} else {
let size = buffer_dimensions(buffer).unwrap();
let format =
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
.map_err(|_| DTError::OutputNoMode(OutputNoMode))? // eh, we have to do some error
.expect("We should be able to convert all hardcoded shm screencopy formats");
let render_buffer =
Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
.map_err(DTError::Rendering)?;
renderer.bind(render_buffer).map_err(DTError::Rendering)?;
}
dt.render_output(
renderer,
age,
&elements,
CLEAR_COLOR, // TODO use a theme neutral color
)
}
let common = &mut state.common;
let draw_cursor = session.draw_cursor();
let result = match &mut state.backend {
BackendData::Kms(kms) => {
let node = get_dmabuf(&buffer)
.ok()
.and_then(|dmabuf| dmabuf.node())
.or_else(|| {
toplevel
.wl_surface()
.and_then(|wl_surface| {
with_renderer_surface_state(&wl_surface, |state| {
let buffer = state.buffer()?;
let dmabuf = get_dmabuf(&*buffer).ok()?;
dmabuf.node()
})
})
.flatten()
})
.unwrap_or(kms.primary_node);
let mut multirenderer = match kms.api.single_renderer(&node) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Couldn't use node for screencopy");
frame.fail(FailureReason::Unknown);
return;
}
};
match render_session::<_, _>(
&mut multirenderer,
session.user_data().get::<SessionData>().unwrap(),
frame,
Transform::Normal,
|buffer, renderer, dt, age, additional_damage| {
render_fn(
buffer,
renderer,
dt,
age,
additional_damage,
draw_cursor,
common,
toplevel,
geometry,
)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
}
}
BackendData::Winit(winit) => match render_session::<_, _>(
winit.backend.renderer(),
session.user_data().get::<SessionData>().unwrap(),
frame,
Transform::Normal,
|buffer, renderer, dt, age, additional_damage| {
render_fn(
buffer,
renderer,
dt,
age,
additional_damage,
draw_cursor,
common,
toplevel,
geometry,
)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
},
BackendData::X11(x11) => match render_session::<_, _>(
&mut x11.renderer,
session.user_data().get::<SessionData>().unwrap(),
frame,
Transform::Normal,
|buffer, renderer, dt, age, additional_damage| {
render_fn(
buffer,
renderer,
dt,
age,
additional_damage,
draw_cursor,
common,
toplevel,
geometry,
)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
},
_ => unreachable!(),
};
if let Some((frame, damage)) = result {
frame.success(Transform::Normal, damage, common.clock.now())
}
}
pub fn render_cursor_to_buffer(
state: &mut State,
session: &CursorSession,
frame: Frame,
seat: &Seat<State>,
) {
let buffer = frame.buffer();
let cursor_size = seat
.cursor_geometry((0.0, 0.0), state.common.clock.now())
.map(|(geo, _hotspot)| geo.size)
.unwrap_or_else(|| Size::from((64, 64)));
let buffer_size = buffer_dimensions(&buffer).unwrap();
if buffer_size != cursor_size {
let constraints = BufferConstraints {
size: cursor_size,
shm: vec![ShmFormat::Argb8888],
dma: None,
};
session.update_constraints(constraints);
if let Some(data) = session.user_data().get::<SessionData>() {
*data.borrow_mut() = SessionUserData::new(OutputDamageTracker::new(
cursor_size.to_logical(1, Transform::Normal).to_physical(1),
1.0,
Transform::Normal,
));
}
frame.fail(FailureReason::BufferConstraints);
return;
}
fn render_fn<R>(
buffer: &WlBuffer,
renderer: &mut R,
dt: &mut OutputDamageTracker,
age: usize,
additional_damage: Vec<Rectangle<i32, BufferCoords>>,
common: &mut Common,
seat: &Seat<State>,
) -> Result<RenderOutputResult, DTError<R>>
where
R: Renderer
+ ImportAll
+ ImportMem
+ ExportMem
+ Bind<Dmabuf>
+ Offscreen<GlesRenderbuffer>
+ Blit<Dmabuf>
+ AsGlowRenderer,
<R as Renderer>::TextureId: Clone + 'static,
<R as Renderer>::Error: From<GlesError>,
CosmicElement<R>: RenderElement<R>,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
let mut elements = cursor::draw_cursor(
renderer,
&seat,
Point::from((0.0, 0.0)),
1.0.into(),
common.clock.now(),
true,
)
.into_iter()
.map(|(elem, _)| RelocateRenderElement::from_element(elem, (0, 0), Relocate::Relative))
.map(WindowCaptureElement::from)
.collect::<Vec<_>>();
elements.extend(
additional_damage
.into_iter()
.filter_map(|rect| {
let logical_rect = rect.to_logical(1, Transform::Normal, &Size::from((64, 64)));
logical_rect.intersection(Rectangle::from_loc_and_size((0, 0), (64, 64)))
})
.map(DamageElement::new)
.map(Into::<WindowCaptureElement<R>>::into),
);
if let Ok(dmabuf) = get_dmabuf(buffer) {
renderer.bind(dmabuf).map_err(DTError::Rendering)?;
} else {
let size = buffer_dimensions(buffer).unwrap();
let format =
with_buffer_contents(buffer, |_, _, data| shm_format_to_fourcc(data.format))
.map_err(|_| DTError::OutputNoMode(OutputNoMode))? // eh, we have to do some error
.expect("We should be able to convert all hardcoded shm screencopy formats");
let render_buffer =
Offscreen::<GlesRenderbuffer>::create_buffer(renderer, format, size)
.map_err(DTError::Rendering)?;
renderer.bind(render_buffer).map_err(DTError::Rendering)?;
}
dt.render_output(renderer, age, &elements, [0.0, 0.0, 0.0, 0.0])
}
let common = &mut state.common;
let result = match &mut state.backend {
BackendData::Kms(kms) => {
let mut multirenderer = match kms.api.single_renderer(&kms.primary_node) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Couldn't use node for screencopy");
frame.fail(FailureReason::Unknown);
return;
}
};
match render_session::<_, _>(
&mut multirenderer,
session.user_data().get::<SessionData>().unwrap(),
frame,
Transform::Normal,
|buffer, renderer, dt, age, additional_damage| {
render_fn(buffer, renderer, dt, age, additional_damage, common, seat)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
}
}
BackendData::Winit(winit) => match render_session::<_, _>(
winit.backend.renderer(),
session.user_data().get::<SessionData>().unwrap(),
frame,
Transform::Normal,
|buffer, renderer, dt, age, additional_damage| {
render_fn(buffer, renderer, dt, age, additional_damage, common, seat)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
},
BackendData::X11(x11) => match render_session::<_, _>(
&mut x11.renderer,
session.user_data().get::<SessionData>().unwrap(),
frame,
Transform::Normal,
|buffer, renderer, dt, age, additional_damage| {
render_fn(buffer, renderer, dt, age, additional_damage, common, seat)
},
) {
Ok(frame) => frame,
Err(err) => {
tracing::warn!(?err, "Failed to render to screencopy buffer");
None
}
},
_ => unreachable!(),
};
if let Some((frame, damage)) = result {
frame.success(Transform::Normal, damage, common.clock.now())
}
}

View file

@ -0,0 +1,357 @@
use std::{
cell::RefCell,
collections::HashMap,
ops::{Deref, DerefMut},
};
use smithay::{
backend::renderer::{damage::OutputDamageTracker, utils::CommitCounter},
output::Output,
reexports::wayland_server::{protocol::wl_buffer::WlBuffer, Resource, Weak},
};
use crate::{
shell::{CosmicSurface, Workspace},
wayland::protocols::screencopy::{CursorSession, FailureReason, Frame, Session},
};
type ScreencopySessionsData = RefCell<ScreencopySessions>;
type PendingScreencopyBuffers = RefCell<Vec<(Session, DropableFrame)>>;
pub type SessionData = RefCell<SessionUserData>;
pub struct SessionUserData {
pub dt: OutputDamageTracker,
commit_counter: CommitCounter,
buffer_age: HashMap<Weak<WlBuffer>, CommitCounter>,
}
impl SessionUserData {
pub fn new(tracker: OutputDamageTracker) -> SessionUserData {
SessionUserData {
dt: tracker,
commit_counter: CommitCounter::default(),
buffer_age: HashMap::new(),
}
}
pub fn age_for_buffer(&mut self, buffer: &WlBuffer) -> usize {
self.buffer_age.retain(|k, _| k.upgrade().is_ok());
let weak = buffer.downgrade();
let age = self
.commit_counter
.distance(self.buffer_age.get(&weak).copied())
.unwrap_or(0);
self.buffer_age.insert(weak, self.commit_counter);
self.commit_counter.increment();
age
}
pub fn reset(&mut self) {
self.commit_counter = CommitCounter::default();
self.buffer_age.clear();
}
}
#[derive(Debug, Default)]
pub struct ScreencopySessions {
sessions: Vec<DropableSession>,
cursor_sessions: Vec<DropableCursorSession>,
}
pub trait SessionHolder {
fn add_session(&mut self, session: Session);
fn remove_session(&mut self, session: Session);
fn sessions(&self) -> Vec<Session>;
fn add_cursor_session(&mut self, session: CursorSession);
fn remove_cursor_session(&mut self, session: CursorSession);
fn cursor_sessions(&self) -> Vec<CursorSession>;
}
pub trait FrameHolder {
fn add_frame(&mut self, session: Session, frame: Frame);
fn remove_frame(&mut self, frame: &Frame);
fn take_pending_frames(&self) -> Vec<(Session, Frame)>;
}
impl SessionHolder for Output {
fn add_session(&mut self, session: Session) {
self.user_data()
.insert_if_missing(ScreencopySessionsData::default);
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.sessions
.push(DropableSession(Some(session)));
}
fn remove_session(&mut self, session: Session) {
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.sessions
.retain(|s| *s != session);
}
fn sessions(&self) -> Vec<Session> {
self.user_data()
.get::<ScreencopySessionsData>()
.map_or(Vec::new(), |sessions| {
sessions
.borrow()
.sessions
.iter()
.flat_map(|s| s.0.clone())
.collect()
})
}
fn add_cursor_session(&mut self, session: CursorSession) {
self.user_data()
.insert_if_missing(ScreencopySessionsData::default);
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.cursor_sessions
.push(DropableCursorSession(Some(session)));
}
fn remove_cursor_session(&mut self, session: CursorSession) {
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.cursor_sessions
.retain(|s| *s != session);
}
fn cursor_sessions(&self) -> Vec<CursorSession> {
self.user_data()
.get::<ScreencopySessionsData>()
.map_or(Vec::new(), |sessions| {
sessions
.borrow()
.cursor_sessions
.iter()
.flat_map(|s| s.0.clone())
.collect()
})
}
}
impl FrameHolder for Output {
fn add_frame(&mut self, session: Session, frame: Frame) {
self.user_data()
.insert_if_missing(PendingScreencopyBuffers::default);
self.user_data()
.get::<PendingScreencopyBuffers>()
.unwrap()
.borrow_mut()
.push((session, DropableFrame(Some(frame))));
}
fn remove_frame(&mut self, frame: &Frame) {
if let Some(pending) = self.user_data().get::<PendingScreencopyBuffers>() {
pending.borrow_mut().retain(|(_, f)| f != frame);
}
}
fn take_pending_frames(&self) -> Vec<(Session, Frame)> {
self.user_data()
.get::<PendingScreencopyBuffers>()
.map(|pending| {
pending
.borrow_mut()
.split_off(0)
.into_iter()
.map(|(s, mut f)| (s, f.0.take().unwrap()))
.collect()
})
.unwrap_or_default()
}
}
impl SessionHolder for Workspace {
fn add_session(&mut self, session: Session) {
self.screencopy
.sessions
.push(DropableSession(Some(session)));
}
fn remove_session(&mut self, session: Session) {
self.screencopy.sessions.retain(|s| *s != session);
}
fn sessions(&self) -> Vec<Session> {
self.screencopy
.sessions
.iter()
.flat_map(|s| s.0.clone())
.collect()
}
fn add_cursor_session(&mut self, session: CursorSession) {
self.screencopy
.cursor_sessions
.push(DropableCursorSession(Some(session)));
}
fn remove_cursor_session(&mut self, session: CursorSession) {
self.screencopy.cursor_sessions.retain(|s| *s != session);
}
fn cursor_sessions(&self) -> Vec<CursorSession> {
self.screencopy
.cursor_sessions
.iter()
.flat_map(|s| s.0.clone())
.collect()
}
}
impl SessionHolder for CosmicSurface {
fn add_session(&mut self, session: Session) {
self.user_data()
.insert_if_missing(ScreencopySessionsData::default);
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.sessions
.push(DropableSession(Some(session)));
}
fn remove_session(&mut self, session: Session) {
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.sessions
.retain(|s| *s != session);
}
fn sessions(&self) -> Vec<Session> {
self.user_data()
.get::<ScreencopySessionsData>()
.map_or(Vec::new(), |sessions| {
sessions
.borrow()
.sessions
.iter()
.flat_map(|s| s.0.clone())
.collect()
})
}
fn add_cursor_session(&mut self, session: CursorSession) {
self.user_data()
.insert_if_missing(ScreencopySessionsData::default);
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.cursor_sessions
.push(DropableCursorSession(Some(session)));
}
fn remove_cursor_session(&mut self, session: CursorSession) {
self.user_data()
.get::<ScreencopySessionsData>()
.unwrap()
.borrow_mut()
.cursor_sessions
.retain(|s| *s != session);
}
fn cursor_sessions(&self) -> Vec<CursorSession> {
self.user_data()
.get::<ScreencopySessionsData>()
.map_or(Vec::new(), |sessions| {
sessions
.borrow()
.cursor_sessions
.iter()
.flat_map(|s| s.0.clone())
.collect()
})
}
}
#[derive(Debug)]
struct DropableSession(Option<Session>);
impl Deref for DropableSession {
type Target = Session;
fn deref(&self) -> &Self::Target {
self.0.as_ref().unwrap()
}
}
impl DerefMut for DropableSession {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut().unwrap()
}
}
impl PartialEq<Session> for DropableSession {
fn eq(&self, other: &Session) -> bool {
self.0.as_ref().map(|s| s == other).unwrap_or(false)
}
}
impl Drop for DropableSession {
fn drop(&mut self) {
if let Some(s) = self.0.take() {
s.stop();
}
}
}
#[derive(Debug)]
struct DropableCursorSession(Option<CursorSession>);
impl Deref for DropableCursorSession {
type Target = CursorSession;
fn deref(&self) -> &Self::Target {
self.0.as_ref().unwrap()
}
}
impl DerefMut for DropableCursorSession {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut().unwrap()
}
}
impl PartialEq<CursorSession> for DropableCursorSession {
fn eq(&self, other: &CursorSession) -> bool {
self.0.as_ref().map(|s| s == other).unwrap_or(false)
}
}
impl Drop for DropableCursorSession {
fn drop(&mut self) {
if let Some(s) = self.0.take() {
s.stop();
}
}
}
#[derive(Debug)]
pub struct DropableFrame(Option<Frame>);
impl Deref for DropableFrame {
type Target = Frame;
fn deref(&self) -> &Self::Target {
self.0.as_ref().unwrap()
}
}
impl DerefMut for DropableFrame {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut().unwrap()
}
}
impl PartialEq<Frame> for DropableFrame {
fn eq(&self, other: &Frame) -> bool {
self.0.as_ref().map(|f| f == other).unwrap_or(false)
}
}
impl Drop for DropableFrame {
fn drop(&mut self) {
if let Some(f) = self.0.take() {
f.fail(FailureReason::Unknown);
}
}
}

View file

@ -39,7 +39,7 @@ impl SessionLockHandler for State {
for output in self.common.shell.outputs() {
self.backend
.schedule_render(&self.common.event_loop_handle, &output, None);
.schedule_render(&self.common.event_loop_handle, &output);
}
}
@ -48,7 +48,7 @@ impl SessionLockHandler for State {
for output in self.common.shell.outputs() {
self.backend
.schedule_render(&self.common.event_loop_handle, &output, None);
.schedule_render(&self.common.event_loop_handle, &output);
}
}

View file

@ -3,7 +3,6 @@
use crate::{
shell::{element::CosmicWindow, grabs::ReleaseMode, CosmicMapped, CosmicSurface, ManagedLayer},
utils::prelude::*,
wayland::protocols::screencopy::SessionType,
};
use smithay::{
delegate_xdg_shell,
@ -30,10 +29,7 @@ use smithay::{
use std::cell::Cell;
use tracing::warn;
use super::{
compositor::client_compositor_state, screencopy::PendingScreencopyBuffers,
toplevel_management::ToplevelManagementExt,
};
use super::{compositor::client_compositor_state, toplevel_management::ToplevelManagementExt};
pub mod popup;
@ -403,32 +399,9 @@ impl XdgShellHandler for State {
}
}
// screencopy
let mut scheduled_sessions = self.schedule_workspace_sessions(surface.wl_surface());
if let Some(output) = output.as_ref() {
if let Some(sessions) = output.user_data().get::<PendingScreencopyBuffers>() {
scheduled_sessions
.get_or_insert_with(Vec::new)
.extend(sessions.borrow_mut().drain(..));
}
self.backend.schedule_render(
&self.common.event_loop_handle,
&output,
scheduled_sessions.as_ref().map(|sessions| {
sessions
.iter()
.filter(|(s, _)| match s.session_type() {
SessionType::Output(o) | SessionType::Workspace(o, _)
if &o == output =>
{
true
}
_ => false,
})
.cloned()
.collect::<Vec<_>>()
}),
);
self.backend
.schedule_render(&self.common.event_loop_handle, &output);
}
}

View file

@ -0,0 +1,343 @@
use super::{
toplevel_info::window_from_handle,
workspace::{WorkspaceHandle, WorkspaceHandler},
};
use crate::shell::CosmicSurface;
use cosmic_protocols::image_source::v1::server::{
zcosmic_image_source_v1::ZcosmicImageSourceV1,
zcosmic_output_image_source_manager_v1::{
Request as OutputSourceRequest, ZcosmicOutputImageSourceManagerV1,
},
zcosmic_toplevel_image_source_manager_v1::{
Request as ToplevelSourceRequest, ZcosmicToplevelImageSourceManagerV1,
},
zcosmic_workspace_image_source_manager_v1::{
Request as WorkspaceSourceRequest, ZcosmicWorkspaceImageSourceManagerV1,
},
};
use smithay::{
output::{Output, WeakOutput},
reexports::wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
},
};
use wayland_backend::server::GlobalId;
#[derive(Debug)]
pub struct ImageSourceState {
output_source_global: GlobalId,
workspace_source_global: GlobalId,
toplevel_source_global: GlobalId,
}
pub struct OutputImageSourceManagerGlobalData {
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
}
pub struct WorkspaceImageSourceManagerGlobalData {
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
}
pub struct ToplevelImageSourceManagerGlobalData {
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ImageSourceData {
Output(WeakOutput),
Workspace(WorkspaceHandle),
Toplevel(CosmicSurface),
Destroyed,
}
impl ImageSourceState {
pub fn new<D, F>(display: &DisplayHandle, client_filter: F) -> ImageSourceState
where
D: GlobalDispatch<ZcosmicOutputImageSourceManagerV1, OutputImageSourceManagerGlobalData>
+ Dispatch<ZcosmicOutputImageSourceManagerV1, ()>
+ GlobalDispatch<
ZcosmicWorkspaceImageSourceManagerV1,
WorkspaceImageSourceManagerGlobalData,
> + Dispatch<ZcosmicWorkspaceImageSourceManagerV1, ()>
+ GlobalDispatch<
ZcosmicToplevelImageSourceManagerV1,
ToplevelImageSourceManagerGlobalData,
> + Dispatch<ZcosmicToplevelImageSourceManagerV1, ()>
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
+ WorkspaceHandler
+ 'static,
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + Clone + 'static,
{
ImageSourceState {
output_source_global: display.create_global::<D, ZcosmicOutputImageSourceManagerV1, _>(
1,
OutputImageSourceManagerGlobalData {
filter: Box::new(client_filter.clone()),
},
),
workspace_source_global: display
.create_global::<D, ZcosmicWorkspaceImageSourceManagerV1, _>(
1,
WorkspaceImageSourceManagerGlobalData {
filter: Box::new(client_filter.clone()),
},
),
toplevel_source_global: display
.create_global::<D, ZcosmicToplevelImageSourceManagerV1, _>(
1,
ToplevelImageSourceManagerGlobalData {
filter: Box::new(client_filter),
},
),
}
}
pub fn output_source_id(&self) -> &GlobalId {
&self.output_source_global
}
pub fn workspace_source_id(&self) -> &GlobalId {
&self.workspace_source_global
}
pub fn toplevel_source_id(&self) -> &GlobalId {
&self.toplevel_source_global
}
}
impl<D> GlobalDispatch<ZcosmicOutputImageSourceManagerV1, OutputImageSourceManagerGlobalData, D>
for ImageSourceState
where
D: GlobalDispatch<ZcosmicOutputImageSourceManagerV1, OutputImageSourceManagerGlobalData>
+ Dispatch<ZcosmicOutputImageSourceManagerV1, ()>
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
+ 'static,
{
fn bind(
_state: &mut D,
_handle: &DisplayHandle,
_client: &Client,
resource: New<ZcosmicOutputImageSourceManagerV1>,
_global_data: &OutputImageSourceManagerGlobalData,
data_init: &mut DataInit<'_, D>,
) {
data_init.init(resource, ());
}
fn can_view(client: Client, global_data: &OutputImageSourceManagerGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D>
GlobalDispatch<ZcosmicWorkspaceImageSourceManagerV1, WorkspaceImageSourceManagerGlobalData, D>
for ImageSourceState
where
D: GlobalDispatch<ZcosmicWorkspaceImageSourceManagerV1, WorkspaceImageSourceManagerGlobalData>
+ Dispatch<ZcosmicWorkspaceImageSourceManagerV1, ()>
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
+ 'static,
{
fn bind(
_state: &mut D,
_handle: &DisplayHandle,
_client: &Client,
resource: New<ZcosmicWorkspaceImageSourceManagerV1>,
_global_data: &WorkspaceImageSourceManagerGlobalData,
data_init: &mut DataInit<'_, D>,
) {
data_init.init(resource, ());
}
fn can_view(client: Client, global_data: &WorkspaceImageSourceManagerGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D> GlobalDispatch<ZcosmicToplevelImageSourceManagerV1, ToplevelImageSourceManagerGlobalData, D>
for ImageSourceState
where
D: GlobalDispatch<ZcosmicToplevelImageSourceManagerV1, ToplevelImageSourceManagerGlobalData>
+ Dispatch<ZcosmicToplevelImageSourceManagerV1, ()>
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
+ 'static,
{
fn bind(
_state: &mut D,
_handle: &DisplayHandle,
_client: &Client,
resource: New<ZcosmicToplevelImageSourceManagerV1>,
_global_data: &ToplevelImageSourceManagerGlobalData,
data_init: &mut DataInit<'_, D>,
) {
data_init.init(resource, ());
}
fn can_view(client: Client, global_data: &ToplevelImageSourceManagerGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D> Dispatch<ZcosmicOutputImageSourceManagerV1, (), D> for ImageSourceState
where
D: Dispatch<ZcosmicOutputImageSourceManagerV1, ()>
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
+ 'static,
{
fn request(
_state: &mut D,
_client: &Client,
_resource: &ZcosmicOutputImageSourceManagerV1,
request: <ZcosmicOutputImageSourceManagerV1 as Resource>::Request,
_data: &(),
_dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
OutputSourceRequest::CreateSource { source, output } => {
let data = match Output::from_resource(&output) {
Some(output) => ImageSourceData::Output(output.downgrade()),
None => ImageSourceData::Destroyed,
};
data_init.init(source, data);
}
_ => {}
}
}
fn destroyed(
_state: &mut D,
_client: wayland_backend::server::ClientId,
_resource: &ZcosmicOutputImageSourceManagerV1,
_data: &(),
) {
}
}
impl<D> Dispatch<ZcosmicWorkspaceImageSourceManagerV1, (), D> for ImageSourceState
where
D: Dispatch<ZcosmicWorkspaceImageSourceManagerV1, ()>
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
+ WorkspaceHandler
+ 'static,
{
fn request(
state: &mut D,
_client: &Client,
_resource: &ZcosmicWorkspaceImageSourceManagerV1,
request: <ZcosmicWorkspaceImageSourceManagerV1 as Resource>::Request,
_data: &(),
_dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
WorkspaceSourceRequest::CreateSource { source, output } => {
let data = match state.workspace_state().workspace_handle(&output) {
Some(workspace) => ImageSourceData::Workspace(workspace),
None => ImageSourceData::Destroyed,
};
data_init.init(source, data);
}
_ => {}
}
}
fn destroyed(
_state: &mut D,
_client: wayland_backend::server::ClientId,
_resource: &ZcosmicWorkspaceImageSourceManagerV1,
_data: &(),
) {
}
}
impl<D> Dispatch<ZcosmicToplevelImageSourceManagerV1, (), D> for ImageSourceState
where
D: Dispatch<ZcosmicToplevelImageSourceManagerV1, ()>
+ Dispatch<ZcosmicImageSourceV1, ImageSourceData>
+ 'static,
{
fn request(
_state: &mut D,
_client: &Client,
_resource: &ZcosmicToplevelImageSourceManagerV1,
request: <ZcosmicToplevelImageSourceManagerV1 as Resource>::Request,
_data: &(),
_dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
ToplevelSourceRequest::CreateSource {
source,
toplevel_handle,
} => {
let data = match window_from_handle(toplevel_handle) {
Some(toplevel) => ImageSourceData::Toplevel(toplevel),
None => ImageSourceData::Destroyed,
};
data_init.init(source, data);
}
_ => {}
}
}
fn destroyed(
_state: &mut D,
_client: wayland_backend::server::ClientId,
_resource: &ZcosmicToplevelImageSourceManagerV1,
_data: &(),
) {
}
}
impl<D> Dispatch<ZcosmicImageSourceV1, ImageSourceData, D> for ImageSourceState
where
D: Dispatch<ZcosmicImageSourceV1, ImageSourceData> + 'static,
{
fn request(
_state: &mut D,
_client: &Client,
_resource: &ZcosmicImageSourceV1,
request: <ZcosmicImageSourceV1 as Resource>::Request,
_data: &ImageSourceData,
_dhandle: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
_ => {}
}
}
fn destroyed(
_state: &mut D,
_client: wayland_backend::server::ClientId,
_resource: &ZcosmicImageSourceV1,
_data: &ImageSourceData,
) {
}
}
macro_rules! delegate_image_source {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::image_source::v1::server::zcosmic_output_image_source_manager_v1::ZcosmicOutputImageSourceManagerV1: $crate::wayland::protocols::image_source::OutputImageSourceManagerGlobalData
] => $crate::wayland::protocols::image_source::ImageSourceState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::image_source::v1::server::zcosmic_output_image_source_manager_v1::ZcosmicOutputImageSourceManagerV1: ()
] => $crate::wayland::protocols::image_source::ImageSourceState);
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::image_source::v1::server::zcosmic_workspace_image_source_manager_v1::ZcosmicWorkspaceImageSourceManagerV1: $crate::wayland::protocols::image_source::WorkspaceImageSourceManagerGlobalData
] => $crate::wayland::protocols::image_source::ImageSourceState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::image_source::v1::server::zcosmic_workspace_image_source_manager_v1::ZcosmicWorkspaceImageSourceManagerV1: ()
] => $crate::wayland::protocols::image_source::ImageSourceState);
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::image_source::v1::server::zcosmic_toplevel_image_source_manager_v1::ZcosmicToplevelImageSourceManagerV1: $crate::wayland::protocols::image_source::ToplevelImageSourceManagerGlobalData
] => $crate::wayland::protocols::image_source::ImageSourceState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::image_source::v1::server::zcosmic_toplevel_image_source_manager_v1::ZcosmicToplevelImageSourceManagerV1: ()
] => $crate::wayland::protocols::image_source::ImageSourceState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::image_source::v1::server::zcosmic_image_source_v1::ZcosmicImageSourceV1: $crate::wayland::protocols::image_source::ImageSourceData
] => $crate::wayland::protocols::image_source::ImageSourceState);
};
}
pub(crate) use delegate_image_source;

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
pub mod drm;
//pub mod export_dmabuf;
pub mod image_source;
pub mod output_configuration;
pub mod screencopy;
pub mod toplevel_info;

File diff suppressed because it is too large Load diff