Skip to content

Commit

Permalink
Idle callback #16 and globals #15
Browse files Browse the repository at this point in the history
  • Loading branch information
thelink2012 committed May 29, 2015
1 parent a60650d commit de82b8b
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 11 deletions.
11 changes: 11 additions & 0 deletions src/iup/callback/callbacks.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@

// http://sourceforge.net/p/iup/iup/2620/tree//trunk/iup/include/iupcbs.h

impl_callback! {
let name = "IDLE_ACTION";
extern fn listener() -> CallbackReturn;
/// Action generated when there are no events or messages to be processed.
///
/// Often used to perform background operations.
pub fn set_idle<F: Callback()>(cb: F);
/// Removes a previosly set up idle_action callback.
pub fn remove_idle() -> Option<Box<_>>;
}

// This is the common version of the ACTION callback, any so called ACTION that does not
// have the `(*mut iup_sys::Ihandle)` signature should be another trait.
impl_callback! {
Expand Down
83 changes: 73 additions & 10 deletions src/iup/callback/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ macro_rules! fbox_c_str {
}

/// Sets a closure as a callback to IUP.
///
/// Note: `$ih` can be a `ptr::null_mut` to set a callback in the global enviroment.
macro_rules! set_fbox_callback {
($ih:expr, $cb_name:expr, $clistener:expr, $rcb:expr, Callback<$($rargs:ty),*>) => {{

Expand All @@ -24,14 +26,20 @@ macro_rules! set_fbox_callback {
let ih = $ih;
let fb: Box<Box<$crate::callback::Callback<$($rargs),*>>> = Box::new(Box::new($rcb));
iup_sys::IupSetAttribute(ih, fbox_c_str!($cb_name), box_into_raw(fb) as *const _);
iup_sys::IupSetCallback(ih, str_to_c_str!($cb_name), transmute($clistener));

if ih.is_null() {
iup_sys::IupSetFunction(str_to_c_str!($cb_name), transmute($clistener));
} else {
iup_sys::IupSetCallback(ih, str_to_c_str!($cb_name), transmute($clistener));
}

}}
}

/// Clears a closure as a callback to IUP.
///
/// Returns a Option<Box<_>> with the previosly set closure.
///
/// Note: `$ih` can be a `ptr::null_mut` to set a callback in the global enviroment.
macro_rules! clear_fbox_callback {
($ih:expr, $cb_name:expr, Callback<$($rargs:ty),*>) => {{
use $crate::iup_sys;
Expand All @@ -49,14 +57,31 @@ macro_rules! clear_fbox_callback {
let inner_box: Box<Box<$crate::callback::Callback<$($rargs),*>>> = transmute(capsule_box);

iup_sys::IupSetAttribute(ih, fbox_c_str!($cb_name), ptr::null());
iup_sys::IupSetCallback(ih, str_to_c_str!($cb_name), transmute(ptr::null::<u8>()));

if ih.is_null() {
iup_sys::IupSetFunction(str_to_c_str!($cb_name), transmute(ptr::null::<u8>()));
} else {
iup_sys::IupSetCallback(ih, str_to_c_str!($cb_name), transmute(ptr::null::<u8>()));
}

Some(*inner_box)
// inner_box itself gets freed now
}
}}
}

macro_rules! get_fbox_callback {
($ih:expr, $cb_name:expr, Callback<$($rargs:ty),*>) => {{
let fbox_ptr = unsafe {
iup_sys::IupGetAttribute($ih, fbox_c_str!($cb_name))
as *mut Box<$crate::callback::Callback<($($rargs),*)>>
};
assert!(fbox_ptr.is_null() == false);
let fbox: &mut Box<_> = unsafe { &mut (*(fbox_ptr)) };
fbox
}}
}

/// Implements a callback binding between C IUP and Rust which accepts closures.
///
/// After this macro is executed the trait `$trait_name` is implemented with the following
Expand All @@ -72,6 +97,7 @@ macro_rules! clear_fbox_callback {
///
macro_rules! impl_callback {

// Used for element callbacks.
(
$(#[$trait_attr:meta])* // allow doc comments here
pub trait $trait_name:ident where Self: Element {
Expand All @@ -95,12 +121,7 @@ macro_rules! impl_callback {

extern fn listener<Self0: $trait_name>(ih: *mut iup_sys::Ihandle, $($ls_arg: $ls_arg_ty),*)
-> $crate::iup_sys::CallbackReturn {
let fbox_ptr = unsafe {
iup_sys::IupGetAttribute(ih, fbox_c_str!($cb_name))
as *mut Box<$crate::callback::Callback<(Self0, $($fn_arg_ty),*)>>
};
assert!(fbox_ptr.is_null() == false);
let fbox: &mut Box<_> = unsafe { &mut (*(fbox_ptr)) };
let fbox: &mut Box<_> = get_fbox_callback!(ih, $cb_name, Callback<(Self0, $($fn_arg_ty),*)>);
let element = unsafe { <Self0 as $crate::Element>::from_raw_unchecked(ih) };
fbox.on_callback((element, $($ls_arg),*))
}
Expand All @@ -122,7 +143,49 @@ macro_rules! impl_callback {
}
}
}
}
};

// Used for global callbacks.
(
let name = $cb_name:expr;
extern fn listener($($ls_arg:ident: $ls_arg_ty:ty),*) -> CallbackReturn;
$(#[$set_func_attr:meta])*
pub fn $set_func:ident<F: Callback($($fn_arg_ty:ty),*)>(cb: F);
$(#[$rem_func_attr:meta])*
pub fn $remove_func:ident() -> Option<Box<_>>;
) => {

$(#[$set_func_attr])*
pub fn $set_func<F>(cb: F)
where F: $crate::callback::Callback<($($fn_arg_ty),*)> {

use std::mem::transmute;
use $crate::iup_sys;
use std::ptr;

extern fn listener($($ls_arg: $ls_arg_ty),*)
-> $crate::iup_sys::CallbackReturn {
let fbox: &mut Box<_> = get_fbox_callback!(ptr::null_mut(), $cb_name, Callback<($($fn_arg_ty),*)>);
fbox.on_callback(($($ls_arg),*))
}

unsafe {
set_fbox_callback!(ptr::null_mut(), $cb_name, listener, cb,
Callback<($($fn_arg_ty),*)>);
}
}

$(#[$rem_func_attr])*
pub fn $remove_func()
-> Option<Box<$crate::callback::Callback<($($fn_arg_ty),*)>>> {
unsafe {
//use std::ptr;
let old_cb = clear_fbox_callback!(ptr::null_mut(), $cb_name,
Callback<($($fn_arg_ty),*)>);
old_cb
}
}
};
}

/// Drops the closure associated with the `$cb_name` (literal) callback in the element `$ih`.
Expand Down
7 changes: 7 additions & 0 deletions src/iup/callback/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ pub trait Callback<Args> : 'static {
fn on_callback(&mut self, args: Args) -> iup_sys::CallbackReturn;
}

// TODO `Callback<Args> for F where F: FnMut<Args, Output=Out>`
// error: angle-bracket notation is not stable when used with the `Fn` family of traits,
// use parentheses [E0215]
//
// This would allow the functions to receive a variadic number of arguments instead of a single
// tuple argument with a variadic length.

impl<Args, Out: Into<CallbackReturn>, F: 'static> Callback<Args> for F where F: FnMut(Args) -> Out {
/// Because of the `impl From<()> for CallbackReturn`, closures that return `()` can be
/// accepted by this impl.
Expand Down
39 changes: 38 additions & 1 deletion src/iup/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,13 @@ pub trait Element where Self: Sized {
/// raw handle has reached safe Rust bounds at least once before using this method.
///
/// It's undefined behaviour if the raw handle is incompatible with `Self` bindings.
/// Instead use the `Element::from_handle` to perform safe downcasting.
unsafe fn from_raw_unchecked(ih: *mut iup_sys::Ihandle) -> Self;

/// Constructs an Element from a raw IUP handle.
///
/// It's undefined behaviour if the raw handle is incompatible with `Self` bindings.
/// Instead use the `Element::from_handle` to perform safe downcasting.
///
/// # Panics
/// Panics if the raw handle is a null pointer.
Expand All @@ -144,7 +146,7 @@ pub trait Element where Self: Sized {
} else {
unsafe {
// Note: DESTROY_CB is used here instead of LDESTROY_CB because the DESTROY_CB
// is called later. LDESTROY_CB is used in callback.rs, see it for more details.
// is called later. LDESTROY_CB is used in callbacks.rs, see it for more details.
iup_sys::IupSetCallback(ih, str_to_c_str!("DESTROY_CB"), on_element_destroy);
Element::from_raw_unchecked(ih)
}
Expand Down Expand Up @@ -469,6 +471,41 @@ pub trait Element where Self: Sized {
}
}


// TODO not exactly sure if this is the place for the global attribute functions

/// Sets an attribute in the global environment.
///
/// If the driver process the attribute then it will not be stored internally.
pub fn set_global<S1, S2>(name: S1, value: S2)
where S1: Into<String>, S2: Into<String> {
let cname = CString::new(name.into()).unwrap();
let cvalue = CString::new(value.into()).unwrap();
unsafe { iup_sys::IupSetStrGlobal(cname.as_ptr(), cvalue.as_ptr()) };
}

/// Returns an attribute value from the global environment.
///
/// The value can be returned from the driver or from the internal storage.
///
/// This function’s return value is not necessarily the same one used by the application to
/// set the attribute’s value.
///
pub fn global<S: Into<String>>(name: S) -> Option<String> {
let cname = CString::new(name.into()).unwrap();
match unsafe { iup_sys::IupGetGlobal(cname.as_ptr()) } {
cvalue if cvalue.is_null() => None,
cvalue => Some(string_from_c_str!(cvalue)),
}
}

/// Clears the value associated with an global attribute.
pub fn clear_attrib<S: Into<String>>(name: S) {
let cname = CString::new(name.into()).unwrap();
unsafe { iup_sys::IupSetGlobal(cname.as_ptr(), ptr::null()) };
}


/// Called whenever a Element gets destroyed.
///
/// Use this to perform frees related to the Rust binding that are per-element.
Expand Down

0 comments on commit de82b8b

Please sign in to comment.