Created
May 24, 2017 15:50
-
-
Save sdroege/d47508b39f55bf454f90673150ccef20 to your computer and use it in GitHub Desktop.
closure.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate libc; | |
extern crate glib; | |
extern crate glib_sys as ffi; | |
extern crate gobject_sys as gobject_ffi; | |
extern crate gtk; | |
extern crate gtk_sys as gtk_ffi; | |
use gtk::prelude::*; | |
use std::mem; | |
use std::ptr; | |
use libc::{c_uint, c_void}; | |
use glib::translate::{FromGlibPtrFull, FromGlibPtrNone, Stash, StashMut, ToGlibPtr, ToGlibPtrMut}; | |
use glib::Value; | |
use glib::ToValue; | |
pub struct Closure { | |
closure: *mut gobject_ffi::GClosure, | |
} | |
impl Closure { | |
pub fn new<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self { | |
unsafe extern "C" fn marshal<F: Fn(&[Value]) -> Option<Value> + 'static>(_closure: *mut gobject_ffi::GClosure, | |
return_value: *mut gobject_ffi::GValue, n_param_values: c_uint, mut param_values: *mut gobject_ffi::GValue, | |
_invocation_hint: *mut c_void, marshal_data: *mut c_void) | |
{ | |
let mut values = vec![]; | |
for _ in 0 .. n_param_values { | |
let value: Value = FromGlibPtrNone::from_glib_none(param_values); | |
values.push(value); | |
param_values = param_values.offset(1); | |
} | |
let callback: &Box<F> = mem::transmute(marshal_data as *mut _); | |
let result = callback(&values); | |
match result { | |
Some(result) => { | |
let v = result.to_glib_full() as *mut _; | |
ptr::swap(return_value, v); | |
} | |
None => { | |
if !return_value.is_null() { | |
*return_value = mem::zeroed(); | |
} | |
}, | |
} | |
} | |
unsafe extern "C" fn finalize<F: Fn(&[Value]) -> Option<Value> + 'static>(notify_data: *mut c_void, | |
_closure: *mut gobject_ffi::GClosure) | |
{ | |
let _callback: Box<Box<F>> = Box::from_raw(notify_data as *mut _); | |
// callback is dropped here. | |
} | |
unsafe { | |
let size = 4 + 4 + 3 * mem::size_of::<*mut c_void>() as u32; | |
let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut()); | |
assert_ne!(closure, ptr::null_mut()); | |
let callback = Box::new(Box::new(callback)); | |
let ptr: *mut Box<F> = Box::into_raw(callback); | |
let ptr: *mut c_void = ptr as *mut _; | |
gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>)); | |
gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>)); | |
gobject_ffi::g_closure_ref(closure); | |
gobject_ffi::g_closure_sink(closure); | |
Closure { | |
closure: closure, | |
} | |
} | |
} | |
pub fn invoke(&mut self, values: &[&ToValue]) -> Option<Value> { | |
let mut result: gobject_ffi::GValue = unsafe { mem::zeroed() }; | |
let values: Vec<_> = values.iter() | |
.map(|v| v.to_value()) | |
.collect(); | |
let mut gvalues: Vec<gobject_ffi::GValue> = values.iter() | |
.map(|v| unsafe { ptr::read(v.to_glib_none().0) }) | |
.collect(); | |
unsafe { | |
gobject_ffi::g_closure_invoke(self.to_glib_none_mut().0, &mut result, gvalues.len() as u32, | |
gvalues.as_mut_ptr(), ptr::null_mut()); | |
} | |
if result.g_type == gobject_ffi::G_TYPE_INVALID { | |
None | |
} | |
else { | |
Some(unsafe { Value::from_glib_full(&mut result as *mut _) }) | |
} | |
} | |
} | |
impl Clone for Closure { | |
fn clone(&self) -> Self { | |
unsafe { gobject_ffi::g_closure_ref(self.closure); } | |
Closure { closure: self.closure } | |
} | |
} | |
impl Drop for Closure { | |
fn drop(&mut self) { | |
unsafe { gobject_ffi::g_closure_unref(self.closure) }; | |
} | |
} | |
impl<'a> ToGlibPtr<'a, *const gobject_ffi::GClosure> for Closure { | |
type Storage = &'a Closure; | |
fn to_glib_none(&'a self) -> Stash<'a, *const gobject_ffi::GClosure, Self> { | |
Stash(self.closure, self) | |
} | |
} | |
impl<'a> ToGlibPtrMut<'a, *mut gobject_ffi::GClosure> for Closure { | |
type Storage = &'a mut Closure; | |
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut gobject_ffi::GClosure, Self> { | |
StashMut(self.closure, self) | |
} | |
} | |
fn main() { | |
if gtk::init().is_err() { | |
println!("Failed to initialize GTK."); | |
return; | |
} | |
let window = gtk::Window::new(gtk::WindowType::Toplevel); | |
window.set_title("First GTK+ Program"); | |
window.set_border_width(10); | |
window.set_position(gtk::WindowPosition::Center); | |
window.set_default_size(350, 70); | |
window.connect_delete_event(|_, _| { | |
gtk::main_quit(); | |
Inhibit(false) | |
}); | |
let button = gtk::Button::new_with_label("Click me!"); | |
{ | |
let mut closure = Closure::new(|_| { | |
println!("Clicked"); | |
None | |
}); | |
closure.invoke(&[]); | |
unsafe { | |
gobject_ffi::g_signal_connect_closure(button.to_glib_none().0, "clicked".to_glib_none().0, closure.to_glib_none_mut().0, ffi::GFALSE); | |
} | |
} | |
window.add(&button); | |
window.show_all(); | |
gtk::main(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment