use crate::{
common::{AnyResult, LeakManager},
generic::{
GenericPlugin, ProjectFile,
binding::{HostAppHandle, PluginRegistry},
},
};
#[doc(hidden)]
pub struct InternalGenericPluginState<T: Send + Sync + GenericPlugin> {
plugin_registry: PluginRegistry,
register_plugin_done: std::sync::Arc<std::sync::atomic::AtomicBool>,
global_leak_manager: LeakManager,
instance: T,
is_edit_handle_ready: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
impl<T: Send + Sync + GenericPlugin> InternalGenericPluginState<T> {
pub fn new(instance: T) -> Self {
Self {
plugin_registry: PluginRegistry::new(),
register_plugin_done: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
global_leak_manager: LeakManager::new(),
instance,
is_edit_handle_ready: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
}
}
}
pub trait GenericSingleton
where
Self: 'static + Send + Sync + crate::generic::GenericPlugin,
{
fn __get_singleton_state() -> &'static std::sync::RwLock<
Option<crate::generic::__bridge::InternalGenericPluginState<Self>>,
>;
fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R {
let lock = Self::__get_singleton_state();
let guard = lock.read().unwrap();
let state = guard.as_ref().expect("Plugin not initialized");
f(&state.instance)
}
fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R {
let lock = Self::__get_singleton_state();
let mut guard = lock.write().unwrap();
let state = guard.as_mut().expect("Plugin not initialized");
f(&mut state.instance)
}
}
pub unsafe fn create_table<T: GenericSingleton>() -> *mut aviutl2_sys::plugin2::COMMON_PLUGIN_TABLE
{
let plugin_state = T::__get_singleton_state();
let mut plugin_state = plugin_state.write().unwrap();
let plugin_state = plugin_state.as_mut().expect("Plugin not initialized");
tracing::info!(
"Creating plugin table for plugin: {}",
std::any::type_name::<T>()
);
let info = plugin_state.instance.plugin_info();
let table = Box::new(aviutl2_sys::plugin2::COMMON_PLUGIN_TABLE {
name: plugin_state
.global_leak_manager
.leak_as_wide_string(&info.name),
information: plugin_state
.global_leak_manager
.leak_as_wide_string(&info.information),
});
Box::into_raw(table)
}
pub unsafe fn create_table_unwind<T: GenericSingleton>()
-> *mut aviutl2_sys::plugin2::COMMON_PLUGIN_TABLE {
match crate::utils::catch_unwind_with_panic_info(|| unsafe { create_table::<T>() }) {
Ok(table) => table,
Err(panic_info) => {
tracing::error!("Panic occurred during table creation: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
std::ptr::null_mut()
}
}
}
pub unsafe fn initialize_plugin_c<T: GenericSingleton>(version: u32) -> bool {
match initialize_plugin::<T>(version) {
Ok(_) => true,
Err(e) => {
tracing::error!("Failed to initialize plugin: {}", e);
let _ = crate::logger::write_error_log(&format!("{e}"));
false
}
}
}
pub unsafe fn initialize_plugin_c_unwind<T: GenericSingleton>(version: u32) -> bool {
match crate::utils::catch_unwind_with_panic_info(|| unsafe {
initialize_plugin_c::<T>(version)
}) {
Ok(result) => result,
Err(panic_info) => {
tracing::error!(
"Panic occurred during plugin initialization: {}",
panic_info
);
let _ = crate::logger::write_error_log(&panic_info);
false
}
}
}
pub(crate) fn initialize_plugin<T: GenericSingleton>(version: u32) -> AnyResult<()> {
crate::common::ensure_minimum_aviutl2_version(version.into())?;
let plugin_state = T::__get_singleton_state();
let info = crate::common::AviUtl2Info {
version: version.into(),
};
let internal = T::new(info)?;
let plugin = InternalGenericPluginState::new(internal);
*plugin_state.write().unwrap() = Some(plugin);
Ok(())
}
fn register_plugin_impl<T: GenericSingleton>(
host: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
unwind: bool,
) {
let plugin_state = T::__get_singleton_state();
let mut plugin_state = plugin_state.write().unwrap();
let plugin_state = plugin_state.as_mut().expect("Plugin not initialized");
let mut handle = unsafe {
HostAppHandle::new(
host,
&mut plugin_state.global_leak_manager,
plugin_state.register_plugin_done.clone(),
&mut plugin_state.plugin_registry,
plugin_state.is_edit_handle_ready.clone(),
)
};
if unwind {
let result =
crate::utils::catch_unwind_with_panic_info(std::panic::AssertUnwindSafe(|| {
T::register(&mut plugin_state.instance, &mut handle)
}));
if let Err(panic_info) = result {
tracing::error!(
"Panic occurred during plugin registration: {:?}",
panic_info
);
let _ = crate::logger::write_error_log(&format!(
"Panic during plugin registration: {}",
panic_info
));
return;
}
handle.register_project_load_handler(on_project_load_impl::<T>);
handle.register_project_save_handler(on_project_save_impl::<T>);
handle.register_clear_cache_handler(on_clear_cache_impl::<T>);
} else {
T::register(&mut plugin_state.instance, &mut handle);
handle.register_project_load_handler(on_project_load_impl::<T>);
handle.register_project_save_handler(on_project_save_impl::<T>);
handle.register_clear_cache_handler(on_clear_cache_impl::<T>);
}
handle.register_event_listener(crate::generic::EventType::UpdateObject, || {
<T as GenericSingleton>::with_instance_mut(|instance| instance.event_update_object_info())
});
handle.register_event_listener(crate::generic::EventType::ChangeEditFrame, || {
<T as GenericSingleton>::with_instance_mut(|instance| instance.event_change_edit_frame())
});
handle.register_event_listener(crate::generic::EventType::ChangeEditScene, || {
<T as GenericSingleton>::with_instance_mut(|instance| instance.event_change_scene_info())
});
handle.register_event_listener(crate::generic::EventType::ChangeFocusObject, || {
<T as GenericSingleton>::with_instance_mut(|instance| instance.event_change_focus_object())
});
plugin_state
.register_plugin_done
.store(true, std::sync::atomic::Ordering::SeqCst);
fn on_project_load_impl<T: GenericSingleton>(project: &mut ProjectFile) {
{
let state = T::__get_singleton_state();
let guard = state.read().unwrap();
let plugin_state = guard.as_ref().expect("Plugin not initialized");
plugin_state
.is_edit_handle_ready
.store(true, std::sync::atomic::Ordering::SeqCst);
}
<T as GenericSingleton>::with_instance_mut(|instance| {
instance.on_project_load(project);
});
}
fn on_project_save_impl<T: GenericSingleton>(project: &mut ProjectFile) {
<T as GenericSingleton>::with_instance_mut(|instance| {
instance.on_project_save(project);
});
}
fn on_clear_cache_impl<T: GenericSingleton>(edit_section: &crate::generic::EditSection) {
<T as GenericSingleton>::with_instance_mut(|instance| {
instance.on_clear_cache(edit_section);
});
}
}
pub unsafe fn register_plugin<T: GenericSingleton>(
host: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
) {
register_plugin_impl::<T>(host, false);
}
pub unsafe fn register_plugin_unwind<T: GenericSingleton>(
host: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
) {
if let Err(panic_info) =
crate::utils::catch_unwind_with_panic_info(|| register_plugin_impl::<T>(host, true))
{
tracing::error!("Panic occurred during register_plugin: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
}
}
pub unsafe fn uninitialize_plugin<T: GenericSingleton>() {
let plugin_state = T::__get_singleton_state();
let mut plugin_state = plugin_state.write().unwrap();
plugin_state.take();
}
pub unsafe fn uninitialize_plugin_c_unwind<T: GenericSingleton>() {
match crate::utils::catch_unwind_with_panic_info(|| unsafe { uninitialize_plugin::<T>() }) {
Ok(()) => {}
Err(panic_info) => {
tracing::error!(
"Panic occurred during plugin uninitialization: {}",
panic_info
);
let _ = crate::logger::write_error_log(&panic_info);
}
}
}
#[macro_export]
macro_rules! register_generic_plugin {
($struct:ident, $($key:ident = $value:expr),* $(,)?) => {
$crate::__internal_module! {
#[unsafe(no_mangle)]
unsafe extern "C" fn RequiredVersion() -> u32 {
$crate::MINIMUM_AVIUTL2_VERSION.into()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializeLogger(logger: *mut $crate::sys::logger2::LOG_HANDLE) {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::logger::__initialize_logger_unwind(logger)
} else {
$crate::logger::__initialize_logger(logger)
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializeConfig(
config: *mut $crate::sys::config2::CONFIG_HANDLE
) {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::config::__initialize_config_handle_unwind(config)
} else {
$crate::config::__initialize_config_handle(config)
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializeCache(
cache: *mut $crate::sys::cache2::CACHE_HANDLE
) {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::cache::__initialize_cache_unwind(cache)
} else {
$crate::cache::__initialize_cache(cache)
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn InitializePlugin(version: u32) -> bool {
unsafe {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::generic::__bridge::initialize_plugin_c_unwind::<$struct>(version)
} else {
$crate::generic::__bridge::initialize_plugin_c::<$struct>(version)
}
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn GetCommonPluginTable() -> *mut $crate::sys::plugin2::COMMON_PLUGIN_TABLE {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
unsafe { $crate::generic::__bridge::create_table_unwind::<$struct>() }
} else {
unsafe { $crate::generic::__bridge::create_table::<$struct>() }
}
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn UninitializePlugin() {
unsafe {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
$crate::generic::__bridge::uninitialize_plugin_c_unwind::<$struct>()
} else {
$crate::generic::__bridge::uninitialize_plugin::<$struct>()
}
}
};
}
#[unsafe(no_mangle)]
unsafe extern "C" fn RegisterPlugin(host: *mut aviutl2::sys::plugin2::HOST_APP_TABLE) {
$crate::comptime_if::comptime_if! {
if unwind where (unwind = true, $( $key = $value ),* ) {
unsafe { $crate::generic::__bridge::register_plugin_unwind::<$struct>(host) };
} else {
unsafe { $crate::generic::__bridge::register_plugin::<$struct>(host) };
}
}
}
}
};
($struct:ident, $($key:ident),* $(,)?) => {
$crate::register_generic_plugin!($struct, $( $key = true ),* );
};
($struct:ident) => {
$crate::register_generic_plugin!($struct, );
};
}