extern crate x11;
extern crate fnv;
use std::sync::RwLock;
use std::thread;
use fnv::FnvHashMap;
use x11::xlib;
mod raw;
use raw::next_event;
pub use x11::keysym;
pub fn start(mut storage: CallbackStorage) {
let mut event = xlib::XEvent { pad: [0; 24] };
loop {
unsafe {
next_event(storage.get_display_mut(), &mut event);
}
storage.dispatch(&mut event);
}
}
pub fn start_async(storage: CallbackStorage) -> thread::JoinHandle<()> {
thread::spawn(move || start(storage))
}
#[repr(u32)]
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
pub enum Modifier {
Shift = xlib::ShiftMask,
CapsLock = xlib::LockMask,
Ctrl = xlib::ControlMask,
Alt = xlib::Mod1Mask,
NumLock = xlib::Mod2Mask,
ScrollLock = xlib::Mod3Mask,
Window = xlib::Mod4Mask,
Mod5 = xlib::Mod5Mask,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum TriggerOn {
Press,
Release,
}
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
pub struct HotKey {
pub key: xlib::KeySym,
pub modifiers: Vec<Modifier>,
pub trigger: TriggerOn,
}
impl HotKey {
pub fn new(key: u32, mut modifiers: Vec<Modifier>, trigger: TriggerOn) -> Self {
modifiers.sort();
modifiers.dedup();
HotKey {
key: key as xlib::KeySym,
modifiers: modifiers,
trigger: trigger,
}
}
}
pub type Callback = fn() -> ();
type KeyMapStorage = RwLock<FnvHashMap<xlib::KeySym, Vec<Callback>>>;
#[derive(Debug)]
pub struct CallbackStorage {
released_storage: KeyMapStorage,
pressed_storage: KeyMapStorage,
display: *mut xlib::Display,
root: xlib::Window,
}
impl Default for CallbackStorage {
fn default() -> Self {
Self::new()
}
}
impl CallbackStorage {
pub fn new() -> Self {
unsafe {
let display = raw::get_display();
let root = raw::get_root(display);
CallbackStorage {
released_storage: RwLock::new(FnvHashMap::default()),
pressed_storage: RwLock::new(FnvHashMap::default()),
display: display,
root: root,
}
}
}
pub fn add(&mut self, key: &HotKey, callback: Callback) {
use std::collections::hash_map::Entry;
let mut storage = if key.trigger == TriggerOn::Press {
self.pressed_storage.write().unwrap()
} else {
self.released_storage.write().unwrap()
};
let entry = storage.entry(key.key);
if let Entry::Vacant(_) = entry {
unsafe {
raw::grab(self.display, self.root, key);
}
}
entry.or_insert_with(Vec::new).push(callback);
}
pub fn remove_all(&mut self, key: &HotKey) {
let mut storage = if key.trigger == TriggerOn::Press {
self.pressed_storage.write().unwrap()
} else {
self.released_storage.write().unwrap()
};
if storage.remove(&key.key).is_some() {
raw::ugrab(self.display, self.root, key);
}
}
#[inline]
fn dispatch(&self, event: &mut xlib::XEvent) {
let event_type = event.get_type();
if event_type == xlib::KeyPress {
self.trigger_press(event);
} else if event_type == xlib::KeyRelease {
self.trigger_release(event);
}
}
#[inline]
fn trigger_press(&self, event: &mut xlib::XEvent) {
let keysym = raw::get_keysym(event.as_mut());
if let Some(callbacks) = self.pressed_storage.read().unwrap().get(&keysym) {
for callback in callbacks {
let callback = callback.to_owned();
thread::spawn(move || { callback(); });
}
}
}
#[inline]
fn trigger_release(&self, event: &mut xlib::XEvent) {
let keysym = raw::get_keysym(event.as_mut());
if let Some(callbacks) = self.released_storage.read().unwrap().get(&keysym) {
for callback in callbacks {
let callback = callback.to_owned();
thread::spawn(move || { callback(); });
}
}
}
unsafe fn get_display_mut(&mut self) -> *mut xlib::Display {
self.display
}
}
unsafe impl Send for CallbackStorage {}