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