1use super::audio::AudioDataContext;
2use super::context::{CreatableSourceContext, GlobalContext, VideoRenderContext};
3use super::video::VideoDataContext;
4use super::{traits::*, SourceContext};
5use super::{EnumActiveContext, EnumAllContext};
6use crate::{
7 data::DataObj,
8 hotkey::{Hotkey, HotkeyCallbacks},
9 wrapper::PtrWrapper,
10};
11use paste::item;
12use std::collections::HashMap;
13use std::convert::TryFrom;
14use std::ffi::c_void;
15use std::mem::forget;
16use std::os::raw::c_char;
17
18use obs_sys::{
19 gs_effect_t, obs_audio_data, obs_data_t, obs_hotkey_id, obs_hotkey_register_source,
20 obs_hotkey_t, obs_key_event, obs_media_state, obs_mouse_event, obs_properties,
21 obs_source_audio_mix, obs_source_enum_proc_t, obs_source_frame, obs_source_t, size_t,
22};
23
24struct DataWrapper<D> {
25 data: D,
26 #[allow(clippy::type_complexity)]
27 hotkey_callbacks: HashMap<obs_hotkey_id, Box<dyn FnMut(&mut Hotkey, &mut D)>>,
28}
29
30impl<D> DataWrapper<D> {
31 pub(crate) unsafe fn register_callbacks(
32 &mut self,
33 callbacks: HotkeyCallbacks<D>,
34 source: *mut obs_source_t,
35 data: *mut c_void,
36 ) {
37 for (name, description, func) in callbacks.into_iter() {
38 let id = obs_hotkey_register_source(
39 source,
40 name.as_ptr(),
41 description.as_ptr(),
42 Some(hotkey_callback::<D>),
43 data,
44 );
45
46 self.hotkey_callbacks.insert(id, func);
47 }
48 }
49}
50
51impl<D> From<D> for DataWrapper<D> {
52 fn from(data: D) -> Self {
53 Self {
54 data,
55 hotkey_callbacks: HashMap::new(),
56 }
57 }
58}
59
60macro_rules! impl_simple_fn {
61 ($($name:ident => $trait:ident $(-> $ret:ty)?)*) => ($(
62 item! {
63 pub unsafe extern "C" fn $name<D: $trait>(
64 data: *mut std::os::raw::c_void,
65 ) $(-> $ret)? {
66 let wrapper = &mut *(data as *mut DataWrapper<D>);
67 D::$name(&mut wrapper.data)
68 }
69 }
70 )*)
71}
72
73pub unsafe extern "C" fn get_name<D: GetNameSource>(_type_data: *mut c_void) -> *const c_char {
74 D::get_name().as_ptr()
75}
76
77impl_simple_fn!(
78 get_width => GetWidthSource -> u32
79 get_height => GetHeightSource -> u32
80
81 activate => ActivateSource
82 deactivate => DeactivateSource
83);
84
85pub unsafe extern "C" fn create<D: Sourceable>(
86 settings: *mut obs_data_t,
87 source: *mut obs_source_t,
88) -> *mut c_void {
89 let mut global = GlobalContext::default();
90 let settings = DataObj::from_raw(settings);
91 let mut context = CreatableSourceContext::from_raw(settings, &mut global);
92 let source_context = SourceContext::from_raw(source);
93
94 let data = D::create(&mut context, source_context);
95
96 let wrapper = DataWrapper::from(data);
97 forget(context.settings);
98 let callbacks = context.hotkey_callbacks;
99
100 let pointer = Box::into_raw(Box::new(wrapper));
101
102 pointer
103 .as_mut()
104 .unwrap()
105 .register_callbacks(callbacks, source, pointer as *mut c_void);
106
107 pointer as *mut c_void
108}
109
110pub unsafe extern "C" fn destroy<D>(data: *mut c_void) {
111 let wrapper: Box<DataWrapper<D>> = Box::from_raw(data as *mut DataWrapper<D>);
112 drop(wrapper);
113}
114
115pub unsafe extern "C" fn update<D: UpdateSource>(data: *mut c_void, settings: *mut obs_data_t) {
116 let mut global = GlobalContext::default();
117 let data: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
118 let mut settings = DataObj::from_raw(settings);
119 D::update(&mut data.data, &mut settings, &mut global);
120 forget(settings);
121}
122
123pub unsafe extern "C" fn video_render<D: VideoRenderSource>(
124 data: *mut std::os::raw::c_void,
125 _effect: *mut gs_effect_t,
126) {
127 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
128 let mut global = GlobalContext::default();
129 let mut render = VideoRenderContext::default();
130 D::video_render(&mut wrapper.data, &mut global, &mut render);
131}
132
133pub unsafe extern "C" fn audio_render<D: AudioRenderSource>(
134 data: *mut std::os::raw::c_void,
135 _ts_out: *mut u64,
136 _audio_output: *mut obs_source_audio_mix,
137 _mixers: u32,
138 _channels: size_t,
139 _sample_rate: size_t,
140) -> bool {
141 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
142 let mut global = GlobalContext::default();
143 D::audio_render(&mut wrapper.data, &mut global);
144 true
146}
147
148pub unsafe extern "C" fn get_properties<D: GetPropertiesSource>(
149 data: *mut std::os::raw::c_void,
150) -> *mut obs_properties {
151 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
152 let properties = D::get_properties(&mut wrapper.data);
153 properties.into_raw()
154}
155
156pub unsafe extern "C" fn enum_active_sources<D: EnumActiveSource>(
157 data: *mut std::os::raw::c_void,
158 _enum_callback: obs_source_enum_proc_t,
159 _param: *mut std::os::raw::c_void,
160) {
161 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
162 let context = EnumActiveContext {};
163 D::enum_active_sources(&mut wrapper.data, &context);
164}
165
166pub unsafe extern "C" fn enum_all_sources<D: EnumAllSource>(
167 data: *mut std::os::raw::c_void,
168 _enum_callback: obs_source_enum_proc_t,
169 _param: *mut std::os::raw::c_void,
170) {
171 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
172 let context = EnumAllContext {};
173 D::enum_all_sources(&mut wrapper.data, &context);
174}
175
176impl_simple_fn!(
177 transition_start => TransitionStartSource
178 transition_stop => TransitionStopSource
179);
180
181pub unsafe extern "C" fn video_tick<D: VideoTickSource>(
182 data: *mut std::os::raw::c_void,
183 seconds: f32,
184) {
185 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
186 D::video_tick(&mut wrapper.data, seconds);
187}
188
189pub unsafe extern "C" fn filter_audio<D: FilterAudioSource>(
190 data: *mut std::os::raw::c_void,
191 audio: *mut obs_audio_data,
192) -> *mut obs_audio_data {
193 let mut context = AudioDataContext::from_raw(audio);
194 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
195 D::filter_audio(&mut wrapper.data, &mut context);
196 audio
197}
198
199pub unsafe extern "C" fn filter_video<D: FilterVideoSource>(
200 data: *mut std::os::raw::c_void,
201 video: *mut obs_source_frame,
202) -> *mut obs_source_frame {
203 let mut context = VideoDataContext::from_raw(video);
204 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
205 D::filter_video(&mut wrapper.data, &mut context);
206 video
207}
208
209pub unsafe extern "C" fn media_play_pause<D: MediaPlayPauseSource>(
210 data: *mut std::os::raw::c_void,
211 pause: bool,
212) {
213 let wrapper = &mut *(data as *mut DataWrapper<D>);
214 D::play_pause(&mut wrapper.data, pause);
215}
216
217pub unsafe extern "C" fn media_get_state<D: MediaGetStateSource>(
218 data: *mut std::os::raw::c_void,
219) -> obs_media_state {
220 let wrapper = &mut *(data as *mut DataWrapper<D>);
221 D::get_state(&mut wrapper.data).to_native()
222}
223
224pub unsafe extern "C" fn media_set_time<D: MediaSetTimeSource>(
225 data: *mut std::os::raw::c_void,
226 milliseconds: i64,
227) {
228 let wrapper = &mut *(data as *mut DataWrapper<D>);
229 D::set_time(&mut wrapper.data, milliseconds);
230}
231
232macro_rules! impl_media {
233 ($($name:ident => $trait:ident $(-> $ret:ty)?)*) => ($(
234 item! {
235 pub unsafe extern "C" fn [<media_$name>]<D: $trait>(
236 data: *mut std::os::raw::c_void,
237 ) $(-> $ret)? {
238 let wrapper = &mut *(data as *mut DataWrapper<D>);
239 D::$name(&mut wrapper.data)
240 }
241 }
242 )*)
243}
244
245impl_media!(
246 stop => MediaStopSource
247 restart => MediaRestartSource
248 next => MediaNextSource
249 previous => MediaPreviousSource
250 get_duration => MediaGetDurationSource -> i64
251 get_time => MediaGetTimeSource -> i64
252);
253
254pub unsafe extern "C" fn get_defaults<D: GetDefaultsSource>(settings: *mut obs_data_t) {
255 let mut settings = DataObj::from_raw(settings);
256 D::get_defaults(&mut settings);
257 forget(settings);
258}
259
260pub unsafe extern "C" fn hotkey_callback<D>(
261 data: *mut c_void,
262 id: obs_hotkey_id,
263 hotkey: *mut obs_hotkey_t,
264 pressed: bool,
265) {
266 let wrapper: &mut DataWrapper<D> = &mut *(data as *mut DataWrapper<D>);
267
268 let data = &mut wrapper.data;
269 let hotkey_callbacks = &mut wrapper.hotkey_callbacks;
270 let mut key = Hotkey::from_raw(hotkey, pressed);
271
272 if let Some(callback) = hotkey_callbacks.get_mut(&id) {
273 callback(&mut key, data);
274 }
275}
276
277pub unsafe extern "C" fn mouse_click<D: MouseClickSource>(
278 data: *mut std::os::raw::c_void,
279 event: *const obs_mouse_event,
280 type_: i32,
281 mouse_up: bool,
282 click_count: u32,
283) {
284 let wrapper = &mut *(data as *mut DataWrapper<D>);
285 D::mouse_click(
286 &mut wrapper.data,
287 *event,
288 super::MouseButton::try_from(type_ as u32).unwrap(),
289 !mouse_up,
290 click_count as u8,
291 )
292}
293
294pub unsafe extern "C" fn mouse_move<D: MouseMoveSource>(
295 data: *mut std::os::raw::c_void,
296 event: *const obs_mouse_event,
297 mouse_leave: bool,
298) {
299 let wrapper = &mut *(data as *mut DataWrapper<D>);
300 D::mouse_move(&mut wrapper.data, *event, mouse_leave);
301}
302
303pub unsafe extern "C" fn mouse_wheel<D: MouseWheelSource>(
304 data: *mut std::os::raw::c_void,
305 event: *const obs_mouse_event,
306 xdelta: i32,
307 ydelta: i32,
308) {
309 let wrapper = &mut *(data as *mut DataWrapper<D>);
310 D::mouse_wheel(&mut wrapper.data, *event, xdelta, ydelta);
311}
312
313pub unsafe extern "C" fn key_click<D: KeyClickSource>(
314 data: *mut std::os::raw::c_void,
315 event: *const obs_key_event,
316 key_up: bool,
317) {
318 let wrapper = &mut *(data as *mut DataWrapper<D>);
319 D::key_click(&mut wrapper.data, *event, !key_up);
320}
321
322pub unsafe extern "C" fn focus<D: FocusSource>(data: *mut std::os::raw::c_void, focus: bool) {
323 let wrapper = &mut *(data as *mut DataWrapper<D>);
324 D::focus(&mut wrapper.data, focus);
325}