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}