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}