267 lines · 9.2 KB · Rust
RawDownload
use crate::{    dispatch_get_main_queue,    dispatch_sys::{        _dispatch_source_type_data_add, dispatch_resume, dispatch_set_context,        dispatch_source_cancel, dispatch_source_create, dispatch_source_merge_data,        dispatch_source_set_event_handler_f, dispatch_source_t, dispatch_suspend,    },};use anyhow::Result;use core_graphics::display::CGDirectDisplayID;use std::ffi::c_void;use util::ResultExt; pub struct DisplayLink {    display_link: sys::DisplayLink,    frame_requests: dispatch_source_t,} impl DisplayLink {    pub fn new(        display_id: CGDirectDisplayID,        data: *mut c_void,        callback: unsafe extern "C" fn(*mut c_void),    ) -> Result<DisplayLink> {        unsafe extern "C" fn display_link_callback(            _display_link_out: *mut sys::CVDisplayLink,            _current_time: *const sys::CVTimeStamp,            _output_time: *const sys::CVTimeStamp,            _flags_in: i64,            _flags_out: *mut i64,            frame_requests: *mut c_void,        ) -> i32 {            let frame_requests = frame_requests as dispatch_source_t;            dispatch_source_merge_data(frame_requests, 1);            0        }         unsafe {            let frame_requests = dispatch_source_create(                &_dispatch_source_type_data_add,                0,                0,                dispatch_get_main_queue(),            );            dispatch_set_context(                crate::dispatch_sys::dispatch_object_t {                    _ds: frame_requests,                },                data,            );            dispatch_source_set_event_handler_f(frame_requests, Some(callback));             let display_link = sys::DisplayLink::new(                display_id,                display_link_callback,                frame_requests as *mut c_void,            )?;             Ok(Self {                display_link,                frame_requests,            })        }    }     pub fn start(&mut self) -> Result<()> {        unsafe {            dispatch_resume(crate::dispatch_sys::dispatch_object_t {                _ds: self.frame_requests,            });            self.display_link.start()?;        }        Ok(())    }     pub fn stop(&mut self) -> Result<()> {        unsafe {            dispatch_suspend(crate::dispatch_sys::dispatch_object_t {                _ds: self.frame_requests,            });            self.display_link.stop()?;        }        Ok(())    }} impl Drop for DisplayLink {    fn drop(&mut self) {        self.stop().log_err();        unsafe {            dispatch_source_cancel(self.frame_requests);        }    }} mod sys {    //! Derived from display-link crate under the following license:    //! <https://github.com/BrainiumLLC/display-link/blob/master/LICENSE-MIT>    //! Apple docs: [CVDisplayLink](https://developer.apple.com/documentation/corevideo/cvdisplaylinkoutputcallback?language=objc)    #![allow(dead_code, non_upper_case_globals)]     use anyhow::Result;    use core_graphics::display::CGDirectDisplayID;    use foreign_types::{foreign_type, ForeignType};    use std::{        ffi::c_void,        fmt::{self, Debug, Formatter},    };     #[derive(Debug)]    pub enum CVDisplayLink {}     foreign_type! {        pub unsafe type DisplayLink {            type CType = CVDisplayLink;            fn drop = CVDisplayLinkRelease;            fn clone = CVDisplayLinkRetain;        }    }     impl Debug for DisplayLink {        fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {            formatter                .debug_tuple("DisplayLink")                .field(&self.as_ptr())                .finish()        }    }     #[repr(C)]    #[derive(Clone, Copy)]    pub(crate) struct CVTimeStamp {        pub version: u32,        pub video_time_scale: i32,        pub video_time: i64,        pub host_time: u64,        pub rate_scalar: f64,        pub video_refresh_period: i64,        pub smpte_time: CVSMPTETime,        pub flags: u64,        pub reserved: u64,    }     pub type CVTimeStampFlags = u64;     pub const kCVTimeStampVideoTimeValid: CVTimeStampFlags = 1 << 0;    pub const kCVTimeStampHostTimeValid: CVTimeStampFlags = 1 << 1;    pub const kCVTimeStampSMPTETimeValid: CVTimeStampFlags = 1 << 2;    pub const kCVTimeStampVideoRefreshPeriodValid: CVTimeStampFlags = 1 << 3;    pub const kCVTimeStampRateScalarValid: CVTimeStampFlags = 1 << 4;    pub const kCVTimeStampTopField: CVTimeStampFlags = 1 << 16;    pub const kCVTimeStampBottomField: CVTimeStampFlags = 1 << 17;    pub const kCVTimeStampVideoHostTimeValid: CVTimeStampFlags =        kCVTimeStampVideoTimeValid | kCVTimeStampHostTimeValid;    pub const kCVTimeStampIsInterlaced: CVTimeStampFlags =        kCVTimeStampTopField | kCVTimeStampBottomField;     #[repr(C)]    #[derive(Clone, Copy, Default)]    pub(crate) struct CVSMPTETime {        pub subframes: i16,        pub subframe_divisor: i16,        pub counter: u32,        pub time_type: u32,        pub flags: u32,        pub hours: i16,        pub minutes: i16,        pub seconds: i16,        pub frames: i16,    }     pub type CVSMPTETimeType = u32;     pub const kCVSMPTETimeType24: CVSMPTETimeType = 0;    pub const kCVSMPTETimeType25: CVSMPTETimeType = 1;    pub const kCVSMPTETimeType30Drop: CVSMPTETimeType = 2;    pub const kCVSMPTETimeType30: CVSMPTETimeType = 3;    pub const kCVSMPTETimeType2997: CVSMPTETimeType = 4;    pub const kCVSMPTETimeType2997Drop: CVSMPTETimeType = 5;    pub const kCVSMPTETimeType60: CVSMPTETimeType = 6;    pub const kCVSMPTETimeType5994: CVSMPTETimeType = 7;     pub type CVSMPTETimeFlags = u32;     pub const kCVSMPTETimeValid: CVSMPTETimeFlags = 1 << 0;    pub const kCVSMPTETimeRunning: CVSMPTETimeFlags = 1 << 1;     pub type CVDisplayLinkOutputCallback = unsafe extern "C" fn(        display_link_out: *mut CVDisplayLink,        // A pointer to the current timestamp. This represents the timestamp when the callback is called.        current_time: *const CVTimeStamp,        // A pointer to the output timestamp. This represents the timestamp for when the frame will be displayed.        output_time: *const CVTimeStamp,        // Unused        flags_in: i64,        // Unused        flags_out: *mut i64,        // A pointer to app-defined data.        display_link_context: *mut c_void,    ) -> i32;     #[link(name = "CoreFoundation", kind = "framework")]    #[link(name = "CoreVideo", kind = "framework")]    #[allow(improper_ctypes, unknown_lints, clippy::duplicated_attributes)]    extern "C" {        pub fn CVDisplayLinkCreateWithActiveCGDisplays(            display_link_out: *mut *mut CVDisplayLink,        ) -> i32;        pub fn CVDisplayLinkSetCurrentCGDisplay(            display_link: &mut DisplayLinkRef,            display_id: u32,        ) -> i32;        pub fn CVDisplayLinkSetOutputCallback(            display_link: &mut DisplayLinkRef,            callback: CVDisplayLinkOutputCallback,            user_info: *mut c_void,        ) -> i32;        pub fn CVDisplayLinkStart(display_link: &mut DisplayLinkRef) -> i32;        pub fn CVDisplayLinkStop(display_link: &mut DisplayLinkRef) -> i32;        pub fn CVDisplayLinkRelease(display_link: *mut CVDisplayLink);        pub fn CVDisplayLinkRetain(display_link: *mut CVDisplayLink) -> *mut CVDisplayLink;    }     impl DisplayLink {        /// Apple docs: [CVDisplayLinkCreateWithCGDisplay](https://developer.apple.com/documentation/corevideo/1456981-cvdisplaylinkcreatewithcgdisplay?language=objc)        pub unsafe fn new(            display_id: CGDirectDisplayID,            callback: CVDisplayLinkOutputCallback,            user_info: *mut c_void,        ) -> Result<Self> {            let mut display_link: *mut CVDisplayLink = 0 as _;             let code = CVDisplayLinkCreateWithActiveCGDisplays(&mut display_link);            anyhow::ensure!(code == 0, "could not create display link, code: {}", code);             let mut display_link = DisplayLink::from_ptr(display_link);             let code = CVDisplayLinkSetOutputCallback(&mut display_link, callback, user_info);            anyhow::ensure!(code == 0, "could not set output callback, code: {}", code);             let code = CVDisplayLinkSetCurrentCGDisplay(&mut display_link, display_id);            anyhow::ensure!(                code == 0,                "could not assign display to display link, code: {}",                code            );             Ok(display_link)        }    }     impl DisplayLinkRef {        /// Apple docs: [CVDisplayLinkStart](https://developer.apple.com/documentation/corevideo/1457193-cvdisplaylinkstart?language=objc)        pub unsafe fn start(&mut self) -> Result<()> {            let code = CVDisplayLinkStart(self);            anyhow::ensure!(code == 0, "could not start display link, code: {}", code);            Ok(())        }         /// Apple docs: [CVDisplayLinkStop](https://developer.apple.com/documentation/corevideo/1457281-cvdisplaylinkstop?language=objc)        pub unsafe fn stop(&mut self) -> Result<()> {            let code = CVDisplayLinkStop(self);            anyhow::ensure!(code == 0, "could not stop display link, code: {}", code);            Ok(())        }    }}