#[macro_export]
macro_rules! plugin {
($x:ty) => {
#[no_mangle]
pub unsafe extern "C" fn hexchat_plugin_init(
plugin_handle: *mut $crate::c::hexchat_plugin,
plugin_name: *mut *const ::std::os::raw::c_char,
plugin_desc: *mut *const ::std::os::raw::c_char,
plugin_version: *mut *const ::std::os::raw::c_char,
arg: *mut ::std::os::raw::c_char,
) -> ::std::os::raw::c_int {
$crate::call::hexchat_plugin_init::<$x>(
plugin_handle,
plugin_name,
plugin_desc,
plugin_version,
arg,
)
}
#[no_mangle]
pub unsafe extern "C" fn hexchat_plugin_deinit(
plugin_handle: *mut $crate::c::hexchat_plugin,
) -> ::std::os::raw::c_int {
$crate::call::hexchat_plugin_deinit::<$x>(plugin_handle)
}
};
}
use parking_lot::{MappedRwLockWriteGuard, RwLock, RwLockWriteGuard};
use std::any::Any;
use std::collections::HashSet;
use std::mem;
use std::os::raw::{c_char, c_int};
use std::panic;
use crate::{
c, to_cstring, Command, Plugin, PrintEventListener, RawServerEventListener,
ServerEventListener, TimerTask, WindowEventListener, ALLOCATED, EXITING,
};
use std::sync::atomic::Ordering;
static PLUGIN: RwLock<Option<PluginDef>> = RwLock::new(None);
static PLUGIN_INSTANCE: RwLock<Option<PluginInstance>> = RwLock::new(None);
unsafe impl Sync for PluginDef {}
unsafe impl Send for PluginDef {}
pub(crate) fn get_plugin() -> MappedRwLockWriteGuard<'static, PluginDef> {
RwLockWriteGuard::map(PLUGIN.write(), |o| o.as_mut().unwrap())
}
pub(crate) fn get_handle() -> *mut c::hexchat_plugin {
get_plugin().ph
}
struct PluginInstance(Box<dyn Any>);
unsafe impl Send for PluginInstance {}
unsafe impl Sync for PluginInstance {}
#[allow(dead_code)]
pub(crate) fn is_loaded() -> bool {
PLUGIN.read().is_some()
}
pub(crate) struct PluginDef {
pub(crate) ph: *mut c::hexchat_plugin,
pub(crate) commands: HashSet<Command>,
pub(crate) print_events: HashSet<PrintEventListener>,
pub(crate) window_events: HashSet<WindowEventListener>,
pub(crate) server_events: HashSet<RawServerEventListener>,
pub(crate) timer_tasks: HashSet<TimerTask>,
pub(crate) typed_server_events: HashSet<ServerEventListener>,
}
pub unsafe fn hexchat_plugin_init<T>(
plugin_handle: *mut c::hexchat_plugin,
plugin_name: *mut *const c_char,
plugin_desc: *mut *const c_char,
plugin_version: *mut *const c_char,
_arg: *mut c_char,
) -> c_int
where
T: Plugin + 'static,
{
{
*ALLOCATED.write() = Some(Vec::new());
let plugin_def = PluginDef {
commands: HashSet::new(),
print_events: HashSet::new(),
window_events: HashSet::new(),
server_events: HashSet::new(),
timer_tasks: HashSet::new(),
typed_server_events: HashSet::new(),
ph: plugin_handle,
};
*PLUGIN.write() = Some(plugin_def);
}
let name = to_cstring(T::NAME);
*plugin_name = name.into_raw();
let desc = to_cstring(T::DESC);
*plugin_desc = desc.into_raw();
let version = to_cstring(T::VERSION);
*plugin_version = version.into_raw();
let t = panic::catch_unwind(T::new);
let t = match t {
Ok(t) => t,
Err(e) => {
crate::send_command(&if let Some(string) = (*e).downcast_ref::<&str>() {
format!(
r#"GUI MSGBOX "Plugin '{} {}' failed to load. Panic message: {}""#,
T::NAME,
T::VERSION,
string
)
} else if let Some(string) = (*e).downcast_ref::<String>() {
format!(
r#"GUI MSGBOX "Plugin '{} {}' failed to load. Panic message: {}""#,
T::NAME,
T::VERSION,
string
)
} else {
format!(
r#"GUI MSGBOX "Plugin '{} {}' failed to load.""#,
T::NAME,
T::VERSION
)
});
return -4;
}
};
*PLUGIN_INSTANCE.write() = Some(PluginInstance(Box::new(t)));
1
}
pub unsafe fn hexchat_plugin_deinit<T>(_plugin_handle: *mut c::hexchat_plugin) -> c_int
where
T: Plugin,
{
let mut plugin = None;
let mut instance = None;
let mut write = PLUGIN.write();
let mut plugin_write = PLUGIN_INSTANCE.write();
mem::swap(&mut plugin, &mut *write);
mem::swap(&mut instance, &mut *plugin_write);
let plugin = match plugin {
Some(p) => p,
None => return 1,
};
let PluginDef {
server_events,
window_events,
print_events,
commands,
timer_tasks,
typed_server_events,
..
} = plugin;
let instance = match instance {
Some(i) => i,
None => return -2,
};
mem::drop(instance);
EXITING.store(false, Ordering::SeqCst);
for event in server_events {
crate::dealloc_raw_server_event_listener(event.0);
}
for event in window_events {
crate::dealloc_window_event_listener(event.0);
}
for event in print_events {
crate::dealloc_print_event_listener(event.0);
}
for command in commands {
crate::dealloc_command(command.0);
}
for timer_task in timer_tasks {
crate::dealloc_timer_task(timer_task.0);
}
for event in typed_server_events {
crate::dealloc_server_event_listener(event.0);
}
let mut vec = None;
let mut lock = ALLOCATED.write();
mem::swap(&mut vec, &mut *lock);
if let Some(vec) = vec {
for func in vec {
let boxed = func.0;
boxed();
}
} else {
return -5;
}
1
}