Files
Paul Lipscomb 7ecc718f5d Reorganize top-level directories with clearer naming convention
app -> app-desktop-macos, presets -> app-presets, server -> remote-server,
daw-config-reaper -> osc-config-daw, plugin-reaper-realearn -> plugin-reaper-relearn.
Updated run.py and presets.py path references accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 17:51:05 -04:00

113 lines
3.9 KiB
Rust

use anyhow::{bail, Context, Result};
use libloading::{Library, Symbol};
use reaper_fluent::Reaper;
use reaper_low::{PluginContext, TypeSpecificPluginContext};
use reaper_macros::reaper_extension_plugin;
use reaper_medium::ReaperSession;
use std::error::Error;
use std::fs;
use std::sync::OnceLock;
// Executing Drop not important because extensions always live until REAPER ends.
static EXTENSION: OnceLock<HelgoboxExtension> = OnceLock::new();
#[reaper_extension_plugin]
fn plugin_main(context: PluginContext) -> std::result::Result<(), Box<dyn Error>> {
let _ = EXTENSION.set(HelgoboxExtension::load(context)?);
Ok(())
}
type ReaperPluginEntry = unsafe extern "C" fn(
h_instance: ::reaper_low::raw::HINSTANCE,
rec: *mut ::reaper_low::raw::reaper_plugin_info_t,
) -> ::std::os::raw::c_int;
#[cfg(target_os = "linux")]
type SwellDllMain = unsafe extern "C" fn(
hinstance: reaper_low::raw::HINSTANCE,
reason: u32,
get_func: Option<
unsafe extern "C" fn(name: *const std::os::raw::c_char) -> *mut std::os::raw::c_void,
>,
) -> ::std::os::raw::c_int;
struct HelgoboxExtension {
/// Just for RAII.
_plugin_library: Option<Library>,
}
impl HelgoboxExtension {
pub fn load(context: PluginContext) -> Result<Self> {
// Install reaper-fluent for global use
let _ = Reaper::install_globally(ReaperSession::load(context));
// Return extension
let extension = Self {
_plugin_library: eagerly_load_plugin_lib(&context).ok(),
};
Ok(extension)
}
#[allow(dead_code)]
pub fn get() -> &'static HelgoboxExtension {
EXTENSION
.get()
.expect("Helgobox extension not yet initialized")
}
}
/// Loads the Helgobox plug-in library eagerly (Justin's idea, awesome!)
fn eagerly_load_plugin_lib(context: &PluginContext) -> Result<Library> {
// Find correct shared library file
let resource_path = Reaper::get()
.medium_reaper()
.get_resource_path(|path| path.to_path_buf());
let plugin_library_path = fs::read_dir(resource_path.join("UserPlugins/FX"))?
.flatten()
.find_map(|item| {
let file_type = item.file_type().ok()?;
if !file_type.is_file() && !file_type.is_symlink() {
return None;
}
let file_name = item.file_name().to_str()?.to_lowercase();
let extension = if cfg!(target_os = "windows") {
".dll"
} else if cfg!(target_os = "macos") {
".vst.dylib"
} else {
".so"
};
let matches = file_name.starts_with("helgobox") && file_name.ends_with(extension);
if !matches {
return None;
}
Some(item.path())
})
.context("couldn't find plug-in library")?;
// Load shared library
let plugin_library = unsafe { Library::new(plugin_library_path)? };
#[cfg(target_os = "linux")]
{
// Linux only: Run SWELL entry point of library (on Windows, SWELL is not necessary, and on
// macOS, SWELL is obtained differently)
let swell_dll_main: Symbol<SwellDllMain> = unsafe { plugin_library.get(b"SWELL_dllMain")? };
unsafe {
swell_dll_main(
context.h_instance(),
reaper_low::raw::DLL_PROCESS_ATTACH,
context.swell_function_provider(),
);
}
}
// Run extension entry point of library
let reaper_plugin_entry: Symbol<ReaperPluginEntry> =
unsafe { plugin_library.get(b"ReaperPluginEntry")? };
let TypeSpecificPluginContext::Extension(ctx) = context.type_specific() else {
bail!("unexpected plug-in context type for extension");
};
let mut original_info_struct = ctx.to_raw();
unsafe {
reaper_plugin_entry(context.h_instance(), &mut original_info_struct as *mut _);
}
Ok(plugin_library)
}