use crate::AviUtl2Info;
use pastey::paste;
use std::num::NonZeroIsize;
pub struct HostAppHandle<'a> {
internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
global_leak_manager: &'a mut crate::common::LeakManager,
kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
plugin_registry: &'a mut crate::generic::PluginRegistry,
is_registerplugin_done: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
pub struct SubPlugin<T> {
plugin: std::marker::PhantomData<T>,
internal: std::sync::Arc<InternalReferenceHandle>,
}
struct InternalReferenceHandle {
uninitialize_fn: fn(),
}
impl Drop for InternalReferenceHandle {
fn drop(&mut self) {
(self.uninitialize_fn)();
}
}
impl<'plugin> HostAppHandle<'plugin> {
pub(crate) unsafe fn new(
internal: *mut aviutl2_sys::plugin2::HOST_APP_TABLE,
global_leak_manager: &'plugin mut crate::common::LeakManager,
kill_switch: std::sync::Arc<std::sync::atomic::AtomicBool>,
plugin_registry: &'plugin mut crate::generic::PluginRegistry,
is_registerplugin_done: std::sync::Arc<std::sync::atomic::AtomicBool>,
) -> Self {
Self {
internal,
global_leak_manager,
kill_switch,
plugin_registry,
is_registerplugin_done,
}
}
fn assert_not_killed(&self) {
if self.kill_switch.load(std::sync::atomic::Ordering::SeqCst) {
panic!("This HostAppHandle is no longer valid.");
}
}
pub fn create_edit_handle(&mut self) -> crate::generic::EditHandle {
self.assert_not_killed();
let raw_handle = unsafe { ((*self.internal).create_edit_handle)() };
unsafe { crate::generic::EditHandle::new(raw_handle, self.is_registerplugin_done.clone()) }
}
pub fn register_import_menu<F>(&mut self, name: &str, callback: F)
where
F: Fn() + 'static + Send + Sync,
{
self.register_menu_internal(name, callback, unsafe {
(*self.internal).register_import_menu_param
});
}
pub fn register_export_menu<F>(&mut self, name: &str, callback: F)
where
F: Fn() + 'static + Send + Sync,
{
self.register_menu_internal(name, callback, unsafe {
(*self.internal).register_export_menu_param
});
}
pub fn register_layer_menu<F>(&mut self, name: &str, callback: F)
where
F: Fn() + 'static + Send + Sync,
{
self.register_menu_internal(name, callback, unsafe {
(*self.internal).register_layer_menu_param
});
}
pub fn register_object_menu<F>(&mut self, name: &str, callback: F)
where
F: Fn() + 'static + Send + Sync,
{
self.register_menu_internal(name, callback, unsafe {
(*self.internal).register_object_menu_param
});
}
pub fn register_edit_menu<F>(&mut self, name: &str, callback: F)
where
F: Fn() + 'static + Send + Sync,
{
self.register_menu_internal(name, callback, unsafe {
(*self.internal).register_edit_menu_param
});
}
pub fn register_config_menu(
&mut self,
name: &str,
callback: extern "C" fn(aviutl2_sys::plugin2::HWND, aviutl2_sys::plugin2::HINSTANCE),
) {
self.assert_not_killed();
unsafe {
((*self.internal).register_config_menu)(
self.global_leak_manager.leak_as_wide_string(name),
callback,
)
};
}
pub fn register_file_drop_handler<F>(
&mut self,
name: &str,
file_filters: &[crate::common::FileFilter],
callback: F,
) where
F: Fn(std::path::PathBuf) + 'static + Send + Sync,
{
self.assert_not_killed();
let callback_box = Box::new(callback);
let callback_ptr = Box::into_raw(callback_box);
let name_wide = self.global_leak_manager.leak_as_wide_string(name);
let file_filter_wide = self
.global_leak_manager
.leak_as_wide_string(&crate::common::format_file_filters(file_filters));
unsafe {
((*self.internal).register_file_drop_param_handler)(
name_wide,
file_filter_wide,
callback_ptr as *mut std::ffi::c_void,
file_drop_trampoline::<F>,
);
}
}
pub fn register_window_client<T: raw_window_handle::HasWindowHandle>(
&mut self,
name: &str,
instance: &T,
) -> Result<(), raw_window_handle::HandleError> {
self.assert_not_killed();
let raw_handle = instance.window_handle()?;
let hwnd = match raw_handle.as_raw() {
raw_window_handle::RawWindowHandle::Win32(handle) => handle.hwnd,
_ => panic!("Only Win32WindowHandle is supported"),
};
unsafe {
((*self.internal).register_window_client)(
self.global_leak_manager.leak_as_wide_string(name),
hwnd.get() as *mut std::ffi::c_void,
);
}
Ok(())
}
pub fn register_menus<T: GenericPluginMenus>(&mut self) {
self.assert_not_killed();
T::register_menus(self);
}
pub fn register_project_load_handler(
&mut self,
callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
) {
self.assert_not_killed();
unsafe {
((*self.internal).register_project_load_handler)(callback);
}
}
pub fn register_project_save_handler(
&mut self,
callback: extern "C" fn(*mut aviutl2_sys::plugin2::PROJECT_FILE),
) {
self.assert_not_killed();
unsafe {
((*self.internal).register_project_save_handler)(callback);
}
}
pub fn register_clear_cache_handler(
&mut self,
callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
) {
self.assert_not_killed();
unsafe {
((*self.internal).register_clear_cache_handler)(callback);
}
}
pub fn register_change_scene_handler(
&mut self,
callback: extern "C" fn(*mut aviutl2_sys::plugin2::EDIT_SECTION),
) {
self.assert_not_killed();
unsafe {
((*self.internal).register_change_scene_handler)(callback);
}
}
fn register_menu_internal<F>(
&mut self,
name: &str,
callback: F,
register_fn: unsafe extern "C" fn(
aviutl2_sys::common::LPCWSTR,
*mut std::ffi::c_void,
unsafe extern "C" fn(*mut std::ffi::c_void),
),
) where
F: Fn() + 'static + Send + Sync,
{
self.assert_not_killed();
let trampoline_param: Box<MenuTrampolineParam<F>> = Box::new(callback);
let trampoline_param_ptr = Box::into_raw(trampoline_param);
unsafe {
register_fn(
self.global_leak_manager.leak_as_wide_string(name),
trampoline_param_ptr as *mut std::ffi::c_void,
menu_trampoline::<F>,
);
}
}
}
type MenuTrampolineParam<F> = F;
unsafe extern "C" fn menu_trampoline<F>(param: *mut std::ffi::c_void)
where
F: Fn() + 'static + Send + Sync,
{
let callback = unsafe { &mut *(param as *mut MenuTrampolineParam<F>) };
if let Err(panic_info) =
crate::utils::catch_unwind_with_panic_info(std::panic::AssertUnwindSafe(callback))
{
tracing::error!("Panic occurred in menu callback: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
}
}
unsafe extern "C" fn file_drop_trampoline<F>(
param: *mut std::ffi::c_void,
file_path: aviutl2_sys::common::LPCWSTR,
) where
F: Fn(std::path::PathBuf) + 'static + Send + Sync,
{
let callback = unsafe { &mut *(param as *mut F) };
let path_str = unsafe { crate::common::load_wide_string(file_path) };
let path = std::path::PathBuf::from(path_str);
if let Err(panic_info) =
crate::utils::catch_unwind_with_panic_info(std::panic::AssertUnwindSafe(|| {
callback(path);
}))
{
tracing::error!("Panic occurred in file drop callback: {}", panic_info);
let _ = crate::logger::write_error_log(&panic_info);
}
}
pub trait GenericPluginMenus {
fn register_menus(host: &mut crate::generic::HostAppHandle);
}
#[doc(inline)]
pub use aviutl2_macros::generic_menus as menus;
#[derive(Default)]
pub(crate) struct PluginRegistry {
#[cfg(feature = "input")]
input_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
#[cfg(feature = "output")]
output_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
#[cfg(feature = "filter")]
filter_plugins: Vec<std::sync::Arc<InternalReferenceHandle>>,
#[cfg(feature = "module")]
script_modules: Vec<std::sync::Arc<InternalReferenceHandle>>,
}
impl PluginRegistry {
pub(crate) fn new() -> Self {
Self::default()
}
}
macro_rules! impl_plugin_registry {
(
$description:literal,
$feature:literal,
$module:ident,
$name:ident,
$register_method:ident,
$PluginTrait:path,
$SingletonTrait:path,
$TableType:ty
) => {
paste! {
impl<T> SubPlugin<T> {
#[cfg(feature = $feature)]
#[doc = concat!($description, "の新しいインスタンスを作成します。")]
pub fn [<new_ $name>](info: &AviUtl2Info) -> crate::AnyResult<Self>
where
T: $PluginTrait + $SingletonTrait + 'static
{
crate::$module::__bridge::initialize_plugin::<T>(info.version.into())?;
let internal = std::sync::Arc::new(InternalReferenceHandle {
uninitialize_fn: || {
unsafe {
crate::$module::__bridge::uninitialize_plugin::<T>();
}
},
});
Ok(Self {
plugin: std::marker::PhantomData,
internal,
})
}
}
#[cfg(feature = $feature)]
impl<'a> HostAppHandle<'a> {
#[doc = concat!($description, "を登録します。")]
pub fn [<register_ $name>]<T: $PluginTrait + $SingletonTrait + 'static>(
&mut self,
handle: &SubPlugin<T>,
) {
self.assert_not_killed();
unsafe { ((*self.internal).$register_method)(crate::$module::__bridge::create_table_unwind::<T>()) };
self.plugin_registry
.[<$name s>]
.push(std::sync::Arc::clone(&handle.internal));
}
#[doc = concat!("unwindなしで", $description, "を登録します。")]
pub fn [<register_ $name _nounwind>]<T: $PluginTrait + $SingletonTrait + 'static>(
&mut self,
handle: &SubPlugin<T>,
) {
self.assert_not_killed();
unsafe { ((*self.internal).$register_method)(crate::$module::__bridge::create_table::<T>()) };
self.plugin_registry
.[<$name s>]
.push(std::sync::Arc::clone(&handle.internal));
}
}
}
};
}
impl_plugin_registry!(
"入力プラグイン",
"input",
input,
input_plugin,
register_input_plugin,
crate::input::InputPlugin,
crate::input::__bridge::InputSingleton,
aviutl2_sys::input2::INPUT_PLUGIN_TABLE
);
impl_plugin_registry!(
"出力プラグイン",
"output",
output,
output_plugin,
register_output_plugin,
crate::output::OutputPlugin,
crate::output::__bridge::OutputSingleton,
aviutl2_sys::output2::OUTPUT_PLUGIN_TABLE
);
impl_plugin_registry!(
"フィルタープラグイン",
"filter",
filter,
filter_plugin,
register_filter_plugin,
crate::filter::FilterPlugin,
crate::filter::__bridge::FilterSingleton,
aviutl2_sys::filter2::FILTER_PLUGIN_TABLE
);
impl_plugin_registry!(
"スクリプトモジュール",
"module",
module,
script_module,
register_script_module,
crate::module::ScriptModule,
crate::module::__bridge::ScriptModuleSingleton,
aviutl2_sys::module2::SCRIPT_MODULE_TABLE
);
#[doc(hidden)]
pub unsafe fn __internal_rwh_from_raw(
hwnd: aviutl2_sys::plugin2::HWND,
hinstance: aviutl2_sys::plugin2::HINSTANCE,
) -> raw_window_handle::Win32WindowHandle {
let mut handle =
raw_window_handle::Win32WindowHandle::new(NonZeroIsize::new(hwnd as isize).unwrap());
handle.hinstance = Some(NonZeroIsize::new(hinstance as isize).unwrap());
handle
}