// Take a look at the license at the top of the repository in the LICENSE file. #[cfg(unix)] use std::os::unix::io::RawFd; use std::{cell::RefCell, mem::transmute, num::NonZeroU32, time::Duration}; use ffi::{self, gboolean, gpointer}; #[cfg(all(not(unix), docsrs))] use libc::c_int as RawFd; #[cfg(any(unix, docsrs))] use crate::IOCondition; use crate::{thread_guard::ThreadGuard, translate::*, ControlFlow, MainContext, Source}; // rustdoc-stripper-ignore-next /// The id of a source that is returned by `idle_add` and `timeout_add`. /// /// This type does not implement `Clone` to prevent calling [`SourceId::remove`] /// multiple times on the same source. #[derive(Debug, Eq, PartialEq)] pub struct SourceId(NonZeroU32); impl SourceId { // rustdoc-stripper-ignore-next /// Returns the internal source ID. pub fn as_raw(&self) -> u32 { self.0.get() } // rustdoc-stripper-ignore-next /// Removes the source with the given id `source_id` from the default main context. /// /// It is a programmer error to attempt to remove a non-existent source. #[doc(alias = "g_source_remove")] pub fn remove(self) { unsafe { result_from_gboolean!( ffi::g_source_remove(self.as_raw()), "Failed to remove source" ) .unwrap() } } } #[doc(hidden)] impl FromGlib for SourceId { #[inline] unsafe fn from_glib(val: u32) -> Self { debug_assert_ne!(val, 0); Self(NonZeroU32::new_unchecked(val)) } } // rustdoc-stripper-ignore-next /// Process identificator #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[doc(alias = "GPid")] pub struct Pid(pub ffi::GPid); unsafe impl Send for Pid {} unsafe impl Sync for Pid {} #[doc(hidden)] impl IntoGlib for Pid { type GlibType = ffi::GPid; #[inline] fn into_glib(self) -> ffi::GPid { self.0 } } #[doc(hidden)] impl FromGlib for Pid { #[inline] unsafe fn from_glib(val: ffi::GPid) -> Self { Self(val) } } unsafe extern "C" fn trampoline ControlFlow + Send + 'static>( func: gpointer, ) -> gboolean { let func: &RefCell = &*(func as *const RefCell); (*func.borrow_mut())().into_glib() } unsafe extern "C" fn trampoline_local ControlFlow + 'static>( func: gpointer, ) -> gboolean { let func: &ThreadGuard> = &*(func as *const ThreadGuard>); (*func.get_ref().borrow_mut())().into_glib() } unsafe extern "C" fn destroy_closure ControlFlow + Send + 'static>(ptr: gpointer) { let _ = Box::>::from_raw(ptr as *mut _); } unsafe extern "C" fn destroy_closure_local ControlFlow + 'static>(ptr: gpointer) { let _ = Box::>>::from_raw(ptr as *mut _); } fn into_raw ControlFlow + Send + 'static>(func: F) -> gpointer { let func: Box> = Box::new(RefCell::new(func)); Box::into_raw(func) as gpointer } fn into_raw_local ControlFlow + 'static>(func: F) -> gpointer { let func: Box>> = Box::new(ThreadGuard::new(RefCell::new(func))); Box::into_raw(func) as gpointer } unsafe extern "C" fn trampoline_child_watch( pid: ffi::GPid, status: i32, func: gpointer, ) { let func: &RefCell = &*(func as *const RefCell); (*func.borrow_mut())(Pid(pid), status) } unsafe extern "C" fn trampoline_child_watch_local( pid: ffi::GPid, status: i32, func: gpointer, ) { let func: &ThreadGuard> = &*(func as *const ThreadGuard>); (*func.get_ref().borrow_mut())(Pid(pid), status) } unsafe extern "C" fn destroy_closure_child_watch( ptr: gpointer, ) { let _ = Box::>::from_raw(ptr as *mut _); } unsafe extern "C" fn destroy_closure_child_watch_local( ptr: gpointer, ) { let _ = Box::>>::from_raw(ptr as *mut _); } fn into_raw_child_watch(func: F) -> gpointer { let func: Box> = Box::new(RefCell::new(func)); Box::into_raw(func) as gpointer } fn into_raw_child_watch_local(func: F) -> gpointer { let func: Box>> = Box::new(ThreadGuard::new(RefCell::new(func))); Box::into_raw(func) as gpointer } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] unsafe extern "C" fn trampoline_unix_fd< F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static, >( fd: i32, condition: ffi::GIOCondition, func: gpointer, ) -> gboolean { let func: &RefCell = &*(func as *const RefCell); (*func.borrow_mut())(fd, from_glib(condition)).into_glib() } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] unsafe extern "C" fn trampoline_unix_fd_local< F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static, >( fd: i32, condition: ffi::GIOCondition, func: gpointer, ) -> gboolean { let func: &ThreadGuard> = &*(func as *const ThreadGuard>); (*func.get_ref().borrow_mut())(fd, from_glib(condition)).into_glib() } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] unsafe extern "C" fn destroy_closure_unix_fd< F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static, >( ptr: gpointer, ) { let _ = Box::>::from_raw(ptr as *mut _); } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] unsafe extern "C" fn destroy_closure_unix_fd_local< F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static, >( ptr: gpointer, ) { let _ = Box::>>::from_raw(ptr as *mut _); } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] fn into_raw_unix_fd ControlFlow + Send + 'static>( func: F, ) -> gpointer { let func: Box> = Box::new(RefCell::new(func)); Box::into_raw(func) as gpointer } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] fn into_raw_unix_fd_local ControlFlow + 'static>( func: F, ) -> gpointer { let func: Box>> = Box::new(ThreadGuard::new(RefCell::new(func))); Box::into_raw(func) as gpointer } // rustdoc-stripper-ignore-next /// Transform a generic FnOnce into a closure that can be used as callback in various glib methods /// /// The resulting function can only be called once and will panic otherwise. It will return `ControlFlow::Break` /// in order to prevent being called twice. #[inline(always)] fn fnmut_callback_wrapper( func: impl FnOnce() + Send + 'static, ) -> impl FnMut() -> ControlFlow + Send + 'static { let mut func = Some(func); move || { let func = func .take() .expect("GSource closure called after returning ControlFlow::Break"); func(); ControlFlow::Break } } // rustdoc-stripper-ignore-next /// Transform a generic FnOnce into a closure that can be used as callback in various glib methods /// /// The resulting function can only be called once and will panic otherwise. It will return `ControlFlow::Break` /// in order to prevent being called twice. /// /// Different to `fnmut_callback_wrapper()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. #[inline(always)] fn fnmut_callback_wrapper_local( func: impl FnOnce() + 'static, ) -> impl FnMut() -> ControlFlow + 'static { let mut func = Some(func); move || { let func = func .take() .expect("GSource closure called after returning glib::ControlFlow::Break"); func(); ControlFlow::Break } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop when it's idle. /// /// `func` will be called repeatedly until it returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. #[doc(alias = "g_idle_add_full")] pub fn idle_add(func: F) -> SourceId where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { from_glib(ffi::g_idle_add_full( ffi::G_PRIORITY_DEFAULT_IDLE, Some(trampoline::), into_raw(func), Some(destroy_closure::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop when it's idle. /// /// `func` will be called repeatedly with `priority` until it returns /// `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. #[doc(alias = "g_idle_add_full")] pub fn idle_add_full(priority: Priority, func: F) -> SourceId where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { from_glib(ffi::g_idle_add_full( priority.into_glib(), Some(trampoline::), into_raw(func), Some(destroy_closure::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop when it's idle. /// /// `func` will be called repeatedly until it returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// In comparison to `idle_add()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_idle_add_full")] #[doc(alias = "g_idle_add_once")] pub fn idle_add_once(func: F) -> SourceId where F: FnOnce() + Send + 'static, { idle_add(fnmut_callback_wrapper(func)) } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop when it's idle. /// /// `func` will be called repeatedly until it returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `idle_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the default main context. #[doc(alias = "g_idle_add_full")] pub fn idle_add_local(func: F) -> SourceId where F: FnMut() -> ControlFlow + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_idle_add_full( ffi::G_PRIORITY_DEFAULT_IDLE, Some(trampoline_local::), into_raw_local(func), Some(destroy_closure_local::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop when it's idle. /// /// `func` will be called repeatedly with `priority` until it returns /// `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `idle_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the default main context. #[doc(alias = "g_idle_add_full")] pub fn idle_add_local_full(priority: Priority, func: F) -> SourceId where F: FnMut() -> ControlFlow + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_idle_add_full( priority.into_glib(), Some(trampoline_local::), into_raw_local(func), Some(destroy_closure_local::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop when it's idle. /// /// `func` will be called repeatedly until it returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `idle_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. /// /// In comparison to `idle_add_local()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_idle_add_full")] pub fn idle_add_local_once(func: F) -> SourceId where F: FnOnce() + 'static, { idle_add_local(fnmut_callback_wrapper_local(func)) } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with millisecond granularity. /// /// `func` will be called repeatedly every `interval` milliseconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. Prefer `timeout_add_seconds` when millisecond /// precision is not necessary. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. #[doc(alias = "g_timeout_add_full")] pub fn timeout_add(interval: Duration, func: F) -> SourceId where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { from_glib(ffi::g_timeout_add_full( ffi::G_PRIORITY_DEFAULT, interval.as_millis() as _, Some(trampoline::), into_raw(func), Some(destroy_closure::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with millisecond granularity. /// /// `func` will be called repeatedly every `interval` milliseconds with `priority` /// until it returns `ControlFlow::Break`. Precise timing is not guaranteed, the /// timeout may be delayed by other events. Prefer `timeout_add_seconds` when /// millisecond precision is not necessary. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. #[doc(alias = "g_timeout_add_full")] pub fn timeout_add_full(interval: Duration, priority: Priority, func: F) -> SourceId where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { from_glib(ffi::g_timeout_add_full( priority.into_glib(), interval.as_millis() as _, Some(trampoline::), into_raw(func), Some(destroy_closure::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with millisecond granularity. /// /// `func` will be called repeatedly every `interval` milliseconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. Prefer `timeout_add_seconds` when millisecond /// precision is not necessary. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// In comparison to `timeout_add()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_timeout_add_full")] #[doc(alias = "g_timeout_add_once")] pub fn timeout_add_once(interval: Duration, func: F) -> SourceId where F: FnOnce() + Send + 'static, { timeout_add(interval, fnmut_callback_wrapper(func)) } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with millisecond granularity. /// /// `func` will be called repeatedly every `interval` milliseconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. Prefer `timeout_add_seconds` when millisecond /// precision is not necessary. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `timeout_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. #[doc(alias = "g_timeout_add_full")] pub fn timeout_add_local(interval: Duration, func: F) -> SourceId where F: FnMut() -> ControlFlow + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_timeout_add_full( ffi::G_PRIORITY_DEFAULT, interval.as_millis() as _, Some(trampoline_local::), into_raw_local(func), Some(destroy_closure_local::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with millisecond granularity. /// /// `func` will be called repeatedly every `interval` milliseconds with `priority` /// until it returns `ControlFlow::Break`. Precise timing is not guaranteed, the /// timeout may be delayed by other events. Prefer `timeout_add_seconds` when /// millisecond precision is not necessary. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `timeout_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. #[doc(alias = "g_timeout_add_full")] pub fn timeout_add_local_full(interval: Duration, priority: Priority, func: F) -> SourceId where F: FnMut() -> ControlFlow + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_timeout_add_full( priority.into_glib(), interval.as_millis() as _, Some(trampoline_local::), into_raw_local(func), Some(destroy_closure_local::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with millisecond granularity. /// /// `func` will be called repeatedly every `interval` milliseconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. Prefer `timeout_add_seconds` when millisecond /// precision is not necessary. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `timeout_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. /// /// In comparison to `timeout_add_local()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_timeout_add_full")] pub fn timeout_add_local_once(interval: Duration, func: F) -> SourceId where F: FnOnce() + 'static, { timeout_add_local(interval, fnmut_callback_wrapper_local(func)) } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with second granularity. /// /// `func` will be called repeatedly every `interval` seconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. #[doc(alias = "g_timeout_add_seconds_full")] pub fn timeout_add_seconds(interval: u32, func: F) -> SourceId where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { from_glib(ffi::g_timeout_add_seconds_full( ffi::G_PRIORITY_DEFAULT, interval, Some(trampoline::), into_raw(func), Some(destroy_closure::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with second granularity. /// /// `func` will be called repeatedly every `interval` seconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// In comparison to `timeout_add_seconds()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_timeout_add_seconds_full")] pub fn timeout_add_seconds_once(interval: u32, func: F) -> SourceId where F: FnOnce() + Send + 'static, { timeout_add_seconds(interval, fnmut_callback_wrapper(func)) } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with second granularity. /// /// `func` will be called repeatedly every `interval` seconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `timeout_add_seconds()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. #[doc(alias = "g_timeout_add_seconds_full")] pub fn timeout_add_seconds_local(interval: u32, func: F) -> SourceId where F: FnMut() -> ControlFlow + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_timeout_add_seconds_full( ffi::G_PRIORITY_DEFAULT, interval, Some(trampoline_local::), into_raw_local(func), Some(destroy_closure_local::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop at regular intervals /// with second granularity. /// /// `func` will be called repeatedly every `interval` seconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `timeout_add_seconds()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. /// /// In comparison to `timeout_add_seconds_local()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_timeout_add_seconds_full")] pub fn timeout_add_seconds_local_once(interval: u32, func: F) -> SourceId where F: FnOnce() + 'static, { timeout_add_seconds_local(interval, fnmut_callback_wrapper_local(func)) } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to when a child /// process exits. /// /// `func` will be called when `pid` exits #[doc(alias = "g_child_watch_add_full")] pub fn child_watch_add(pid: Pid, func: F) -> SourceId where F: FnMut(Pid, i32) + Send + 'static, { unsafe { from_glib(ffi::g_child_watch_add_full( ffi::G_PRIORITY_DEFAULT, pid.0, Some(trampoline_child_watch::), into_raw_child_watch(func), Some(destroy_closure_child_watch::), )) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to when a child /// process exits. /// /// `func` will be called when `pid` exits /// /// Different to `child_watch_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. #[doc(alias = "g_child_watch_add_full")] pub fn child_watch_add_local(pid: Pid, func: F) -> SourceId where F: FnMut(Pid, i32) + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_child_watch_add_full( ffi::G_PRIORITY_DEFAULT, pid.0, Some(trampoline_child_watch_local::), into_raw_child_watch_local(func), Some(destroy_closure_child_watch_local::), )) } } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop whenever a UNIX signal is raised. /// /// `func` will be called repeatedly every time `signum` is raised until it /// returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. #[doc(alias = "g_unix_signal_add_full")] pub fn unix_signal_add(signum: i32, func: F) -> SourceId where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { from_glib(ffi::g_unix_signal_add_full( ffi::G_PRIORITY_DEFAULT, signum, Some(trampoline::), into_raw(func), Some(destroy_closure::), )) } } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop whenever a UNIX signal is raised. /// /// `func` will be called repeatedly every time `signum` is raised until it /// returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// In comparison to `unix_signal_add()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_unix_signal_add_full")] pub fn unix_signal_add_once(signum: i32, func: F) -> SourceId where F: FnOnce() + Send + 'static, { unix_signal_add(signum, fnmut_callback_wrapper(func)) } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop whenever a UNIX signal is raised. /// /// `func` will be called repeatedly every time `signum` is raised until it /// returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `unix_signal_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. #[doc(alias = "g_unix_signal_add_full")] pub fn unix_signal_add_local(signum: i32, func: F) -> SourceId where F: FnMut() -> ControlFlow + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_unix_signal_add_full( ffi::G_PRIORITY_DEFAULT, signum, Some(trampoline_local::), into_raw_local(func), Some(destroy_closure_local::), )) } } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the default main loop whenever a UNIX signal is raised. /// /// `func` will be called repeatedly every time `signum` is raised until it /// returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `unix_signal_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. /// /// In comparison to `unix_signal_add_local()`, this only requires `func` to be /// `FnOnce`, and will automatically return `ControlFlow::Break`. #[doc(alias = "g_unix_signal_add_full")] pub fn unix_signal_add_local_once(signum: i32, func: F) -> SourceId where F: FnOnce() + 'static, { unix_signal_add_local(signum, fnmut_callback_wrapper_local(func)) } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a /// UNIX file descriptor reaches the given IO condition. /// /// `func` will be called repeatedly while the file descriptor matches the given IO condition /// until it returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. #[doc(alias = "g_unix_fd_add_full")] pub fn unix_fd_add(fd: RawFd, condition: IOCondition, func: F) -> SourceId where F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static, { unsafe { from_glib(ffi::g_unix_fd_add_full( ffi::G_PRIORITY_DEFAULT, fd, condition.into_glib(), Some(trampoline_unix_fd::), into_raw_unix_fd(func), Some(destroy_closure_unix_fd::), )) } } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a /// UNIX file descriptor reaches the given IO condition. /// /// `func` will be called repeatedly while the file descriptor matches the given IO condition /// until it returns `ControlFlow::Break`. /// /// The default main loop almost always is the main loop of the main thread. /// Thus, the closure is called on the main thread. /// /// Different to `unix_fd_add()`, this does not require `func` to be /// `Send` but can only be called from the thread that owns the main context. /// /// This function panics if called from a different thread than the one that /// owns the main context. #[doc(alias = "g_unix_fd_add_full")] pub fn unix_fd_add_local(fd: RawFd, condition: IOCondition, func: F) -> SourceId where F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static, { unsafe { let context = MainContext::default(); let _acquire = context .acquire() .expect("default main context already acquired by another thread"); from_glib(ffi::g_unix_fd_add_full( ffi::G_PRIORITY_DEFAULT, fd, condition.into_glib(), Some(trampoline_unix_fd_local::), into_raw_unix_fd_local(func), Some(destroy_closure_unix_fd_local::), )) } } // rustdoc-stripper-ignore-next /// The priority of sources #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct Priority(i32); impl Priority { #[doc(alias = "G_PRIORITY_HIGH")] pub const HIGH: Self = Self(ffi::G_PRIORITY_HIGH); #[doc(alias = "G_PRIORITY_DEFAULT")] pub const DEFAULT: Self = Self(ffi::G_PRIORITY_DEFAULT); #[doc(alias = "G_PRIORITY_HIGH_IDLE")] pub const HIGH_IDLE: Self = Self(ffi::G_PRIORITY_HIGH_IDLE); #[doc(alias = "G_PRIORITY_DEFAULT_IDLE")] pub const DEFAULT_IDLE: Self = Self(ffi::G_PRIORITY_DEFAULT_IDLE); #[doc(alias = "G_PRIORITY_LOW")] pub const LOW: Self = Self(ffi::G_PRIORITY_LOW); } impl Default for Priority { fn default() -> Self { Self::DEFAULT } } #[doc(hidden)] impl IntoGlib for Priority { type GlibType = i32; #[inline] fn into_glib(self) -> i32 { self.0 } } #[doc(hidden)] impl FromGlib for Priority { #[inline] unsafe fn from_glib(val: i32) -> Self { Self::from(val) } } impl From for Priority { fn from(value: i32) -> Self { Self(value) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the return `Source` is attached to when it's idle. /// /// `func` will be called repeatedly until it returns `ControlFlow::Break`. #[doc(alias = "g_idle_source_new")] pub fn idle_source_new(name: Option<&str>, priority: Priority, func: F) -> Source where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { let source = ffi::g_idle_source_new(); ffi::g_source_set_callback( source, Some(trampoline::), into_raw(func), Some(destroy_closure::), ); ffi::g_source_set_priority(source, priority.into_glib()); if let Some(name) = name { ffi::g_source_set_name(source, name.to_glib_none().0); } from_glib_full(source) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to at regular /// intervals with millisecond granularity. /// /// `func` will be called repeatedly every `interval` milliseconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. Prefer `timeout_add_seconds` when millisecond /// precision is not necessary. #[doc(alias = "g_timeout_source_new")] pub fn timeout_source_new( interval: Duration, name: Option<&str>, priority: Priority, func: F, ) -> Source where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { let source = ffi::g_timeout_source_new(interval.as_millis() as _); ffi::g_source_set_callback( source, Some(trampoline::), into_raw(func), Some(destroy_closure::), ); ffi::g_source_set_priority(source, priority.into_glib()); if let Some(name) = name { ffi::g_source_set_name(source, name.to_glib_none().0); } from_glib_full(source) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to at regular /// intervals with second granularity. /// /// `func` will be called repeatedly every `interval` seconds until it /// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may /// be delayed by other events. #[doc(alias = "g_timeout_source_new_seconds")] pub fn timeout_source_new_seconds( interval: u32, name: Option<&str>, priority: Priority, func: F, ) -> Source where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { let source = ffi::g_timeout_source_new_seconds(interval); ffi::g_source_set_callback( source, Some(trampoline::), into_raw(func), Some(destroy_closure::), ); ffi::g_source_set_priority(source, priority.into_glib()); if let Some(name) = name { ffi::g_source_set_name(source, name.to_glib_none().0); } from_glib_full(source) } } // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to when a child /// process exits. /// /// `func` will be called when `pid` exits #[doc(alias = "g_child_watch_source_new")] pub fn child_watch_source_new( pid: Pid, name: Option<&str>, priority: Priority, func: F, ) -> Source where F: FnMut(Pid, i32) + Send + 'static, { unsafe { let source = ffi::g_child_watch_source_new(pid.0); ffi::g_source_set_callback( source, Some(transmute::< _, unsafe extern "C" fn(ffi::gpointer) -> ffi::gboolean, >(trampoline_child_watch:: as *const ())), into_raw_child_watch(func), Some(destroy_closure_child_watch::), ); ffi::g_source_set_priority(source, priority.into_glib()); if let Some(name) = name { ffi::g_source_set_name(source, name.to_glib_none().0); } from_glib_full(source) } } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a /// UNIX signal is raised. /// /// `func` will be called repeatedly every time `signum` is raised until it /// returns `ControlFlow::Break`. #[doc(alias = "g_unix_signal_source_new")] pub fn unix_signal_source_new( signum: i32, name: Option<&str>, priority: Priority, func: F, ) -> Source where F: FnMut() -> ControlFlow + Send + 'static, { unsafe { let source = ffi::g_unix_signal_source_new(signum); ffi::g_source_set_callback( source, Some(trampoline::), into_raw(func), Some(destroy_closure::), ); ffi::g_source_set_priority(source, priority.into_glib()); if let Some(name) = name { ffi::g_source_set_name(source, name.to_glib_none().0); } from_glib_full(source) } } #[cfg(any(unix, docsrs))] #[cfg_attr(docsrs, doc(cfg(unix)))] // rustdoc-stripper-ignore-next /// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a /// UNIX file descriptor reaches the given IO condition. /// /// `func` will be called repeatedly while the file descriptor matches the given IO condition /// until it returns `ControlFlow::Break`. #[doc(alias = "g_unix_fd_source_new")] pub fn unix_fd_source_new( fd: RawFd, condition: IOCondition, name: Option<&str>, priority: Priority, func: F, ) -> Source where F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static, { unsafe { let source = ffi::g_unix_fd_source_new(fd, condition.into_glib()); ffi::g_source_set_callback( source, Some(transmute::< _, unsafe extern "C" fn(ffi::gpointer) -> ffi::gboolean, >(trampoline_unix_fd:: as *const ())), into_raw_unix_fd(func), Some(destroy_closure_unix_fd::), ); ffi::g_source_set_priority(source, priority.into_glib()); if let Some(name) = name { ffi::g_source_set_name(source, name.to_glib_none().0); } from_glib_full(source) } } impl Source { #[doc(alias = "g_source_attach")] pub fn attach(&self, context: Option<&MainContext>) -> SourceId { unsafe { from_glib(ffi::g_source_attach( self.to_glib_none().0, context.to_glib_none().0, )) } } }