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