diff --git a/glib-sys/src/lib.rs b/glib-sys/src/lib.rs index 6e00faf7..c954a283 100644 --- a/glib-sys/src/lib.rs +++ b/glib-sys/src/lib.rs @@ -23,6 +23,7 @@ pub type gpointer = *mut c_void; pub type GSourceFunc = extern "C" fn(user_data: gpointer) -> gboolean; pub type GCallback = extern "C" fn(); pub type GClosureNotify = extern "C" fn(data: gpointer, closure: gpointer); +pub type GDestroyNotify = extern "C" fn(data: gpointer); #[repr(C)] pub struct GAppInfo; @@ -75,9 +76,9 @@ pub struct GPid; pub struct GPollFD; /// Represents a day between January 1, Year 1 and a few thousand years in the future. None of its members should be accessed directly. -/// +/// /// If the GDate is obtained from g_date_new(), it will be safe to mutate but invalid and thus not safe for calendrical computations. -/// +/// /// If it's declared on the stack, it will contain garbage so must be initialized with g_date_clear(). g_date_clear() makes the date /// invalid but sane. An invalid date doesn't represent a day, it's "empty." A date becomes valid after you set it to a Julian day or /// you set a day, month, and year. @@ -348,14 +349,12 @@ extern "C" { pub fn g_timeout_source_new () -> *mut GSource; pub fn g_timeout_source_new_seconds (interval: c_uint) -> *mut GSource; //pub fn g_timeout_add (interval: c_uint, function: GSourceFunc, data: gpointer) -> c_uint; - pub fn g_timeout_add (interval: c_uint, function: gpointer, data: gpointer) -> c_uint; - //pub fn g_timeout_add_full (); + pub fn g_timeout_add_full (priority: c_int, interval: c_uint, function: GSourceFunc, data: gpointer, notify: GDestroyNotify) -> c_uint; //pub fn g_timeout_add_seconds (interval: c_uint, function: GSourceFunc, data: gpointer) -> c_uint; - pub fn g_timeout_add_seconds (interval: c_uint, function: gpointer, data: gpointer) -> c_uint; - //pub fn g_timeout_add_seconds_full (); + pub fn g_timeout_add_seconds_full (priority: c_int, interval: c_uint, function: GSourceFunc, data: gpointer, notify: GDestroyNotify) -> c_uint; pub fn g_idle_source_new () -> *mut GSource; - //pub fn g_idle_add (); - //pub fn g_idle_add_full (); + // pub fn g_idle_add (function: GSourceFunc, data: gpointer) -> c_uint; + pub fn g_idle_add_full (priority: c_int, function: GSourceFunc, data: gpointer, notify: GDestroyNotify) -> c_uint; pub fn g_idle_remove_by_data (data: gpointer) -> gboolean; pub fn g_child_watch_source_new (pid: GPid) -> *mut GSource; //pub fn g_child_watch_add (); diff --git a/src/lib.rs b/src/lib.rs index 19d99a5d..d009536d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ pub use self::slist::{SList, SElem}; pub use self::glib_container::GlibContainer; pub use self::error::{Error}; pub use self::permission::Permission; -pub use self::timeout_func::timeout; +pub use self::source::{Continue, idle_add, timeout_add, timeout_add_seconds}; pub use self::traits::FFIGObject; pub use self::value::{Value, ValuePublic}; pub use types::Type; @@ -32,7 +32,7 @@ pub mod glib_container; mod error; mod permission; pub mod signal; -pub mod timeout_func; +pub mod source; pub mod traits; pub mod translate; mod value; diff --git a/src/source.rs b/src/source.rs new file mode 100644 index 00000000..69d845c8 --- /dev/null +++ b/src/source.rs @@ -0,0 +1,134 @@ +// Copyright 2013-2015, The Rust-GNOME Project Developers. +// See the COPYRIGHT file at the top-level directory of this distribution. +// Licensed under the MIT license, see the LICENSE file or + +//! Manages available sources of events for the main loop + +use std::cell::RefCell; +use std::ops::DerefMut; +use std::mem::transmute; +use ffi::{gboolean, gpointer, g_idle_add_full, g_timeout_add_full, g_timeout_add_seconds_full}; +use translate::ToGlib; + +/// Return type of idle and timeout functions. +/// +/// In the callback, return `Continue(true)` to continue scheduling the callback +/// in the main loop or `Continue(false)` to remove it from the main loop. +pub struct Continue(pub bool); + +impl ToGlib for Continue { + type GlibType = gboolean; + + #[inline] + fn to_glib(&self) -> gboolean { + self.0.to_glib() + } +} + + +// Box::into_raw stability workaround +unsafe fn into_raw(b: Box) -> *mut T { transmute(b) } + +extern "C" fn trampoline(func: &RefCell Continue + 'static>>) -> gboolean { + func.borrow_mut().deref_mut()().to_glib() +} + +extern "C" fn destroy_closure(ptr: gpointer) { + unsafe { + // Box::from_raw API stability workaround + let ptr = ptr as *mut RefCell Continue + 'static>>; + let _: Box Continue + 'static>>> = transmute(ptr); + } +} + +const PRIORITY_DEFAULT: i32 = 0; +const PRIORITY_DEFAULT_IDLE: i32 = 200; + + +/// Adds a function to be called whenever there are no higher priority events pending to the default main loop. +/// +/// The function is given the default idle priority, `PRIORITY_DEFAULT_IDLE`. +/// If the function returns `Continue(false)` it is automatically removed from +/// the list of event sources and will not be called again. +/// +/// # Examples +/// +/// ```ignore +/// let mut i = 0; +/// idle_add(move || { +/// println!("Idle: {}", i); +/// i += 1; +/// Continue(if i <= 10 { true } else { false }) +/// }); +/// ``` +pub fn idle_add(func: F) -> u32 + where F: FnMut() -> Continue + 'static { + let f: Box Continue + 'static>>> = Box::new(RefCell::new(Box::new(func))); + unsafe { + g_idle_add_full(PRIORITY_DEFAULT_IDLE, transmute(trampoline), + into_raw(f) as gpointer, destroy_closure) + } +} + +/// Sets a function to be called at regular intervals, with the default priority, `PRIORITY_DEFAULT`. +/// +/// The function is called repeatedly until it returns `Continue(false)`, at which point the timeout is +/// automatically destroyed and the function will not be called again. The first call to the +/// function will be at the end of the first interval . +/// +/// Note that timeout functions may be delayed, due to the processing of other event sources. Thus +/// they should not be relied on for precise timing. After each call to the timeout function, the +/// time of the next timeout is recalculated based on the current time and the given interval (it +/// does not try to 'catch up' time lost in delays). +/// +/// If you want to have a timer in the "seconds" range and do not care about the exact time of the +/// first call of the timer, use the `timeout_add_seconds()` function; this function allows for more +/// optimizations and more efficient system power usage. +/// +/// The interval given is in terms of monotonic time, not wall clock time. +/// See `g_get_monotonic_time()` in glib documentation. +/// +/// # Examples +/// +/// ```ignore +/// timeout_add(3000, || { +/// println!("This prints once every 3 seconds"); +/// Continue(true) +/// }); +/// ``` +pub fn timeout_add(interval: u32, func: F) -> u32 + where F: FnMut() -> Continue + 'static { + let f: Box Continue + 'static>>> = Box::new(RefCell::new(Box::new(func))); + unsafe { + g_timeout_add_full(PRIORITY_DEFAULT, interval, transmute(trampoline), + into_raw(f) as gpointer, destroy_closure) + } +} + +/// Sets a function to be called at regular intervals with the default priority, `PRIORITY_DEFAULT`. +/// +/// The function is called repeatedly until it returns `Continue(false)`, at which point the timeout +/// is automatically destroyed and the function will not be called again. +/// +/// Note that the first call of the timer may not be precise for timeouts of one second. If you need +/// finer precision and have such a timeout, you may want to use `timeout_add()` instead. +/// +/// The interval given is in terms of monotonic time, not wall clock time. +/// See `g_get_monotonic_time()` in glib documentation. +/// +/// # Examples +/// +/// ```ignore +/// timeout_add_seconds(10, || { +/// println!("This prints once every 10 seconds"); +/// Continue(true) +/// }); +/// ``` +pub fn timeout_add_seconds(interval: u32, func: F) -> u32 + where F: FnMut() -> Continue + 'static { + let f: Box Continue + 'static>>> = Box::new(RefCell::new(Box::new(func))); + unsafe { + g_timeout_add_seconds_full(PRIORITY_DEFAULT, interval, transmute(trampoline), + into_raw(f) as gpointer, destroy_closure) + } +} diff --git a/src/timeout_func.rs b/src/timeout_func.rs deleted file mode 100644 index 328d462d..00000000 --- a/src/timeout_func.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013-2015, The Rust-GNOME Project Developers. -// See the COPYRIGHT file at the top-level directory of this distribution. -// Licensed under the MIT license, see the LICENSE file or - -//! callback timeout functions - -pub mod timeout { - use ffi; - - /// Sets a function to be called at regular intervals, with the default priority, G_PRIORITY_DEFAULT. - /// The function is called repeatedly until it returns FALSE, at which point the timeout is - /// automatically destroyed and the function will not be called again. The first call to the - /// function will be at the end of the first interval . - /// - /// Note that timeout functions may be delayed, due to the processing of other event sources. Thus - /// they should not be relied on for precise timing. After each call to the timeout function, the - /// time of the next timeout is recalculated based on the current time and the given interval (it - /// does not try to 'catch up' time lost in delays). - /// - /// If you want to have a timer in the "seconds" range and do not care about the exact time of the - /// first call of the timer, use the g_timeout_add_seconds() function; this function allows for more - /// optimizations and more efficient system power usage. - /// - /// This internally creates a main loop source using g_timeout_source_new() and attaches it to the - /// global GMainContext using g_source_attach(), so the callback will be invoked in whichever thread - /// is running that main context. You can do these steps manually if you need greater control or to - /// use a custom main context. - /// - /// The interval given is in terms of monotonic time, not wall clock time. See g_get_monotonic_time(). - pub fn add(interval: u32, func: fn(&mut T) -> i32, data: &T) -> u32 { - let tmp = data as *const T; - let tmp_f = func as ffi::gpointer; - - unsafe { ffi::g_timeout_add(interval, tmp_f, tmp as ffi::gpointer) } - } - - /// Sets a function to be called at regular intervals with the default priority, G_PRIORITY_DEFAULT. - /// The function is called repeatedly until it returns FALSE, at which point the timeout is automatically - /// destroyed and the function will not be called again. - /// - /// This internally creates a main loop source using g_timeout_source_new_seconds() and attaches it to - /// the main loop context using g_source_attach(). You can do these steps manually if you need greater - /// control. Also see g_timeout_add_seconds_full(). - /// - /// Note that the first call of the timer may not be precise for timeouts of one second. If you need - /// finer precision and have such a timeout, you may want to use g_timeout_add() instead. - /// - /// The interval given is in terms of monotonic time, not wall clock time. See g_get_monotonic_time(). - pub fn add_seconds(interval: u32, func: fn(&mut T) -> i32, data: &T) -> u32 { - let tmp = data as *const T; - let tmp_f = func as ffi::gpointer; - - unsafe { ffi::g_timeout_add_seconds(interval, tmp_f, tmp as ffi::gpointer) } - } -}