obs_wrapper/
module.rs

1use crate::output::{traits::Outputable, OutputInfo, OutputInfoBuilder};
2use crate::source::{traits::Sourceable, SourceInfo, SourceInfoBuilder};
3use crate::string::ObsString;
4use obs_sys::{
5    obs_module_t, obs_output_info, obs_register_output_s, obs_register_source_s, obs_source_info,
6    size_t,
7};
8use std::marker::PhantomData;
9
10pub struct LoadContext {
11    __marker: PhantomData<()>,
12    sources: Vec<*mut obs_source_info>,
13    outputs: Vec<*mut obs_output_info>,
14}
15
16impl LoadContext {
17    /// # Safety
18    /// LoadContext can only be used at specific times. Creating it could cause
19    /// UB if done at the wrong time.
20    pub unsafe fn new() -> LoadContext {
21        LoadContext {
22            __marker: PhantomData,
23            sources: vec![],
24            outputs: vec![],
25        }
26    }
27
28    pub fn create_source_builder<D: Sourceable>(&self) -> SourceInfoBuilder<D> {
29        SourceInfoBuilder::new()
30    }
31
32    pub fn create_output_builder<D: Outputable>(&self) -> OutputInfoBuilder<D> {
33        OutputInfoBuilder::new()
34    }
35
36    pub fn register_source(&mut self, source: SourceInfo) {
37        let pointer = source.into_raw();
38        unsafe {
39            obs_register_source_s(pointer, std::mem::size_of::<obs_source_info>() as size_t);
40        };
41        self.sources.push(pointer);
42    }
43
44    pub fn register_output(&mut self, output: OutputInfo) {
45        let pointer = unsafe {
46            let pointer = output.into_raw();
47            obs_register_output_s(pointer, std::mem::size_of::<obs_output_info>() as size_t);
48            pointer
49        };
50        self.outputs.push(pointer);
51    }
52}
53
54impl Drop for LoadContext {
55    fn drop(&mut self) {
56        unsafe {
57            for pointer in self.sources.drain(..) {
58                drop(Box::from_raw(pointer))
59            }
60            for pointer in self.outputs.drain(..) {
61                drop(Box::from_raw(pointer))
62            }
63        }
64    }
65}
66
67pub trait Module {
68    fn new(ctx: ModuleContext) -> Self;
69    fn get_ctx(&self) -> &ModuleContext;
70    fn load(&mut self, _load_context: &mut LoadContext) -> bool {
71        true
72    }
73    fn unload(&mut self) {}
74    fn post_load(&mut self) {}
75    fn description() -> ObsString;
76    fn name() -> ObsString;
77    fn author() -> ObsString;
78}
79
80#[macro_export]
81macro_rules! obs_register_module {
82    ($t:ty) => {
83        static mut OBS_MODULE: Option<$t> = None;
84        static mut LOAD_CONTEXT: Option<$crate::module::LoadContext> = None;
85
86        #[allow(missing_safety_doc)]
87        #[no_mangle]
88        pub unsafe extern "C" fn obs_module_set_pointer(raw: *mut $crate::obs_sys::obs_module_t) {
89            OBS_MODULE = Some(<$t>::new(ModuleContext::new(raw)));
90        }
91
92        #[allow(missing_safety_doc)]
93        #[no_mangle]
94        pub unsafe extern "C" fn obs_current_module() -> *mut $crate::obs_sys::obs_module_t {
95            if let Some(module) = &OBS_MODULE {
96                module.get_ctx().get_raw()
97            } else {
98                panic!("Could not get current module!");
99            }
100        }
101
102        #[allow(missing_safety_doc)]
103        #[no_mangle]
104        pub unsafe extern "C" fn obs_module_ver() -> u32 {
105            $crate::obs_sys::LIBOBS_API_MAJOR_VER
106        }
107
108        #[allow(missing_safety_doc)]
109        #[no_mangle]
110        pub unsafe extern "C" fn obs_module_load() -> bool {
111            let mut module = OBS_MODULE.as_mut().expect("Could not get current module!");
112            let mut context = unsafe { $crate::module::LoadContext::new() };
113            let ret = module.load(&mut context);
114            LOAD_CONTEXT = Some(context);
115
116            ret
117        }
118
119        #[allow(missing_safety_doc)]
120        #[no_mangle]
121        pub unsafe extern "C" fn obs_module_unload() {
122            let mut module = OBS_MODULE.as_mut().expect("Could not get current module!");
123            module.unload();
124        }
125
126        #[allow(missing_safety_doc)]
127        #[no_mangle]
128        pub unsafe extern "C" fn obs_module_post_load() {
129            let mut module = OBS_MODULE.as_mut().expect("Could not get current module!");
130            module.post_load();
131        }
132
133        #[allow(missing_safety_doc)]
134        #[no_mangle]
135        pub unsafe extern "C" fn obs_module_name() -> *const std::os::raw::c_char {
136            <$t>::name().as_ptr()
137        }
138
139        #[allow(missing_safety_doc)]
140        #[no_mangle]
141        pub unsafe extern "C" fn obs_module_description() -> *const std::os::raw::c_char {
142            <$t>::description().as_ptr()
143        }
144
145        #[allow(missing_safety_doc)]
146        #[no_mangle]
147        pub unsafe extern "C" fn obs_module_author() -> *const std::os::raw::c_char {
148            <$t>::author().as_ptr()
149        }
150    };
151}
152
153pub struct ModuleContext {
154    raw: *mut obs_module_t,
155}
156
157impl ModuleContext {
158    /// # Safety
159    /// Creates a ModuleContext from a pointer to the raw obs_module data which
160    /// if modified could cause UB.
161    pub unsafe fn new(raw: *mut obs_module_t) -> Self {
162        Self { raw }
163    }
164
165    /// # Safety
166    /// Returns a pointer to the raw obs_module data which if modified could
167    /// cause UB.
168    pub unsafe fn get_raw(&self) -> *mut obs_module_t {
169        self.raw
170    }
171}