Skip to content

Instantly share code, notes, and snippets.

@sdroege
Created May 24, 2017 15:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sdroege/d47508b39f55bf454f90673150ccef20 to your computer and use it in GitHub Desktop.
Save sdroege/d47508b39f55bf454f90673150ccef20 to your computer and use it in GitHub Desktop.
closure.rs
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