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
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#[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#[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#[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#[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#[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
286pub fn signal_future(signum: i32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
291 signal_future_with_priority(Priority::default(), signum)
292}
293
294#[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#[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#[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#[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#[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#[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#[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#[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}