2026-01-13 18:26:26 -08:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
|
|
2024-06-07 19:04:00 +02:00
|
|
|
use std::{borrow::Borrow, collections::HashMap, sync::Mutex};
|
2024-03-12 19:42:48 +01:00
|
|
|
|
|
|
|
|
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},
|
2026-01-13 18:26:26 -08:00
|
|
|
wayland::{
|
|
|
|
|
dmabuf::get_dmabuf,
|
|
|
|
|
image_capture_source::ImageCaptureSource,
|
|
|
|
|
image_copy_capture::{
|
|
|
|
|
BufferConstraints, CursorSession, CursorSessionRef, DmabufConstraints, Frame, FrameRef,
|
|
|
|
|
ImageCopyCaptureHandler, ImageCopyCaptureState, Session, SessionRef,
|
|
|
|
|
},
|
|
|
|
|
seat::WaylandFocus,
|
|
|
|
|
},
|
2024-03-12 19:42:48 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
|
shell::CosmicSurface,
|
|
|
|
|
state::{BackendData, State},
|
|
|
|
|
utils::prelude::{
|
|
|
|
|
OutputExt, PointExt, PointGlobalExt, PointLocalExt, RectExt, RectLocalExt, SeatExt,
|
|
|
|
|
},
|
2026-01-13 18:26:26 -08:00
|
|
|
wayland::protocols::image_capture_source::ImageCaptureSourceKind,
|
2024-03-12 19:42:48 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
mod render;
|
|
|
|
|
mod user_data;
|
|
|
|
|
pub use self::render::*;
|
|
|
|
|
use self::user_data::*;
|
2026-01-13 18:26:26 -08:00
|
|
|
pub use self::user_data::{FrameHolder, ImageCopySessions, SessionData, SessionHolder};
|
2024-03-12 19:42:48 +01:00
|
|
|
|
2026-01-13 18:26:26 -08:00
|
|
|
impl ImageCopyCaptureHandler for State {
|
|
|
|
|
fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState {
|
|
|
|
|
&mut self.common.image_copy_capture_state
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:26:26 -08:00
|
|
|
fn capture_constraints(&mut self, source: &ImageCaptureSource) -> Option<BufferConstraints> {
|
|
|
|
|
let kind = source.user_data().get::<ImageCaptureSourceKind>().unwrap();
|
|
|
|
|
match kind {
|
|
|
|
|
ImageCaptureSourceKind::Output(weak) => weak
|
2024-03-12 19:42:48 +01:00
|
|
|
.upgrade()
|
|
|
|
|
.and_then(|output| constraints_for_output(&output, &mut self.backend)),
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Workspace(handle) => {
|
2025-05-20 17:41:27 +02:00
|
|
|
let shell = self.common.shell.read();
|
2025-10-16 13:50:32 +02:00
|
|
|
let output = shell.workspaces.space_for_handle(handle)?.output();
|
2024-04-10 15:49:08 +02:00
|
|
|
constraints_for_output(output, &mut self.backend)
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Toplevel(window) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
constraints_for_toplevel(window, &mut self.backend)
|
|
|
|
|
}
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
|
|
|
|
|
fn cursor_capture_constraints(
|
2025-03-03 16:19:41 -08:00
|
|
|
&mut self,
|
2026-01-13 18:26:26 -08:00
|
|
|
_source: &ImageCaptureSource,
|
2025-03-03 16:19:41 -08:00
|
|
|
) -> Option<BufferConstraints> {
|
2024-03-12 19:42:48 +01:00
|
|
|
let size = if let Some((geometry, _)) = self
|
|
|
|
|
.common
|
2024-04-05 13:53:35 +02:00
|
|
|
.shell
|
2024-04-10 15:49:08 +02:00
|
|
|
.read()
|
2024-04-05 13:53:35 +02:00
|
|
|
.seats
|
|
|
|
|
.last_active()
|
2024-03-12 19:42:48 +01:00
|
|
|
.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) {
|
2026-01-13 18:26:26 -08:00
|
|
|
let kind = session
|
|
|
|
|
.source()
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<ImageCaptureSourceKind>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone();
|
|
|
|
|
match kind {
|
|
|
|
|
ImageCaptureSourceKind::Output(weak) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
let Some(mut output) = weak.upgrade() else {
|
|
|
|
|
session.stop();
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-07 19:04:00 +02:00
|
|
|
session.user_data().insert_if_missing_threadsafe(|| {
|
|
|
|
|
Mutex::new(SessionUserData::new(OutputDamageTracker::from_output(
|
2024-03-12 19:42:48 +01:00
|
|
|
&output,
|
|
|
|
|
)))
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
output.add_session(session);
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Workspace(handle) => {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
let Some(workspace) = shell.workspaces.space_for_handle_mut(&handle) else {
|
2024-03-12 19:42:48 +01:00
|
|
|
session.stop();
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-07 19:04:00 +02:00
|
|
|
session.user_data().insert_if_missing_threadsafe(|| {
|
|
|
|
|
Mutex::new(SessionUserData::new(OutputDamageTracker::from_output(
|
2024-03-12 19:42:48 +01:00
|
|
|
workspace.output(),
|
|
|
|
|
)))
|
|
|
|
|
});
|
|
|
|
|
workspace.add_session(session);
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Toplevel(mut toplevel) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
let size = toplevel.geometry().size.to_physical(1);
|
2024-06-07 19:04:00 +02:00
|
|
|
session.user_data().insert_if_missing_threadsafe(|| {
|
|
|
|
|
Mutex::new(SessionUserData::new(OutputDamageTracker::new(
|
2024-03-12 19:42:48 +01:00
|
|
|
size,
|
|
|
|
|
1.0,
|
|
|
|
|
Transform::Normal,
|
|
|
|
|
)))
|
|
|
|
|
});
|
|
|
|
|
toplevel.add_session(session);
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Destroyed => unreachable!(),
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
|
2024-03-12 19:42:48 +01:00
|
|
|
fn new_cursor_session(&mut self, session: CursorSession) {
|
|
|
|
|
let (pointer_loc, pointer_size, hotspot) = {
|
2025-05-20 17:41:27 +02:00
|
|
|
let seat = self.common.shell.read().seats.last_active().clone();
|
2024-03-12 19:42:48 +01:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-07 19:04:00 +02:00
|
|
|
session.user_data().insert_if_missing_threadsafe(|| {
|
|
|
|
|
Mutex::new(SessionUserData::new(OutputDamageTracker::new(
|
2024-03-12 19:42:48 +01:00
|
|
|
pointer_size.to_logical(1, Transform::Normal).to_physical(1),
|
|
|
|
|
1.0,
|
|
|
|
|
Transform::Normal,
|
|
|
|
|
)))
|
|
|
|
|
});
|
|
|
|
|
|
2026-01-13 18:26:26 -08:00
|
|
|
let kind = session
|
|
|
|
|
.source()
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<ImageCaptureSourceKind>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone();
|
|
|
|
|
match kind {
|
|
|
|
|
ImageCaptureSourceKind::Output(weak) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
let Some(mut output) = weak.upgrade() else {
|
|
|
|
|
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);
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Workspace(handle) => {
|
2025-05-20 17:41:27 +02:00
|
|
|
let mut shell = self.common.shell.write();
|
2024-04-10 15:49:08 +02:00
|
|
|
let Some(workspace) = shell.workspaces.space_for_handle_mut(&handle) else {
|
2024-03-12 19:42:48 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Toplevel(mut toplevel) => {
|
2025-05-20 17:41:27 +02:00
|
|
|
let shell = self.common.shell.read();
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(element) = shell.element_for_surface(&toplevel) {
|
2024-03-12 19:42:48 +01:00
|
|
|
if element.has_active_window(&toplevel) {
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(workspace) = shell.space_for(element) {
|
2024-03-12 19:42:48 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Destroyed => unreachable!(),
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:26:26 -08:00
|
|
|
fn frame(&mut self, session: &SessionRef, frame: Frame) {
|
|
|
|
|
let kind = session
|
|
|
|
|
.source()
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<ImageCaptureSourceKind>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone();
|
|
|
|
|
match kind {
|
|
|
|
|
ImageCaptureSourceKind::Output(weak) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
let Some(mut output) = weak.upgrade() else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-13 18:26:26 -08:00
|
|
|
output.add_frame(session.clone(), frame);
|
2024-06-10 20:41:29 +02:00
|
|
|
self.backend.schedule_render(&output);
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Workspace(handle) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
render_workspace_to_buffer(self, session, frame, handle)
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Toplevel(toplevel) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
render_window_to_buffer(self, session, frame, &toplevel)
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Destroyed => unreachable!(),
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:26:26 -08:00
|
|
|
fn cursor_frame(&mut self, session: &CursorSessionRef, frame: Frame) {
|
2024-03-12 19:42:48 +01:00
|
|
|
if !session.has_cursor() {
|
|
|
|
|
frame.success(Transform::Normal, Vec::new(), self.common.clock.now());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-20 17:41:27 +02:00
|
|
|
let seat = self.common.shell.read().seats.last_active().clone();
|
2026-01-13 18:26:26 -08:00
|
|
|
render_cursor_to_buffer(self, session, frame, &seat);
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
fn frame_aborted(&mut self, frame: FrameRef) {
|
2025-05-20 17:41:27 +02:00
|
|
|
let shell = self.common.shell.read();
|
2024-04-10 15:49:08 +02:00
|
|
|
for mut output in shell.outputs().cloned() {
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
output.remove_frame(&frame);
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
fn session_destroyed(&mut self, session: SessionRef) {
|
2026-01-13 18:26:26 -08:00
|
|
|
let kind = session
|
|
|
|
|
.source()
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<ImageCaptureSourceKind>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone();
|
|
|
|
|
match kind {
|
|
|
|
|
ImageCaptureSourceKind::Output(weak) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
if let Some(mut output) = weak.upgrade() {
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
output.remove_session(&session);
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Workspace(handle) => {
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(workspace) = self
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.write()
|
|
|
|
|
.workspaces
|
|
|
|
|
.space_for_handle_mut(&handle)
|
2024-03-12 19:42:48 +01:00
|
|
|
{
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
workspace.remove_session(&session)
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Toplevel(mut toplevel) => toplevel.remove_session(&session),
|
|
|
|
|
ImageCaptureSourceKind::Destroyed => unreachable!(),
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
fn cursor_session_destroyed(&mut self, session: CursorSessionRef) {
|
2026-01-13 18:26:26 -08:00
|
|
|
let kind = session
|
|
|
|
|
.source()
|
|
|
|
|
.user_data()
|
|
|
|
|
.get::<ImageCaptureSourceKind>()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.clone();
|
|
|
|
|
match kind {
|
|
|
|
|
ImageCaptureSourceKind::Output(weak) => {
|
2024-03-12 19:42:48 +01:00
|
|
|
if let Some(mut output) = weak.upgrade() {
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
output.remove_cursor_session(&session);
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Workspace(handle) => {
|
2024-04-10 15:49:08 +02:00
|
|
|
if let Some(workspace) = self
|
|
|
|
|
.common
|
|
|
|
|
.shell
|
|
|
|
|
.write()
|
|
|
|
|
.workspaces
|
|
|
|
|
.space_for_handle_mut(&handle)
|
2024-03-12 19:42:48 +01:00
|
|
|
{
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
workspace.remove_cursor_session(&session)
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Toplevel(mut toplevel) => {
|
protocols/screencopy: Make frame/session send stopped/fail on drop
Previously, `Frame` was stored in KMS frame udata, but in some cases the
udata was dropped without a capture happening, and `Frame` did not
implement `Drop`, so `fail` was never sent.
Instead, rename `DropableFrame` to `Frame` and `Frame` to `FrameRef`, so
we can have a single instance of `Frame`, that will send `fail` on drop.
This guarantees either `.success` or `.fail` are send, as long as its
not leaked.
This seems to fix https://github.com/pop-os/cosmic-comp/issues/1305.
xdg-desktop-portal-cosmic prints an error, buy retries (as it should for
an `Unknown` error; though maybe there should be a retry limit) and the
session continues working.
(Not sure if it should be sending `failed`, or queing it with the next
frame so it can send `success` to the client, but this works and is
desirable as a failsafe anyway.)
`Session` and `CursorSession` are similiarly updated.
`.fail()`, `.success()`, and `.stop()` now consume
`Frame`/`Session`/`CursorSession`. So to stop a session, it is now
necessary to call `.remove_session()`, but then simply dropping with
send `.stop()`.
Factoring out some `Request::Capture` handling into a `capture_frame`
function seems to clean up error handling and such a bit.
2025-04-30 14:32:33 -07:00
|
|
|
toplevel.remove_cursor_session(&session)
|
2025-03-03 16:19:41 -08:00
|
|
|
}
|
2026-01-13 18:26:26 -08:00
|
|
|
ImageCaptureSourceKind::Destroyed => unreachable!(),
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-08-05 10:18:42 -07:00
|
|
|
let mut renderer = backend
|
2025-05-21 22:07:46 +02:00
|
|
|
.offscreen_renderer(|kms| {
|
2025-10-16 13:50:32 +02:00
|
|
|
kms.target_node_for_output(output)
|
2025-05-21 22:07:46 +02:00
|
|
|
.or(*kms.primary_node.read().unwrap())
|
|
|
|
|
})
|
2024-08-05 10:18:42 -07:00
|
|
|
.unwrap();
|
|
|
|
|
Some(constraints_for_renderer(mode, renderer.as_mut()))
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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()?;
|
|
|
|
|
|
2024-08-05 10:18:42 -07:00
|
|
|
let mut renderer = backend
|
|
|
|
|
.offscreen_renderer(|kms| {
|
2024-03-12 19:42:48 +01:00
|
|
|
let dma_node = with_renderer_surface_state(&wl_surface, |state| {
|
|
|
|
|
let buffer = state.buffer()?;
|
2025-10-16 13:50:32 +02:00
|
|
|
let dmabuf = get_dmabuf(buffer).ok()?;
|
2024-03-12 19:42:48 +01:00
|
|
|
dmabuf.node()
|
|
|
|
|
})
|
|
|
|
|
.flatten();
|
|
|
|
|
|
2025-05-21 22:07:46 +02:00
|
|
|
dma_node.or(*kms.primary_node.read().unwrap())
|
2024-08-05 10:18:42 -07:00
|
|
|
})
|
|
|
|
|
.unwrap();
|
2024-03-12 19:42:48 +01:00
|
|
|
|
2024-08-05 10:18:42 -07:00
|
|
|
Some(constraints_for_renderer(size, renderer.as_mut()))
|
2024-03-12 19:42:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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()
|
2024-06-27 12:37:21 +02:00
|
|
|
.contains(&Capability::_10Bit)
|
2024-03-12 19:42:48 +01:00
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:26:26 -08:00
|
|
|
smithay::delegate_image_copy_capture!(State);
|