use libc::{c_double, c_int, c_uint, c_char};
use std::slice;
use std::ffi::CStr;
use std::str;
use std::mem;
use std::sync::mpsc::Sender;
use std::path::PathBuf;
use super::*;
macro_rules! callback (
(
type Args = ($($arg:ident: $arg_ty:ty),*);
type Callback = $Callback:ident;
let ext_set = $ext_set:expr;
fn callback($($ext_arg:ident: $ext_arg_ty:ty),*) $call:expr
) => (
thread_local!(static CALLBACK_KEY: RefCell<Option<Box<Object<Args> + 'static>>> = RefCell::new(None));
type Args = ($($arg_ty),*,);
trait Object<T> {
fn call(&self, args: T);
}
impl<UserData> Object<Args> for ::Callback<fn($($arg_ty),*, &UserData), UserData> {
fn call(&self, ($($arg),*,): Args) {
(self.f)($($arg),*, &self.data);
}
}
pub fn set<UserData: 'static>(f: ::$Callback<UserData>) {
let mut boxed_cb = Some(Box::new(f) as Box<Object<Args> + 'static>);
CALLBACK_KEY.with(|cb| {
*cb.borrow_mut() = boxed_cb.take();
});
($ext_set)(Some(callback as extern "C" fn($($ext_arg: $ext_arg_ty),*)));
}
pub fn unset() {
CALLBACK_KEY.with(|cb| {
*cb.borrow_mut() = None;
});
($ext_set)(None);
}
extern "C" fn callback($($ext_arg: $ext_arg_ty),*) {
CALLBACK_KEY.with(|cb| {
match *cb.borrow() {
Some(ref cb) => unsafe { cb.call($call) },
_ => {}
}
})
}
)
);
pub mod error {
use libc::{c_int, c_char};
use std::cell::RefCell;
use std::mem;
callback!(
type Args = (error: ::Error, description: String);
type Callback = ErrorCallback;
let ext_set = |cb| unsafe { ::ffi::glfwSetErrorCallback(cb) };
fn callback(error: c_int, description: *const c_char) {
(mem::transmute(error), ::string_from_c_str(description))
}
);
}
pub mod monitor {
use libc::{c_int};
use std::cell::RefCell;
use std::mem;
callback!(
type Args = (monitor: ::Monitor, event: ::MonitorEvent);
type Callback = MonitorCallback;
let ext_set = |cb| unsafe { ::ffi::glfwSetMonitorCallback(cb) };
fn callback(monitor: *mut ::ffi::GLFWmonitor, event: c_int) {
let monitor = ::Monitor {
ptr: monitor
};
(monitor, mem::transmute(event))
}
);
}
pub mod joystick {
use libc::{c_int};
use std::cell::RefCell;
use std::mem;
callback!(
type Args = (joystick_id: ::JoystickId, event: ::JoystickEvent);
type Callback = JoystickCallback;
let ext_set = |cb| unsafe { ::ffi::glfwSetJoystickCallback(cb) };
fn callback(joystick_id: c_int, event: c_int) {
(mem::transmute(joystick_id), mem::transmute(event))
}
);
}
unsafe fn get_sender<'a>(window: &'a *mut ffi::GLFWwindow) -> &'a Sender<(f64, WindowEvent)> {
mem::transmute(ffi::glfwGetWindowUserPointer(*window))
}
macro_rules! window_callback (
(fn $name:ident () => $event:ident) => (
pub static $name: (extern "C" fn(window: *mut ffi::GLFWwindow)) = {
extern "C" fn actual_callback(window: *mut ffi::GLFWwindow) {
unsafe { get_sender(&window).send((ffi::glfwGetTime() as f64, WindowEvent::$event)).unwrap();}
}
actual_callback
};
);
(fn $name:ident ($($ext_arg:ident: $ext_arg_ty:ty),*) => $event:ident($($arg_conv:expr),*)) => (
pub static $name: (extern "C" fn(window: *mut ffi::GLFWwindow $(, $ext_arg: $ext_arg_ty)*)) = {
extern "C" fn actual_callback(window: *mut ffi::GLFWwindow $(, $ext_arg: $ext_arg_ty)*) {
unsafe { get_sender(&window).send((ffi::glfwGetTime() as f64, WindowEvent::$event($($arg_conv),*))).unwrap(); }
}
actual_callback
};
);
);
window_callback!(fn window_pos_callback(xpos: c_int, ypos: c_int) => Pos(xpos as i32, ypos as i32));
window_callback!(fn window_size_callback(width: c_int, height: c_int) => Size(width as i32, height as i32));
window_callback!(fn window_close_callback() => Close);
window_callback!(fn window_refresh_callback() => Refresh);
window_callback!(fn window_focus_callback(focused: c_int) => Focus(focused == ffi::TRUE));
window_callback!(fn window_iconify_callback(iconified: c_int) => Iconify(iconified == ffi::TRUE));
window_callback!(fn framebuffer_size_callback(width: c_int, height: c_int) => FramebufferSize(width as i32, height as i32));
window_callback!(fn mouse_button_callback(button: c_int, action: c_int, mods: c_int) => MouseButton(mem::transmute(button), mem::transmute(action), modifiers::Modifiers::from_bits(mods).unwrap()));
window_callback!(fn cursor_pos_callback(xpos: c_double, ypos: c_double) => CursorPos(xpos as f64, ypos as f64));
window_callback!(fn cursor_enter_callback(entered: c_int) => CursorEnter(entered == ffi::TRUE));
window_callback!(fn scroll_callback(xpos: c_double, ypos: c_double) => Scroll(xpos as f64, ypos as f64));
window_callback!(fn key_callback(key: c_int, scancode: c_int, action: c_int, mods: c_int) => Key(mem::transmute(key), scancode, mem::transmute(action), modifiers::Modifiers::from_bits(mods).unwrap()));
window_callback!(fn char_callback(character: c_uint) => Char(::std::char::from_u32(character).unwrap()));
window_callback!(fn char_mods_callback(character: c_uint, mods: c_int) => CharModifiers(::std::char::from_u32(character).unwrap(), modifiers::Modifiers::from_bits(mods).unwrap()));
window_callback!(fn drop_callback(num_paths: c_int, paths: *mut *const c_char) => FileDrop(slice::from_raw_parts(paths, num_paths as usize).iter().map(|path| PathBuf::from(str::from_utf8(CStr::from_ptr(*path).to_bytes()).unwrap().to_string())).collect()));