1use crate::AviUtl2Info;
2use pastey::paste;
3use std::num::NonZeroIsize;
4
5pub struct HostAppHandle<'a> {
12 internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
13 global_leak_manager: &'a mut crate::common::LeakManager,
14 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
15 plugin_registry: &'a mut crate::generic::PluginRegistry,
16 is_registerplugin_done: std::sync::Arc<std::sync::atomic::AtomicBool>,
17}
18
19pub struct SubPlugin<T> {
21 plugin: std::marker::PhantomData<T>,
22 internal: std::sync::Arc<InternalReferenceHandle>,
23}
24struct InternalReferenceHandle {
25 uninitialize_fn: fn(),
26}
27impl Drop for InternalReferenceHandle {
28 fn drop(&mut self) {
29 (self.uninitialize_fn)();
30 }
31}
32
33impl<'plugin> HostAppHandle<'plugin> {
34 pub(crate) unsafe fn new(
35 internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
36 global_leak_manager: &'plugin mut crate::common::LeakManager,
37 kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
38 plugin_registry: &'plugin mut crate::generic::PluginRegistry,
39 is_registerplugin_done: std::sync::Arc<std::sync::atomic::AtomicBool>,
40 ) -> Self {
41 Self {
42 internal,
43 global_leak_manager,
44 kill_switch,
45 plugin_registry,
46 is_registerplugin_done,
47 }
48 }
49
50 fn assert_not_killed(&self) {
51 if self.kill_switch.load(std::sync::atomic::Ordering::SeqCst) {
52 panic!("This HostAppHandle is no longer valid.");
53 }
54 }
55
56 pub fn create_edit_handle(&mut self) -> crate::generic::EditHandle {
58 self.assert_not_killed();
59 let raw_handle = unsafe { ((*self.internal).create_edit_handle)() };
60 unsafe { crate::generic::EditHandle::new(raw_handle, self.is_registerplugin_done.clone()) }
61 }
62
63 pub fn register_import_menu<F>(&mut self, name: &str, callback: F)
69 where
70 F: Fn() + 'static + Send + Sync,
71 {
72 self.register_menu_internal(name, callback, unsafe {
73 (*self.internal).register_import_menu_param
74 });
75 }
76
77 pub fn register_export_menu<F>(&mut self, name: &str, callback: F)
83 where
84 F: Fn() + 'static + Send + Sync,
85 {
86 self.register_menu_internal(name, callback, unsafe {
87 (*self.internal).register_export_menu_param
88 });
89 }
90
91 pub fn register_layer_menu<F>(&mut self, name: &str, callback: F)
99 where
100 F: Fn() + 'static + Send + Sync,
101 {
102 self.register_menu_internal(name, callback, unsafe {
103 (*self.internal).register_layer_menu_param
104 });
105 }
106
107 pub fn register_object_menu<F>(&mut self, name: &str, callback: F)
115 where
116 F: Fn() + 'static + Send + Sync,
117 {
118 self.register_menu_internal(name, callback, unsafe {
119 (*self.internal).register_object_menu_param
120 });
121 }
122
123 pub fn register_edit_menu<F>(&mut self, name: &str, callback: F)
130 where
131 F: Fn() + 'static + Send + Sync,
132 {
133 self.register_menu_internal(name, callback, unsafe {
134 (*self.internal).register_edit_menu_param
135 });
136 }
137
138 pub fn register_config_menu(
145 &mut self,
146 name: &str,
147 callback: extern "C" fn(aviutl2_sys::plugin2::HWND, aviutl2_sys::plugin2::HINSTANCE),
148 ) {
149 self.assert_not_killed();
150 unsafe {
151 ((*self.internal).register_config_menu)(
152 self.global_leak_manager.leak_as_wide_string(name),
153 callback,
154 )
155 };
156 }
157
158 pub fn register_object_item_menu<F>(&mut self, name: &str, callback: F)
172 where
173 F: Fn(crate::generic::ObjectHandle, &str, usize, &str) + 'static + Send + Sync,
174 {
175 self.assert_not_killed();
176 let trampoline_param: Box<F> = Box::new(callback);
177 let trampoline_param_ptr = Box::into_raw(trampoline_param);
178 let name_wide = self.global_leak_manager.leak_as_wide_string(name);
179 unsafe {
180 ((*self.internal).register_object_item_menu_param)(
181 name_wide,
182 false,
183 trampoline_param_ptr as *mut std::ffi::c_void,
184 trampoline::<F>,
185 );
186 }
187
188 unsafe extern "C" fn trampoline<F>(
189 param: *mut std::ffi::c_void,
190 object_handle: aviutl2_sys::plugin2::OBJECT_HANDLE,
191 name_and_index: aviutl2_sys::common::LPCWSTR,
192 item_name: aviutl2_sys::common::LPCWSTR,
193 ) where
194 F: Fn(crate::generic::ObjectHandle, &str, usize, &str) + 'static + Send + Sync,
195 {
196 let callback = unsafe { &mut *(param as *mut F) };
197 let object_handle = crate::generic::ObjectHandle::from(object_handle);
198 let name_and_index_str = unsafe { crate::common::load_wide_string(name_and_index) };
199 let (name_str, index) = parse_name_and_index(&name_and_index_str);
200 let item_name_str = unsafe { crate::common::load_wide_string(item_name) };
201 if let Err(panic_info) =
202 crate::utils::catch_unwind_with_panic_info(std::panic::AssertUnwindSafe(|| {
203 callback(object_handle, name_str, index, &item_name_str);
204 }))
205 {
206 tracing::error!(
207 "Panic occurred in object item menu callback: {}",
208 panic_info
209 );
210 let _ = crate::logger::write_error_log(&panic_info);
211 }
212 }
213 }
214
215 pub fn register_object_item_and_effect_menu<F>(&mut self, name: &str, callback: F)
229 where
230 F: Fn(crate::generic::ObjectHandle, &str, usize, Option<&str>) + 'static + Send + Sync,
231 {
232 self.assert_not_killed();
233 let trampoline_param: Box<F> = Box::new(callback);
234 let trampoline_param_ptr = Box::into_raw(trampoline_param);
235 let name_wide = self.global_leak_manager.leak_as_wide_string(name);
236 unsafe {
237 ((*self.internal).register_object_item_menu_param)(
238 name_wide,
239 true,
240 trampoline_param_ptr as *mut std::ffi::c_void,
241 trampoline::<F>,
242 );
243 }
244
245 unsafe extern "C" fn trampoline<F>(
246 param: *mut std::ffi::c_void,
247 object_handle: aviutl2_sys::plugin2::OBJECT_HANDLE,
248 effect_name_and_index: aviutl2_sys::common::LPCWSTR,
249 item_name: aviutl2_sys::common::LPCWSTR,
250 ) where
251 F: Fn(crate::generic::ObjectHandle, &str, usize, Option<&str>) + 'static + Send + Sync,
252 {
253 let callback = unsafe { &mut *(param as *mut F) };
254 let object_handle = crate::generic::ObjectHandle::from(object_handle);
255 let effect_name_and_index =
256 unsafe { crate::common::load_wide_string(effect_name_and_index) };
257 let item_name = (!item_name.is_null())
258 .then(|| unsafe { crate::common::load_wide_string(item_name) });
259
260 let (effect_name_str, index) = parse_name_and_index(&effect_name_and_index);
261 let item_name_option = item_name.as_deref();
262 if let Err(panic_info) =
263 crate::utils::catch_unwind_with_panic_info(std::panic::AssertUnwindSafe(|| {
264 callback(object_handle, effect_name_str, index, item_name_option);
265 }))
266 {
267 tracing::error!(
268 "Panic occurred in object item menu with effect callback: {}",
269 panic_info
270 );
271 let _ = crate::logger::write_error_log(&panic_info);
272 }
273 }
274 }
275
276 pub fn register_file_drop_handler<F>(
284 &mut self,
285 name: &str,
286 file_filters: &[crate::common::FileFilter],
287 callback: F,
288 ) where
289 F: Fn(std::path::PathBuf) + 'static + Send + Sync,
290 {
291 self.assert_not_killed();
292 let callback_box = Box::new(callback);
293 let callback_ptr = Box::into_raw(callback_box);
294 let name_wide = self.global_leak_manager.leak_as_wide_string(name);
295 let file_filter_wide = self
296 .global_leak_manager
297 .leak_as_wide_string(&crate::common::format_file_filters(file_filters));
298 unsafe {
299 ((*self.internal).register_file_drop_param_handler)(
300 name_wide,
301 file_filter_wide,
302 callback_ptr as *mut std::ffi::c_void,
303 file_drop_trampoline::<F>,
304 );
305 }
306 }
307
308 pub fn register_window_client<T: raw_window_handle::HasWindowHandle>(
314 &mut self,
315 name: &str,
316 instance: &T,
317 ) -> Result<(), raw_window_handle::HandleError> {
318 self.assert_not_killed();
319 let raw_handle = instance.window_handle()?;
320 let hwnd = match raw_handle.as_raw() {
321 raw_window_handle::RawWindowHandle::Win32(handle) => handle.hwnd,
322 _ => panic!("Only Win32WindowHandle is supported"),
323 };
324 unsafe {
325 ((*self.internal).register_window_client)(
326 self.global_leak_manager.leak_as_wide_string(name),
327 hwnd.get() as *mut std::ffi::c_void,
328 );
329 }
330 Ok(())
331 }
332
333 pub fn register_menus<T: GenericPluginMenus>(&mut self) {
339 self.assert_not_killed();
340 T::register_menus(self);
341 }
342
343 pub fn register_project_load_handler(
351 &mut self,
352 callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
353 ) {
354 self.assert_not_killed();
355 unsafe {
356 ((*self.internal).register_project_load_handler)(callback);
357 }
358 }
359
360 pub fn register_project_save_handler(
367 &mut self,
368 callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
369 ) {
370 self.assert_not_killed();
371 unsafe {
372 ((*self.internal).register_project_save_handler)(callback);
373 }
374 }
375
376 pub fn register_clear_cache_handler(
383 &mut self,
384 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
385 ) {
386 self.assert_not_killed();
387 unsafe {
388 ((*self.internal).register_clear_cache_handler)(callback);
389 }
390 }
391
392 pub fn register_change_scene_handler(
399 &mut self,
400 callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
401 ) {
402 self.assert_not_killed();
403 unsafe {
404 ((*self.internal).register_change_scene_handler)(callback);
405 }
406 }
407
408 fn register_menu_internal<F>(
409 &mut self,
410 name: &str,
411 callback: F,
412 register_fn: unsafe extern "C" fn(
413 aviutl2_sys::common::LPCWSTR,
414 *mut std::ffi::c_void,
415 unsafe extern "C" fn(*mut std::ffi::c_void),
416 ),
417 ) where
418 F: Fn() + 'static + Send + Sync,
419 {
420 self.assert_not_killed();
421 let trampoline_param: Box<MenuTrampolineParam<F>> = Box::new(callback);
422 let trampoline_param_ptr = Box::into_raw(trampoline_param);
423 unsafe {
424 register_fn(
425 self.global_leak_manager.leak_as_wide_string(name),
426 trampoline_param_ptr as *mut std::ffi::c_void,
427 menu_trampoline::<F>,
428 );
429 }
430 }
431}
432
433fn parse_name_and_index(name_and_index: &str) -> (&str, usize) {
434 let Some(pos) = name_and_index.rfind(':') else {
435 return (name_and_index, 0);
436 };
437 let index_str = &name_and_index[pos + 1..];
438 if let Ok(index) = index_str.parse::<usize>() {
439 let name = &name_and_index[..pos];
440 (name, index)
441 } else {
442 (name_and_index, 0)
443 }
444}
445
446type MenuTrampolineParam<F> = F;
447
448unsafe extern "C" fn menu_trampoline<F>(param: *mut std::ffi::c_void)
449where
450 F: Fn() + 'static + Send + Sync,
451{
452 let callback = unsafe { &mut *(param as *mut MenuTrampolineParam<F>) };
453 if let Err(panic_info) =
454 crate::utils::catch_unwind_with_panic_info(std::panic::AssertUnwindSafe(callback))
455 {
456 tracing::error!("Panic occurred in menu callback: {}", panic_info);
457 let _ = crate::logger::write_error_log(&panic_info);
458 }
459}
460
461unsafe extern "C" fn file_drop_trampoline<F>(
462 param: *mut std::ffi::c_void,
463 file_path: aviutl2_sys::common::LPCWSTR,
464) where
465 F: Fn(std::path::PathBuf) + 'static + Send + Sync,
466{
467 let callback = unsafe { &mut *(param as *mut F) };
468 let path_str = unsafe { crate::common::load_wide_string(file_path) };
469 let path = std::path::PathBuf::from(path_str);
470 if let Err(panic_info) =
471 crate::utils::catch_unwind_with_panic_info(std::panic::AssertUnwindSafe(|| {
472 callback(path);
473 }))
474 {
475 tracing::error!("Panic occurred in file drop callback: {}", panic_info);
476 let _ = crate::logger::write_error_log(&panic_info);
477 }
478}
479
480pub trait GenericPluginMenus {
489 fn register_menus(host: &mut crate::generic::HostAppHandle);
490}
491
492#[doc(inline)]
493pub use aviutl2_macros::generic_menus as menus;
494
495#[derive(Default)]
496pub(crate) struct PluginRegistry {
497 #[cfg(feature = "input")]
498 input_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
499 #[cfg(feature = "output")]
500 output_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
501 #[cfg(feature = "filter")]
502 filter_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
503 #[cfg(feature = "module")]
504 script_modules: Vec<std::sync::Arc<InternalReferenceHandle>>,
505}
506impl PluginRegistry {
507 pub(crate) fn new() -> Self {
508 Self::default()
509 }
510}
511
512macro_rules! impl_plugin_registry {
513 (
514 $description:literal,
515 $feature:literal,
516 $module:ident,
517 $name:ident,
518 $register_method:ident,
519 $PluginTrait:path,
520 $SingletonTrait:path,
521 $TableType:ty
522 ) => {
523 paste! {
524 impl<T> SubPlugin<T> {
525 #[cfg(feature = $feature)]
526 #[doc = concat!($description, "の新しいインスタンスを作成します。")]
527 pub fn [<new_ $name>](info: &AviUtl2Info) -> crate::AnyResult<Self>
528 where
529 T: $PluginTrait + $SingletonTrait + 'static
530 {
531 crate::$module::__bridge::initialize_plugin::<T>(info.version.into())?;
532 let internal = std::sync::Arc::new(InternalReferenceHandle {
533 uninitialize_fn: || {
534 unsafe {
535 crate::$module::__bridge::uninitialize_plugin::<T>();
536 }
537 },
538 });
539 Ok(Self {
540 plugin: std::marker::PhantomData,
541 internal,
542 })
543 }
544 }
545 #[cfg(feature = $feature)]
546 impl<'a> HostAppHandle<'a> {
547 #[doc = concat!($description, "を登録します。")]
548 pub fn [<register_ $name>]<T: $PluginTrait + $SingletonTrait + 'static>(
549 &mut self,
550 handle: &SubPlugin<T>,
551 ) {
552 self.assert_not_killed();
553 unsafe { ((*self.internal).$register_method)(crate::$module::__bridge::create_table_unwind::<T>()) };
554 self.plugin_registry
555 .[<$name s>]
556 .push(std::sync::Arc::clone(&handle.internal));
557 }
558 #[doc = concat!("unwindなしで", $description, "を登録します。")]
559 pub fn [<register_ $name _nounwind>]<T: $PluginTrait + $SingletonTrait + 'static>(
560 &mut self,
561 handle: &SubPlugin<T>,
562 ) {
563 self.assert_not_killed();
564 unsafe { ((*self.internal).$register_method)(crate::$module::__bridge::create_table::<T>()) };
565 self.plugin_registry
566 .[<$name s>]
567 .push(std::sync::Arc::clone(&handle.internal));
568 }
569 }
570 }
571 };
572}
573
574impl_plugin_registry!(
575 "入力プラグイン",
576 "input",
577 input,
578 input_plugin,
579 register_input_plugin,
580 crate::input::InputPlugin,
581 crate::input::__bridge::InputSingleton,
582 aviutl2_sys::input2::INPUT_PLUGIN_TABLE
583);
584impl_plugin_registry!(
585 "出力プラグイン",
586 "output",
587 output,
588 output_plugin,
589 register_output_plugin,
590 crate::output::OutputPlugin,
591 crate::output::__bridge::OutputSingleton,
592 aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE
593);
594impl_plugin_registry!(
595 "フィルタープラグイン",
596 "filter",
597 filter,
598 filter_plugin,
599 register_filter_plugin,
600 crate::filter::FilterPlugin,
601 crate::filter::__bridge::FilterSingleton,
602 aviutl2_sys::filter2::FILTER_PLUGIN_TABLE
603);
604#[cfg(feature = "module")]
605impl<T> SubPlugin<T> {
606 pub fn new_script_module(info: &AviUtl2Info) -> crate::AnyResult<Self>
608 where
609 T: crate::module::ScriptModule + crate::module::__bridge::ScriptModuleSingleton + 'static,
610 {
611 crate::module::__bridge::initialize_plugin::<T>(info.version.into())?;
612 let internal = std::sync::Arc::new(InternalReferenceHandle {
613 uninitialize_fn: || unsafe {
614 crate::module::__bridge::uninitialize_plugin::<T>();
615 },
616 });
617 Ok(Self {
618 plugin: std::marker::PhantomData,
619 internal,
620 })
621 }
622}
623
624#[cfg(feature = "module")]
625impl<'a> HostAppHandle<'a> {
626 pub fn register_script_module<
632 T: crate::module::ScriptModule + crate::module::__bridge::ScriptModuleSingleton + 'static,
633 >(
634 &mut self,
635 name: Option<&str>,
636 handle: &SubPlugin<T>,
637 ) {
638 self.assert_not_killed();
639 unsafe {
640 if let Some(name_str) = name {
641 ((*self.internal).register_script_module_name)(
642 crate::module::__bridge::create_table_unwind::<T>(),
643 self.global_leak_manager.leak_as_wide_string(name_str),
644 )
645 } else {
646 ((*self.internal).register_script_module)(
647 crate::module::__bridge::create_table_unwind::<T>(),
648 )
649 }
650 };
651 self.plugin_registry
652 .script_modules
653 .push(std::sync::Arc::clone(&handle.internal));
654 }
655
656 pub fn register_script_module_nounwind<
662 T: crate::module::ScriptModule + crate::module::__bridge::ScriptModuleSingleton + 'static,
663 >(
664 &mut self,
665 name: Option<&str>,
666 handle: &SubPlugin<T>,
667 ) {
668 self.assert_not_killed();
669 unsafe {
670 if let Some(name_str) = name {
671 ((*self.internal).register_script_module_name)(
672 crate::module::__bridge::create_table::<T>(),
673 self.global_leak_manager.leak_as_wide_string(name_str),
674 )
675 } else {
676 ((*self.internal).register_script_module)(
677 crate::module::__bridge::create_table::<T>(),
678 );
679 }
680 };
681 self.plugin_registry
682 .script_modules
683 .push(std::sync::Arc::clone(&handle.internal));
684 }
685}
686
687#[doc(hidden)]
688pub unsafe fn __internal_rwh_from_raw(
689 hwnd: aviutl2_sys::plugin2::HWND,
690 hinstance: aviutl2_sys::plugin2::HINSTANCE,
691) -> raw_window_handle::Win32WindowHandle {
692 let mut handle =
693 raw_window_handle::Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
694 handle.hinstance = Some(NonZeroIsize::new(hinstance as isize).unwrap());
695 handle
696}