glib_unix/
functions.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::cell::RefCell;
4use std::future::Future;
5use std::mem::transmute;
6use std::os::fd::{FromRawFd, RawFd};
7use std::pin::Pin;
8
9use glib::ffi::gpointer;
10use glib::thread_guard::ThreadGuard;
11use glib::translate::*;
12use glib::{ControlFlow, IOCondition, Priority, Source, SourceId};
13
14#[cfg(feature = "futures")]
15use glib::{SourceFuture, SourceStream};
16
17#[cfg(feature = "futures")]
18use futures_core::stream::Stream;
19
20fn into_raw<F: FnMut() -> ControlFlow + Send + 'static>(func: F) -> gpointer {
21    let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
22    Box::into_raw(func) as gpointer
23}
24
25fn into_raw_local<F: FnMut() -> ControlFlow + 'static>(func: F) -> gpointer {
26    let func: Box<ThreadGuard<RefCell<F>>> = Box::new(ThreadGuard::new(RefCell::new(func)));
27    Box::into_raw(func) as gpointer
28}
29
30unsafe extern "C" fn trampoline<F: FnMut() -> ControlFlow + Send + 'static>(
31    func: gpointer,
32) -> glib::ffi::gboolean {
33    let func: &RefCell<F> = &*(func as *const RefCell<F>);
34    (*func.borrow_mut())().into_glib()
35}
36
37unsafe extern "C" fn trampoline_local<F: FnMut() -> ControlFlow + 'static>(
38    func: gpointer,
39) -> glib::ffi::gboolean {
40    let func: &ThreadGuard<RefCell<F>> = &*(func as *const ThreadGuard<RefCell<F>>);
41    (*func.get_ref().borrow_mut())().into_glib()
42}
43
44unsafe extern "C" fn destroy_closure<F: FnMut() -> ControlFlow + Send + 'static>(ptr: gpointer) {
45    let _ = Box::<RefCell<F>>::from_raw(ptr as *mut _);
46}
47
48unsafe extern "C" fn destroy_closure_local<F: FnMut() -> ControlFlow + 'static>(ptr: gpointer) {
49    let _ = Box::<ThreadGuard<RefCell<F>>>::from_raw(ptr as *mut _);
50}
51
52unsafe extern "C" fn trampoline_unix_fd<
53    F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static,
54>(
55    fd: i32,
56    condition: glib::ffi::GIOCondition,
57    func: gpointer,
58) -> glib::ffi::gboolean {
59    let func: &RefCell<F> = &*(func as *const RefCell<F>);
60    (*func.borrow_mut())(fd, from_glib(condition)).into_glib()
61}
62
63unsafe extern "C" fn trampoline_unix_fd_local<
64    F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static,
65>(
66    fd: i32,
67    condition: glib::ffi::GIOCondition,
68    func: gpointer,
69) -> glib::ffi::gboolean {
70    let func: &ThreadGuard<RefCell<F>> = &*(func as *const ThreadGuard<RefCell<F>>);
71    (*func.get_ref().borrow_mut())(fd, from_glib(condition)).into_glib()
72}
73
74unsafe extern "C" fn destroy_closure_unix_fd<
75    F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static,
76>(
77    ptr: gpointer,
78) {
79    let _ = Box::<RefCell<F>>::from_raw(ptr as *mut _);
80}
81
82unsafe extern "C" fn destroy_closure_unix_fd_local<
83    F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static,
84>(
85    ptr: gpointer,
86) {
87    let _ = Box::<ThreadGuard<RefCell<F>>>::from_raw(ptr as *mut _);
88}
89
90fn into_raw_unix_fd<F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static>(
91    func: F,
92) -> gpointer {
93    let func: Box<RefCell<F>> = Box::new(RefCell::new(func));
94    Box::into_raw(func) as gpointer
95}
96
97fn into_raw_unix_fd_local<F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static>(
98    func: F,
99) -> gpointer {
100    let func: Box<ThreadGuard<RefCell<F>>> = Box::new(ThreadGuard::new(RefCell::new(func)));
101    Box::into_raw(func) as gpointer
102}
103
104#[inline(always)]
105fn fnmut_callback_wrapper(
106    func: impl FnOnce() + Send + 'static,
107) -> impl FnMut() -> ControlFlow + Send + 'static {
108    let mut func = Some(func);
109    move || {
110        let func = func
111            .take()
112            .expect("GSource closure called after returning ControlFlow::Break");
113        func();
114        ControlFlow::Break
115    }
116}
117
118#[inline(always)]
119fn fnmut_callback_wrapper_local(
120    func: impl FnOnce() + 'static,
121) -> impl FnMut() -> ControlFlow + 'static {
122    let mut func = Some(func);
123    move || {
124        let func = func
125            .take()
126            .expect("GSource closure called after returning glib::ControlFlow::Break");
127        func();
128        ControlFlow::Break
129    }
130}
131
132#[doc(alias = "g_unix_open_pipe")]
133pub fn unix_open_pipe(flags: i32) -> Result<(RawFd, RawFd), glib::Error> {
134    unsafe {
135        let mut fds = [0, 2];
136        let mut error = std::ptr::null_mut();
137        let _ = ffi::g_unix_open_pipe(&mut fds, flags, &mut error);
138        if error.is_null() {
139            Ok((
140                FromRawFd::from_raw_fd(fds[0]),
141                FromRawFd::from_raw_fd(fds[1]),
142            ))
143        } else {
144            Err(from_glib_full(error))
145        }
146    }
147}
148
149// rustdoc-stripper-ignore-next
150/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
151///
152/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
153#[cfg(feature = "futures")]
154pub fn signal_stream(signum: i32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
155    signal_stream_with_priority(Priority::default(), signum)
156}
157
158// rustdoc-stripper-ignore-next
159/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
160///
161/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
162#[cfg(feature = "futures")]
163pub fn signal_stream_with_priority(
164    priority: Priority,
165    signum: i32,
166) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
167    Box::pin(SourceStream::new(move |send| {
168        signal_source_new(signum, None, priority, move || {
169            if send.unbounded_send(()).is_err() {
170                ControlFlow::Break
171            } else {
172                ControlFlow::Continue
173            }
174        })
175    }))
176}
177
178// rustdoc-stripper-ignore-next
179/// Create a `Future` that will resolve once the given UNIX signal is raised
180///
181/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
182#[cfg(feature = "futures")]
183pub fn signal_future_with_priority(
184    priority: Priority,
185    signum: i32,
186) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
187    Box::pin(SourceFuture::new(move |send| {
188        let mut send = Some(send);
189        signal_source_new(signum, None, priority, move || {
190            let _ = send.take().unwrap().send(());
191            ControlFlow::Break
192        })
193    }))
194}
195
196// rustdoc-stripper-ignore-next
197/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
198/// UNIX signal is raised.
199///
200/// `func` will be called repeatedly every time `signum` is raised until it
201/// returns `ControlFlow::Break`.
202#[doc(alias = "g_unix_signal_source_new")]
203pub fn signal_source_new<F>(
204    signum: i32,
205    name: Option<&str>,
206    priority: Priority,
207    func: F,
208) -> glib::Source
209where
210    F: FnMut() -> glib::ControlFlow + Send + 'static,
211{
212    unsafe {
213        let source = ffi::g_unix_signal_source_new(signum);
214        glib::ffi::g_source_set_callback(
215            source,
216            Some(trampoline::<F>),
217            into_raw(func),
218            Some(destroy_closure::<F>),
219        );
220        glib::ffi::g_source_set_priority(source, priority.into_glib());
221
222        if let Some(name) = name {
223            glib::ffi::g_source_set_name(source, name.to_glib_none().0);
224        }
225
226        from_glib_full(source)
227    }
228}
229
230// rustdoc-stripper-ignore-next
231/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
232/// UNIX file descriptor reaches the given IO condition.
233///
234/// `func` will be called repeatedly while the file descriptor matches the given IO condition
235/// until it returns `ControlFlow::Break`.
236#[doc(alias = "g_unix_fd_source_new")]
237pub fn fd_source_new<F>(
238    fd: RawFd,
239    condition: IOCondition,
240    name: Option<&str>,
241    priority: Priority,
242    func: F,
243) -> Source
244where
245    F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static,
246{
247    unsafe {
248        let source = ffi::g_unix_fd_source_new(fd, condition.into_glib());
249        glib::ffi::g_source_set_callback(
250            source,
251            Some(transmute::<
252                *const (),
253                unsafe extern "C" fn(glib::ffi::gpointer) -> glib::ffi::gboolean,
254            >(trampoline_unix_fd::<F> as *const ())),
255            into_raw_unix_fd(func),
256            Some(destroy_closure_unix_fd::<F>),
257        );
258        glib::ffi::g_source_set_priority(source, priority.into_glib());
259
260        if let Some(name) = name {
261            glib::ffi::g_source_set_name(source, name.to_glib_none().0);
262        }
263
264        from_glib_full(source)
265    }
266}
267
268// rustdoc-stripper-ignore-next
269/// Create a `Future` that will resolve once the given UNIX signal is raised
270///
271/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
272pub fn signal_future(signum: i32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
273    signal_future_with_priority(Priority::default(), signum)
274}
275
276// rustdoc-stripper-ignore-next
277/// Adds a closure to be called by the default main loop whenever a UNIX signal is raised.
278///
279/// `func` will be called repeatedly every time `signum` is raised until it
280/// returns `ControlFlow::Break`.
281///
282/// The default main loop almost always is the main loop of the main thread.
283/// Thus, the closure is called on the main thread.
284#[doc(alias = "g_unix_signal_add_full")]
285pub fn unix_signal_add<F>(signum: i32, func: F) -> SourceId
286where
287    F: FnMut() -> ControlFlow + Send + 'static,
288{
289    unsafe {
290        from_glib(ffi::g_unix_signal_add_full(
291            Priority::default().into_glib(),
292            signum,
293            Some(trampoline::<F>),
294            into_raw(func),
295            Some(destroy_closure::<F>),
296        ))
297    }
298}
299
300// rustdoc-stripper-ignore-next
301/// Adds a closure to be called by the default main loop whenever a UNIX signal is raised.
302///
303/// `func` will be called repeatedly every time `signum` is raised until it
304/// returns `ControlFlow::Break`.
305///
306/// The default main loop almost always is the main loop of the main thread.
307/// Thus, the closure is called on the main thread.
308///
309/// In comparison to `unix_signal_add()`, this only requires `func` to be
310/// `FnOnce`, and will automatically return `ControlFlow::Break`.
311#[doc(alias = "g_unix_signal_add_full")]
312pub fn unix_signal_add_once<F>(signum: i32, func: F) -> SourceId
313where
314    F: FnOnce() + Send + 'static,
315{
316    unix_signal_add(signum, fnmut_callback_wrapper(func))
317}
318
319// rustdoc-stripper-ignore-next
320/// Adds a closure to be called by the default main loop whenever a UNIX signal is raised.
321///
322/// `func` will be called repeatedly every time `signum` is raised until it
323/// returns `ControlFlow::Break`.
324///
325/// The default main loop almost always is the main loop of the main thread.
326/// Thus, the closure is called on the main thread.
327///
328/// Different to `unix_signal_add()`, this does not require `func` to be
329/// `Send` but can only be called from the thread that owns the main context.
330///
331/// This function panics if called from a different thread than the one that
332/// owns the main context.
333#[doc(alias = "g_unix_signal_add_full")]
334pub fn unix_signal_add_local<F>(signum: i32, func: F) -> SourceId
335where
336    F: FnMut() -> ControlFlow + 'static,
337{
338    unsafe {
339        let context = glib::MainContext::default();
340        let _acquire = context
341            .acquire()
342            .expect("default main context already acquired by another thread");
343        from_glib(ffi::g_unix_signal_add_full(
344            Priority::default().into_glib(),
345            signum,
346            Some(trampoline_local::<F>),
347            into_raw_local(func),
348            Some(destroy_closure_local::<F>),
349        ))
350    }
351}
352
353// rustdoc-stripper-ignore-next
354/// Adds a closure to be called by the default main loop whenever a UNIX signal is raised.
355///
356/// `func` will be called repeatedly every time `signum` is raised until it
357/// returns `ControlFlow::Break`.
358///
359/// The default main loop almost always is the main loop of the main thread.
360/// Thus, the closure is called on the main thread.
361///
362/// Different to `unix_signal_add()`, this does not require `func` to be
363/// `Send` but can only be called from the thread that owns the main context.
364///
365/// This function panics if called from a different thread than the one that
366/// owns the main context.
367///
368/// In comparison to `unix_signal_add_local()`, this only requires `func` to be
369/// `FnOnce`, and will automatically return `ControlFlow::Break`.
370#[doc(alias = "g_unix_signal_add_full")]
371pub fn unix_signal_add_local_once<F>(signum: i32, func: F) -> SourceId
372where
373    F: FnOnce() + 'static,
374{
375    unix_signal_add_local(signum, fnmut_callback_wrapper_local(func))
376}
377
378// rustdoc-stripper-ignore-next
379/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
380/// UNIX file descriptor reaches the given IO condition.
381///
382/// `func` will be called repeatedly while the file descriptor matches the given IO condition
383/// until it returns `ControlFlow::Break`.
384///
385/// The default main loop almost always is the main loop of the main thread.
386/// Thus, the closure is called on the main thread.
387#[doc(alias = "g_unix_fd_add_full")]
388pub fn unix_fd_add<F>(fd: RawFd, condition: IOCondition, func: F) -> SourceId
389where
390    F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static,
391{
392    unsafe {
393        from_glib(ffi::g_unix_fd_add_full(
394            Priority::default().into_glib(),
395            fd,
396            condition.into_glib(),
397            Some(trampoline_unix_fd::<F>),
398            into_raw_unix_fd(func),
399            Some(destroy_closure_unix_fd::<F>),
400        ))
401    }
402}
403
404// rustdoc-stripper-ignore-next
405/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
406/// UNIX file descriptor reaches the given IO condition.
407///
408/// `func` will be called repeatedly with `priority` while the file descriptor matches the given IO condition
409/// until it returns `ControlFlow::Break`.
410///
411/// The default main loop almost always is the main loop of the main thread.
412/// Thus, the closure is called on the main thread.
413#[doc(alias = "g_unix_fd_add_full")]
414pub fn unix_fd_add_full<F>(
415    fd: RawFd,
416    priority: Priority,
417    condition: IOCondition,
418    func: F,
419) -> SourceId
420where
421    F: FnMut(RawFd, IOCondition) -> ControlFlow + Send + 'static,
422{
423    unsafe {
424        from_glib(ffi::g_unix_fd_add_full(
425            priority.into_glib(),
426            fd,
427            condition.into_glib(),
428            Some(trampoline_unix_fd::<F>),
429            into_raw_unix_fd(func),
430            Some(destroy_closure_unix_fd::<F>),
431        ))
432    }
433}
434
435// rustdoc-stripper-ignore-next
436/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
437/// UNIX file descriptor reaches the given IO condition.
438///
439/// `func` will be called repeatedly while the file descriptor matches the given IO condition
440/// until it returns `ControlFlow::Break`.
441///
442/// The default main loop almost always is the main loop of the main thread.
443/// Thus, the closure is called on the main thread.
444///
445/// Different to `unix_fd_add()`, this does not require `func` to be
446/// `Send` but can only be called from the thread that owns the main context.
447///
448/// This function panics if called from a different thread than the one that
449/// owns the main context.
450#[doc(alias = "g_unix_fd_add_full")]
451pub fn unix_fd_add_local<F>(fd: RawFd, condition: IOCondition, func: F) -> SourceId
452where
453    F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static,
454{
455    unsafe {
456        let context = glib::MainContext::default();
457        let _acquire = context
458            .acquire()
459            .expect("default main context already acquired by another thread");
460        from_glib(ffi::g_unix_fd_add_full(
461            Priority::default().into_glib(),
462            fd,
463            condition.into_glib(),
464            Some(trampoline_unix_fd_local::<F>),
465            into_raw_unix_fd_local(func),
466            Some(destroy_closure_unix_fd_local::<F>),
467        ))
468    }
469}
470
471// rustdoc-stripper-ignore-next
472/// Adds a closure to be called by the main loop the returned `Source` is attached to whenever a
473/// UNIX file descriptor reaches the given IO condition.
474///
475/// `func` will be called repeatedly with `priority` while the file descriptor matches the given IO condition
476/// until it returns `ControlFlow::Break`.
477///
478/// The default main loop almost always is the main loop of the main thread.
479/// Thus, the closure is called on the main thread.
480///
481/// Different to `unix_fd_add()`, this does not require `func` to be
482/// `Send` but can only be called from the thread that owns the main context.
483///
484/// This function panics if called from a different thread than the one that
485/// owns the main context.
486#[doc(alias = "g_unix_fd_add_full")]
487pub fn unix_fd_add_local_full<F>(
488    fd: RawFd,
489    priority: Priority,
490    condition: IOCondition,
491    func: F,
492) -> SourceId
493where
494    F: FnMut(RawFd, IOCondition) -> ControlFlow + 'static,
495{
496    unsafe {
497        let context = glib::MainContext::default();
498        let _acquire = context
499            .acquire()
500            .expect("default main context already acquired by another thread");
501        from_glib(ffi::g_unix_fd_add_full(
502            priority.into_glib(),
503            fd,
504            condition.into_glib(),
505            Some(trampoline_unix_fd_local::<F>),
506            into_raw_unix_fd_local(func),
507            Some(destroy_closure_unix_fd_local::<F>),
508        ))
509    }
510}