use super::{ModuleLoader, VstModule};
use crate::error::{Error, Result};
use std::path::Path;
use vst3::Steinberg::IPluginFactory;
#[cfg(target_os = "linux")]
use libloading::{Library, Symbol};
type ModuleEntryFunc = unsafe extern "C" fn() -> bool;
type ModuleExitFunc = unsafe extern "C" fn() -> bool;
type GetPluginFactoryFunc = unsafe extern "C" fn() -> *mut IPluginFactory;
pub struct LinuxModule {
#[allow(dead_code)]
library: Library,
path: std::path::PathBuf,
module_exit: Symbol<'static, ModuleExitFunc>,
get_factory_fn: Symbol<'static, GetPluginFactoryFunc>,
}
fn arch_mismatch_detail(binary: &Path) -> Option<String> {
use super::arch;
let data = std::fs::read(binary).ok()?;
let name = arch::elf_machine_name(arch::detect_elf_machine(&data)?);
arch::mismatch_detail("shared object", name)
}
impl LinuxModule {
fn load_internal(path: &Path) -> Result<Self> {
unsafe {
log::info!("=== Linux VST3 MODULE LOADING START ===");
log::info!("Loading VST3 shared object: {}", path.display());
let binary =
crate::discovery::get_vst3_binary_path(path).unwrap_or_else(|_| path.to_path_buf());
log::debug!("Step 1: Loading shared object: {}", binary.display());
let library = Library::new(&binary).map_err(|e| {
arch_mismatch_detail(&binary)
.map(Error::PluginLoadFailed)
.unwrap_or_else(|| {
Error::PluginLoadFailed(format!("Failed to load shared object: {}", e))
})
})?;
log::debug!("Shared object loaded successfully");
log::debug!("Step 2: Getting ModuleEntry function...");
let module_entry = library
.get::<ModuleEntryFunc>(b"ModuleEntry")
.map_err(|_| {
Error::PluginLoadFailed(
"Shared object does not export the required 'ModuleEntry' function"
.to_string(),
)
})?;
log::debug!("ModuleEntry function found");
log::debug!("Step 3: Getting ModuleExit function...");
let module_exit = library.get::<ModuleExitFunc>(b"ModuleExit").map_err(|_| {
Error::PluginLoadFailed(
"Shared object does not export the required 'ModuleExit' function".to_string(),
)
})?;
log::debug!("ModuleExit function found");
log::debug!("Step 4: Calling ModuleEntry...");
let entry_result = module_entry();
if !entry_result {
return Err(Error::PluginLoadFailed(
"ModuleEntry function returned false".to_string(),
));
}
log::debug!("ModuleEntry called successfully");
log::debug!("Step 5: Getting GetPluginFactory function...");
let get_factory_fn = library
.get::<GetPluginFactoryFunc>(b"GetPluginFactory")
.map_err(|e| {
let _ = module_exit();
Error::PluginLoadFailed(format!("Failed to find GetPluginFactory: {}", e))
})?;
log::debug!("GetPluginFactory function found");
let module_exit: Symbol<'static, ModuleExitFunc> = std::mem::transmute(module_exit);
let get_factory_fn: Symbol<'static, GetPluginFactoryFunc> =
std::mem::transmute(get_factory_fn);
log::info!("=== Linux VST3 MODULE LOADING COMPLETE ===");
log::info!("Shared object loaded successfully: {}", path.display());
Ok(LinuxModule {
library,
path: path.to_path_buf(),
module_exit,
get_factory_fn,
})
}
}
}
impl VstModule for LinuxModule {
fn get_factory(&self) -> Result<*mut IPluginFactory> {
let factory = unsafe { (self.get_factory_fn)() };
if factory.is_null() {
Err(Error::PluginLoadFailed(
"GetPluginFactory returned null".to_string(),
))
} else {
Ok(factory)
}
}
fn path(&self) -> &Path {
&self.path
}
}
impl Drop for LinuxModule {
fn drop(&mut self) {
unsafe {
log::debug!("=== Linux VST3 MODULE CLEANUP START ===");
log::debug!("Calling ModuleExit...");
let exit_result = (self.module_exit)();
if exit_result {
log::debug!("ModuleExit called successfully");
} else {
log::warn!("ModuleExit returned false");
}
log::debug!("Unloading shared object...");
log::debug!("Shared object unloaded");
log::debug!("=== Linux VST3 MODULE CLEANUP COMPLETE ===");
}
}
}
pub struct LinuxModuleLoader;
impl ModuleLoader for LinuxModuleLoader {
fn load(path: &Path) -> Result<Box<dyn VstModule>> {
let module = LinuxModule::load_internal(path)?;
Ok(Box::new(module))
}
}