mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
source code
This commit is contained in:
111
libs/scrap/src/quartz/capturer.rs
Normal file
111
libs/scrap/src/quartz/capturer.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
libs/scrap/src/quartz/config.rs
Normal file
75
libs/scrap/src/quartz/config.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use std::ptr;
|
||||
|
||||
use libc::c_void;
|
||||
|
||||
use super::ffi::*;
|
||||
|
||||
//TODO: Color space, YCbCr matrix.
|
||||
pub struct Config {
|
||||
/// Whether the cursor is visible.
|
||||
pub cursor: bool,
|
||||
/// Whether it should letterbox or stretch.
|
||||
pub letterbox: bool,
|
||||
/// Minimum seconds per frame.
|
||||
pub throttle: f64,
|
||||
/// How many frames are allocated.
|
||||
/// 3 is the recommended value.
|
||||
/// 8 is the maximum value.
|
||||
pub queue_length: i8,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Don't forget to CFRelease this!
|
||||
pub fn build(self) -> CFDictionaryRef {
|
||||
unsafe {
|
||||
let throttle = CFNumberCreate(
|
||||
ptr::null_mut(),
|
||||
CFNumberType::Float64,
|
||||
&self.throttle as *const _ as *const c_void,
|
||||
);
|
||||
let queue_length = CFNumberCreate(
|
||||
ptr::null_mut(),
|
||||
CFNumberType::SInt8,
|
||||
&self.queue_length as *const _ as *const c_void,
|
||||
);
|
||||
|
||||
let keys: [CFStringRef; 4] = [
|
||||
kCGDisplayStreamShowCursor,
|
||||
kCGDisplayStreamPreserveAspectRatio,
|
||||
kCGDisplayStreamMinimumFrameTime,
|
||||
kCGDisplayStreamQueueDepth,
|
||||
];
|
||||
let values: [*mut c_void; 4] = [
|
||||
cfbool(self.cursor),
|
||||
cfbool(self.letterbox),
|
||||
throttle,
|
||||
queue_length,
|
||||
];
|
||||
|
||||
let res = CFDictionaryCreate(
|
||||
ptr::null_mut(),
|
||||
keys.as_ptr(),
|
||||
values.as_ptr(),
|
||||
4,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks,
|
||||
);
|
||||
|
||||
CFRelease(throttle);
|
||||
CFRelease(queue_length);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
cursor: false,
|
||||
letterbox: true,
|
||||
throttle: 0.0,
|
||||
queue_length: 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
63
libs/scrap/src/quartz/display.rs
Normal file
63
libs/scrap/src/quartz/display.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use std::mem;
|
||||
|
||||
use super::ffi::*;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Display(u32);
|
||||
|
||||
impl Display {
|
||||
pub fn primary() -> Display {
|
||||
Display(unsafe { CGMainDisplayID() })
|
||||
}
|
||||
|
||||
pub fn online() -> Result<Vec<Display>, CGError> {
|
||||
unsafe {
|
||||
let mut arr: [u32; 16] = mem::MaybeUninit::uninit().assume_init();
|
||||
let mut len: u32 = 0;
|
||||
|
||||
match CGGetOnlineDisplayList(16, arr.as_mut_ptr(), &mut len) {
|
||||
CGError::Success => (),
|
||||
x => return Err(x),
|
||||
}
|
||||
|
||||
let mut res = Vec::with_capacity(16);
|
||||
for i in 0..len as usize {
|
||||
res.push(Display(*arr.get_unchecked(i)));
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn width(self) -> usize {
|
||||
unsafe { CGDisplayPixelsWide(self.0) }
|
||||
}
|
||||
|
||||
pub fn height(self) -> usize {
|
||||
unsafe { CGDisplayPixelsHigh(self.0) }
|
||||
}
|
||||
|
||||
pub fn is_builtin(self) -> bool {
|
||||
unsafe { CGDisplayIsBuiltin(self.0) != 0 }
|
||||
}
|
||||
|
||||
pub fn is_primary(self) -> bool {
|
||||
unsafe { CGDisplayIsMain(self.0) != 0 }
|
||||
}
|
||||
|
||||
pub fn is_active(self) -> bool {
|
||||
unsafe { CGDisplayIsActive(self.0) != 0 }
|
||||
}
|
||||
|
||||
pub fn is_online(self) -> bool {
|
||||
unsafe { CGDisplayIsOnline(self.0) != 0 }
|
||||
}
|
||||
|
||||
pub fn bounds(self) -> CGRect {
|
||||
unsafe { CGDisplayBounds(self.0) }
|
||||
}
|
||||
}
|
||||
240
libs/scrap/src/quartz/ffi.rs
Normal file
240
libs/scrap/src/quartz/ffi.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use block::RcBlock;
|
||||
use libc::c_void;
|
||||
|
||||
pub type CGDisplayStreamRef = *mut c_void;
|
||||
pub type CFDictionaryRef = *mut c_void;
|
||||
pub type CFBooleanRef = *mut c_void;
|
||||
pub type CFNumberRef = *mut c_void;
|
||||
pub type CFStringRef = *mut c_void;
|
||||
pub type CGDisplayStreamUpdateRef = *mut c_void;
|
||||
pub type IOSurfaceRef = *mut c_void;
|
||||
pub type DispatchQueue = *mut c_void;
|
||||
pub type DispatchQueueAttr = *mut c_void;
|
||||
pub type CFAllocatorRef = *mut c_void;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CFDictionaryKeyCallBacks {
|
||||
callbacks: [usize; 5],
|
||||
version: i32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CFDictionaryValueCallBacks {
|
||||
callbacks: [usize; 4],
|
||||
version: i32,
|
||||
}
|
||||
|
||||
macro_rules! pixel_format {
|
||||
($a:expr, $b:expr, $c:expr, $d:expr) => {
|
||||
($a as i32) << 24 | ($b as i32) << 16 | ($c as i32) << 8 | ($d as i32)
|
||||
};
|
||||
}
|
||||
|
||||
pub const SURFACE_LOCK_READ_ONLY: u32 = 0x0000_0001;
|
||||
pub const SURFACE_LOCK_AVOID_SYNC: u32 = 0x0000_0002;
|
||||
|
||||
pub fn cfbool(x: bool) -> CFBooleanRef {
|
||||
unsafe {
|
||||
if x {
|
||||
kCFBooleanTrue
|
||||
} else {
|
||||
kCFBooleanFalse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum CGDisplayStreamFrameStatus {
|
||||
/// A new frame was generated.
|
||||
FrameComplete = 0,
|
||||
/// A new frame was not generated because the display did not change.
|
||||
FrameIdle = 1,
|
||||
/// A new frame was not generated because the display has gone blank.
|
||||
FrameBlank = 2,
|
||||
/// The display stream was stopped.
|
||||
Stopped = 3,
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum CFNumberType {
|
||||
/* Fixed-width types */
|
||||
SInt8 = 1,
|
||||
SInt16 = 2,
|
||||
SInt32 = 3,
|
||||
SInt64 = 4,
|
||||
Float32 = 5,
|
||||
Float64 = 6,
|
||||
/* 64-bit IEEE 754 */
|
||||
/* Basic C types */
|
||||
Char = 7,
|
||||
Short = 8,
|
||||
Int = 9,
|
||||
Long = 10,
|
||||
LongLong = 11,
|
||||
Float = 12,
|
||||
Double = 13,
|
||||
/* Other */
|
||||
CFIndex = 14,
|
||||
NSInteger = 15,
|
||||
CGFloat = 16,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
#[must_use]
|
||||
pub enum CGError {
|
||||
Success = 0,
|
||||
Failure = 1000,
|
||||
IllegalArgument = 1001,
|
||||
InvalidConnection = 1002,
|
||||
InvalidContext = 1003,
|
||||
CannotComplete = 1004,
|
||||
NotImplemented = 1006,
|
||||
RangeCheck = 1007,
|
||||
TypeCheck = 1008,
|
||||
InvalidOperation = 1010,
|
||||
NoneAvailable = 1011,
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum PixelFormat {
|
||||
/// Packed Little Endian ARGB8888
|
||||
Argb8888 = pixel_format!('B', 'G', 'R', 'A'),
|
||||
/// Packed Little Endian ARGB2101010
|
||||
Argb2101010 = pixel_format!('l', '1', '0', 'r'),
|
||||
/// 2-plane "video" range YCbCr 4:2:0
|
||||
YCbCr420Video = pixel_format!('4', '2', '0', 'v'),
|
||||
/// 2-plane "full" range YCbCr 4:2:0
|
||||
YCbCr420Full = pixel_format!('4', '2', '0', 'f'),
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
pub type CGDisplayStreamFrameAvailableHandler = *const c_void;
|
||||
|
||||
pub type FrameAvailableHandler = RcBlock<
|
||||
(
|
||||
CGDisplayStreamFrameStatus, // status
|
||||
u64, // displayTime
|
||||
IOSurfaceRef, // frameSurface
|
||||
CGDisplayStreamUpdateRef, // updateRef
|
||||
),
|
||||
(),
|
||||
>;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type CGFloat = f64;
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
pub type CGFloat = f32;
|
||||
#[repr(C)]
|
||||
pub struct CGPoint {
|
||||
pub x: CGFloat,
|
||||
pub y: CGFloat,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct CGSize {
|
||||
pub width: CGFloat,
|
||||
pub height: CGFloat,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct CGRect {
|
||||
pub origin: CGPoint,
|
||||
pub size: CGSize,
|
||||
}
|
||||
|
||||
#[link(name = "System", kind = "dylib")]
|
||||
#[link(name = "CoreGraphics", kind = "framework")]
|
||||
#[link(name = "CoreFoundation", kind = "framework")]
|
||||
#[link(name = "IOSurface", kind = "framework")]
|
||||
extern "C" {
|
||||
// CoreGraphics
|
||||
|
||||
pub static kCGDisplayStreamShowCursor: CFStringRef;
|
||||
pub static kCGDisplayStreamPreserveAspectRatio: CFStringRef;
|
||||
pub static kCGDisplayStreamMinimumFrameTime: CFStringRef;
|
||||
pub static kCGDisplayStreamQueueDepth: CFStringRef;
|
||||
|
||||
pub fn CGDisplayStreamCreateWithDispatchQueue(
|
||||
display: u32,
|
||||
output_width: usize,
|
||||
output_height: usize,
|
||||
pixel_format: PixelFormat,
|
||||
properties: CFDictionaryRef,
|
||||
queue: DispatchQueue,
|
||||
handler: CGDisplayStreamFrameAvailableHandler,
|
||||
) -> CGDisplayStreamRef;
|
||||
|
||||
pub fn CGDisplayStreamStart(displayStream: CGDisplayStreamRef) -> CGError;
|
||||
|
||||
pub fn CGDisplayStreamStop(displayStream: CGDisplayStreamRef) -> CGError;
|
||||
|
||||
pub fn CGMainDisplayID() -> u32;
|
||||
pub fn CGDisplayPixelsWide(display: u32) -> usize;
|
||||
pub fn CGDisplayPixelsHigh(display: u32) -> usize;
|
||||
|
||||
pub fn CGGetOnlineDisplayList(
|
||||
max_displays: u32,
|
||||
online_displays: *mut u32,
|
||||
display_count: *mut u32,
|
||||
) -> CGError;
|
||||
|
||||
pub fn CGDisplayIsBuiltin(display: u32) -> i32;
|
||||
pub fn CGDisplayIsMain(display: u32) -> i32;
|
||||
pub fn CGDisplayIsActive(display: u32) -> i32;
|
||||
pub fn CGDisplayIsOnline(display: u32) -> i32;
|
||||
|
||||
pub fn CGDisplayBounds(display: u32) -> CGRect;
|
||||
|
||||
// IOSurface
|
||||
|
||||
pub fn IOSurfaceGetAllocSize(buffer: IOSurfaceRef) -> usize;
|
||||
pub fn IOSurfaceGetBaseAddress(buffer: IOSurfaceRef) -> *mut c_void;
|
||||
pub fn IOSurfaceIncrementUseCount(buffer: IOSurfaceRef);
|
||||
pub fn IOSurfaceDecrementUseCount(buffer: IOSurfaceRef);
|
||||
pub fn IOSurfaceLock(buffer: IOSurfaceRef, options: u32, seed: *mut u32) -> i32;
|
||||
pub fn IOSurfaceUnlock(buffer: IOSurfaceRef, options: u32, seed: *mut u32) -> i32;
|
||||
pub fn IOSurfaceGetBaseAddressOfPlane(buffer: IOSurfaceRef, index: usize) -> *mut c_void;
|
||||
pub fn IOSurfaceGetBytesPerRowOfPlane(buffer: IOSurfaceRef, index: usize) -> usize;
|
||||
|
||||
// Dispatch
|
||||
|
||||
pub fn dispatch_queue_create(label: *const i8, attr: DispatchQueueAttr) -> DispatchQueue;
|
||||
|
||||
pub fn dispatch_release(object: DispatchQueue);
|
||||
|
||||
// Core Foundation
|
||||
|
||||
pub static kCFTypeDictionaryKeyCallBacks: CFDictionaryKeyCallBacks;
|
||||
pub static kCFTypeDictionaryValueCallBacks: CFDictionaryValueCallBacks;
|
||||
|
||||
// EVEN THE BOOLEANS ARE REFERENCES.
|
||||
pub static kCFBooleanTrue: CFBooleanRef;
|
||||
pub static kCFBooleanFalse: CFBooleanRef;
|
||||
|
||||
pub fn CFNumberCreate(
|
||||
allocator: CFAllocatorRef,
|
||||
theType: CFNumberType,
|
||||
valuePtr: *const c_void,
|
||||
) -> CFNumberRef;
|
||||
|
||||
pub fn CFDictionaryCreate(
|
||||
allocator: CFAllocatorRef,
|
||||
keys: *const *mut c_void,
|
||||
values: *const *mut c_void,
|
||||
numValues: i64,
|
||||
keyCallBacks: *const CFDictionaryKeyCallBacks,
|
||||
valueCallBacks: *const CFDictionaryValueCallBacks,
|
||||
) -> CFDictionaryRef;
|
||||
|
||||
pub fn CFRetain(cf: *const c_void);
|
||||
pub fn CFRelease(cf: *const c_void);
|
||||
}
|
||||
79
libs/scrap/src/quartz/frame.rs
Normal file
79
libs/scrap/src/quartz/frame.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::{ops, ptr, slice};
|
||||
|
||||
use super::ffi::*;
|
||||
|
||||
pub struct Frame {
|
||||
surface: IOSurfaceRef,
|
||||
inner: &'static [u8],
|
||||
i420: *mut u8,
|
||||
i420_len: usize,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub unsafe fn new(surface: IOSurfaceRef) -> Frame {
|
||||
CFRetain(surface);
|
||||
IOSurfaceIncrementUseCount(surface);
|
||||
|
||||
IOSurfaceLock(surface, SURFACE_LOCK_READ_ONLY, ptr::null_mut());
|
||||
|
||||
let inner = slice::from_raw_parts(
|
||||
IOSurfaceGetBaseAddress(surface) as *const u8,
|
||||
IOSurfaceGetAllocSize(surface),
|
||||
);
|
||||
|
||||
Frame {
|
||||
surface,
|
||||
inner,
|
||||
i420: ptr::null_mut(),
|
||||
i420_len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nv12_to_i420<'a>(&'a mut self, w: usize, h: usize, i420: &'a mut Vec<u8>) {
|
||||
if self.inner.is_empty() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
let plane0 = IOSurfaceGetBaseAddressOfPlane(self.surface, 0);
|
||||
let stride0 = IOSurfaceGetBytesPerRowOfPlane(self.surface, 0);
|
||||
let plane1 = IOSurfaceGetBaseAddressOfPlane(self.surface, 1);
|
||||
let stride1 = IOSurfaceGetBytesPerRowOfPlane(self.surface, 1);
|
||||
crate::common::nv12_to_i420(
|
||||
plane0 as _,
|
||||
stride0 as _,
|
||||
plane1 as _,
|
||||
stride1 as _,
|
||||
w,
|
||||
h,
|
||||
i420,
|
||||
);
|
||||
self.i420 = i420.as_mut_ptr() as _;
|
||||
self.i420_len = i420.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for Frame {
|
||||
type Target = [u8];
|
||||
fn deref<'a>(&'a self) -> &'a [u8] {
|
||||
if self.i420.is_null() {
|
||||
self.inner
|
||||
} else {
|
||||
unsafe {
|
||||
let inner = slice::from_raw_parts(self.i420 as *const u8, self.i420_len);
|
||||
inner
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Frame {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
IOSurfaceUnlock(self.surface, SURFACE_LOCK_READ_ONLY, ptr::null_mut());
|
||||
|
||||
IOSurfaceDecrementUseCount(self.surface);
|
||||
CFRelease(self.surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
libs/scrap/src/quartz/mod.rs
Normal file
11
libs/scrap/src/quartz/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
pub use self::capturer::Capturer;
|
||||
pub use self::config::Config;
|
||||
pub use self::display::Display;
|
||||
pub use self::ffi::{CGError, PixelFormat};
|
||||
pub use self::frame::Frame;
|
||||
|
||||
mod capturer;
|
||||
mod config;
|
||||
mod display;
|
||||
pub mod ffi;
|
||||
mod frame;
|
||||
Reference in New Issue
Block a user