1use 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#[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#[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#[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#[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#[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
268pub fn signal_future(signum: i32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
273 signal_future_with_priority(Priority::default(), signum)
274}
275
276#[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#[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#[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#[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#[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#[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#[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#[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}