use std::num::NonZeroIsize;
use crate::common::{ChildKillablePointer, KillablePointer};
use crate::generic::EditSection;
#[derive(Debug)]
pub struct EditHandle {
pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE,
pub(crate) is_ready: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
unsafe impl Send for EditHandle {}
unsafe impl Sync for EditHandle {}
#[derive(thiserror::Error, Debug)]
pub enum EditHandleError {
#[error("api call failed")]
ApiCallFailed,
}
impl EditHandle {
pub(crate) unsafe fn new(
internal: *mut aviutl2_sys::plugin2::EDIT_HANDLE,
is_ready: std::sync::Arc<std::sync::atomic::AtomicBool>,
) -> Self {
Self { internal, is_ready }
}
pub fn is_ready(&self) -> bool {
self.is_ready.load(std::sync::atomic::Ordering::Acquire)
}
pub fn call_edit_section<'a, T, F>(&self, callback: F) -> Result<T, EditHandleError>
where
T: Send + 'static,
F: FnOnce(&mut EditSection) -> T + Send + 'a,
{
assert!(
self.is_ready(),
"call_edit_section cannot be called before register_plugin is done"
);
type CallbackParam<'a, F, T> = (ChildKillablePointer<Option<F>>, &'a mut Option<T>);
let closure = Some(callback);
let param = KillablePointer::new(closure);
let child_param = param.create_child();
extern "C" fn trampoline<F, T>(
param: *mut std::ffi::c_void,
edit_section: *mut aviutl2_sys::plugin2::EDIT_SECTION,
) where
T: Send + 'static,
F: FnOnce(&mut EditSection) -> T,
{
unsafe {
let (child_param, result_ptr) = &mut *(param as *mut CallbackParam<F, T>);
let callback = child_param.as_mut().take().expect("Callback already taken");
let mut edit_section = EditSection::from_raw(edit_section);
let res = callback(&mut edit_section);
result_ptr.replace(res);
}
}
let trampoline_static = trampoline::<F, T>
as extern "C" fn(*mut std::ffi::c_void, *mut aviutl2_sys::plugin2::EDIT_SECTION);
let mut result = None;
let param = Box::<CallbackParam<F, T>>::new((child_param, &mut result));
let param_ptr = Box::into_raw(param);
let success = unsafe {
((*self.internal).call_edit_section_param)(
param_ptr as *mut std::ffi::c_void,
trampoline_static,
)
};
drop(unsafe { Box::from_raw(param_ptr) });
if success {
Ok(result.expect("Callback did not set result"))
} else {
Err(EditHandleError::ApiCallFailed)
}
}
pub fn get_edit_info(&self) -> crate::generic::EditInfo {
assert!(
self.is_ready(),
"call_edit_section cannot be called before register_plugin is done"
);
let mut raw_info = std::mem::MaybeUninit::<aviutl2_sys::plugin2::EDIT_INFO>::uninit();
unsafe {
((*self.internal).get_edit_info)(
raw_info.as_mut_ptr(),
std::mem::size_of::<aviutl2_sys::plugin2::EDIT_INFO>() as _,
);
let edit_info = raw_info.assume_init();
crate::generic::EditInfo::from_raw(&edit_info)
}
}
pub fn restart_host_app(&self) {
assert!(
self.is_ready(),
"call_edit_section cannot be called before register_plugin is done"
);
unsafe {
((*self.internal).restart_host_app)();
}
}
pub fn enumerate_effects<F>(&self, callback: F)
where
F: FnMut(Effect),
{
assert!(
self.is_ready(),
"call_edit_section cannot be called before register_plugin is done"
);
type CallbackParam<F> = ChildKillablePointer<F>;
extern "C" fn trampoline<F>(
param: *mut std::ffi::c_void,
name: aviutl2_sys::common::LPCWSTR,
r#type: i32,
flag: i32,
) where
F: FnMut(Effect),
{
let callback = unsafe { &mut *(param as *mut CallbackParam<F>) };
let callback = unsafe { callback.as_mut() };
let name_str = unsafe { crate::common::load_wide_string(name) };
let effect = Effect {
name: name_str,
effect_type: EffectType::from(r#type),
flag: EffectFlag::from_bits(flag),
};
callback(effect);
}
let trampoline_static = trampoline::<F>
as extern "C" fn(*mut std::ffi::c_void, aviutl2_sys::common::LPCWSTR, i32, i32);
let callback_guard = KillablePointer::new(callback);
let child_param = callback_guard.create_child();
let param = Box::new(child_param);
let param_ptr = Box::into_raw(param);
unsafe {
((*self.internal).enum_effect_name)(
param_ptr as *mut std::ffi::c_void,
trampoline_static,
);
}
drop(unsafe { Box::from_raw(param_ptr) });
}
pub fn get_effects(&self) -> Vec<Effect> {
assert!(
self.is_ready(),
"call_edit_section cannot be called before register_plugin is done"
);
let mut effects = Vec::new();
self.enumerate_effects(|effect| {
effects.push(effect);
});
effects
}
pub fn enumerate_modules<F>(&self, callback: F)
where
F: FnMut(ModuleInfo),
{
assert!(
self.is_ready(),
"call_edit_section cannot be called before register_plugin is done"
);
type CallbackParam<F> = ChildKillablePointer<F>;
extern "C" fn trampoline<F>(
param: *mut std::ffi::c_void,
module: *mut aviutl2_sys::plugin2::MODULE_INFO,
) where
F: FnMut(ModuleInfo),
{
let callback = unsafe { &mut *(param as *mut CallbackParam<F>) };
let callback = unsafe { callback.as_mut() };
let module_info = ModuleInfo {
module_type: ModuleType::from(unsafe { (*module).r#type }),
name: unsafe { crate::common::load_wide_string((*module).name) },
information: unsafe { crate::common::load_wide_string((*module).information) },
};
callback(module_info);
}
let trampoline_static = trampoline::<F>
as unsafe extern "C" fn(*mut std::ffi::c_void, *mut aviutl2_sys::plugin2::MODULE_INFO);
let callback_guard = KillablePointer::new(callback);
let child_param = callback_guard.create_child();
let param = Box::new(child_param);
let param_ptr = Box::into_raw(param);
unsafe {
((*self.internal).enum_module_info)(
param_ptr as *mut std::ffi::c_void,
trampoline_static,
);
}
drop(unsafe { Box::from_raw(param_ptr) });
}
pub fn get_modules(&self) -> Vec<ModuleInfo> {
assert!(
self.is_ready(),
"call_edit_section cannot be called before register_plugin is done"
);
let mut modules = Vec::new();
self.enumerate_modules(|module| {
modules.push(module);
});
modules
}
pub fn get_host_app_window_raw(&self) -> Option<raw_window_handle::Win32WindowHandle> {
let hwnd = unsafe { ((*self.internal).get_host_app_window)() };
NonZeroIsize::new(hwnd as isize).map(raw_window_handle::Win32WindowHandle::new)
}
pub unsafe fn get_host_app_window(&'_ self) -> Option<raw_window_handle::WindowHandle<'_>> {
self.get_host_app_window_raw().map(|handle| unsafe {
raw_window_handle::WindowHandle::borrow_raw(raw_window_handle::RawWindowHandle::Win32(
handle,
))
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Effect {
pub name: String,
pub effect_type: EffectType,
pub flag: EffectFlag,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EffectType {
Filter,
Input,
SceneChange,
Other(i32),
}
define_bitflag! {
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct EffectFlag: i32 {
video: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_VIDEO,
audio: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_AUDIO,
as_filter: aviutl2_sys::plugin2::EDIT_HANDLE::EFFECT_FLAG_FILTER,
}
}
impl From<i32> for EffectType {
fn from(value: i32) -> Self {
match value {
1 => EffectType::Filter,
2 => EffectType::Input,
3 => EffectType::SceneChange,
other => EffectType::Other(other),
}
}
}
impl From<EffectType> for i32 {
fn from(value: EffectType) -> Self {
match value {
EffectType::Filter => 1,
EffectType::Input => 2,
EffectType::SceneChange => 3,
EffectType::Other(other) => other,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModuleInfo {
pub module_type: ModuleType,
pub name: String,
pub information: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModuleType {
ScriptFilter,
ScriptObject,
ScriptCamera,
ScriptTrack,
ScriptModule,
PluginInput,
PluginOutput,
PluginFilter,
PluginGeneric,
Other(i32),
}
impl From<i32> for ModuleType {
fn from(value: i32) -> Self {
match value {
1 => ModuleType::ScriptFilter,
2 => ModuleType::ScriptObject,
3 => ModuleType::ScriptCamera,
4 => ModuleType::ScriptTrack,
5 => ModuleType::ScriptModule,
6 => ModuleType::PluginInput,
7 => ModuleType::PluginOutput,
8 => ModuleType::PluginFilter,
9 => ModuleType::PluginGeneric,
other => ModuleType::Other(other),
}
}
}
impl From<ModuleType> for i32 {
fn from(value: ModuleType) -> Self {
match value {
ModuleType::ScriptFilter => 1,
ModuleType::ScriptObject => 2,
ModuleType::ScriptCamera => 3,
ModuleType::ScriptTrack => 4,
ModuleType::ScriptModule => 5,
ModuleType::PluginInput => 6,
ModuleType::PluginOutput => 7,
ModuleType::PluginFilter => 8,
ModuleType::PluginGeneric => 9,
ModuleType::Other(other) => other,
}
}
}
#[derive(Debug)]
pub struct GlobalEditHandle {
edit_handle: std::sync::OnceLock<crate::generic::EditHandle>,
}
impl GlobalEditHandle {
pub const fn new() -> Self {
Self {
edit_handle: std::sync::OnceLock::new(),
}
}
pub fn init(&self, edit_handle: crate::generic::EditHandle) {
let _ = self
.edit_handle
.set(edit_handle)
.map_err(|_| tracing::warn!("GlobalEditHandle was already initialized"));
}
pub fn is_ready(&self) -> bool {
self.edit_handle
.get()
.is_some_and(|handle| handle.is_ready())
}
}
impl Default for GlobalEditHandle {
fn default() -> Self {
Self::new()
}
}
impl std::ops::Deref for GlobalEditHandle {
type Target = crate::generic::EditHandle;
fn deref(&self) -> &Self::Target {
self.edit_handle
.get()
.expect("GlobalEditHandle is not initialized")
}
}