use std::rc::Rc;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::geometry::Size;
use crate::prop::{IntoProp, Prop};
use crate::style::Style;
use crate::tree::{Change, NodeId, NodeKind, Tree, borrow, replace};
use crate::widgets::{Element, ElementKind, Fit, Widget};
use crate::window::Window;
static NEXT_VIEW: AtomicU64 = AtomicU64::new(0);
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum CaptureSource {
Output(String),
Toplevel(u64),
}
pub(crate) struct ScreencopyProps {
pub(crate) source: Prop<CaptureSource>,
pub(crate) live: Option<Prop<bool>>,
pub(crate) paint_cursors: Option<Prop<bool>>,
pub(crate) fit: Option<Prop<Fit>>,
}
pub struct ScreencopyView {
props: ScreencopyProps,
style: Option<Prop<Style>>,
}
impl ScreencopyView {
pub fn new(source: impl IntoProp<CaptureSource>) -> ScreencopyView {
ScreencopyView {
props: ScreencopyProps {
source: source.into_prop(),
live: None,
paint_cursors: None,
fit: None,
},
style: None,
}
}
pub fn live(mut self, live: impl IntoProp<bool>) -> ScreencopyView {
self.props.live = Some(live.into_prop());
self
}
pub fn paint_cursors(mut self, paint: impl IntoProp<bool>) -> ScreencopyView {
self.props.paint_cursors = Some(paint.into_prop());
self
}
pub fn fit(mut self, fit: impl IntoProp<Fit>) -> ScreencopyView {
self.props.fit = Some(fit.into_prop());
self
}
pub fn style(mut self, style: impl IntoProp<Style>) -> ScreencopyView {
self.style = Some(style.into_prop());
self
}
}
impl Widget for ScreencopyView {
fn into_element(self: Box<Self>) -> Element {
Element {
name: "ScreencopyView",
style: self.style,
kind: ElementKind::Capture(self.props),
children: Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CaptureFrame {
pub width: u32,
pub height: u32,
pub rgba: Rc<[u8]>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct CaptureRequest {
pub id: u64,
pub source: CaptureSource,
pub live: bool,
pub paint_cursors: bool,
pub size: Size,
}
pub(crate) struct CaptureNode {
pub(crate) id: u64,
pub(crate) source: Option<CaptureSource>,
pub(crate) live: bool,
pub(crate) paint_cursors: bool,
pub(crate) fit: Fit,
pub(crate) frame: Option<CaptureFrame>,
}
impl CaptureNode {
pub(crate) fn new() -> CaptureNode {
CaptureNode {
id: NEXT_VIEW.fetch_add(1, Ordering::Relaxed),
source: None,
live: false,
paint_cursors: false,
fit: Fit::default(),
frame: None,
}
}
}
impl Tree {
pub(crate) fn bind_capture(&mut self, id: NodeId, props: ScreencopyProps) {
self.captures.push(id);
self.bind(id, props.source, "source", |tree, id, value| {
tree.update_capture(id, |capture| {
if capture.source.as_ref() == Some(&value) {
return Change::None;
}
capture.source = Some(value);
match capture.frame.take() {
Some(_) => Change::Paint,
None => Change::None,
}
});
});
self.bind_opt(id, props.live, "live", |tree, id, value| {
tree.update_capture(id, |capture| {
capture.live = value;
Change::None
});
});
self.bind_opt(
id,
props.paint_cursors,
"paint_cursors",
|tree, id, value| {
tree.update_capture(id, |capture| {
capture.paint_cursors = value;
Change::None
});
},
);
self.bind_opt(id, props.fit, "fit", |tree, id, value| {
tree.update_capture(id, |capture| match replace(&mut capture.fit, value) {
Change::None => Change::None,
_ => Change::Paint,
});
});
}
fn update_capture(&mut self, id: NodeId, f: impl FnOnce(&mut CaptureNode) -> Change) {
self.update(id, |node| match &mut node.kind {
NodeKind::Capture(capture) => f(capture),
_ => Change::None,
});
}
}
impl Window {
pub fn captures(&self) -> Vec<CaptureRequest> {
let tree = self.tree.borrow();
tree
.captures
.iter()
.filter_map(|&id| {
let node = tree.node(id)?;
let NodeKind::Capture(capture) = &node.kind else {
return None;
};
let content = node.content;
if node.rect.is_none() || content.is_empty() {
return None;
}
Some(CaptureRequest {
id: capture.id,
source: capture.source.clone()?,
live: capture.live,
paint_cursors: capture.paint_cursors,
size: Size::new(content.width, content.height),
})
})
.collect()
}
pub fn show_capture(&self, id: u64, frame: CaptureFrame) -> bool {
let mut tree = borrow(&self.tree);
let found = tree.captures.iter().copied().find(|&node| {
matches!(tree.node(node).map(|node| &node.kind),
Some(NodeKind::Capture(capture)) if capture.id == id)
});
let Some(node) = found else {
return false;
};
let mut shown = false;
tree.update(node, |node| match &mut node.kind {
NodeKind::Capture(capture) if capture.frame.as_ref() != Some(&frame) => {
capture.frame = Some(frame);
shown = true;
Change::Paint
}
_ => Change::None,
});
shown
}
}