1use glib::translate::*;
2use io_lifetimes::{BorrowedFd, OwnedFd};
3use std::boxed::Box as Box_;
4use std::os::unix::io::{IntoRawFd, RawFd};
5
6use std::ptr;
7
8use crate::{ffi, prelude::*, Pty};
9
10impl Pty {
11 #[doc(alias = "vte_pty_get_fd")]
18 #[doc(alias = "get_fd")]
19 pub fn fd(&self) -> BorrowedFd<'_> {
20 unsafe {
21 let raw_fd = ffi::vte_pty_get_fd(self.to_glib_none().0);
22 BorrowedFd::borrow_raw(raw_fd)
23 }
24 }
25
26 #[doc(alias = "vte_pty_new_foreign_sync")]
41 #[doc(alias = "new_foreign_sync")]
42 pub fn foreign_sync(
43 fd: OwnedFd,
44 cancellable: Option<&impl IsA<gio::Cancellable>>,
45 ) -> Result<Pty, glib::Error> {
46 assert_initialized_main_thread!();
47 unsafe {
48 let mut error = ptr::null_mut();
49 let ret = ffi::vte_pty_new_foreign_sync(
50 fd.into_raw_fd(),
51 cancellable.map(|p| p.as_ref()).to_glib_none().0,
52 &mut error,
53 );
54 if error.is_null() {
55 Ok(from_glib_full(ret))
56 } else {
57 Err(from_glib_full(error))
58 }
59 }
60 }
61
62 #[doc(alias = "vte_pty_spawn_async")]
88 #[allow(clippy::too_many_arguments)]
89 pub fn spawn_async<P: FnOnce(Result<glib::Pid, glib::Error>) + 'static, Q: Fn() + 'static>(
90 &self,
91 working_directory: Option<&str>,
92 argv: &[&str],
93 envv: &[&str],
94 spawn_flags: glib::SpawnFlags,
95 child_setup: Q,
96 timeout: i32,
97 cancellable: Option<&impl IsA<gio::Cancellable>>,
98 callback: P,
99 ) {
100 assert_initialized_main_thread!();
101 let main_context = glib::MainContext::ref_thread_default();
102 let is_main_context_owner = main_context.is_owner();
103 let has_acquired_main_context = (!is_main_context_owner)
104 .then(|| main_context.acquire().ok())
105 .flatten();
106 assert!(
107 is_main_context_owner || has_acquired_main_context.is_some(),
108 "Async operations only allowed if the thread is owning the MainContext"
109 );
110 let child_setup_data: Box_<glib::thread_guard::ThreadGuard<Q>> =
111 Box_::new(glib::thread_guard::ThreadGuard::new(child_setup));
112
113 unsafe extern "C" fn child_setup_func<Q: Fn() + 'static>(user_data: glib::ffi::gpointer) {
114 let callback: Box_<glib::thread_guard::ThreadGuard<Q>> =
115 Box_::from_raw(user_data as *mut _);
116 let callback = callback.into_inner();
117 callback()
118 }
119
120 let child_setup = Some(child_setup_func::<Q> as _);
121 let callback_data: Box_<glib::thread_guard::ThreadGuard<P>> =
122 Box_::new(glib::thread_guard::ThreadGuard::new(callback));
123
124 unsafe extern "C" fn spawn_async_trampoline<
125 P: FnOnce(Result<glib::Pid, glib::Error>) + 'static,
126 >(
127 _source_object: *mut glib::gobject_ffi::GObject,
128 res: *mut gio::ffi::GAsyncResult,
129 user_data: glib::ffi::gpointer,
130 ) {
131 let mut error = ptr::null_mut();
132 let mut child_pid = 0;
133 let _ = ffi::vte_pty_spawn_finish(
134 _source_object as *mut _,
135 res,
136 &mut child_pid,
137 &mut error,
138 );
139 let result = if error.is_null() {
140 Ok(from_glib(child_pid))
141 } else {
142 Err(from_glib_full(error))
143 };
144 let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
145 Box_::from_raw(user_data as *mut _);
146 let callback: P = callback.into_inner();
147 callback(result);
148 }
149
150 let callback = Some(spawn_async_trampoline::<P> as _);
151
152 unsafe extern "C" fn child_setup_data_destroy_func<Q: Fn() + 'static>(
153 data: glib::ffi::gpointer,
154 ) {
155 let _callback: Box_<Q> = Box_::from_raw(data as *mut _);
156 }
157
158 let destroy_call8 = Some(child_setup_data_destroy_func::<Q> as _);
159 let super_callback0: Box_<glib::thread_guard::ThreadGuard<Q>> = child_setup_data;
160 let super_callback1: Box_<glib::thread_guard::ThreadGuard<P>> = callback_data;
161 unsafe {
162 ffi::vte_pty_spawn_async(
163 self.to_glib_none().0,
164 working_directory.to_glib_none().0,
165 argv.to_glib_none().0,
166 envv.to_glib_none().0,
167 spawn_flags.into_glib(),
168 child_setup,
169 Box_::into_raw(super_callback0) as *mut _,
170 destroy_call8,
171 timeout,
172 cancellable.map(|p| p.as_ref()).to_glib_none().0,
173 callback,
174 Box_::into_raw(super_callback1) as *mut _,
175 );
176 }
177 }
178
179 #[doc(alias = "vte_pty_spawn_async")]
180 #[allow(clippy::too_many_arguments)]
181 pub fn spawn_future<Q: Fn() + 'static>(
182 &self,
183 working_directory: Option<&str>,
184 argv: &[&str],
185 envv: &[&str],
186 spawn_flags: glib::SpawnFlags,
187 child_setup: Q,
188 timeout: i32,
189 ) -> std::pin::Pin<
190 Box_<dyn std::future::Future<Output = Result<glib::Pid, glib::Error>> + 'static>,
191 > {
192 let working_directory = working_directory.map(ToOwned::to_owned);
193 let argv: Vec<String> = argv.iter().map(|p| p.to_string()).collect();
194 let envv: Vec<String> = envv.iter().map(|p| p.to_string()).collect();
195 Box_::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
196 let argv: Vec<&str> = argv.iter().map(|s| s.as_str()).collect();
197 let envv: Vec<&str> = envv.iter().map(|s| s.as_str()).collect();
198 obj.spawn_async(
199 working_directory.as_deref(),
200 &argv,
201 &envv,
202 spawn_flags,
203 child_setup,
204 timeout,
205 Some(cancellable),
206 move |res| {
207 send.resolve(res);
208 },
209 );
210 }))
211 }
212
213 #[allow(clippy::too_many_arguments)]
275 pub unsafe fn spawn_with_fds_async<
276 P: FnOnce(Result<glib::Pid, glib::Error>) + 'static,
277 Q: Fn() + 'static,
278 >(
279 &self,
280 working_directory: Option<&str>,
281 argv: &[&str],
282 envv: &[&str],
283 fds: Vec<OwnedFd>,
284 map_fds: &[RawFd],
285 spawn_flags: glib::SpawnFlags,
286 child_setup: Q,
287 timeout: i32,
288 cancellable: Option<&impl IsA<gio::Cancellable>>,
289 callback: P,
290 ) {
291 assert_initialized_main_thread!();
292 let main_context = glib::MainContext::ref_thread_default();
293 let is_main_context_owner = main_context.is_owner();
294 let has_acquired_main_context = (!is_main_context_owner)
295 .then(|| main_context.acquire().ok())
296 .flatten();
297 assert!(
298 is_main_context_owner || has_acquired_main_context.is_some(),
299 "Async operations only allowed if the thread is owning the MainContext"
300 );
301 let n_fds = fds.len() as _;
302 let n_map_fds = map_fds.len() as _;
303 let child_setup_data: Box_<glib::thread_guard::ThreadGuard<Q>> =
304 Box_::new(glib::thread_guard::ThreadGuard::new(child_setup));
305 unsafe extern "C" fn child_setup_func<Q: Fn() + 'static>(user_data: glib::ffi::gpointer) {
306 let callback: Box_<glib::thread_guard::ThreadGuard<Q>> =
307 Box_::from_raw(user_data as *mut _);
308 let callback = callback.into_inner();
309 callback()
310 }
311
312 let child_setup = Some(child_setup_func::<Q> as _);
313
314 let callback_data: Box_<glib::thread_guard::ThreadGuard<P>> =
315 Box_::new(glib::thread_guard::ThreadGuard::new(callback));
316
317 unsafe extern "C" fn spawn_with_fds_trampoline<
318 P: FnOnce(Result<glib::Pid, glib::Error>) + 'static,
319 >(
320 _source_object: *mut glib::gobject_ffi::GObject,
321 res: *mut gio::ffi::GAsyncResult,
322 user_data: glib::ffi::gpointer,
323 ) {
324 let mut error = ptr::null_mut();
325 let mut child_pid = 0;
326 let _ = ffi::vte_pty_spawn_finish(
327 _source_object as *mut _,
328 res,
329 &mut child_pid,
330 &mut error,
331 );
332 let result = if error.is_null() {
333 Ok(from_glib(child_pid))
334 } else {
335 Err(from_glib_full(error))
336 };
337 let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
338 Box_::from_raw(user_data as *mut _);
339 let callback = callback.into_inner();
340 callback(result)
341 }
342
343 let callback = Some(spawn_with_fds_trampoline::<P> as _);
344
345 unsafe extern "C" fn child_setup_data_destroy_func<Q: Fn() + 'static>(
346 data: glib::ffi::gpointer,
347 ) {
348 let _callback: Box_<Q> = Box_::from_raw(data as *mut _);
349 }
350
351 let destroy_call12 = Some(child_setup_data_destroy_func::<Q> as _);
352 let super_callback0: Box_<glib::thread_guard::ThreadGuard<Q>> = child_setup_data;
353 let super_callback1: Box_<glib::thread_guard::ThreadGuard<P>> = callback_data;
354 let fds: Vec<RawFd> = fds.into_iter().map(|x| x.into_raw_fd()).collect();
355 unsafe {
356 ffi::vte_pty_spawn_with_fds_async(
357 self.to_glib_none().0,
358 working_directory.to_glib_none().0,
359 argv.to_glib_none().0,
360 envv.to_glib_none().0,
361 fds.to_glib_none().0,
362 n_fds,
363 map_fds.to_glib_none().0,
364 n_map_fds,
365 spawn_flags.into_glib(),
366 child_setup,
367 Box_::into_raw(super_callback0) as *mut _,
368 destroy_call12,
369 timeout,
370 cancellable.map(|p| p.as_ref()).to_glib_none().0,
371 callback,
372 Box_::into_raw(super_callback1) as *mut _,
373 );
374 }
375 }
376
377 #[doc(alias = "vte_pty_spawn_async")]
381 #[allow(clippy::too_many_arguments)]
382 pub unsafe fn spawn_with_fds_future<Q: Fn() + 'static>(
383 &self,
384 working_directory: Option<&str>,
385 argv: &[&str],
386 envv: &[&str],
387 fds: Vec<OwnedFd>,
388 map_fds: &[RawFd],
389 spawn_flags: glib::SpawnFlags,
390 child_setup: Q,
391 timeout: i32,
392 ) -> std::pin::Pin<
393 Box_<dyn std::future::Future<Output = Result<glib::Pid, glib::Error>> + 'static>,
394 > {
395 let working_directory = working_directory.map(ToOwned::to_owned);
396 let argv: Vec<String> = argv.iter().map(|x| x.to_string()).collect();
397 let envv: Vec<String> = envv.iter().map(|x| x.to_string()).collect();
398 let map_fds = map_fds.to_vec();
399 Box_::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
400 let argv: Vec<&str> = argv.iter().map(|s| s.as_str()).collect();
401 let envv: Vec<&str> = envv.iter().map(|s| s.as_str()).collect();
402 obj.spawn_with_fds_async(
403 working_directory.as_deref(),
404 &argv,
405 &envv,
406 fds,
407 &map_fds,
408 spawn_flags,
409 child_setup,
410 timeout,
411 Some(cancellable),
412 move |res| {
413 send.resolve(res);
414 },
415 );
416 }))
417 }
418}