rustdesk/libs/scrap/src/quartz/capturer.rs
2021-03-29 15:59:14 +08:00

112 lines
2.7 KiB
Rust

use std::ptr;
use block::{Block, ConcreteBlock};
use libc::c_void;
use std::sync::{Arc, Mutex};
use super::config::Config;
use super::display::Display;
use super::ffi::*;
use super::frame::Frame;
pub struct Capturer {
stream: CGDisplayStreamRef,
queue: DispatchQueue,
width: usize,
height: usize,
format: PixelFormat,
display: Display,
stopped: Arc<Mutex<bool>>,
}
impl Capturer {
pub fn new<F: Fn(Frame) + 'static>(
display: Display,
width: usize,
height: usize,
format: PixelFormat,
config: Config,
handler: F,
) -> Result<Capturer, CGError> {
let stopped = Arc::new(Mutex::new(false));
let cloned_stopped = stopped.clone();
let handler: FrameAvailableHandler = ConcreteBlock::new(move |status, _, surface, _| {
use self::CGDisplayStreamFrameStatus::*;
if status == Stopped {
let mut lock = cloned_stopped.lock().unwrap();
*lock = true;
return;
}
if status == FrameComplete {
handler(unsafe { Frame::new(surface) });
}
})
.copy();
let queue = unsafe {
dispatch_queue_create(
b"quadrupleslap.scrap\0".as_ptr() as *const i8,
ptr::null_mut(),
)
};
let stream = unsafe {
let config = config.build();
let stream = CGDisplayStreamCreateWithDispatchQueue(
display.id(),
width,
height,
format,
config,
queue,
&*handler as *const Block<_, _> as *const c_void,
);
CFRelease(config);
stream
};
match unsafe { CGDisplayStreamStart(stream) } {
CGError::Success => Ok(Capturer {
stream,
queue,
width,
height,
format,
display,
stopped,
}),
x => Err(x),
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn format(&self) -> PixelFormat {
self.format
}
pub fn display(&self) -> Display {
self.display
}
}
impl Drop for Capturer {
fn drop(&mut self) {
unsafe {
let _ = CGDisplayStreamStop(self.stream);
loop {
if *self.stopped.lock().unwrap() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(30));
}
CFRelease(self.stream);
dispatch_release(self.queue);
}
}
}