use alloc::vec::Vec;
use azul_core::callbacks::Update;
use azul_core::dom::{ComponentEventFilter, DatasetMergeCallbackType, Dom, EventFilter};
use azul_core::refany::{OptionRefAny, RefAny};
use azul_core::resources::{ImageRef, RawImageFormat};
use azul_core::screencap::ScreenCaptureConfig;
use azul_core::task::{ThreadId, ThreadReceiver};
use azul_core::video::VideoFrame;
use super::capture_common::{
invoke_on_frame, present_frame, screen_backend, OnVideoFrame, OnVideoFrameCallback,
OptionOnVideoFrame,
};
use crate::callbacks::{Callback, CallbackInfo, CallbackType};
use crate::thread::{
Thread, ThreadCallback, ThreadReceiveMsg, ThreadSender, ThreadWriteBackMsg, WriteBackCallback,
};
const DEFAULT_W: u32 = 1280;
const DEFAULT_H: u32 = 720;
pub struct ScreenCaptureWidgetState {
pub config: ScreenCaptureConfig,
pub started: bool,
pub gl_texture_id: Option<u32>,
pub on_frame: OptionOnVideoFrame,
}
#[repr(C)]
pub struct ScreenCaptureWidget {
pub config: ScreenCaptureConfig,
pub on_frame: OptionOnVideoFrame,
}
impl ScreenCaptureWidget {
pub fn create(config: ScreenCaptureConfig) -> Self {
Self {
config,
on_frame: OptionOnVideoFrame::None,
}
}
pub fn set_on_frame<C: Into<OnVideoFrameCallback>>(&mut self, data: RefAny, on_frame: C) {
self.on_frame = Some(OnVideoFrame {
refany: data,
callback: on_frame.into(),
})
.into();
}
pub fn with_on_frame<C: Into<OnVideoFrameCallback>>(
mut self,
data: RefAny,
on_frame: C,
) -> Self {
self.set_on_frame(data, on_frame);
self
}
pub fn dom(self) -> Dom {
let state = ScreenCaptureWidgetState {
config: self.config,
started: false,
gl_texture_id: None,
on_frame: self.on_frame,
};
let dataset = RefAny::new(state);
let placeholder = ImageRef::null_image(
DEFAULT_W as usize,
DEFAULT_H as usize,
RawImageFormat::BGRA8,
b"azul-screencap-placeholder".to_vec(),
);
Dom::create_image(placeholder)
.with_dataset(OptionRefAny::Some(dataset.clone()))
.with_merge_callback(merge_screencap_state as DatasetMergeCallbackType)
.with_callback(
EventFilter::Component(ComponentEventFilter::AfterMount),
dataset,
Callback::from(screencap_on_after_mount as CallbackType),
)
}
}
extern "C" fn screencap_on_after_mount(mut data: RefAny, mut info: CallbackInfo) -> Update {
{
let mut s = match data.downcast_mut::<ScreenCaptureWidgetState>() {
Some(s) => s,
None => return Update::DoNothing,
};
if s.started {
return Update::DoNothing;
}
s.started = true;
}
info.add_thread(
ThreadId::unique(),
Thread::create(
RefAny::new(()),
data.clone(),
ThreadCallback::new(screencap_worker),
),
);
Update::DoNothing
}
extern "C" fn screencap_worker(_init: RefAny, mut sender: ThreadSender, _recv: ThreadReceiver) {
if let Some(backend) = screen_backend() {
let handle = (backend.open)(0, DEFAULT_W as u32, DEFAULT_H as u32);
if handle != 0 {
let mut buf: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
loop {
let (fw, fh) = (backend.read)(handle, &mut buf);
if fw == 0 || fh == 0 {
break;
}
let frame = VideoFrame {
width: fw,
height: fh,
bytes: buf.clone().into(),
};
if !sender.send(ThreadReceiveMsg::WriteBack(ThreadWriteBackMsg::new(
WriteBackCallback::new(screencap_writeback),
RefAny::new(frame),
))) {
break;
}
}
(backend.close)(handle);
return;
}
}
let (w, h) = (DEFAULT_W as usize, DEFAULT_H as usize);
let mut tick: u32 = 0;
loop {
let band = (tick as usize) % h;
let mut bytes = Vec::with_capacity(w * h * 4);
for y in 0..h {
let v = if y.abs_diff(band) < 8 { 235u8 } else { 28u8 };
for _ in 0..w {
bytes.extend_from_slice(&[v, v, v, 255]);
}
}
let frame = VideoFrame {
width: w as u32,
height: h as u32,
bytes: bytes.into(),
};
let sent = sender.send(ThreadReceiveMsg::WriteBack(ThreadWriteBackMsg::new(
WriteBackCallback::new(screencap_writeback),
RefAny::new(frame),
)));
if !sent {
break;
}
std::thread::sleep(std::time::Duration::from_millis(33));
tick = tick.wrapping_add(12);
}
}
extern "C" fn screencap_writeback(
mut writeback_data: RefAny,
mut frame_data: RefAny,
mut info: CallbackInfo,
) -> Update {
let (current, hook) = match writeback_data.downcast_ref::<ScreenCaptureWidgetState>() {
Some(s) => (s.gl_texture_id, s.on_frame.clone()),
None => (None, OptionOnVideoFrame::None),
};
let mut user_update = Update::DoNothing;
let new_id = match frame_data.downcast_ref::<VideoFrame>() {
Some(frame) => {
let id = present_frame(&mut info, writeback_data.clone(), current, &frame);
user_update = invoke_on_frame(&hook, &mut info, &frame);
id
}
None => return Update::DoNothing,
};
if let Some(mut s) = writeback_data.downcast_mut::<ScreenCaptureWidgetState>() {
s.gl_texture_id = new_id;
}
user_update
}
extern "C" fn merge_screencap_state(mut new_data: RefAny, mut old_data: RefAny) -> RefAny {
{
let new_guard = new_data.downcast_mut::<ScreenCaptureWidgetState>();
let old_guard = old_data.downcast_ref::<ScreenCaptureWidgetState>();
if let (Some(mut new_g), Some(old_g)) = (new_guard, old_guard) {
new_g.started = old_g.started;
new_g.gl_texture_id = old_g.gl_texture_id;
}
}
new_data
}