1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! Apple docs: [CVDisplayLink](https://developer.apple.com/documentation/corevideo/cvdisplaylinkoutputcallback?language=objc)

use foreign_types::{foreign_type, ForeignType};
use std::{
    ffi::c_void,
    fmt::{Debug, Formatter, Result},
};

#[derive(Debug)]
pub enum CVDisplayLink {}

foreign_type! {
    type CType = CVDisplayLink;
    fn drop = CVDisplayLinkRelease;
    fn clone = CVDisplayLinkRetain;
    pub struct DisplayLink;
    pub struct DisplayLinkRef;
}

impl Debug for DisplayLink {
    fn fmt(&self, formatter: &mut Formatter) -> Result {
        formatter
            .debug_tuple("DisplayLink")
            .field(&self.as_ptr())
            .finish()
    }
}

#[doc(hidden)]
pub enum Unimplemented {}

#[repr(C)]
pub struct CVTimeStamp {
    pub version:              u32,
    pub video_timescale:      i32,
    pub video_time:           i64,
    pub host_time:            u64,
    pub rate_scalar:          f64,
    pub video_refresh_period: i64,

    #[doc(hidden)]
    _unimplemented: Unimplemented,
}

pub type CVDisplayLinkOutputCallback = unsafe extern "C" fn(
    display_link_out: *mut CVDisplayLink,
    in_now_timestamp: *const CVTimeStamp,
    in_output_timestamp: *const CVTimeStamp,
    flags_in: i64,
    flagsOut: *mut i64,
    display_link_context: *mut c_void,
) -> i32;

#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "CoreVideo", kind = "framework")]
#[allow(improper_ctypes)]
extern "C" {
    pub fn CVDisplayLinkCreateWithActiveCGDisplays(
        display_link_out: *mut *mut CVDisplayLink,
    ) -> 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: [CVDisplayLinkCreateWithActiveCGDisplays](https://developer.apple.com/documentation/corevideo/1456863-cvdisplaylinkcreatewithactivecgd?language=objc)
    pub unsafe fn new() -> Option<Self> {
        let mut display_link: *mut CVDisplayLink = 0 as _;
        let code = CVDisplayLinkCreateWithActiveCGDisplays(&mut display_link);
        if code == 0 {
            Some(DisplayLink::from_ptr(display_link))
        } else {
            None
        }
    }
}

impl DisplayLinkRef {
    /// Apple docs: [CVDisplayLinkSetOutputCallback](https://developer.apple.com/documentation/corevideo/1457096-cvdisplaylinksetoutputcallback?language=objc)
    pub unsafe fn set_output_callback(
        &mut self,
        callback: CVDisplayLinkOutputCallback,
        user_info: *mut c_void,
    ) {
        assert_eq!(CVDisplayLinkSetOutputCallback(self, callback, user_info), 0);
    }

    /// Apple docs: [CVDisplayLinkStart](https://developer.apple.com/documentation/corevideo/1457193-cvdisplaylinkstart?language=objc)
    pub unsafe fn start(&mut self) {
        assert_eq!(CVDisplayLinkStart(self), 0);
    }

    /// Apple docs: [CVDisplayLinkStop](https://developer.apple.com/documentation/corevideo/1457281-cvdisplaylinkstop?language=objc)
    pub unsafe fn stop(&mut self) {
        assert_eq!(CVDisplayLinkStop(self), 0);
    }
}