glib/
source.rs

1// Copyright 2013-2018, The Gtk-rs Project Developers.
2// See the COPYRIGHT file at the top-level directory of this distribution.
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5use glib_sys::{self, gboolean, gpointer};
6#[cfg(all(not(unix), feature = "dox"))]
7use libc::c_int as RawFd;
8use std::cell::RefCell;
9use std::mem::transmute;
10#[cfg(unix)]
11use std::os::unix::io::RawFd;
12use std::process;
13use std::thread;
14use translate::{from_glib, from_glib_full, FromGlib, ToGlib, ToGlibPtr};
15#[cfg(any(unix, feature = "dox"))]
16use IOCondition;
17
18use MainContext;
19use Source;
20
21/// The id of a source that is returned by `idle_add` and `timeout_add`.
22#[derive(Debug, Eq, PartialEq)]
23pub struct SourceId(u32);
24
25#[doc(hidden)]
26impl ToGlib for SourceId {
27    type GlibType = u32;
28
29    #[inline]
30    fn to_glib(&self) -> u32 {
31        self.0
32    }
33}
34
35#[doc(hidden)]
36impl FromGlib<u32> for SourceId {
37    #[inline]
38    fn from_glib(val: u32) -> SourceId {
39        assert_ne!(val, 0);
40        SourceId(val)
41    }
42}
43
44/// Process identificator
45#[derive(Copy, Clone, Debug, Eq, PartialEq)]
46pub struct Pid(pub glib_sys::GPid);
47
48unsafe impl Send for Pid {}
49unsafe impl Sync for Pid {}
50
51/// Continue calling the closure in the future iterations or drop it.
52///
53/// This is the return type of `idle_add` and `timeout_add` closures.
54///
55/// `Continue(true)` keeps the closure assigned, to be rerun when appropriate.
56///
57/// `Continue(false)` disconnects and drops it.
58#[derive(Copy, Clone, Debug, PartialEq, Eq)]
59pub struct Continue(pub bool);
60
61#[doc(hidden)]
62impl ToGlib for Continue {
63    type GlibType = gboolean;
64
65    #[inline]
66    fn to_glib(&self) -> gboolean {
67        self.0.to_glib()
68    }
69}
70
71/// Unwinding propagation guard. Aborts the process if destroyed while
72/// panicking.
73#[deprecated(note = "Rustc has this functionality built-in since 1.26.0")]
74pub struct CallbackGuard(());
75
76#[allow(deprecated)]
77impl CallbackGuard {
78    pub fn new() -> CallbackGuard {
79        CallbackGuard(())
80    }
81}
82
83#[allow(deprecated)]
84impl Default for CallbackGuard {
85    fn default() -> Self {
86        Self::new()
87    }
88}
89
90#[allow(deprecated)]
91impl Drop for CallbackGuard {
92    fn drop(&mut self) {
93        use std::io::stderr;
94        use std::io::Write;
95
96        if thread::panicking() {
97            let _ = stderr().write(b"Uncaught panic, exiting\n");
98            process::abort();
99        }
100    }
101}
102
103unsafe extern "C" fn trampoline<F: FnMut() -> Continue + 'static>(func: gpointer) -> gboolean {
104    let func: &RefCell<F> = &*(func as *const RefCell<F>);
105    (&mut *func.borrow_mut())().to_glib()
106}
107
108unsafe extern "C" fn destroy_closure<F: FnMut() -> Continue + 'static>(ptr: gpointer) {
109    Box::<RefCell<F>>::from_raw(ptr as *mut _);
110}
111
112fn into_raw<F: FnMut() -> Continue + 'static>(func: F) -> gpointer {
113    let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
114    Box::into_raw(func) as gpointer
115}
116
117unsafe extern "C" fn trampoline_child_watch<F: FnMut(Pid, i32) + 'static>(
118    pid: glib_sys::GPid,
119    status: i32,
120    func: gpointer,
121) {
122    let func: &RefCell<F> = &*(func as *const RefCell<F>);
123    (&mut *func.borrow_mut())(Pid(pid), status)
124}
125
126unsafe extern "C" fn destroy_closure_child_watch<F: FnMut(Pid, i32) + 'static>(ptr: gpointer) {
127    Box::<RefCell<F>>::from_raw(ptr as *mut _);
128}
129
130fn into_raw_child_watch<F: FnMut(Pid, i32) + 'static>(func: F) -> gpointer {
131    let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
132    Box::into_raw(func) as gpointer
133}
134
135#[cfg(any(unix, feature = "dox"))]
136unsafe extern "C" fn trampoline_unix_fd<F: FnMut(RawFd, IOCondition) -> Continue + 'static>(
137    fd: i32,
138    condition: glib_sys::GIOCondition,
139    func: gpointer,
140) -> gboolean {
141    let func: &RefCell<F> = &*(func as *const RefCell<F>);
142    (&mut *func.borrow_mut())(fd, from_glib(condition)).to_glib()
143}
144
145#[cfg(any(unix, feature = "dox"))]
146unsafe extern "C" fn destroy_closure_unix_fd<F: FnMut(RawFd, IOCondition) -> Continue + 'static>(
147    ptr: gpointer,
148) {
149    Box::<RefCell<F>>::from_raw(ptr as *mut _);
150}
151
152#[cfg(any(unix, feature = "dox"))]
153fn into_raw_unix_fd<F: FnMut(RawFd, IOCondition) -> Continue + 'static>(func: F) -> gpointer {
154    let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
155    Box::into_raw(func) as gpointer
156}
157
158/// Adds a closure to be called by the default main loop when it's idle.
159///
160/// `func` will be called repeatedly until it returns `Continue(false)`.
161///
162/// The default main loop almost always is the main loop of the main thread.
163/// Thus the closure is called on the main thread.
164pub fn idle_add<F>(func: F) -> SourceId
165where
166    F: FnMut() -> Continue + Send + 'static,
167{
168    unsafe {
169        from_glib(glib_sys::g_idle_add_full(
170            glib_sys::G_PRIORITY_DEFAULT_IDLE,
171            Some(trampoline::<F>),
172            into_raw(func),
173            Some(destroy_closure::<F>),
174        ))
175    }
176}
177
178/// Adds a closure to be called by the default main loop when it's idle.
179///
180/// `func` will be called repeatedly until it returns `Continue(false)`.
181///
182/// The default main loop almost always is the main loop of the main thread.
183/// Thus the closure is called on the main thread.
184///
185/// Different to `idle_add()`, this does not require `func` to be
186/// `Send` but can only be called from the thread that owns the main context.
187///
188/// This function panics if called from a different thread than the one that
189/// owns the main context.
190pub fn idle_add_local<F>(func: F) -> SourceId
191where
192    F: FnMut() -> Continue + 'static,
193{
194    unsafe {
195        assert!(MainContext::default().is_owner());
196        from_glib(glib_sys::g_idle_add_full(
197            glib_sys::G_PRIORITY_DEFAULT_IDLE,
198            Some(trampoline::<F>),
199            into_raw(func),
200            Some(destroy_closure::<F>),
201        ))
202    }
203}
204
205/// Adds a closure to be called by the default main loop at regular intervals
206/// with millisecond granularity.
207///
208/// `func` will be called repeatedly every `interval` milliseconds until it
209/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
210/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
211/// precision is not necessary.
212///
213/// The default main loop almost always is the main loop of the main thread.
214/// Thus the closure is called on the main thread.
215pub fn timeout_add<F>(interval: u32, func: F) -> SourceId
216where
217    F: FnMut() -> Continue + Send + 'static,
218{
219    unsafe {
220        from_glib(glib_sys::g_timeout_add_full(
221            glib_sys::G_PRIORITY_DEFAULT,
222            interval,
223            Some(trampoline::<F>),
224            into_raw(func),
225            Some(destroy_closure::<F>),
226        ))
227    }
228}
229
230/// Adds a closure to be called by the default main loop at regular intervals
231/// with millisecond granularity.
232///
233/// `func` will be called repeatedly every `interval` milliseconds until it
234/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
235/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
236/// precision is not necessary.
237///
238/// The default main loop almost always is the main loop of the main thread.
239/// Thus the closure is called on the main thread.
240///
241/// Different to `timeout_add()`, this does not require `func` to be
242/// `Send` but can only be called from the thread that owns the main context.
243///
244/// This function panics if called from a different thread than the one that
245/// owns the main context.
246pub fn timeout_add_local<F>(interval: u32, func: F) -> SourceId
247where
248    F: FnMut() -> Continue + 'static,
249{
250    unsafe {
251        assert!(MainContext::default().is_owner());
252        from_glib(glib_sys::g_timeout_add_full(
253            glib_sys::G_PRIORITY_DEFAULT,
254            interval,
255            Some(trampoline::<F>),
256            into_raw(func),
257            Some(destroy_closure::<F>),
258        ))
259    }
260}
261
262/// Adds a closure to be called by the default main loop at regular intervals
263/// with second granularity.
264///
265/// `func` will be called repeatedly every `interval` seconds until it
266/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
267/// be delayed by other events.
268///
269/// The default main loop almost always is the main loop of the main thread.
270/// Thus the closure is called on the main thread.
271pub fn timeout_add_seconds<F>(interval: u32, func: F) -> SourceId
272where
273    F: FnMut() -> Continue + Send + 'static,
274{
275    unsafe {
276        from_glib(glib_sys::g_timeout_add_seconds_full(
277            glib_sys::G_PRIORITY_DEFAULT,
278            interval,
279            Some(trampoline::<F>),
280            into_raw(func),
281            Some(destroy_closure::<F>),
282        ))
283    }
284}
285
286/// Adds a closure to be called by the default main loop at regular intervals
287/// with second granularity.
288///
289/// `func` will be called repeatedly every `interval` seconds until it
290/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
291/// be delayed by other events.
292///
293/// The default main loop almost always is the main loop of the main thread.
294/// Thus the closure is called on the main thread.
295///
296/// Different to `timeout_add_seconds()`, this does not require `func` to be
297/// `Send` but can only be called from the thread that owns the main context.
298///
299/// This function panics if called from a different thread than the one that
300/// owns the main context.
301pub fn timeout_add_seconds_local<F>(interval: u32, func: F) -> SourceId
302where
303    F: FnMut() -> Continue + 'static,
304{
305    unsafe {
306        assert!(MainContext::default().is_owner());
307        from_glib(glib_sys::g_timeout_add_seconds_full(
308            glib_sys::G_PRIORITY_DEFAULT,
309            interval,
310            Some(trampoline::<F>),
311            into_raw(func),
312            Some(destroy_closure::<F>),
313        ))
314    }
315}
316
317/// Adds a closure to be called by the main loop the returned `Source` is attached to when a child
318/// process exits.
319///
320/// `func` will be called when `pid` exits
321pub fn child_watch_add<F>(pid: Pid, func: F) -> SourceId
322where
323    F: FnMut(Pid, i32) + Send + 'static,
324{
325    unsafe {
326        from_glib(glib_sys::g_child_watch_add_full(
327            glib_sys::G_PRIORITY_DEFAULT,
328            pid.0,
329            Some(transmute(trampoline_child_watch::<F> as usize)),
330            into_raw_child_watch(func),
331            Some(destroy_closure_child_watch::<F>),
332        ))
333    }
334}
335
336/// Adds a closure to be called by the main loop the returned `Source` is attached to when a child
337/// process exits.
338///
339/// `func` will be called when `pid` exits
340///
341/// Different to `child_watch_add()`, this does not require `func` to be
342/// `Send` but can only be called from the thread that owns the main context.
343///
344/// This function panics if called from a different thread than the one that
345/// owns the main context.
346pub fn child_watch_add_local<F>(pid: Pid, func: F) -> SourceId
347where
348    F: FnMut(Pid, i32) + 'static,
349{
350    unsafe {
351        assert!(MainContext::default().is_owner());
352        from_glib(glib_sys::g_child_watch_add_full(
353            glib_sys::G_PRIORITY_DEFAULT,
354            pid.0,
355            Some(transmute(trampoline_child_watch::<F> as usize)),
356            into_raw_child_watch(func),
357            Some(destroy_closure_child_watch::<F>),
358        ))
359    }
360}
361
362#[cfg(any(unix, feature = "dox"))]
363/// Adds a closure to be called by the default main loop whenever a UNIX signal is raised.
364///
365/// `func` will be called repeatedly every time `signum` is raised until it
366/// returns `Continue(false)`.
367///
368/// The default main loop almost always is the main loop of the main thread.
369/// Thus the closure is called on the main thread.
370pub fn unix_signal_add<F>(signum: i32, func: F) -> SourceId
371where
372    F: FnMut() -> Continue + Send + 'static,
373{
374    unsafe {
375        from_glib(glib_sys::g_unix_signal_add_full(
376            glib_sys::G_PRIORITY_DEFAULT,
377            signum,
378            Some(trampoline::<F>),
379            into_raw(func),
380            Some(destroy_closure::<F>),
381        ))
382    }
383}
384
385#[cfg(any(unix, feature = "dox"))]
386/// Adds a closure to be called by the default main loop whenever a UNIX signal is raised.
387///
388/// `func` will be called repeatedly every time `signum` is raised until it
389/// returns `Continue(false)`.
390///
391/// The default main loop almost always is the main loop of the main thread.
392/// Thus the closure is called on the main thread.
393///
394/// Different to `unix_signal_add()`, this does not require `func` to be
395/// `Send` but can only be called from the thread that owns the main context.
396///
397/// This function panics if called from a different thread than the one that
398/// owns the main context.
399pub fn unix_signal_add_local<F>(signum: i32, func: F) -> SourceId
400where
401    F: FnMut() -> Continue + 'static,
402{
403    unsafe {
404        assert!(MainContext::default().is_owner());
405        from_glib(glib_sys::g_unix_signal_add_full(
406            glib_sys::G_PRIORITY_DEFAULT,
407            signum,
408            Some(trampoline::<F>),
409            into_raw(func),
410            Some(destroy_closure::<F>),
411        ))
412    }
413}
414
415#[cfg(any(unix, feature = "dox"))]
416/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
417/// UNIX file descriptor reaches the given IO condition.
418///
419/// `func` will be called repeatedly while the file descriptor matches the given IO condition
420/// until it returns `Continue(false)`.
421///
422/// The default main loop almost always is the main loop of the main thread.
423/// Thus the closure is called on the main thread.
424pub fn unix_fd_add<F>(fd: RawFd, condition: IOCondition, func: F) -> SourceId
425where
426    F: FnMut(RawFd, IOCondition) -> Continue + Send + 'static,
427{
428    unsafe {
429        from_glib(glib_sys::g_unix_fd_add_full(
430            glib_sys::G_PRIORITY_DEFAULT,
431            fd,
432            condition.to_glib(),
433            Some(transmute(trampoline_unix_fd::<F> as usize)),
434            into_raw_unix_fd(func),
435            Some(destroy_closure_unix_fd::<F>),
436        ))
437    }
438}
439
440#[cfg(any(unix, feature = "dox"))]
441/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
442/// UNIX file descriptor reaches the given IO condition.
443///
444/// `func` will be called repeatedly while the file descriptor matches the given IO condition
445/// until it returns `Continue(false)`.
446///
447/// The default main loop almost always is the main loop of the main thread.
448/// Thus the closure is called on the main thread.
449///
450/// Different to `unix_fd_add()`, this does not require `func` to be
451/// `Send` but can only be called from the thread that owns the main context.
452///
453/// This function panics if called from a different thread than the one that
454/// owns the main context.
455pub fn unix_fd_add_local<F>(fd: RawFd, condition: IOCondition, func: F) -> SourceId
456where
457    F: FnMut(RawFd, IOCondition) -> Continue + 'static,
458{
459    unsafe {
460        assert!(MainContext::default().is_owner());
461        from_glib(glib_sys::g_unix_fd_add_full(
462            glib_sys::G_PRIORITY_DEFAULT,
463            fd,
464            condition.to_glib(),
465            Some(transmute(trampoline_unix_fd::<F> as usize)),
466            into_raw_unix_fd(func),
467            Some(destroy_closure_unix_fd::<F>),
468        ))
469    }
470}
471
472/// Removes the source with the given id `source_id` from the default main context.
473///
474/// It is a programmer error to attempt to remove a non-existent source.
475/// Note: source id are reused.
476///
477/// For historical reasons, the native function always returns true, so we
478/// ignore it here.
479#[allow(clippy::needless_pass_by_value)]
480pub fn source_remove(source_id: SourceId) {
481    unsafe {
482        glib_sys::g_source_remove(source_id.to_glib());
483    }
484}
485
486/// The priority of sources
487///
488#[derive(Clone, Copy, Debug, Eq, PartialEq)]
489pub struct Priority(i32);
490
491#[doc(hidden)]
492impl ToGlib for Priority {
493    type GlibType = i32;
494
495    #[inline]
496    fn to_glib(&self) -> i32 {
497        self.0
498    }
499}
500
501#[doc(hidden)]
502impl FromGlib<i32> for Priority {
503    #[inline]
504    fn from_glib(val: i32) -> Priority {
505        Priority(val)
506    }
507}
508
509impl Default for Priority {
510    fn default() -> Priority {
511        PRIORITY_DEFAULT
512    }
513}
514
515pub const PRIORITY_HIGH: Priority = Priority(glib_sys::G_PRIORITY_HIGH);
516pub const PRIORITY_DEFAULT: Priority = Priority(glib_sys::G_PRIORITY_DEFAULT);
517pub const PRIORITY_HIGH_IDLE: Priority = Priority(glib_sys::G_PRIORITY_HIGH_IDLE);
518pub const PRIORITY_DEFAULT_IDLE: Priority = Priority(glib_sys::G_PRIORITY_DEFAULT_IDLE);
519pub const PRIORITY_LOW: Priority = Priority(glib_sys::G_PRIORITY_LOW);
520
521/// Adds a closure to be called by the main loop the return `Source` is attached to when it's idle.
522///
523/// `func` will be called repeatedly until it returns `Continue(false)`.
524pub fn idle_source_new<F>(name: Option<&str>, priority: Priority, func: F) -> Source
525where
526    F: FnMut() -> Continue + Send + 'static,
527{
528    unsafe {
529        let source = glib_sys::g_idle_source_new();
530        glib_sys::g_source_set_callback(
531            source,
532            Some(trampoline::<F>),
533            into_raw(func),
534            Some(destroy_closure::<F>),
535        );
536        glib_sys::g_source_set_priority(source, priority.to_glib());
537
538        if let Some(name) = name {
539            glib_sys::g_source_set_name(source, name.to_glib_none().0);
540        }
541
542        from_glib_full(source)
543    }
544}
545
546/// Adds a closure to be called by the main loop the returned `Source` is attached to at regular
547/// intervals with millisecond granularity.
548///
549/// `func` will be called repeatedly every `interval` milliseconds until it
550/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
551/// be delayed by other events. Prefer `timeout_add_seconds` when millisecond
552/// precision is not necessary.
553pub fn timeout_source_new<F>(
554    interval: u32,
555    name: Option<&str>,
556    priority: Priority,
557    func: F,
558) -> Source
559where
560    F: FnMut() -> Continue + Send + 'static,
561{
562    unsafe {
563        let source = glib_sys::g_timeout_source_new(interval);
564        glib_sys::g_source_set_callback(
565            source,
566            Some(trampoline::<F>),
567            into_raw(func),
568            Some(destroy_closure::<F>),
569        );
570        glib_sys::g_source_set_priority(source, priority.to_glib());
571
572        if let Some(name) = name {
573            glib_sys::g_source_set_name(source, name.to_glib_none().0);
574        }
575
576        from_glib_full(source)
577    }
578}
579
580/// Adds a closure to be called by the main loop the returned `Source` is attached to at regular
581/// intervals with second granularity.
582///
583/// `func` will be called repeatedly every `interval` seconds until it
584/// returns `Continue(false)`. Precise timing is not guaranteed, the timeout may
585/// be delayed by other events.
586pub fn timeout_source_new_seconds<F>(
587    interval: u32,
588    name: Option<&str>,
589    priority: Priority,
590    func: F,
591) -> Source
592where
593    F: FnMut() -> Continue + Send + 'static,
594{
595    unsafe {
596        let source = glib_sys::g_timeout_source_new_seconds(interval);
597        glib_sys::g_source_set_callback(
598            source,
599            Some(trampoline::<F>),
600            into_raw(func),
601            Some(destroy_closure::<F>),
602        );
603        glib_sys::g_source_set_priority(source, priority.to_glib());
604
605        if let Some(name) = name {
606            glib_sys::g_source_set_name(source, name.to_glib_none().0);
607        }
608
609        from_glib_full(source)
610    }
611}
612
613/// Adds a closure to be called by the main loop the returned `Source` is attached to when a child
614/// process exits.
615///
616/// `func` will be called when `pid` exits
617pub fn child_watch_source_new<F>(
618    pid: Pid,
619    name: Option<&str>,
620    priority: Priority,
621    func: F,
622) -> Source
623where
624    F: FnMut(Pid, i32) + Send + 'static,
625{
626    unsafe {
627        let source = glib_sys::g_child_watch_source_new(pid.0);
628        glib_sys::g_source_set_callback(
629            source,
630            Some(transmute(trampoline_child_watch::<F> as usize)),
631            into_raw_child_watch(func),
632            Some(destroy_closure_child_watch::<F>),
633        );
634        glib_sys::g_source_set_priority(source, priority.to_glib());
635
636        if let Some(name) = name {
637            glib_sys::g_source_set_name(source, name.to_glib_none().0);
638        }
639
640        from_glib_full(source)
641    }
642}
643
644#[cfg(any(unix, feature = "dox"))]
645/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
646/// UNIX signal is raised.
647///
648/// `func` will be called repeatedly every time `signum` is raised until it
649/// returns `Continue(false)`.
650pub fn unix_signal_source_new<F>(
651    signum: i32,
652    name: Option<&str>,
653    priority: Priority,
654    func: F,
655) -> Source
656where
657    F: FnMut() -> Continue + Send + 'static,
658{
659    unsafe {
660        let source = glib_sys::g_unix_signal_source_new(signum);
661        glib_sys::g_source_set_callback(
662            source,
663            Some(trampoline::<F>),
664            into_raw(func),
665            Some(destroy_closure::<F>),
666        );
667        glib_sys::g_source_set_priority(source, priority.to_glib());
668
669        if let Some(name) = name {
670            glib_sys::g_source_set_name(source, name.to_glib_none().0);
671        }
672
673        from_glib_full(source)
674    }
675}
676
677#[cfg(any(unix, feature = "dox"))]
678/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
679/// UNIX file descriptor reaches the given IO condition.
680///
681/// `func` will be called repeatedly while the file descriptor matches the given IO condition
682/// until it returns `Continue(false)`.
683pub fn unix_fd_source_new<F>(
684    fd: RawFd,
685    condition: IOCondition,
686    name: Option<&str>,
687    priority: Priority,
688    func: F,
689) -> Source
690where
691    F: FnMut(RawFd, IOCondition) -> Continue + Send + 'static,
692{
693    unsafe {
694        let source = glib_sys::g_unix_fd_source_new(fd, condition.to_glib());
695        glib_sys::g_source_set_callback(
696            source,
697            Some(transmute(trampoline_unix_fd::<F> as usize)),
698            into_raw_unix_fd(func),
699            Some(destroy_closure_unix_fd::<F>),
700        );
701        glib_sys::g_source_set_priority(source, priority.to_glib());
702
703        if let Some(name) = name {
704            glib_sys::g_source_set_name(source, name.to_glib_none().0);
705        }
706
707        from_glib_full(source)
708    }
709}
710
711impl Source {
712    pub fn attach(&self, context: Option<&MainContext>) -> SourceId {
713        unsafe {
714            from_glib(glib_sys::g_source_attach(
715                self.to_glib_none().0,
716                context.to_glib_none().0,
717            ))
718        }
719    }
720
721    pub fn remove(tag: SourceId) -> Result<(), ::BoolError> {
722        unsafe {
723            glib_result_from_gboolean!(
724                glib_sys::g_source_remove(tag.to_glib()),
725                "Failed to remove source"
726            )
727        }
728    }
729}