Compare commits

...

4 Commits

Author SHA1 Message Date
SoniEx2 8f4f74845e Bump version 2019-04-21 12:10:11 -03:00
SoniEx2 b4d9f1aa07 Fix Context issue 2019-04-21 12:03:31 -03:00
SoniEx2 100119003a Add hexchat_print and hexchat_print_attrs 2019-04-21 11:54:09 -03:00
SoniEx2 7597bdc9d4 [Project] Rust hexchat-plugin
Rust crate that allows one to write hexchat plugins in Rust.
2019-04-21 11:01:41 -03:00
2 changed files with 65 additions and 36 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "hexchat-plugin"
version = "0.2.10"
version = "0.2.11"
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
description = "Lets you write HexChat plugins in Rust"
license = "AGPL-3.0+"

View File

@ -117,10 +117,10 @@
* -[ ] HEXCHAT_FD_{READ, WRITE, EXCEPTION, NOTSOCKET}
* -[x] hexchat_command (for commandf, use command(&format!("...")), it is equivalent.)
* -[x] hexchat_print (for printf, use print(&format!("...")), it is equivalent.)
* -[ ] hexchat_emit_print
* -[ ] hexchat_emit_print_attrs
* -[ ] hexchat_send_modes
* -[ ] hexchat_nickcmp
* -[x] hexchat_emit_print
* -[x] hexchat_emit_print_attrs
* -[x] hexchat_send_modes
* -[x] hexchat_nickcmp
* -[ ] hexchat_strip
* -[x] ~~hexchat_free~~ not available - use Drop impls.
* -[x] ~~hexchat_event_attrs_create~~ not available - converted as needed
@ -628,13 +628,6 @@ impl PluginHandle {
// NOTE: using a closure is nicer.
// TODO check if this is actually safe
pub fn ensure_valid_context<F, R>(&mut self, f: F) -> R where F: FnOnce(EnsureValidContext) -> R {
// let ctx = self.get_context();
// if !self.set_context(ctx) {
// // invalid context
// // TODO fix up the context
// unimplemented!()
// }
// f(EnsureValidContext { ph: self })
let ctx = self.get_context();
// need this here because we don't have NLL yet
let res = self.with_context(&ctx, f);
@ -652,23 +645,12 @@ impl PluginHandle {
/// Note: The returned context may be invalid. Use [`set_context`] to check.
///
/// [`set_context`]: #method.set_context
// This needs to be fixed by hexchat. I cannot make the value become null when it's invalid
// without invoking UB. This is because I can't set_context to null.
pub fn get_context(&mut self) -> Context {
let ctxp = unsafe { ((*self.ph).hexchat_get_context)(self.ph) };
unsafe { wrap_context(self, ctxp) }
// let ctxp = std::panic::AssertUnwindSafe(Rc::new(unsafe { ((*self.ph).hexchat_get_context)(self.ph) }));
// let weak_ctxp = Rc::downgrade(&ctxp); // calling the Closure should drop the Context (sort of)
// let closure: Rc<Cell<Option<PrintHookHandle>>> = Rc::new(Cell::new(None));
// let hook = std::panic::AssertUnwindSafe(Rc::downgrade(&closure)); // dropping the Context should drop the Closure
// self.skip_pri_ck = true;
// closure.set(Some(self.hook_print("Close Context", move |ph, _| {
// let _ = &ctxp;
// let _: Option<PrintHookHandle> = hook.upgrade().unwrap().replace(None);
// EAT_NONE
// }, libc::c_int::min_value())));
// self.skip_pri_ck = false;
// Context { ctx: weak_ctxp, closure }
// This needs to be fixed by hexchat. I cannot make the value become null when it's invalid
// without invoking UB. This is because I can't set_context to null.
let ok = unsafe { ((*self.ph).hexchat_set_context)(self.ph, ctxp) };
unsafe { wrap_context(self, if ok == 0 { ptr::null() } else { ctxp }) }
}
/// Sets the current context.
@ -1027,7 +1009,7 @@ impl PluginHandle {
}
impl<'a> EventAttrs<'a> {
fn new() -> EventAttrs<'a> {
pub fn new() -> EventAttrs<'a> {
EventAttrs {
server_time: None,
_dummy: PhantomData,
@ -1132,14 +1114,57 @@ impl<'a> EnsureValidContext<'a> {
}
}
pub fn emit_print(self) {
// TODO
unimplemented!()
pub fn emit_print<'b, I: IntoIterator<Item=&'b str>>(self, event: &str, args: I) -> bool {
let ph = self.ph.ph;
let event = CString::new(event).unwrap();
let mut args_cs: [Option<CString>; 4] = [None, None, None, None];
{
let mut iter = args.into_iter();
for i in 0..4 {
args_cs[i] = iter.next().map(|x| CString::new(x).unwrap());
if args_cs[i].is_none() {
break;
}
}
if iter.next().is_some() {
// it's better to panic so as to get bug reports when we need to increase this
panic!("too many arguments to emit_print (max 4), or iterator not fused");
}
}
let mut argv: [*const libc::c_char; 5] = [ptr::null(); 5];
for i in 0..4 {
argv[i] = args_cs[i].as_ref().map_or(ptr::null(), |s| s.as_ptr());
}
unsafe {
((*ph).hexchat_emit_print)(ph, event.as_ptr(), argv[0], argv[1], argv[2], argv[3], argv[4]) != 0
}
}
pub fn emit_print_attrs(self) {
// TODO
unimplemented!()
pub fn emit_print_attrs<'b, I: IntoIterator<Item=&'b str>>(self, attrs: EventAttrs, event: &str, args: I) -> bool {
let ph = self.ph.ph;
let event = CString::new(event).unwrap();
let mut args_cs: [Option<CString>; 4] = [None, None, None, None];
{
let mut iter = args.into_iter();
for i in 0..4 {
args_cs[i] = iter.next().map(|x| CString::new(x).unwrap());
if args_cs[i].is_none() {
break;
}
}
if let Some(_) = iter.next() {
// it's better to panic so as to get bug reports when we need to increase this
panic!("too many arguments to emit_print_attrs (max 4), or iterator not fused");
}
}
let mut argv: [*const libc::c_char; 5] = [ptr::null(); 5];
for i in 0..4 {
argv[i] = args_cs[i].as_ref().map_or(ptr::null(), |s| s.as_ptr());
}
let helper = HexchatEventAttrsHelper::new_with(ph, attrs);
unsafe {
((*ph).hexchat_emit_print_attrs)(ph, helper.0, event.as_ptr(), argv[0], argv[1], argv[2], argv[3], argv[4]) != 0
}
}
// ******** //
@ -1247,8 +1272,12 @@ unsafe fn wrap_context(ph: &mut PluginHandle, ctx: *const internals::HexchatCont
let hook = std::panic::AssertUnwindSafe(Rc::downgrade(&closure)); // dropping the Context should drop the Closure
ph.skip_pri_ck = true;
closure.set(Some(ph.hook_print("Close Context", move |ph, _| {
let _ = &ctxp;
let _: Option<PrintHookHandle> = hook.upgrade().unwrap().replace(None);
// need to be careful not to recurse or leak memory
let ph = ph.ph;
let ctx = ((*ph).hexchat_get_context)(ph);
if **ctxp == ctx {
let _: Option<PrintHookHandle> = hook.upgrade().unwrap().replace(None);
}
EAT_NONE
}, libc::c_int::min_value())));
ph.skip_pri_ck = false;