Rust Bindings for Hexchat Plugin API
This commit is contained in:
commit
61da531d19
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
/target/
|
||||||
|
**/*.rs.bk
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "hexchat-plugin"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* Hexchat Plugin API Bindings for Rust - Internals
|
||||||
|
* Copyright (C) 2018 Soni L.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
//! Implementation details, mostly.
|
||||||
|
//!
|
||||||
|
//! This also includes the hexchat_plugin struct, from hexchat-plugin.h. Note that we use the
|
||||||
|
//! struct even on non-Windows platforms because it's a lot easier that way. Should be safe, tho.
|
||||||
|
use libc;
|
||||||
|
|
||||||
|
// apparently this is the right way to do these
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum HexchatList {
|
||||||
|
__One,
|
||||||
|
__Two,
|
||||||
|
}
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum HexchatHook {
|
||||||
|
__One,
|
||||||
|
__Two,
|
||||||
|
}
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum HexchatContext {
|
||||||
|
__One,
|
||||||
|
__Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
// not in hexchat-plugin.h
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum PluginGuiHandle {
|
||||||
|
__One,
|
||||||
|
__Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct HexchatEventAttrs {
|
||||||
|
server_time_utc: libc::time_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type HexchatPlugin = Ph;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Ph {
|
||||||
|
pub hexchat_hook_command: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char,
|
||||||
|
pri: libc::c_int,
|
||||||
|
/* CALLBACK */
|
||||||
|
callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int>,
|
||||||
|
help_text: *const libc::c_char,
|
||||||
|
userdata: *mut libc::c_void) -> *const HexchatHook>,
|
||||||
|
pub hexchat_hook_server: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char,
|
||||||
|
pri: libc::c_int,
|
||||||
|
/* CALLBACK */
|
||||||
|
callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int>,
|
||||||
|
userdata: *mut libc::c_void) -> *const HexchatHook>,
|
||||||
|
pub hexchat_hook_print: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char,
|
||||||
|
pri: libc::c_int,
|
||||||
|
/* CALLBACK */
|
||||||
|
callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, user_data: *mut libc::c_void) -> libc::c_int>,
|
||||||
|
userdata: *mut libc::c_void) -> *const HexchatHook>,
|
||||||
|
pub hexchat_hook_timer: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
timeout: libc::c_int,
|
||||||
|
/* CALLBACK */
|
||||||
|
callback: Option<unsafe extern "C" fn(user_data: *mut libc::c_void) -> libc::c_int>,
|
||||||
|
userdata: *mut libc::c_void) -> *const HexchatHook>,
|
||||||
|
pub hexchat_hook_fd: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
fd: libc::c_int,
|
||||||
|
flags: libc::c_int,
|
||||||
|
/* CALLBACK */
|
||||||
|
callback: Option<unsafe extern "C" fn(fd: libc::c_int, flags: libc::c_int, user_data: *mut libc::c_void) -> libc::c_int>,
|
||||||
|
userdata: *mut libc::c_void) -> *const HexchatHook>,
|
||||||
|
pub hexchat_unhook: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
hook: *const HexchatHook) -> *const libc::c_void>,
|
||||||
|
pub hexchat_print: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
text: *const libc::c_char)>,
|
||||||
|
pub hexchat_printf: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
format: *const libc::c_char, ...)>,
|
||||||
|
pub hexchat_command: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
command: *const libc::c_char)>,
|
||||||
|
pub hexchat_commandf: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
format: *const libc::c_char, ...)>,
|
||||||
|
pub hexchat_nickcmp: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
s1: *const libc::c_char,
|
||||||
|
s2: *const libc::c_char) -> libc::c_int>,
|
||||||
|
pub hexchat_set_context: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
ctx: *const HexchatContext) -> libc::c_int>,
|
||||||
|
pub hexchat_find_context: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
servname: *const libc::c_char,
|
||||||
|
channel: *const libc::c_char) -> *const HexchatContext>,
|
||||||
|
pub hexchat_get_context: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin) -> *const HexchatContext>,
|
||||||
|
pub hexchat_get_info: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
id: *const libc::c_char) -> *const libc::c_char>,
|
||||||
|
pub hexchat_get_prefs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char,
|
||||||
|
string: *mut *const libc::c_char,
|
||||||
|
integer: *mut libc::c_int) -> libc::c_int>,
|
||||||
|
pub hexchat_list_get: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char) -> *const HexchatList>,
|
||||||
|
pub hexchat_list_free: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
xlist: *const HexchatList)>,
|
||||||
|
pub hexchat_list_fields: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char) -> *const *const libc::c_char>,
|
||||||
|
pub hexchat_list_next: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
xlist: *const HexchatList) -> libc::c_int>,
|
||||||
|
pub hexchat_list_str: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
xlist: *const HexchatList,
|
||||||
|
name: *const libc::c_char) -> *const libc::c_char>,
|
||||||
|
pub hexchat_list_int: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
xlist: *const HexchatList,
|
||||||
|
name: *const libc::c_char) -> libc::c_int>,
|
||||||
|
pub hexchat_plugingui_add: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
filename: *const libc::c_char,
|
||||||
|
name: *const libc::c_char,
|
||||||
|
desc: *const libc::c_char,
|
||||||
|
version: *const libc::c_char,
|
||||||
|
reserved: *mut char) -> *const PluginGuiHandle>,
|
||||||
|
pub hexchat_plugingui_remove: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
handle: *const PluginGuiHandle)>,
|
||||||
|
pub hexchat_emit_print: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
event_name: *const libc::c_char, ...) -> libc::c_int>,
|
||||||
|
// this is VERY NAUGHTY.
|
||||||
|
// TODO see if hexchat's gonna provide a proper userdata field at some point.
|
||||||
|
// it appears this function isn't used anywhere by hexchat so we reuse its pointer.
|
||||||
|
// on linux, it's a dummy anyway.
|
||||||
|
// another option would've been to use one of the printf functions.
|
||||||
|
// TODO test this on platforms hexchat doesn't build on, like AVR.
|
||||||
|
pub userdata: *mut libc::c_void,
|
||||||
|
/*pub hexchat_read_fd: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
src: *const libc::c_void,
|
||||||
|
buf: *mut char,
|
||||||
|
len: *mut libc::c_int) -> libc::c_int>,*/
|
||||||
|
pub hexchat_list_time: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
xlist: *const HexchatList,
|
||||||
|
name: *const libc::c_char) -> libc::time_t>,
|
||||||
|
pub hexchat_gettext: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
msgid: *const libc::c_char) -> *const libc::c_char>,
|
||||||
|
pub hexchat_send_modes: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
targets: *mut *const libc::c_char,
|
||||||
|
ntargets: libc::c_int,
|
||||||
|
modes_per_line: libc::c_int,
|
||||||
|
sign: libc::c_char,
|
||||||
|
mode: libc::c_char)>,
|
||||||
|
pub hexchat_strip: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
string: *const libc::c_char,
|
||||||
|
len: libc::c_int,
|
||||||
|
flags: libc::c_int) -> *const libc::c_char>,
|
||||||
|
pub hexchat_free: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
ptr: *const libc::c_void)>,
|
||||||
|
pub hexchat_pluginpref_set_str: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
var: *const libc::c_char,
|
||||||
|
value: *const libc::c_char) -> libc::c_int>,
|
||||||
|
pub hexchat_pluginpref_get_str: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
var: *const libc::c_char,
|
||||||
|
dest: *mut char) -> libc::c_int>,
|
||||||
|
pub hexchat_pluginpref_set_int: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
var: *const libc::c_char,
|
||||||
|
value: libc::c_int) -> libc::c_int>,
|
||||||
|
pub hexchat_pluginpref_get_int: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
var: *const libc::c_char) -> libc::c_int>,
|
||||||
|
pub hexchat_pluginpref_delete: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
var: *const libc::c_char) -> libc::c_int>,
|
||||||
|
pub hexchat_pluginpref_list: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
dest: *mut char) -> libc::c_int>,
|
||||||
|
pub hexchat_hook_server_attrs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char,
|
||||||
|
pri: libc::c_int,
|
||||||
|
/* CALLBACK */
|
||||||
|
callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, word_eol: *const *const libc::c_char, attrs: *const HexchatEventAttrs, user_data: *mut libc::c_void) -> libc::c_int>,
|
||||||
|
userdata: *mut libc::c_void) -> *const HexchatHook>,
|
||||||
|
pub hexchat_hook_print_attrs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
name: *const libc::c_char,
|
||||||
|
pri: libc::c_int,
|
||||||
|
/* CALLBACK */
|
||||||
|
callback: Option<unsafe extern "C" fn(word: *const *const libc::c_char, attrs: *const HexchatEventAttrs, user_data: *mut libc::c_void) -> libc::c_int>,
|
||||||
|
userdata: *mut libc::c_void) -> *const HexchatHook>,
|
||||||
|
pub hexchat_emit_print_attrs: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin, attrs: *const HexchatEventAttrs,
|
||||||
|
event_name: *const libc::c_char, ...) -> libc::c_int>,
|
||||||
|
pub hexchat_event_attrs_create: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin) -> *mut HexchatEventAttrs>,
|
||||||
|
pub hexchat_event_attrs_free: Option<unsafe extern "C" fn(ph: *mut HexchatPlugin,
|
||||||
|
attrs: *const HexchatEventAttrs)>,
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Hexchat Plugin API Bindings for Rust
|
||||||
|
* Copyright (C) 2018 Soni L.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
//! <!-- TODO (placeholder) -->
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub extern crate libc;
|
||||||
|
|
||||||
|
mod internals;
|
||||||
|
|
||||||
|
use std::panic::catch_unwind;
|
||||||
|
use std::thread;
|
||||||
|
use std::ffi::{CString, CStr};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
const EMPTY_CSTRING_DATA: &[u8] = b"\0";
|
||||||
|
|
||||||
|
/// A hexchat plugin
|
||||||
|
pub trait Plugin {
|
||||||
|
fn init(&mut self, &mut PluginHandle) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A handle for a hexchat plugin
|
||||||
|
pub struct PluginHandle {
|
||||||
|
ph: *mut internals::Ph,
|
||||||
|
filename: Option<CString>,
|
||||||
|
registered: bool,
|
||||||
|
fakehandle: *const internals::PluginGuiHandle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginHandle {
|
||||||
|
pub fn register(&mut self, name: &str, desc: &str, ver: &str) {
|
||||||
|
unsafe {
|
||||||
|
let ph = &mut *self.ph;
|
||||||
|
if let Some(hexchat_plugingui_add) = ph.hexchat_plugingui_add {
|
||||||
|
let filename = self.filename.take().expect("Attempt to re-register a plugin");
|
||||||
|
let name = CString::new(name).unwrap();
|
||||||
|
let desc = CString::new(desc).unwrap();
|
||||||
|
let ver = CString::new(ver).unwrap();
|
||||||
|
self.fakehandle = hexchat_plugingui_add(self.ph, filename.as_ptr(), name.as_ptr(), desc.as_ptr(), ver.as_ptr(), ::std::ptr::null_mut());
|
||||||
|
}
|
||||||
|
self.registered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe fn hexchat_plugin_init<T>(plugin_handle: *mut libc::c_void,
|
||||||
|
plugin_name: *mut *const libc::c_char,
|
||||||
|
plugin_desc: *mut *const libc::c_char,
|
||||||
|
plugin_version: *mut *const libc::c_char,
|
||||||
|
arg: *const libc::c_char) -> libc::c_int
|
||||||
|
where T: Plugin + Default {
|
||||||
|
if plugin_handle.is_null() {
|
||||||
|
// we can't really do anything here.
|
||||||
|
eprintln!("hexchat_plugin_init called with a null plugin_handle. This is an error!");
|
||||||
|
// TODO maybe call abort.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ph = &mut *(plugin_handle as *mut internals::Ph);
|
||||||
|
// clear the "userdata" field first thing - if the deinit function gets called (wrong hexchat
|
||||||
|
// version, other issues), we don't wanna try to drop the hexchat_dummy or hexchat_read_fd
|
||||||
|
// function as if it were a Box!
|
||||||
|
ph.userdata = ::std::ptr::null_mut();
|
||||||
|
// we set these to empty strings because rust strings aren't nul-terminated. which means we
|
||||||
|
// need to *allocate* nul-terminated strings for the real values, and hexchat has no way of
|
||||||
|
// freeing these.
|
||||||
|
// BUT before we set them, read the filename off plugin_name - we'll need it!
|
||||||
|
// (TODO figure out how to make this NOT break the plugins list)
|
||||||
|
let filename = CStr::from_ptr(*plugin_name).to_owned();
|
||||||
|
let empty_cstr = CStr::from_bytes_with_nul_unchecked(EMPTY_CSTRING_DATA).as_ptr();
|
||||||
|
*plugin_name = empty_cstr;
|
||||||
|
*plugin_desc = empty_cstr;
|
||||||
|
*plugin_version = empty_cstr;
|
||||||
|
// do some version checks for safety
|
||||||
|
if let Some(hexchat_get_info) = ph.hexchat_get_info {
|
||||||
|
let ver: *const libc::c_char = hexchat_get_info(ph, CStr::from_bytes_with_nul_unchecked(b"version\0").as_ptr());
|
||||||
|
let cstr = CStr::from_ptr(ver);
|
||||||
|
if let Ok(ver) = cstr.to_str() {
|
||||||
|
let mut iter = ver.split('.');
|
||||||
|
let a = iter.next().map(i32::from_str).and_then(Result::ok);
|
||||||
|
let b = iter.next().map(i32::from_str).and_then(Result::ok);
|
||||||
|
let c = iter.next().map(i32::from_str).and_then(Result::ok);
|
||||||
|
if !match a.unwrap_or(0) {
|
||||||
|
0 | 1 => false,
|
||||||
|
2 => match b.unwrap_or(0) {
|
||||||
|
// range patterns are a bit broken
|
||||||
|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 => false,
|
||||||
|
9 => match c.unwrap_or(0) {
|
||||||
|
0 | 1 | 2 | 3 | 4 | 5 => false,
|
||||||
|
6 =>
|
||||||
|
// min acceptable version = 2.9.6
|
||||||
|
true,
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
} {
|
||||||
|
// TODO print: "error loading plugin: hexchat too old"?
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let r: thread::Result<Option<Box<_>>> = catch_unwind(|| {
|
||||||
|
let mut plug = T::default();
|
||||||
|
let mut pluginhandle = PluginHandle {
|
||||||
|
ph: plugin_handle as *mut _,
|
||||||
|
registered: false,
|
||||||
|
filename: Some(filename),
|
||||||
|
fakehandle: ::std::ptr::null(),
|
||||||
|
};
|
||||||
|
if plug.init(&mut pluginhandle) {
|
||||||
|
if pluginhandle.registered {
|
||||||
|
Some(Box::new((plug, pluginhandle.fakehandle)))
|
||||||
|
} else {
|
||||||
|
// TODO log: forgot to call register
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if r.is_ok() {
|
||||||
|
if let Ok(Some(plug)) = r {
|
||||||
|
ph.userdata = Box::into_raw(plug) as *mut libc::c_void;
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO try to log panic?
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub unsafe fn hexchat_plugin_deinit<T>(plugin_handle: *mut libc::c_void) where T: Plugin {
|
||||||
|
// plugin_handle should never be null, but just in case...
|
||||||
|
if !plugin_handle.is_null() {
|
||||||
|
let ph = &mut *(plugin_handle as *mut internals::Ph);
|
||||||
|
if !ph.userdata.is_null() {
|
||||||
|
//
|
||||||
|
let userdata = ph.userdata;
|
||||||
|
ph.userdata = ::std::ptr::null_mut();
|
||||||
|
// we use an explicit drop (instead of an implicit one) so this is less confusing/weird
|
||||||
|
// to read.
|
||||||
|
catch_unwind(|| {
|
||||||
|
let ph = &mut *(plugin_handle as *mut internals::Ph);
|
||||||
|
let (plug, fakehandle) = *Box::from_raw(userdata as *mut (T, *const internals::PluginGuiHandle));
|
||||||
|
if let Some(hexchat_plugingui_remove) = ph.hexchat_plugingui_remove {
|
||||||
|
hexchat_plugingui_remove(ph, fakehandle);
|
||||||
|
}
|
||||||
|
::std::mem::drop(plug); // suppress compiler warnings
|
||||||
|
}).ok();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("hexchat_plugin_deinit called with a null plugin_handle. This is an error!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exports a hexchat plugin.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! hexchat_plugin {
|
||||||
|
($t:ty) => {
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn hexchat_plugin_init(plugin_handle: *mut $crate::libc::c_void,
|
||||||
|
plugin_name: *mut *const $crate::libc::c_char,
|
||||||
|
plugin_desc: *mut *const $crate::libc::c_char,
|
||||||
|
plugin_version: *mut *const $crate::libc::c_char,
|
||||||
|
arg: *const $crate::libc::c_char) -> $crate::libc::c_int {
|
||||||
|
$crate::hexchat_plugin_init::<$t>(plugin_handle, plugin_name, plugin_desc, plugin_version, arg)
|
||||||
|
}
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn hexchat_plugin_deinit(plugin_handle: *mut $crate::libc::c_void) {
|
||||||
|
$crate::hexchat_plugin_deinit::<$t>(plugin_handle);
|
||||||
|
}
|
||||||
|
// unlike what the documentation states, there's no need to define hexchat_plugin_get_info.
|
||||||
|
// so we don't. it'd be impossible to make it work well with rust anyway.
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue