fmod/studio/system/
callback.rs

1// Copyright (c) 2024 Lily Lyons
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use fmod_sys::*;
8use std::ffi::c_void;
9
10use crate::studio::{Bank, System, SystemCallbackMask};
11
12#[cfg(feature = "userdata-abstraction")]
13use crate::userdata::{get_userdata, insert_userdata, set_userdata, Userdata};
14
15#[cfg(feature = "userdata-abstraction")]
16#[allow(unused_variables)]
17pub trait SystemCallback {
18    fn preupdate(system: System, userdata: Option<Userdata>) -> Result<()> {
19        Ok(())
20    }
21
22    fn postupdate(system: System, userdata: Option<Userdata>) -> Result<()> {
23        Ok(())
24    }
25
26    fn bank_unload(system: System, bank: Bank, userdata: Option<Userdata>) -> Result<()> {
27        Ok(())
28    }
29
30    fn liveupdate_connected(system: System, userdata: Option<Userdata>) -> Result<()> {
31        Ok(())
32    }
33
34    fn liveupdate_disconnected(system: System, userdata: Option<Userdata>) -> Result<()> {
35        Ok(())
36    }
37}
38
39#[cfg(not(feature = "userdata-abstraction"))]
40#[allow(unused_variables)]
41pub trait SystemCallback {
42    fn preupdate(system: System, userdata: *mut c_void) -> Result<()> {
43        Ok(())
44    }
45
46    fn postupdate(system: System, userdata: *mut c_void) -> Result<()> {
47        Ok(())
48    }
49
50    fn bank_unload(system: System, bank: Bank, userdata: *mut c_void) -> Result<()> {
51        Ok(())
52    }
53
54    fn liveupdate_connected(system: System, userdata: *mut c_void) -> Result<()> {
55        Ok(())
56    }
57
58    fn liveupdate_disconnected(system: System, userdata: *mut c_void) -> Result<()> {
59        Ok(())
60    }
61}
62
63unsafe extern "C" fn callback_impl<C: SystemCallback>(
64    system: *mut FMOD_STUDIO_SYSTEM,
65    kind: FMOD_SYSTEM_CALLBACK_TYPE,
66    command_data: *mut c_void,
67    userdata: *mut c_void,
68) -> FMOD_RESULT {
69    // FIXME handle panics
70    let system = System::from(system);
71
72    #[cfg(feature = "userdata-abstraction")]
73    let userdata = get_userdata(userdata.into());
74
75    let result = match kind {
76        FMOD_STUDIO_SYSTEM_CALLBACK_PREUPDATE => C::preupdate(system, userdata),
77        FMOD_STUDIO_SYSTEM_CALLBACK_POSTUPDATE => C::postupdate(system, userdata),
78        FMOD_STUDIO_SYSTEM_CALLBACK_BANK_UNLOAD => {
79            let bank = Bank::from(command_data.cast());
80            C::bank_unload(system, bank, userdata)
81        }
82        FMOD_STUDIO_SYSTEM_CALLBACK_LIVEUPDATE_CONNECTED => {
83            C::liveupdate_connected(system, userdata)
84        }
85        FMOD_STUDIO_SYSTEM_CALLBACK_LIVEUPDATE_DISCONNECTED => {
86            C::liveupdate_disconnected(system, userdata)
87        }
88        _ => {
89            eprintln!("warning: unknown event callback type {kind}");
90            return FMOD_RESULT::FMOD_OK;
91        }
92    };
93    result.into()
94}
95
96#[cfg(feature = "userdata-abstraction")]
97impl System {
98    pub fn set_userdata(&self, userdata: Userdata) -> Result<()> {
99        let pointer = self.get_raw_userdata()?;
100        if pointer.is_null() {
101            let key = insert_userdata(userdata, *self);
102            self.set_raw_userdata(key.into())?;
103        } else {
104            set_userdata(pointer.into(), userdata);
105        }
106
107        Ok(())
108    }
109
110    pub fn get_userdata(&self) -> Result<Option<Userdata>> {
111        let pointer = self.get_raw_userdata()?;
112        Ok(get_userdata(pointer.into()))
113    }
114}
115
116impl System {
117    #[allow(clippy::not_unsafe_ptr_arg_deref)] // fmod doesn't dereference the passed in pointer, and the user dereferencing it is unsafe anyway
118    pub fn set_raw_userdata(&self, userdata: *mut c_void) -> Result<()> {
119        unsafe { FMOD_Studio_System_SetUserData(self.inner, userdata).to_result() }
120    }
121
122    pub fn get_raw_userdata(&self) -> Result<*mut c_void> {
123        let mut userdata = std::ptr::null_mut();
124        unsafe {
125            FMOD_Studio_System_GetUserData(self.inner, &mut userdata).to_result()?;
126        }
127        Ok(userdata)
128    }
129
130    pub fn set_callback<C: SystemCallback>(&self, mask: SystemCallbackMask) -> Result<()> {
131        unsafe {
132            FMOD_Studio_System_SetCallback(self.inner, Some(callback_impl::<C>), mask.into())
133                .to_result()
134        }
135    }
136}