1use std::rc::Rc;
6use std::sync::atomic::{AtomicU64, Ordering};
7
8use crate::geometry::Size;
9use crate::prop::{IntoProp, Prop};
10use crate::style::Style;
11use crate::tree::{Change, NodeId, NodeKind, Tree, borrow, replace};
12use crate::widgets::{Element, ElementKind, Fit, Widget};
13use crate::window::Window;
14
15static NEXT_VIEW: AtomicU64 = AtomicU64::new(0);
18
19#[non_exhaustive]
21#[derive(Clone, Debug, PartialEq, Eq, Hash)]
22pub enum CaptureSource {
23 Output(String),
25 Toplevel(u64),
27}
28
29pub(crate) struct ScreencopyProps {
30 pub(crate) source: Prop<CaptureSource>,
31 pub(crate) live: Option<Prop<bool>>,
32 pub(crate) paint_cursors: Option<Prop<bool>>,
33 pub(crate) fit: Option<Prop<Fit>>,
34}
35
36pub struct ScreencopyView {
55 props: ScreencopyProps,
56 style: Option<Prop<Style>>,
57}
58
59impl ScreencopyView {
60 pub fn new(source: impl IntoProp<CaptureSource>) -> ScreencopyView {
63 ScreencopyView {
64 props: ScreencopyProps {
65 source: source.into_prop(),
66 live: None,
67 paint_cursors: None,
68 fit: None,
69 },
70 style: None,
71 }
72 }
73
74 pub fn live(mut self, live: impl IntoProp<bool>) -> ScreencopyView {
77 self.props.live = Some(live.into_prop());
78 self
79 }
80
81 pub fn paint_cursors(mut self, paint: impl IntoProp<bool>) -> ScreencopyView {
83 self.props.paint_cursors = Some(paint.into_prop());
84 self
85 }
86
87 pub fn fit(mut self, fit: impl IntoProp<Fit>) -> ScreencopyView {
89 self.props.fit = Some(fit.into_prop());
90 self
91 }
92
93 pub fn style(mut self, style: impl IntoProp<Style>) -> ScreencopyView {
95 self.style = Some(style.into_prop());
96 self
97 }
98}
99
100impl Widget for ScreencopyView {
101 fn into_element(self: Box<Self>) -> Element {
102 Element {
103 name: "ScreencopyView",
104 style: self.style,
105 kind: ElementKind::Capture(self.props),
106 children: Vec::new(),
107 }
108 }
109}
110
111#[derive(Clone, Debug, PartialEq, Eq)]
113pub struct CaptureFrame {
114 pub width: u32,
116 pub height: u32,
118 pub rgba: Rc<[u8]>,
121}
122
123#[derive(Clone, Debug, PartialEq)]
125pub struct CaptureRequest {
126 pub id: u64,
128 pub source: CaptureSource,
130 pub live: bool,
132 pub paint_cursors: bool,
134 pub size: Size,
137}
138
139pub(crate) struct CaptureNode {
140 pub(crate) id: u64,
141 pub(crate) source: Option<CaptureSource>,
143 pub(crate) live: bool,
144 pub(crate) paint_cursors: bool,
145 pub(crate) fit: Fit,
146 pub(crate) frame: Option<CaptureFrame>,
147}
148
149impl CaptureNode {
150 pub(crate) fn new() -> CaptureNode {
151 CaptureNode {
152 id: NEXT_VIEW.fetch_add(1, Ordering::Relaxed),
153 source: None,
154 live: false,
155 paint_cursors: false,
156 fit: Fit::default(),
157 frame: None,
158 }
159 }
160}
161
162impl Tree {
163 pub(crate) fn bind_capture(&mut self, id: NodeId, props: ScreencopyProps) {
166 self.captures.push(id);
167 self.bind(id, props.source, "source", |tree, id, value| {
168 tree.update_capture(id, |capture| {
169 if capture.source.as_ref() == Some(&value) {
170 return Change::None;
171 }
172 capture.source = Some(value);
173 match capture.frame.take() {
175 Some(_) => Change::Paint,
176 None => Change::None,
177 }
178 });
179 });
180 self.bind_opt(id, props.live, "live", |tree, id, value| {
181 tree.update_capture(id, |capture| {
182 capture.live = value;
183 Change::None
184 });
185 });
186 self.bind_opt(
187 id,
188 props.paint_cursors,
189 "paint_cursors",
190 |tree, id, value| {
191 tree.update_capture(id, |capture| {
192 capture.paint_cursors = value;
193 Change::None
194 });
195 },
196 );
197 self.bind_opt(id, props.fit, "fit", |tree, id, value| {
198 tree.update_capture(id, |capture| match replace(&mut capture.fit, value) {
199 Change::None => Change::None,
200 _ => Change::Paint,
201 });
202 });
203 }
204
205 fn update_capture(&mut self, id: NodeId, f: impl FnOnce(&mut CaptureNode) -> Change) {
206 self.update(id, |node| match &mut node.kind {
207 NodeKind::Capture(capture) => f(capture),
208 _ => Change::None,
209 });
210 }
211}
212
213impl Window {
214 pub fn captures(&self) -> Vec<CaptureRequest> {
217 let tree = self.tree.borrow();
218 tree
219 .captures
220 .iter()
221 .filter_map(|&id| {
222 let node = tree.node(id)?;
223 let NodeKind::Capture(capture) = &node.kind else {
224 return None;
225 };
226 let content = node.content;
227 if node.rect.is_none() || content.is_empty() {
228 return None;
229 }
230 Some(CaptureRequest {
231 id: capture.id,
232 source: capture.source.clone()?,
233 live: capture.live,
234 paint_cursors: capture.paint_cursors,
235 size: Size::new(content.width, content.height),
236 })
237 })
238 .collect()
239 }
240
241 pub fn show_capture(&self, id: u64, frame: CaptureFrame) -> bool {
246 let mut tree = borrow(&self.tree);
247 let found = tree.captures.iter().copied().find(|&node| {
248 matches!(tree.node(node).map(|node| &node.kind),
249 Some(NodeKind::Capture(capture)) if capture.id == id)
250 });
251 let Some(node) = found else {
252 return false;
253 };
254 let mut shown = false;
255 tree.update(node, |node| match &mut node.kind {
256 NodeKind::Capture(capture) if capture.frame.as_ref() != Some(&frame) => {
257 capture.frame = Some(frame);
258 shown = true;
259 Change::Paint
260 }
261 _ => Change::None,
262 });
263 shown
264 }
265}