Commit 6f4f29570be0
tarball6 files changed+136-47
@@ -1,22 +1,25 @@
1
-//! ext-image-copy-capture of whole outputs, for grim and screen sharing2
-//! portals.1
+//! ext-image-copy-capture of outputs and single windows, for grim and screen2
+//! sharing portals.33
44
use crate::scene::Draw;55
use crate::state::{State, now_ns};6
-use monolith_layout::OutputId;7
-use monolith_render_vk::VulkanImage;6
+use monolith_layout::{OutputId, WindowId};7
+use monolith_render_vk::{VulkanImage, VulkanRenderer};88
use smithay::backend::allocator::Fourcc;99
use smithay::backend::allocator::dmabuf::Dmabuf;1010
use smithay::backend::renderer::damage::OutputDamageTracker;11
+use smithay::backend::renderer::element::AsRenderElements;12
+use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;1113
use smithay::backend::renderer::{Bind, ExportMem, Offscreen};1214
use smithay::output::{Output, WeakOutput};1315
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;1416
use smithay::reexports::wayland_server::protocol::wl_shm;15
-use smithay::utils::{Point, Rectangle, Size, Transform};17
+use smithay::utils::{Point, Rectangle, Scale, Size, Transform};1618
use smithay::wayland::dmabuf::get_dmabuf;19
+use smithay::wayland::foreign_toplevel_list::ForeignToplevelHandle;1720
use smithay::wayland::image_capture_source::{1821
ImageCaptureSource, ImageCaptureSourceHandler, OutputCaptureSourceHandler,19
- OutputCaptureSourceState,22
+ OutputCaptureSourceState, ToplevelCaptureSourceHandler, ToplevelCaptureSourceState,2023
};2124
use smithay::wayland::image_copy_capture::{2225
BufferConstraints, CaptureFailureReason, DmabufConstraints, Frame, ImageCopyCaptureHandler,@@ -26,21 +29,38 @@ use smithay::wayland::shm::{shm_format_to_fourcc, with_buffer_contents_mut};
2629
use std::collections::HashSet;2730
use std::time::Duration;2831
32
+/// What a capture source shows.33
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]34
+enum Target {35
+ Output(String),36
+ Window(WindowId),37
+}38
+2939
#[derive(Default)]3040
pub struct Captures {3141
sessions: Vec<Session>,32
- /// Frames waiting for their output to show something new.42
+ /// Frames waiting for their target to show something new.3343
waiting: Vec<(SessionRef, Frame)>,34
- damaged: HashSet<String>,44
+ damaged: HashSet<Target>,3545
}3646
3747
impl Captures {3848
/// Records that `output` drew a frame with new content.39
- pub fn damaged(&mut self, output: &Output) {49
+ pub fn output_damaged(&mut self, output: &Output) {50
+ if !self.waiting.is_empty() {51
+ self.damaged.insert(Target::Output(output.name()));52
+ }53
+ }54
+55
+ pub fn window_damaged(&mut self, id: WindowId) {4056
if !self.waiting.is_empty() {41
- self.damaged.insert(output.name());57
+ self.damaged.insert(Target::Window(id));4258
}4359
}60
+61
+ pub fn wants_damage(&self) -> bool {62
+ !self.waiting.is_empty()63
+ }4464
}4565
4666
impl ImageCaptureSourceHandler for State {@@ -57,29 +77,30 @@ impl OutputCaptureSourceHandler for State {
5777
}5878
}5979
80
+impl ToplevelCaptureSourceHandler for State {81
+ fn toplevel_capture_source_state(&mut self) -> &mut ToplevelCaptureSourceState {82
+ &mut self.toplevel_capture_source_state83
+ }84
+85
+ fn toplevel_source_created(86
+ &mut self,87
+ source: ImageCaptureSource,88
+ toplevel: ForeignToplevelHandle,89
+ ) {90
+ if let Some(id) = self.list_handles.window(&toplevel) {91
+ source.user_data().insert_if_missing(|| id);92
+ }93
+ }94
+}95
+6096
impl ImageCopyCaptureHandler for State {6197
fn image_copy_capture_state(&mut self) -> &mut ImageCopyCaptureState {6298
&mut self.image_copy_capture_state6399
}64100
65101
fn capture_constraints(&mut self, source: &ImageCaptureSource) -> Option<BufferConstraints> {66
- let output = source.user_data().get::<WeakOutput>()?.upgrade()?;67
- let mode = output.current_mode()?;68
- let dma = self.render_node.map(|node| {69
- let mut formats: Vec<(Fourcc, Vec<_>)> = Vec::new();70
- for f in self.renderer.render_formats().iter() {71
- match formats.iter_mut().find(|(c, _)| *c == f.code) {72
- Some((_, mods)) => mods.push(f.modifier),73
- None => formats.push((f.code, vec![f.modifier])),74
- }75
- }76
- DmabufConstraints { node, formats }77
- });78
- Some(BufferConstraints {79
- size: (mode.size.w, mode.size.h).into(),80
- shm: vec![wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888],81
- dma,82
- })102
+ let size = self.target_size(&self.target(source)?)?;103
+ Some(self.constraints(size))83104
}84105
85106
fn new_session(&mut self, session: Session) {@@ -103,7 +124,7 @@ impl ImageCopyCaptureHandler for State {
103124
}104125
105126
impl State {106
- /// Completes waiting frames on outputs that drew something new.127
+ /// Completes waiting frames whose target showed something new.107128
pub fn complete_captures(&mut self) {108129
if self.captures.damaged.is_empty() {109130
return;@@ -112,7 +133,8 @@ impl State {
112133
let (ready, waiting) = std::mem::take(&mut self.captures.waiting)113134
.into_iter()114135
.partition::<Vec<_>, _>(|(s, _)| {115
- source_output(&s.source()).is_some_and(|o| damaged.contains(&o.name()))136
+ self.target(&s.source())137
+ .is_some_and(|t| damaged.contains(&t))116138
});117139
self.captures.waiting = waiting;118140
for (session, frame) in ready {@@ -120,24 +142,69 @@ impl State {
120142
}121143
}122144
145
+ fn target(&self, source: &ImageCaptureSource) -> Option<Target> {146
+ let data = source.user_data();147
+ if let Some(output) = data.get::<WeakOutput>() {148
+ return Some(Target::Output(output.upgrade()?.name()));149
+ }150
+ let id = *data.get::<WindowId>()?;151
+ self.windows.contains_key(&id).then_some(Target::Window(id))152
+ }153
+154
+ fn target_size(&self, target: &Target) -> Option<(i32, i32)> {155
+ let size: (i32, i32) = match target {156
+ Target::Output(name) => self.output_named(name)?.1.current_mode()?.size.into(),157
+ Target::Window(id) => self.windows.get(id)?.geometry().size.into(),158
+ };159
+ (size.0 > 0 && size.1 > 0).then_some(size)160
+ }161
+162
+ fn constraints(&self, size: (i32, i32)) -> BufferConstraints {163
+ let dma = self.render_node.map(|node| {164
+ let mut formats: Vec<(Fourcc, Vec<_>)> = Vec::new();165
+ for f in self.renderer.render_formats().iter() {166
+ match formats.iter_mut().find(|(c, _)| *c == f.code) {167
+ Some((_, mods)) => mods.push(f.modifier),168
+ None => formats.push((f.code, vec![f.modifier])),169
+ }170
+ }171
+ DmabufConstraints { node, formats }172
+ });173
+ BufferConstraints {174
+ size: size.into(),175
+ shm: vec![wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888],176
+ dma,177
+ }178
+ }179
+123180
fn capture(&mut self, session: &SessionRef, frame: Frame) {124
- let Some(output) = source_output(&session.source()) else {181
+ let Some(target) = self.target(&session.source()) else {125182
frame.fail(CaptureFailureReason::Stopped);126183
return;127184
};128
- let Some(o) = self.layout_output(&output) else {129
- frame.fail(CaptureFailureReason::Stopped);185
+ let Some(size) = self.target_size(&target) else {186
+ frame.fail(CaptureFailureReason::Unknown);130187
return;131188
};132
- let Some(mode) = output.current_mode() else {133
- frame.fail(CaptureFailureReason::Unknown);189
+ if session.current_constraints().map(|c| (c.size.w, c.size.h)) != Some(size) {190
+ session.update_constraints(self.constraints(size));191
+ frame.fail(CaptureFailureReason::BufferConstraints);134192
return;193
+ }194
+ let elements = match &target {195
+ Target::Output(name) => {196
+ let Some((o, output)) = self.output_named(name) else {197
+ frame.fail(CaptureFailureReason::Stopped);198
+ return;199
+ };200
+ self.output_elements(o, &output, session.draw_cursor())201
+ }202
+ Target::Window(id) => window_elements(&mut self.renderer, &self.windows[id]),135203
};136
- let elements = self.output_elements(o, &output, session.draw_cursor());137204
let buffer = frame.buffer();138205
let result = match get_dmabuf(&buffer) {139
- Ok(dmabuf) => self.capture_dmabuf(dmabuf.clone(), mode.size.into(), &elements),140
- Err(_) => self.capture_shm(&buffer, mode.size.into(), &elements),206
+ Ok(dmabuf) => self.capture_dmabuf(dmabuf.clone(), size, &elements),207
+ Err(_) => self.capture_shm(&buffer, size, &elements),141208
};142209
match result {143210
Ok(()) => {@@ -226,19 +293,23 @@ impl State {
226293
elements227294
}228295
229
- pub fn layout_output(&self, output: &Output) -> Option<OutputId> {296
+ fn output_named(&self, name: &str) -> Option<(OutputId, Output)> {230297
self.layout231298
.outputs()232
- .map(|(o, _)| o)233
- .find(|&o| self.smithay_output(o).as_ref() == Some(output))299
+ .filter_map(|(o, _)| Some((o, self.smithay_output(o)?)))300
+ .find(|(_, out)| out.name() == name)234301
}235302
}236303
304
+/// A window alone, with its popups, its geometry at the origin.305
+fn window_elements(renderer: &mut VulkanRenderer, window: &smithay::desktop::Window) -> Vec<Draw> {306
+ let at = Point::from((-window.geometry().loc.x, -window.geometry().loc.y));307
+ let elems: Vec<WaylandSurfaceRenderElement<VulkanRenderer>> =308
+ window.render_elements(renderer, at, Scale::from(1.0), 1.0);309
+ elems.into_iter().map(Draw::Surface).collect()310
+}311
+237312
/// Marks a session that has had a frame, so later ones wait for damage.238313
struct Delivered;239314
240315
const CLEAR: [f32; 4] = [0.07, 0.075, 0.09, 1.0];241
-242
-fn source_output(source: &ImageCaptureSource) -> Option<Output> {243
- source.user_data().get::<WeakOutput>()?.upgrade()244
-}@@ -678,7 +678,7 @@ fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64) -> bool {
678678
Ok(()) => {679679
surface.queued = true;680680
lock.shown(&surface.output);681
- captures.damaged(&surface.output);681
+ captures.output_damaged(&surface.output);682682
}683683
Err(e) => tracing::warn!("queueing a frame failed: {e}"),684684
}@@ -80,6 +80,11 @@ impl CompositorHandler for State {
8080
{8181
window.on_commit();8282
}83
+ if self.captures.wants_damage()84
+ && let Some(id) = self.window_id(&root)85
+ {86
+ self.captures.window_damaged(id);87
+ }8388
}8489
if let Some(i) = self8590
.unmapped@@ -268,7 +268,7 @@ pub fn render(state: &mut State) {
268268
}269269
crate::layers::send_frames(&host.output, elapsed);270270
if damage.is_some() {271
- captures.damaged(&host.output);271
+ captures.output_damaged(&host.output);272272
}273273
let Some(damage) = damage else {274274
if *needs_redraw {@@ -28,7 +28,9 @@ use smithay::wayland::compositor::{CompositorClientState, CompositorState, with_
2828
use smithay::wayland::dmabuf::{DmabufGlobal, DmabufState};2929
use smithay::wayland::idle_inhibit::IdleInhibitManagerState;3030
use smithay::wayland::idle_notify::IdleNotifierState;31
-use smithay::wayland::image_capture_source::{ImageCaptureSourceState, OutputCaptureSourceState};31
+use smithay::wayland::image_capture_source::{32
+ ImageCaptureSourceState, OutputCaptureSourceState, ToplevelCaptureSourceState,33
+};3234
use smithay::wayland::image_copy_capture::ImageCopyCaptureState;3335
use smithay::wayland::output::OutputManagerState;3436
use smithay::wayland::seat::WaylandFocus;@@ -113,6 +115,7 @@ pub struct State {
113115
pub layer_shell_state: WlrLayerShellState,114116
pub _image_capture_source_state: ImageCaptureSourceState,115117
pub output_capture_source_state: OutputCaptureSourceState,118
+ pub toplevel_capture_source_state: ToplevelCaptureSourceState,116119
pub image_copy_capture_state: ImageCopyCaptureState,117120
pub activation_state: XdgActivationState,118121
pub primary_selection_state: PrimarySelectionState,@@ -238,6 +241,7 @@ impl State {
238241
layer_shell_state: WlrLayerShellState::new::<State>(&dh),239242
_image_capture_source_state: ImageCaptureSourceState::new(),240243
output_capture_source_state: OutputCaptureSourceState::new::<State>(&dh),244
+ toplevel_capture_source_state: ToplevelCaptureSourceState::new::<State>(&dh),241245
image_copy_capture_state: ImageCopyCaptureState::new::<State>(&dh),242246
activation_state: XdgActivationState::new::<State>(&dh),243247
wlr_data_control_state: WlrDataControlState::new::<State, _>(@@ -14,6 +14,15 @@ use std::collections::HashMap;
1414
#[derive(Default)]1515
pub struct ListHandles(HashMap<WindowId, ForeignToplevelHandle>);1616
17
+impl ListHandles {18
+ pub fn window(&self, toplevel: &ForeignToplevelHandle) -> Option<WindowId> {19
+ self.020
+ .iter()21
+ .find(|(_, h)| h.identifier() == toplevel.identifier())22
+ .map(|(id, _)| *id)23
+ }24
+}25
+1726
pub fn key(id: WindowId) -> u64 {1827
slotmap::Key::data(&id).as_ffi()1928
}