fmod/studio/system/
callback.rs

1// Copyright (c) 2024 Melody Madeline 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::{FmodResultExt, Result};
11use crate::{
12    panic_wrapper,
13    studio::{Bank, System, SystemCallbackMask},
14};
15
16/// Trait for this particular FMOD callback.
17///
18/// No `self` parameter is passed to the callback!
19///
20/// Callbacks are called from the Studio Update Thread in default / async mode and the main (calling) thread in synchronous mode.
21#[allow(unused_variables)]
22pub trait SystemCallback {
23    /// Called at the start of the main Studio update. For async mode this will be on its own thread.
24    fn preupdate(system: System, userdata: *mut c_void) -> Result<()> {
25        Ok(())
26    }
27
28    /// Called at the end of the main Studio update. For async mode this will be on its own thread.
29    fn postupdate(system: System, userdata: *mut c_void) -> Result<()> {
30        Ok(())
31    }
32
33    /// Called directly when a bank has just been unloaded, after all resources are freed.
34    fn bank_unload(system: System, bank: Bank, userdata: *mut c_void) -> Result<()> {
35        Ok(())
36    }
37
38    /// Called after a live update connection has been established.
39    fn liveupdate_connected(system: System, userdata: *mut c_void) -> Result<()> {
40        Ok(())
41    }
42
43    /// Called after live update session disconnects.
44    fn liveupdate_disconnected(system: System, userdata: *mut c_void) -> Result<()> {
45        Ok(())
46    }
47}
48
49unsafe extern "C" fn callback_impl<C: SystemCallback>(
50    system: *mut FMOD_STUDIO_SYSTEM,
51    kind: FMOD_SYSTEM_CALLBACK_TYPE,
52    command_data: *mut c_void,
53    userdata: *mut c_void,
54) -> FMOD_RESULT {
55    panic_wrapper(|| {
56        let system = unsafe { System::from_ffi(system) };
57
58        let result = match kind {
59            FMOD_STUDIO_SYSTEM_CALLBACK_PREUPDATE => C::preupdate(system, userdata),
60            FMOD_STUDIO_SYSTEM_CALLBACK_POSTUPDATE => C::postupdate(system, userdata),
61            FMOD_STUDIO_SYSTEM_CALLBACK_BANK_UNLOAD => {
62                let bank = unsafe { Bank::from_ffi(command_data.cast()) };
63                C::bank_unload(system, bank, userdata)
64            }
65            FMOD_STUDIO_SYSTEM_CALLBACK_LIVEUPDATE_CONNECTED => {
66                C::liveupdate_connected(system, userdata)
67            }
68            FMOD_STUDIO_SYSTEM_CALLBACK_LIVEUPDATE_DISCONNECTED => {
69                C::liveupdate_disconnected(system, userdata)
70            }
71            _ => {
72                eprintln!("warning: unknown event callback type {kind}");
73                return FMOD_RESULT::FMOD_OK;
74            }
75        };
76        FMOD_RESULT::from_result(result)
77    })
78}
79
80impl System {
81    /// Sets the user data.
82    #[allow(clippy::not_unsafe_ptr_arg_deref)] // fmod doesn't dereference the passed in pointer, and the user dereferencing it is unsafe anyway
83    pub fn set_userdata(&self, userdata: *mut c_void) -> Result<()> {
84        unsafe { FMOD_Studio_System_SetUserData(self.inner.as_ptr(), userdata).to_result() }
85    }
86
87    /// Retrieves the user data.
88    pub fn get_userdata(&self) -> Result<*mut c_void> {
89        let mut userdata = std::ptr::null_mut();
90        unsafe {
91            FMOD_Studio_System_GetUserData(self.inner.as_ptr(), &raw mut userdata).to_result()?;
92        }
93        Ok(userdata)
94    }
95
96    /// Sets a callback for the Studio System.
97    pub fn set_callback<C: SystemCallback>(&self, mask: SystemCallbackMask) -> Result<()> {
98        unsafe {
99            FMOD_Studio_System_SetCallback(
100                self.inner.as_ptr(),
101                Some(callback_impl::<C>),
102                mask.into(),
103            )
104            .to_result()
105        }
106    }
107}