1use crate::{
2 common::{AnyResult, LeakManager, alert_error},
3 module::{ScriptModule, ScriptModuleTable},
4};
5
6#[doc(hidden)]
7pub struct InternalScriptModuleState<T: Send + Sync + ScriptModule> {
8 plugin_info: ScriptModuleTable,
9 global_leak_manager: LeakManager,
10
11 pub instance: T,
12}
13
14impl<T: Send + Sync + ScriptModule> InternalScriptModuleState<T> {
15 pub fn new(instance: T) -> Self {
16 let plugin_info = instance.plugin_info();
17 Self {
18 plugin_info,
19 global_leak_manager: LeakManager::new(),
20 instance,
21 }
22 }
23}
24
25pub trait ScriptModuleSingleton
26where
27 Self: ScriptModule + Sized + Send + Sync + 'static,
28{
29 fn __get_singleton_state() -> &'static std::sync::RwLock<Option<InternalScriptModuleState<Self>>>;
30
31 fn with_instance<R>(f: impl FnOnce(&Self) -> R) -> R {
32 let lock = Self::__get_singleton_state();
33 let guard = lock.read().unwrap();
34 let state = guard.as_ref().expect("Plugin not initialized");
35 f(&state.instance)
36 }
37
38 fn with_instance_mut<R>(f: impl FnOnce(&mut Self) -> R) -> R {
39 let lock = Self::__get_singleton_state();
40 let mut guard = lock.write().unwrap();
41 let state = guard.as_mut().expect("Plugin not initialized");
42 f(&mut state.instance)
43 }
44}
45
46pub unsafe fn initialize_plugin_c<T: ScriptModuleSingleton>(version: u32) -> bool {
47 match initialize_plugin::<T>(version) {
48 Ok(_) => true,
49 Err(e) => {
50 log::error!("Failed to initialize plugin: {}", e);
51 alert_error(&e);
52 false
53 }
54 }
55}
56
57pub(crate) fn initialize_plugin<T: ScriptModuleSingleton>(version: u32) -> AnyResult<()> {
58 let plugin_state = T::__get_singleton_state();
59 let info = crate::common::AviUtl2Info {
60 version: version.into(),
61 };
62 let internal = T::new(info)?;
63 let plugin = InternalScriptModuleState::new(internal);
64 *plugin_state.write().unwrap() = Some(plugin);
65
66 Ok(())
67}
68pub unsafe fn uninitialize_plugin<T: ScriptModuleSingleton>() {
69 let plugin_state = T::__get_singleton_state();
70 *plugin_state.write().unwrap() = None;
71}
72
73pub unsafe fn create_table<T: ScriptModuleSingleton>()
74-> *mut aviutl2_sys::module2::SCRIPT_MODULE_TABLE {
75 let plugin_state_lock = T::__get_singleton_state();
76 let plugin_state = plugin_state_lock.read().unwrap();
77 let plugin_state = plugin_state.as_ref().expect("Plugin not initialized");
78 let plugin_info = &plugin_state.plugin_info;
79 let information = if cfg!(debug_assertions) {
80 format!("(Debug Build) {}", plugin_info.information)
81 } else {
82 plugin_info.information.clone()
83 };
84
85 let module_functions: Vec<aviutl2_sys::module2::SCRIPT_MODULE_FUNCTION> = plugin_info
86 .functions
87 .iter()
88 .map(|f| aviutl2_sys::module2::SCRIPT_MODULE_FUNCTION {
89 name: plugin_state
90 .global_leak_manager
91 .leak_as_wide_string(&f.name),
92 func: f.func,
93 })
94 .chain(std::iter::once(
95 aviutl2_sys::module2::SCRIPT_MODULE_FUNCTION {
96 name: std::ptr::null(),
97 func: unreachable_function,
98 },
99 ))
100 .collect();
101 let functions_ptr = plugin_state
102 .global_leak_manager
103 .leak_value_vec(module_functions);
104
105 let table = aviutl2_sys::module2::SCRIPT_MODULE_TABLE {
107 information: plugin_state
108 .global_leak_manager
109 .leak_as_wide_string(&information),
110 functions: functions_ptr,
111 };
112 let table = Box::new(table);
113 Box::leak(table)
114}
115
116extern "C" fn unreachable_function(_: *mut aviutl2_sys::module2::SCRIPT_MODULE_PARAM) {
117 unreachable!("This function should never be called");
118}
119
120#[macro_export]
122macro_rules! register_script_module {
123 ($struct:ident) => {
124 ::aviutl2::__internal_module! {
125 #[unsafe(no_mangle)]
126 unsafe extern "C" fn InitializeLogger(logger: *mut $crate::sys::logger2::LOG_HANDLE) {
127 $crate::logger::__initialize_logger(logger)
128 }
129
130 #[unsafe(no_mangle)]
131 unsafe extern "C" fn InitializePlugin(version: u32) -> bool {
132 unsafe { $crate::module::__bridge::initialize_plugin_c::<$struct>(version) }
133 }
134
135 #[unsafe(no_mangle)]
136 unsafe extern "C" fn UninitializePlugin() {
137 unsafe { $crate::module::__bridge::uninitialize_plugin::<$struct>() }
138 }
139
140 #[unsafe(no_mangle)]
141 unsafe extern "C" fn GetScriptModuleTable()
142 -> *mut aviutl2::sys::module2::SCRIPT_MODULE_TABLE {
143 unsafe { $crate::module::__bridge::create_table::<$struct>() }
144 }
145 }
146 };
147}