use libc::c_int;
use std::{os::raw::c_void, ptr, time::Duration};
use weechat_sys::{t_weechat_plugin, WEECHAT_RC_OK};
use super::Hook;
use crate::Weechat;
pub struct TimerHook {
_hook: Hook,
_hook_data: Box<TimerHookData>,
}
pub enum RemainingCalls {
Infinite,
Finite(i32),
}
impl From<i32> for RemainingCalls {
fn from(remaining: i32) -> Self {
match remaining {
-1 => RemainingCalls::Infinite,
r => RemainingCalls::Finite(r),
}
}
}
pub trait TimerCallback {
fn callback(&mut self, weechat: &Weechat, remaining_calls: RemainingCalls);
}
impl<T: FnMut(&Weechat, RemainingCalls) + 'static> TimerCallback for T {
fn callback(&mut self, weechat: &Weechat, remaining_calls: RemainingCalls) {
self(weechat, remaining_calls)
}
}
struct TimerHookData {
callback: Box<dyn TimerCallback>,
weechat_ptr: *mut t_weechat_plugin,
}
impl TimerHook {
pub fn new(
interval: Duration,
align_second: i32,
max_calls: i32,
callback: impl TimerCallback + 'static,
) -> Result<TimerHook, ()> {
unsafe extern "C" fn c_hook_cb(
pointer: *const c_void,
_data: *mut c_void,
remaining: i32,
) -> c_int {
let hook_data: &mut TimerHookData = { &mut *(pointer as *mut TimerHookData) };
let cb = &mut hook_data.callback;
cb.callback(
&Weechat::from_ptr(hook_data.weechat_ptr),
RemainingCalls::from(remaining),
);
WEECHAT_RC_OK
}
Weechat::check_thread();
let weechat = unsafe { Weechat::weechat() };
let data = Box::new(TimerHookData {
callback: Box::new(callback),
weechat_ptr: weechat.ptr,
});
let data_ref = Box::leak(data);
let hook_timer = weechat.get().hook_timer.unwrap();
let hook_ptr = unsafe {
hook_timer(
weechat.ptr,
interval.as_millis() as i64,
align_second,
max_calls,
Some(c_hook_cb),
data_ref as *const _ as *const c_void,
ptr::null_mut(),
)
};
let hook_data = unsafe { Box::from_raw(data_ref) };
if hook_ptr.is_null() {
Err(())
} else {
Ok(TimerHook {
_hook: Hook {
ptr: hook_ptr,
weechat_ptr: weechat.ptr,
},
_hook_data: hook_data,
})
}
}
}