glib/
source.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cell::RefCell, mem::transmute, num::NonZeroU32, time::Duration};
4
5use crate::ffi::{self, gboolean, gpointer};
6
7use crate::{thread_guard::ThreadGuard, translate::*, ControlFlow, MainContext, Source};
8
9// rustdoc-stripper-ignore-next
10/// The id of a source that is returned by `idle_add` and `timeout_add`.
11///
12/// This type does not implement `Clone` to prevent calling [`SourceId::remove`]
13/// multiple times on the same source.
14#[derive(Debug, Eq, PartialEq)]
15pub struct SourceId(NonZeroU32);
16
17impl SourceId {
18    // rustdoc-stripper-ignore-next
19    /// Returns the internal source ID.
20    pub fn as_raw(&self) -> u32 {
21        self.0.get()
22    }
23
24    // rustdoc-stripper-ignore-next
25    /// Removes the source with the given id `source_id` from the default main context.
26    ///
27    /// It is a programmer error to attempt to remove a non-existent source.
28    #[doc(alias = "g_source_remove")]
29    pub fn remove(self) {
30        unsafe {
31            result_from_gboolean!(
32                ffi::g_source_remove(self.as_raw()),
33                "Failed to remove source"
34            )
35            .unwrap()
36        }
37    }
38}
39
40#[doc(hidden)]
41impl FromGlib<u32> for SourceId {
42    #[inline]
43    unsafe fn from_glib(val: u32) -> Self {
44        debug_assert_ne!(val, 0);
45        Self(NonZeroU32::new_unchecked(val))
46    }
47}
48
49// rustdoc-stripper-ignore-next
50/// Process identifier
51#[derive(Copy, Clone, Debug, Eq, PartialEq)]
52#[doc(alias = "GPid")]
53pub struct Pid(pub ffi::GPid);
54
55unsafe impl Send for Pid {}
56unsafe impl Sync for Pid {}
57
58#[doc(hidden)]
59impl IntoGlib for Pid {
60    type GlibType = ffi::GPid;
61
62    #[inline]
63    fn into_glib(self) -> ffi::GPid {
64        self.0
65    }
66}
67
68#[doc(hidden)]
69impl FromGlib<ffi::GPid> for Pid {
70    #[inline]
71    unsafe fn from_glib(val: ffi::GPid) -> Self {
72        Self(val)
73    }
74}
75
76unsafe extern "C" fn trampoline<F: FnMut() -> ControlFlow + Send + 'static>(
77    func: gpointer,
78) -> gboolean {
79    let func: &RefCell<F> = &*(func as *const RefCell<F>);
80    (*func.borrow_mut())().into_glib()
81}
82
83unsafe extern "C" fn trampoline_local<F: FnMut() -> ControlFlow + 'static>(
84    func: gpointer,
85) -> gboolean {
86    let func: &ThreadGuard<RefCell<F>> = &*(func as *const ThreadGuard<RefCell<F>>);
87    (*func.get_ref().borrow_mut())().into_glib()
88}
89
90unsafe extern "C" fn destroy_closure<F: FnMut() -> ControlFlow + Send + 'static>(ptr: gpointer) {
91    let _ = Box::<RefCell<F>>::from_raw(ptr as *mut _);
92}
93
94unsafe extern "C" fn destroy_closure_local<F: FnMut() -> ControlFlow + 'static>(ptr: gpointer) {
95    let _ = Box::<ThreadGuard<RefCell<F>>>::from_raw(ptr as *mut _);
96}
97
98fn into_raw<F: FnMut() -> ControlFlow + Send + 'static>(func: F) -> gpointer {
99    let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
100    Box::into_raw(func) as gpointer
101}
102
103fn into_raw_local<F: FnMut() -> ControlFlow + 'static>(func: F) -> gpointer {
104    let func: Box<ThreadGuard<RefCell<F>>> = Box::new(ThreadGuard::new(RefCell::new(func)));
105    Box::into_raw(func) as gpointer
106}
107
108unsafe extern "C" fn trampoline_child_watch<F: FnMut(Pid, i32) + Send + 'static>(
109    pid: ffi::GPid,
110    status: i32,
111    func: gpointer,
112) {
113    let func: &RefCell<F> = &*(func as *const RefCell<F>);
114    (*func.borrow_mut())(Pid(pid), status)
115}
116
117unsafe extern "C" fn trampoline_child_watch_local<F: FnMut(Pid, i32) + 'static>(
118    pid: ffi::GPid,
119    status: i32,
120    func: gpointer,
121) {
122    let func: &ThreadGuard<RefCell<F>> = &*(func as *const ThreadGuard<RefCell<F>>);
123    (*func.get_ref().borrow_mut())(Pid(pid), status)
124}
125
126unsafe extern "C" fn destroy_closure_child_watch<F: FnMut(Pid, i32) + Send + 'static>(
127    ptr: gpointer,
128) {
129    let _ = Box::<RefCell<F>>::from_raw(ptr as *mut _);
130}
131
132unsafe extern "C" fn destroy_closure_child_watch_local<F: FnMut(Pid, i32) + 'static>(
133    ptr: gpointer,
134) {
135    let _ = Box::<ThreadGuard<RefCell<F>>>::from_raw(ptr as *mut _);
136}
137
138fn into_raw_child_watch<F: FnMut(Pid, i32) + Send + 'static>(func: F) -> gpointer {
139    let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
140    Box::into_raw(func) as gpointer
141}
142
143fn into_raw_child_watch_local<F: FnMut(Pid, i32) + 'static>(func: F) -> gpointer {
144    let func: Box<ThreadGuard<RefCell<F>>> = Box::new(ThreadGuard::new(RefCell::new(func)));
145    Box::into_raw(func) as gpointer
146}
147
148// rustdoc-stripper-ignore-next
149/// Transform a generic FnOnce into a closure that can be used as callback in various glib methods
150///
151/// The resulting function can only be called once and will panic otherwise. It will return `ControlFlow::Break`
152/// in order to prevent being called twice.
153#[inline(always)]
154fn fnmut_callback_wrapper(
155    func: impl FnOnce() + Send + 'static,
156) -> impl FnMut() -> ControlFlow + Send + 'static {
157    let mut func = Some(func);
158    move || {
159        let func = func
160            .take()
161            .expect("GSource closure called after returning ControlFlow::Break");
162        func();
163        ControlFlow::Break
164    }
165}
166
167// rustdoc-stripper-ignore-next
168/// Transform a generic FnOnce into a closure that can be used as callback in various glib methods
169///
170/// The resulting function can only be called once and will panic otherwise. It will return `ControlFlow::Break`
171/// in order to prevent being called twice.
172///
173/// Different to `fnmut_callback_wrapper()`, this does not require `func` to be
174/// `Send` but can only be called from the thread that owns the main context.
175#[inline(always)]
176fn fnmut_callback_wrapper_local(
177    func: impl FnOnce() + 'static,
178) -> impl FnMut() -> ControlFlow + 'static {
179    let mut func = Some(func);
180    move || {
181        let func = func
182            .take()
183            .expect("GSource closure called after returning glib::ControlFlow::Break");
184        func();
185        ControlFlow::Break
186    }
187}
188
189// rustdoc-stripper-ignore-next
190/// Adds a closure to be called by the default main loop when it's idle.
191///
192/// `func` will be called repeatedly until it returns `ControlFlow::Break`.
193///
194/// The default main loop almost always is the main loop of the main thread.
195/// Thus, the closure is called on the main thread.
196#[doc(alias = "g_idle_add_full")]
197pub fn idle_add<F>(func: F) -> SourceId
198where
199    F: FnMut() -> ControlFlow + Send + 'static,
200{
201    unsafe {
202        from_glib(ffi::g_idle_add_full(
203            ffi::G_PRIORITY_DEFAULT_IDLE,
204            Some(trampoline::<F>),
205            into_raw(func),
206            Some(destroy_closure::<F>),
207        ))
208    }
209}
210
211// rustdoc-stripper-ignore-next
212/// Adds a closure to be called by the default main loop when it's idle.
213///
214/// `func` will be called repeatedly with `priority` until it returns
215/// `ControlFlow::Break`.
216///
217/// The default main loop almost always is the main loop of the main thread.
218/// Thus, the closure is called on the main thread.
219#[doc(alias = "g_idle_add_full")]
220pub fn idle_add_full<F>(priority: Priority, func: F) -> SourceId
221where
222    F: FnMut() -> ControlFlow + Send + 'static,
223{
224    unsafe {
225        from_glib(ffi::g_idle_add_full(
226            priority.into_glib(),
227            Some(trampoline::<F>),
228            into_raw(func),
229            Some(destroy_closure::<F>),
230        ))
231    }
232}
233
234// rustdoc-stripper-ignore-next
235/// Adds a closure to be called by the default main loop when it's idle.
236///
237/// `func` will be called repeatedly until it returns `ControlFlow::Break`.
238///
239/// The default main loop almost always is the main loop of the main thread.
240/// Thus, the closure is called on the main thread.
241///
242/// In comparison to `idle_add()`, this only requires `func` to be
243/// `FnOnce`, and will automatically return `ControlFlow::Break`.
244#[doc(alias = "g_idle_add_full")]
245#[doc(alias = "g_idle_add_once")]
246pub fn idle_add_once<F>(func: F) -> SourceId
247where
248    F: FnOnce() + Send + 'static,
249{
250    idle_add(fnmut_callback_wrapper(func))
251}
252
253// rustdoc-stripper-ignore-next
254/// Adds a closure to be called by the default main loop when it's idle.
255///
256/// `func` will be called repeatedly until it returns `ControlFlow::Break`.
257///
258/// The default main loop almost always is the main loop of the main thread.
259/// Thus, the closure is called on the main thread.
260///
261/// Different to `idle_add()`, this does not require `func` to be
262/// `Send` but can only be called from the thread that owns the main context.
263///
264/// This function panics if called from a different thread than the one that
265/// owns the default main context.
266#[doc(alias = "g_idle_add_full")]
267pub fn idle_add_local<F>(func: F) -> SourceId
268where
269    F: FnMut() -> ControlFlow + 'static,
270{
271    unsafe {
272        let context = MainContext::default();
273        let _acquire = context
274            .acquire()
275            .expect("default main context already acquired by another thread");
276        from_glib(ffi::g_idle_add_full(
277            ffi::G_PRIORITY_DEFAULT_IDLE,
278            Some(trampoline_local::<F>),
279            into_raw_local(func),
280            Some(destroy_closure_local::<F>),
281        ))
282    }
283}
284
285// rustdoc-stripper-ignore-next
286/// Adds a closure to be called by the default main loop when it's idle.
287///
288/// `func` will be called repeatedly with `priority` until it returns
289/// `ControlFlow::Break`.
290///
291/// The default main loop almost always is the main loop of the main thread.
292/// Thus, the closure is called on the main thread.
293///
294/// Different to `idle_add()`, this does not require `func` to be
295/// `Send` but can only be called from the thread that owns the main context.
296///
297/// This function panics if called from a different thread than the one that
298/// owns the default main context.
299#[doc(alias = "g_idle_add_full")]
300pub fn idle_add_local_full<F>(priority: Priority, func: F) -> SourceId
301where
302    F: FnMut() -> ControlFlow + 'static,
303{
304    unsafe {
305        let context = MainContext::default();
306        let _acquire = context
307            .acquire()
308            .expect("default main context already acquired by another thread");
309        from_glib(ffi::g_idle_add_full(
310            priority.into_glib(),
311            Some(trampoline_local::<F>),
312            into_raw_local(func),
313            Some(destroy_closure_local::<F>),
314        ))
315    }
316}
317
318// rustdoc-stripper-ignore-next
319/// Adds a closure to be called by the default main loop when it's idle.
320///
321/// `func` will be called repeatedly until it returns `ControlFlow::Break`.
322///
323/// The default main loop almost always is the main loop of the main thread.
324/// Thus, the closure is called on the main thread.
325///
326/// Different to `idle_add()`, this does not require `func` to be
327/// `Send` but can only be called from the thread that owns the main context.
328///
329/// This function panics if called from a different thread than the one that
330/// owns the main context.
331///
332/// In comparison to `idle_add_local()`, this only requires `func` to be
333/// `FnOnce`, and will automatically return `ControlFlow::Break`.
334#[doc(alias = "g_idle_add_full")]
335pub fn idle_add_local_once<F>(func: F) -> SourceId
336where
337    F: FnOnce() + 'static,
338{
339    idle_add_local(fnmut_callback_wrapper_local(func))
340}
341
342// rustdoc-stripper-ignore-next
343/// Adds a closure to be called by the default main loop at regular intervals
344/// with millisecond granularity.
345///
346/// `func` will be called repeatedly every `interval` milliseconds until it
347/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
348/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
349/// precision is not necessary.
350///
351/// The default main loop almost always is the main loop of the main thread.
352/// Thus, the closure is called on the main thread.
353#[doc(alias = "g_timeout_add_full")]
354pub fn timeout_add<F>(interval: Duration, func: F) -> SourceId
355where
356    F: FnMut() -> ControlFlow + Send + 'static,
357{
358    unsafe {
359        from_glib(ffi::g_timeout_add_full(
360            ffi::G_PRIORITY_DEFAULT,
361            interval.as_millis() as _,
362            Some(trampoline::<F>),
363            into_raw(func),
364            Some(destroy_closure::<F>),
365        ))
366    }
367}
368
369// rustdoc-stripper-ignore-next
370/// Adds a closure to be called by the default main loop at regular intervals
371/// with millisecond granularity.
372///
373/// `func` will be called repeatedly every `interval` milliseconds with `priority`
374/// until it returns `ControlFlow::Break`. Precise timing is not guaranteed, the
375/// timeout may be delayed by other events. Prefer `timeout_add_seconds` when
376/// millisecond precision is not necessary.
377///
378/// The default main loop almost always is the main loop of the main thread.
379/// Thus, the closure is called on the main thread.
380#[doc(alias = "g_timeout_add_full")]
381pub fn timeout_add_full<F>(interval: Duration, priority: Priority, func: F) -> SourceId
382where
383    F: FnMut() -> ControlFlow + Send + 'static,
384{
385    unsafe {
386        from_glib(ffi::g_timeout_add_full(
387            priority.into_glib(),
388            interval.as_millis() as _,
389            Some(trampoline::<F>),
390            into_raw(func),
391            Some(destroy_closure::<F>),
392        ))
393    }
394}
395
396// rustdoc-stripper-ignore-next
397/// Adds a closure to be called by the default main loop at regular intervals
398/// with millisecond granularity.
399///
400/// `func` will be called repeatedly every `interval` milliseconds until it
401/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
402/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
403/// precision is not necessary.
404///
405/// The default main loop almost always is the main loop of the main thread.
406/// Thus, the closure is called on the main thread.
407///
408/// In comparison to `timeout_add()`, this only requires `func` to be
409/// `FnOnce`, and will automatically return `ControlFlow::Break`.
410#[doc(alias = "g_timeout_add_full")]
411#[doc(alias = "g_timeout_add_once")]
412pub fn timeout_add_once<F>(interval: Duration, func: F) -> SourceId
413where
414    F: FnOnce() + Send + 'static,
415{
416    timeout_add(interval, fnmut_callback_wrapper(func))
417}
418
419// rustdoc-stripper-ignore-next
420/// Adds a closure to be called by the default main loop at regular intervals
421/// with millisecond granularity.
422///
423/// `func` will be called repeatedly every `interval` milliseconds until it
424/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
425/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
426/// precision is not necessary.
427///
428/// The default main loop almost always is the main loop of the main thread.
429/// Thus, the closure is called on the main thread.
430///
431/// Different to `timeout_add()`, this does not require `func` to be
432/// `Send` but can only be called from the thread that owns the main context.
433///
434/// This function panics if called from a different thread than the one that
435/// owns the main context.
436#[doc(alias = "g_timeout_add_full")]
437pub fn timeout_add_local<F>(interval: Duration, func: F) -> SourceId
438where
439    F: FnMut() -> ControlFlow + 'static,
440{
441    unsafe {
442        let context = MainContext::default();
443        let _acquire = context
444            .acquire()
445            .expect("default main context already acquired by another thread");
446        from_glib(ffi::g_timeout_add_full(
447            ffi::G_PRIORITY_DEFAULT,
448            interval.as_millis() as _,
449            Some(trampoline_local::<F>),
450            into_raw_local(func),
451            Some(destroy_closure_local::<F>),
452        ))
453    }
454}
455
456// rustdoc-stripper-ignore-next
457/// Adds a closure to be called by the default main loop at regular intervals
458/// with millisecond granularity.
459///
460/// `func` will be called repeatedly every `interval` milliseconds with `priority`
461/// until it returns `ControlFlow::Break`. Precise timing is not guaranteed, the
462/// timeout may be delayed by other events. Prefer `timeout_add_seconds` when
463/// millisecond precision is not necessary.
464///
465/// The default main loop almost always is the main loop of the main thread.
466/// Thus, the closure is called on the main thread.
467///
468/// Different to `timeout_add()`, this does not require `func` to be
469/// `Send` but can only be called from the thread that owns the main context.
470///
471/// This function panics if called from a different thread than the one that
472/// owns the main context.
473#[doc(alias = "g_timeout_add_full")]
474pub fn timeout_add_local_full<F>(interval: Duration, priority: Priority, func: F) -> SourceId
475where
476    F: FnMut() -> ControlFlow + 'static,
477{
478    unsafe {
479        let context = MainContext::default();
480        let _acquire = context
481            .acquire()
482            .expect("default main context already acquired by another thread");
483        from_glib(ffi::g_timeout_add_full(
484            priority.into_glib(),
485            interval.as_millis() as _,
486            Some(trampoline_local::<F>),
487            into_raw_local(func),
488            Some(destroy_closure_local::<F>),
489        ))
490    }
491}
492
493// rustdoc-stripper-ignore-next
494/// Adds a closure to be called by the default main loop at regular intervals
495/// with millisecond granularity.
496///
497/// `func` will be called repeatedly every `interval` milliseconds until it
498/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
499/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
500/// precision is not necessary.
501///
502/// The default main loop almost always is the main loop of the main thread.
503/// Thus, the closure is called on the main thread.
504///
505/// Different to `timeout_add()`, this does not require `func` to be
506/// `Send` but can only be called from the thread that owns the main context.
507///
508/// This function panics if called from a different thread than the one that
509/// owns the main context.
510///
511/// In comparison to `timeout_add_local()`, this only requires `func` to be
512/// `FnOnce`, and will automatically return `ControlFlow::Break`.
513#[doc(alias = "g_timeout_add_full")]
514pub fn timeout_add_local_once<F>(interval: Duration, func: F) -> SourceId
515where
516    F: FnOnce() + 'static,
517{
518    timeout_add_local(interval, fnmut_callback_wrapper_local(func))
519}
520
521// rustdoc-stripper-ignore-next
522/// Adds a closure to be called by the default main loop at regular intervals
523/// with second granularity.
524///
525/// `func` will be called repeatedly every `interval` seconds until it
526/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
527/// be delayed by other events.
528///
529/// The default main loop almost always is the main loop of the main thread.
530/// Thus, the closure is called on the main thread.
531#[doc(alias = "g_timeout_add_seconds_full")]
532pub fn timeout_add_seconds<F>(interval: u32, func: F) -> SourceId
533where
534    F: FnMut() -> ControlFlow + Send + 'static,
535{
536    unsafe {
537        from_glib(ffi::g_timeout_add_seconds_full(
538            ffi::G_PRIORITY_DEFAULT,
539            interval,
540            Some(trampoline::<F>),
541            into_raw(func),
542            Some(destroy_closure::<F>),
543        ))
544    }
545}
546
547// rustdoc-stripper-ignore-next
548/// Adds a closure to be called by the default main loop at regular intervals
549/// with second granularity.
550///
551/// `func` will be called repeatedly every `interval` seconds until it
552/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
553/// be delayed by other events.
554///
555/// The default main loop almost always is the main loop of the main thread.
556/// Thus, the closure is called on the main thread.
557///
558/// In comparison to `timeout_add_seconds()`, this only requires `func` to be
559/// `FnOnce`, and will automatically return `ControlFlow::Break`.
560#[doc(alias = "g_timeout_add_seconds_full")]
561pub fn timeout_add_seconds_once<F>(interval: u32, func: F) -> SourceId
562where
563    F: FnOnce() + Send + 'static,
564{
565    timeout_add_seconds(interval, fnmut_callback_wrapper(func))
566}
567
568// rustdoc-stripper-ignore-next
569/// Adds a closure to be called by the default main loop at regular intervals
570/// with second granularity.
571///
572/// `func` will be called repeatedly every `interval` seconds until it
573/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
574/// be delayed by other events.
575///
576/// The default main loop almost always is the main loop of the main thread.
577/// Thus, the closure is called on the main thread.
578///
579/// Different to `timeout_add_seconds()`, this does not require `func` to be
580/// `Send` but can only be called from the thread that owns the main context.
581///
582/// This function panics if called from a different thread than the one that
583/// owns the main context.
584#[doc(alias = "g_timeout_add_seconds_full")]
585pub fn timeout_add_seconds_local<F>(interval: u32, func: F) -> SourceId
586where
587    F: FnMut() -> ControlFlow + 'static,
588{
589    unsafe {
590        let context = MainContext::default();
591        let _acquire = context
592            .acquire()
593            .expect("default main context already acquired by another thread");
594        from_glib(ffi::g_timeout_add_seconds_full(
595            ffi::G_PRIORITY_DEFAULT,
596            interval,
597            Some(trampoline_local::<F>),
598            into_raw_local(func),
599            Some(destroy_closure_local::<F>),
600        ))
601    }
602}
603
604// rustdoc-stripper-ignore-next
605/// Adds a closure to be called by the default main loop at regular intervals
606/// with second granularity.
607///
608/// `func` will be called repeatedly every `interval` seconds until it
609/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
610/// be delayed by other events.
611///
612/// The default main loop almost always is the main loop of the main thread.
613/// Thus, the closure is called on the main thread.
614///
615/// Different to `timeout_add_seconds()`, this does not require `func` to be
616/// `Send` but can only be called from the thread that owns the main context.
617///
618/// This function panics if called from a different thread than the one that
619/// owns the main context.
620///
621/// In comparison to `timeout_add_seconds_local()`, this only requires `func` to be
622/// `FnOnce`, and will automatically return `ControlFlow::Break`.
623#[doc(alias = "g_timeout_add_seconds_full")]
624pub fn timeout_add_seconds_local_once<F>(interval: u32, func: F) -> SourceId
625where
626    F: FnOnce() + 'static,
627{
628    timeout_add_seconds_local(interval, fnmut_callback_wrapper_local(func))
629}
630
631// rustdoc-stripper-ignore-next
632/// Adds a closure to be called by the main loop the returned `Source` is attached to when a child
633/// process exits.
634///
635/// `func` will be called when `pid` exits
636#[doc(alias = "g_child_watch_add_full")]
637pub fn child_watch_add<F>(pid: Pid, func: F) -> SourceId
638where
639    F: FnMut(Pid, i32) + Send + 'static,
640{
641    unsafe {
642        from_glib(ffi::g_child_watch_add_full(
643            ffi::G_PRIORITY_DEFAULT,
644            pid.0,
645            Some(trampoline_child_watch::<F>),
646            into_raw_child_watch(func),
647            Some(destroy_closure_child_watch::<F>),
648        ))
649    }
650}
651
652// rustdoc-stripper-ignore-next
653/// Adds a closure to be called by the main loop the returned `Source` is attached to when a child
654/// process exits.
655///
656/// `func` will be called when `pid` exits
657///
658/// Different to `child_watch_add()`, this does not require `func` to be
659/// `Send` but can only be called from the thread that owns the main context.
660///
661/// This function panics if called from a different thread than the one that
662/// owns the main context.
663#[doc(alias = "g_child_watch_add_full")]
664pub fn child_watch_add_local<F>(pid: Pid, func: F) -> SourceId
665where
666    F: FnMut(Pid, i32) + 'static,
667{
668    unsafe {
669        let context = MainContext::default();
670        let _acquire = context
671            .acquire()
672            .expect("default main context already acquired by another thread");
673        from_glib(ffi::g_child_watch_add_full(
674            ffi::G_PRIORITY_DEFAULT,
675            pid.0,
676            Some(trampoline_child_watch_local::<F>),
677            into_raw_child_watch_local(func),
678            Some(destroy_closure_child_watch_local::<F>),
679        ))
680    }
681}
682
683// rustdoc-stripper-ignore-next
684/// The priority of sources
685#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
686pub struct Priority(i32);
687
688impl Priority {
689    #[doc(alias = "G_PRIORITY_HIGH")]
690    pub const HIGH: Self = Self(ffi::G_PRIORITY_HIGH);
691    #[doc(alias = "G_PRIORITY_DEFAULT")]
692    pub const DEFAULT: Self = Self(ffi::G_PRIORITY_DEFAULT);
693    #[doc(alias = "G_PRIORITY_HIGH_IDLE")]
694    pub const HIGH_IDLE: Self = Self(ffi::G_PRIORITY_HIGH_IDLE);
695    #[doc(alias = "G_PRIORITY_DEFAULT_IDLE")]
696    pub const DEFAULT_IDLE: Self = Self(ffi::G_PRIORITY_DEFAULT_IDLE);
697    #[doc(alias = "G_PRIORITY_LOW")]
698    pub const LOW: Self = Self(ffi::G_PRIORITY_LOW);
699}
700
701impl Default for Priority {
702    fn default() -> Self {
703        Self::DEFAULT
704    }
705}
706
707#[doc(hidden)]
708impl IntoGlib for Priority {
709    type GlibType = i32;
710
711    #[inline]
712    fn into_glib(self) -> i32 {
713        self.0
714    }
715}
716
717#[doc(hidden)]
718impl FromGlib<i32> for Priority {
719    #[inline]
720    unsafe fn from_glib(val: i32) -> Self {
721        Self::from(val)
722    }
723}
724
725impl From<i32> for Priority {
726    fn from(value: i32) -> Self {
727        Self(value)
728    }
729}
730
731// rustdoc-stripper-ignore-next
732/// Adds a closure to be called by the main loop the return `Source` is attached to when it's idle.
733///
734/// `func` will be called repeatedly until it returns `ControlFlow::Break`.
735#[doc(alias = "g_idle_source_new")]
736pub fn idle_source_new<F>(name: Option<&str>, priority: Priority, func: F) -> Source
737where
738    F: FnMut() -> ControlFlow + Send + 'static,
739{
740    unsafe {
741        let source = ffi::g_idle_source_new();
742        ffi::g_source_set_callback(
743            source,
744            Some(trampoline::<F>),
745            into_raw(func),
746            Some(destroy_closure::<F>),
747        );
748        ffi::g_source_set_priority(source, priority.into_glib());
749
750        if let Some(name) = name {
751            ffi::g_source_set_name(source, name.to_glib_none().0);
752        }
753
754        from_glib_full(source)
755    }
756}
757
758// rustdoc-stripper-ignore-next
759/// Adds a closure to be called by the main loop the returned `Source` is attached to at regular
760/// intervals with millisecond granularity.
761///
762/// `func` will be called repeatedly every `interval` milliseconds until it
763/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
764/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
765/// precision is not necessary.
766#[doc(alias = "g_timeout_source_new")]
767pub fn timeout_source_new<F>(
768    interval: Duration,
769    name: Option<&str>,
770    priority: Priority,
771    func: F,
772) -> Source
773where
774    F: FnMut() -> ControlFlow + Send + 'static,
775{
776    unsafe {
777        let source = ffi::g_timeout_source_new(interval.as_millis() as _);
778        ffi::g_source_set_callback(
779            source,
780            Some(trampoline::<F>),
781            into_raw(func),
782            Some(destroy_closure::<F>),
783        );
784        ffi::g_source_set_priority(source, priority.into_glib());
785
786        if let Some(name) = name {
787            ffi::g_source_set_name(source, name.to_glib_none().0);
788        }
789
790        from_glib_full(source)
791    }
792}
793
794// rustdoc-stripper-ignore-next
795/// Adds a closure to be called by the main loop the returned `Source` is attached to at regular
796/// intervals with second granularity.
797///
798/// `func` will be called repeatedly every `interval` seconds until it
799/// returns `ControlFlow::Break`. Precise timing is not guaranteed, the timeout may
800/// be delayed by other events.
801#[doc(alias = "g_timeout_source_new_seconds")]
802pub fn timeout_source_new_seconds<F>(
803    interval: u32,
804    name: Option<&str>,
805    priority: Priority,
806    func: F,
807) -> Source
808where
809    F: FnMut() -> ControlFlow + Send + 'static,
810{
811    unsafe {
812        let source = ffi::g_timeout_source_new_seconds(interval);
813        ffi::g_source_set_callback(
814            source,
815            Some(trampoline::<F>),
816            into_raw(func),
817            Some(destroy_closure::<F>),
818        );
819        ffi::g_source_set_priority(source, priority.into_glib());
820
821        if let Some(name) = name {
822            ffi::g_source_set_name(source, name.to_glib_none().0);
823        }
824
825        from_glib_full(source)
826    }
827}
828
829// rustdoc-stripper-ignore-next
830/// Adds a closure to be called by the main loop the returned `Source` is attached to when a child
831/// process exits.
832///
833/// `func` will be called when `pid` exits
834#[doc(alias = "g_child_watch_source_new")]
835pub fn child_watch_source_new<F>(
836    pid: Pid,
837    name: Option<&str>,
838    priority: Priority,
839    func: F,
840) -> Source
841where
842    F: FnMut(Pid, i32) + Send + 'static,
843{
844    unsafe {
845        let source = ffi::g_child_watch_source_new(pid.0);
846        ffi::g_source_set_callback(
847            source,
848            Some(transmute::<
849                *mut (),
850                unsafe extern "C" fn(ffi::gpointer) -> ffi::gboolean,
851            >(trampoline_child_watch::<F> as *mut ())),
852            into_raw_child_watch(func),
853            Some(destroy_closure_child_watch::<F>),
854        );
855        ffi::g_source_set_priority(source, priority.into_glib());
856
857        if let Some(name) = name {
858            ffi::g_source_set_name(source, name.to_glib_none().0);
859        }
860
861        from_glib_full(source)
862    }
863}
864
865impl Source {
866    #[doc(alias = "g_source_attach")]
867    pub fn attach(&self, context: Option<&MainContext>) -> SourceId {
868        unsafe {
869            from_glib(ffi::g_source_attach(
870                self.to_glib_none().0,
871                context.to_glib_none().0,
872            ))
873        }
874    }
875
876    /// Gets the [`MainContext`][crate::MainContext] with which the source is associated.
877    ///
878    /// You can call this on a source that has been destroyed, provided
879    /// that the [`MainContext`][crate::MainContext] it was attached to still exists (in which
880    /// case it will return that [`MainContext`][crate::MainContext]). In particular, you can
881    /// always call this function on the source returned from
882    /// [`main_current_source()`][crate::main_current_source()]. But calling this function on a source
883    /// whose [`MainContext`][crate::MainContext] has been destroyed is an error.
884    ///
885    /// If the associated [`MainContext`][crate::MainContext] could be destroy concurrently from
886    /// a different thread, then this function is not safe to call and
887    /// [`dup_context()`][Self::dup_context()] should be used instead.
888    ///
889    /// # Returns
890    ///
891    /// the main context with which the
892    ///   source is associated, or `NULL` if the context has not yet been added to a
893    ///   source
894    #[doc(alias = "g_source_get_context")]
895    #[doc(alias = "get_context")]
896    #[doc(alias = "g_source_dup_context")]
897    pub fn context(&self) -> Option<MainContext> {
898        #[cfg(feature = "v2_86")]
899        unsafe {
900            from_glib_full(ffi::g_source_dup_context(self.to_glib_none().0))
901        }
902        #[cfg(not(feature = "v2_86"))]
903        unsafe {
904            from_glib_none(ffi::g_source_get_context(self.to_glib_none().0))
905        }
906    }
907}