use libc::{c_char, c_void};
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::ptr;
use std::sync::Once;
#[link(name = "keybinder-3.0")]
extern "C" {
fn keybinder_init();
fn keybinder_bind(
keystring: *const c_char,
handler: unsafe extern "C" fn(*const c_char, *mut c_void),
user_data: *mut c_void,
) -> bool;
fn keybinder_get_current_event_time() -> u32;
fn keybinder_set_use_cooked_accelerators(use_cooked: bool);
fn keybinder_unbind_all(keystring: *const c_char);
fn keybinder_supported() -> bool;
}
static INIT: Once = Once::new();
struct Payload<T> {
user_handler: fn(String, &T),
user_data: T,
}
unsafe extern "C" fn handler_impl<T>(c_keystring: *const c_char, data: *mut c_void) {
let keystring = CStr::from_ptr(c_keystring).to_str().unwrap();
let payload = ptr::NonNull::new(data as *mut Payload<T>).unwrap().as_mut();
(payload.user_handler)(keystring.to_string(), &payload.user_data)
}
#[derive(Debug)]
pub struct KeyBinder<T> {
data_ptrs: HashMap<String, *mut c_void>,
_marker: PhantomData<T>,
}
impl<T> KeyBinder<T> {
pub fn new(use_cooked: bool) -> Result<Self, ()> {
if !unsafe { keybinder_supported() } {
return Err(());
}
INIT.call_once(|| unsafe { keybinder_init() });
unsafe {
keybinder_set_use_cooked_accelerators(use_cooked);
}
Ok(Self {
data_ptrs: HashMap::new(),
_marker: PhantomData,
})
}
pub fn bind(&mut self, keystring: &str, user_handler: fn(String, &T), user_data: T) -> bool {
self.unbind(keystring);
let c_keystring = CString::new(keystring).unwrap();
let payload_ptr = Box::leak(Box::new(Payload {
user_data,
user_handler,
})) as *const _ as *mut c_void;
self.data_ptrs.insert(keystring.to_string(), payload_ptr);
unsafe { keybinder_bind(c_keystring.as_ptr(), handler_impl::<T>, payload_ptr) }
}
pub fn unbind(&mut self, keystring: &str) {
if self.data_ptrs.contains_key(keystring) {
unsafe {
Self::unbind_impl(keystring, *self.data_ptrs.get(keystring).unwrap());
}
self.data_ptrs.remove(keystring).unwrap();
}
}
unsafe fn unbind_impl(keystring: &str, data_ptr: *mut c_void) {
let c_keystring = CString::new(keystring).unwrap();
let _ = Box::<Payload<T>>::from_raw(data_ptr as *mut Payload<T>);
keybinder_unbind_all(c_keystring.as_ptr());
}
}
pub fn get_current_event_time() -> u32 {
unsafe { keybinder_get_current_event_time() }
}
impl<T> Drop for KeyBinder<T> {
fn drop(&mut self) {
for keystring in self.data_ptrs.keys() {
unsafe {
Self::unbind_impl(keystring, *self.data_ptrs.get(keystring).unwrap());
}
}
}
}