use c_str_macro::c_str;
use std::ptr::NonNull;
use reaper_low::{
add_cpp_control_surface, raw, remove_cpp_control_surface, IReaperControlSurface,
ReaperPluginContext,
};
use crate::infostruct_keeper::InfostructKeeper;
use crate::{
concat_c_strs, delegating_hook_command, delegating_hook_post_command, delegating_toggle_action,
CommandId, DelegatingControlSurface, MainThreadScope, MediumAudioHookRegister,
MediumGaccelRegister, MediumHookCommand, MediumHookPostCommand, MediumOnAudioBuffer,
MediumReaperControlSurface, MediumToggleAction, RealTimeAudioThreadScope, ReaperFunctionError,
ReaperFunctionResult, ReaperFunctions, ReaperStringArg, RegistrationObject,
};
use reaper_low;
use reaper_low::raw::audio_hook_register_t;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Default)]
pub struct Reaper {
functions: ReaperFunctions<MainThreadScope>,
gaccel_registers: InfostructKeeper<MediumGaccelRegister, raw::gaccel_register_t>,
audio_hook_registers: InfostructKeeper<MediumAudioHookRegister, raw::audio_hook_register_t>,
csurf_insts: HashMap<NonNull<raw::IReaperControlSurface>, Box<Box<dyn IReaperControlSurface>>>,
plugin_registrations: HashSet<RegistrationObject<'static>>,
audio_hook_registrations: HashSet<NonNull<raw::audio_hook_register_t>>,
}
impl Reaper {
pub fn new(low: reaper_low::Reaper) -> Reaper {
Reaper {
functions: ReaperFunctions::new(low),
gaccel_registers: Default::default(),
audio_hook_registers: Default::default(),
csurf_insts: Default::default(),
plugin_registrations: Default::default(),
audio_hook_registrations: Default::default(),
}
}
pub fn load(context: &ReaperPluginContext) -> Reaper {
let low = reaper_low::Reaper::load(context);
Reaper::new(low)
}
pub fn functions(&self) -> &ReaperFunctions<MainThreadScope> {
&self.functions
}
pub fn create_real_time_functions(&self) -> ReaperFunctions<RealTimeAudioThreadScope> {
ReaperFunctions::new(self.functions.low().clone())
}
pub unsafe fn plugin_register_add(
&mut self,
object: RegistrationObject,
) -> ReaperFunctionResult<i32> {
self.plugin_registrations
.insert(object.clone().into_owned());
let infostruct = object.ptr_to_raw();
let result = self
.functions
.low()
.plugin_register(object.key_into_raw().as_ptr(), infostruct);
if result == 0 {
return Err(ReaperFunctionError::new("couldn't register thing"));
}
Ok(result)
}
pub unsafe fn plugin_register_remove(&mut self, object: RegistrationObject) -> i32 {
let infostruct = object.ptr_to_raw();
let name_with_minus = concat_c_strs(c_str!("-"), object.clone().key_into_raw().as_ref());
let result = self
.functions
.low()
.plugin_register(name_with_minus.as_ptr(), infostruct);
self.plugin_registrations.remove(&object.into_owned());
result
}
pub fn plugin_register_add_hook_command<T: MediumHookCommand>(
&mut self,
) -> ReaperFunctionResult<()> {
unsafe {
self.plugin_register_add(RegistrationObject::HookCommand(
delegating_hook_command::<T>,
))?;
}
Ok(())
}
pub fn plugin_register_remove_hook_command<T: MediumHookCommand>(&mut self) {
unsafe {
self.plugin_register_remove(RegistrationObject::HookCommand(
delegating_hook_command::<T>,
));
}
}
pub fn plugin_register_add_toggle_action<T: MediumToggleAction>(
&mut self,
) -> ReaperFunctionResult<()> {
unsafe {
self.plugin_register_add(RegistrationObject::ToggleAction(
delegating_toggle_action::<T>,
))?
};
Ok(())
}
pub fn plugin_register_remove_toggle_action<T: MediumToggleAction>(&mut self) {
unsafe {
self.plugin_register_remove(RegistrationObject::ToggleAction(
delegating_toggle_action::<T>,
));
}
}
pub fn plugin_register_add_hook_post_command<T: MediumHookPostCommand>(
&mut self,
) -> ReaperFunctionResult<()> {
unsafe {
self.plugin_register_add(RegistrationObject::HookPostCommand(
delegating_hook_post_command::<T>,
))?
};
Ok(())
}
pub fn plugin_register_remove_hook_post_command<T: MediumHookPostCommand>(&mut self) {
unsafe {
self.plugin_register_remove(RegistrationObject::HookPostCommand(
delegating_hook_post_command::<T>,
));
}
}
pub fn plugin_register_add_command_id<'a>(
&mut self,
command_name: impl Into<ReaperStringArg<'a>>,
) -> ReaperFunctionResult<CommandId> {
let raw_id = unsafe {
self.plugin_register_add(RegistrationObject::CommandId(
command_name.into().into_inner(),
))?
};
Ok(CommandId(raw_id as _))
}
pub fn plugin_register_add_gaccel(
&mut self,
register: MediumGaccelRegister,
) -> ReaperFunctionResult<NonNull<raw::gaccel_register_t>> {
let handle = self.gaccel_registers.keep(register);
unsafe { self.plugin_register_add(RegistrationObject::Gaccel(handle))? };
Ok(handle)
}
pub fn plugin_register_remove_gaccel(&mut self, handle: NonNull<raw::gaccel_register_t>) {
unsafe { self.plugin_register_remove(RegistrationObject::Gaccel(handle)) };
}
pub fn plugin_register_add_csurf_inst(
&mut self,
control_surface: impl MediumReaperControlSurface + 'static,
) -> ReaperFunctionResult<NonNull<raw::IReaperControlSurface>> {
let rust_control_surface =
DelegatingControlSurface::new(control_surface, &self.functions.get_app_version());
let rust_control_surface: Box<Box<dyn IReaperControlSurface>> =
Box::new(Box::new(rust_control_surface));
let cpp_control_surface =
unsafe { add_cpp_control_surface(rust_control_surface.as_ref().into()) };
self.csurf_insts
.insert(cpp_control_surface, rust_control_surface);
unsafe { self.plugin_register_add(RegistrationObject::CsurfInst(cpp_control_surface))? };
Ok(cpp_control_surface)
}
pub fn plugin_register_remove_csurf_inst(
&mut self,
handle: NonNull<raw::IReaperControlSurface>,
) {
unsafe {
self.plugin_register_remove(RegistrationObject::CsurfInst(handle));
}
self.csurf_insts.remove(&handle);
unsafe {
remove_cpp_control_surface(handle);
}
}
pub unsafe fn audio_reg_hardware_hook_add_unchecked(
&mut self,
register: NonNull<audio_hook_register_t>,
) -> ReaperFunctionResult<()> {
self.audio_hook_registrations.insert(register);
let result = self
.functions
.low()
.Audio_RegHardwareHook(true, register.as_ptr());
if result == 0 {
return Err(ReaperFunctionError::new("couldn't register audio hook"));
}
Ok(())
}
pub unsafe fn audio_reg_hardware_hook_remove_unchecked(
&mut self,
register: NonNull<audio_hook_register_t>,
) {
self.functions
.low()
.Audio_RegHardwareHook(false, register.as_ptr());
self.audio_hook_registrations.remove(®ister);
}
pub fn audio_reg_hardware_hook_add<T: MediumOnAudioBuffer + 'static>(
&mut self,
callback: T,
) -> ReaperFunctionResult<NonNull<audio_hook_register_t>> {
let handle = self
.audio_hook_registers
.keep(MediumAudioHookRegister::new(callback));
unsafe { self.audio_reg_hardware_hook_add_unchecked(handle)? };
Ok(handle)
}
pub fn audio_reg_hardware_hook_remove(&mut self, handle: NonNull<audio_hook_register_t>) {
unsafe { self.audio_reg_hardware_hook_remove_unchecked(handle) };
let _ = self.audio_hook_registers.release(handle);
}
}
impl Drop for Reaper {
fn drop(&mut self) {
for handle in self.audio_hook_registrations.clone() {
unsafe {
self.audio_reg_hardware_hook_remove_unchecked(handle);
}
}
for reg in self.plugin_registrations.clone() {
unsafe {
self.plugin_register_remove(reg);
}
}
}
}