ohos_vsync/
lib.rs

1//! Bindings to the `native_vsync` library on OpenHarmony
2//!
3//! This library can be used to receive callbacks on vsync signals
4
5use core::ffi::{c_int, c_void};
6
7use log::debug;
8use ohos_vsync_sys::{
9    OH_NativeVSync, OH_NativeVSync_Create, OH_NativeVSync_Destroy, OH_NativeVSync_FrameCallback,
10    OH_NativeVSync_GetPeriod, OH_NativeVSync_RequestFrame,
11};
12
13mod log;
14
15pub struct NativeVsync {
16    raw: *mut OH_NativeVSync,
17}
18
19#[derive(Debug)]
20pub enum NativeVsyncError {
21    InvalidArgs,
22    CreateFailed,
23    RawErr(c_int),
24}
25
26impl NativeVsync {
27    /// Creates a new `NativeVsync` instance with the given name.
28    pub fn new(name: &str) -> Result<Self, NativeVsyncError> {
29        let name_len: u32 = name
30            .len()
31            .try_into()
32            .map_err(|_| NativeVsyncError::InvalidArgs)?;
33        // SAFETY: OH_NativeVSync_Create will not attempt to modify the string.
34        // The official example also shows usage of a name without a trailing `\0`
35        let raw = unsafe { OH_NativeVSync_Create(name.as_ptr().cast(), name_len) };
36        Ok(NativeVsync { raw })
37    }
38
39    /// Create from a raw pointer to a valid `OH_NativeVSync` instance.
40    ///
41    /// # Safety
42    ///
43    /// `native_vsync` must be a valid, live OH_NativeVSync instance.
44    /// The ownership of `native_vsync` must be exclusive and is transferred to the new object.
45    pub unsafe fn from_raw(native_vsync: *mut OH_NativeVSync) -> Self {
46        debug_assert!(!native_vsync.is_null());
47        debug_assert!(native_vsync.is_aligned());
48        Self { raw: native_vsync }
49    }
50
51    /// Returns the reference to the raw OH_NativeVSync and consumes self
52    ///
53    /// `NativeVsync::from_raw` can be used to reconstruct Self later.
54    /// This can be used to pass the owned NativeVsync object to the callback function.
55    pub fn into_raw(self) -> *mut OH_NativeVSync {
56        let raw = self.raw;
57        core::mem::forget(self);
58        raw
59    }
60
61    /// Request a Callback to `callback` on the next Vsync frame
62    ///
63    /// `data` will be passed to the callback.
64    ///
65    /// # Safety
66    ///
67    /// If data is used in the callback then data must live long enough and be ThreadSafe to use.
68    /// Todo: Define the requirements more clearly.
69    pub unsafe fn request_raw_callback(
70        &self,
71        callback: OH_NativeVSync_FrameCallback,
72        data: *mut c_void,
73    ) -> Result<(), NativeVsyncError> {
74        let res = unsafe { OH_NativeVSync_RequestFrame(self.raw, callback, data) };
75        if res == 0 {
76            Ok(())
77        } else {
78            Err(NativeVsyncError::RawErr(res))
79        }
80    }
81
82    /// Request a Callback to `callback` on the next Vsync frame passing self to the callback
83    ///
84    /// The Callback receives a raw pointer to the native vsync instance, that can be used with
85    /// `NativeVsync::from_raw()` to pass `Self` to the callback.
86    ///
87    /// # Safety
88    ///
89    /// Probably this function is safe to use. The only open question is if it's sound for the
90    /// callback to reconstruct the NativeVsync object and then drop the object, since that would
91    /// also destroy the thread the callback is running on. It's unclear if the thread would be
92    /// destroyed immediately, or after the callback returns.
93    ///
94    /// ## Note
95    ///
96    /// The callback function should be aware that `data` points to the native vsync instance.
97    /// Failure to reconstruct `NativeVsync` would lead to a memory leak.
98    pub unsafe fn request_raw_callback_with_self(
99        self,
100        callback: OH_NativeVSync_FrameCallback,
101    ) -> Result<(), NativeVsyncError> {
102        let res =
103            // SAFETY:
104            unsafe { OH_NativeVSync_RequestFrame(self.raw, callback, self.raw as *mut c_void) };
105        if res == 0 {
106            core::mem::forget(self);
107            Ok(())
108        } else {
109            // implicit drop / destroy
110            Err(NativeVsyncError::RawErr(res))
111        }
112    }
113
114    /// Returns the vsync period in nanoseconds.
115    pub fn get_period(&self) -> Result<u64, NativeVsyncError> {
116        let period = unsafe {
117            let mut period: i64 = -1;
118            let res = OH_NativeVSync_GetPeriod(self.raw, (&mut period) as *mut i64);
119            if res == 0 {
120                debug_assert!(period > 0, "Period must be a positive non-zero integer");
121                period as u64
122            } else {
123                debug!("OH_NativeVSync_GetPeriod failed with {res}");
124                return Err(NativeVsyncError::RawErr(res));
125            }
126        };
127        Ok(period)
128    }
129}
130
131impl Drop for NativeVsync {
132    fn drop(&mut self) {
133        // SAFETY: We never leaked the pointer, so we are sure we still own the
134        // nativeVsync object and can destroy it.
135        unsafe { OH_NativeVSync_Destroy(self.raw) };
136    }
137}