1use glib::translate::*;
4
5use std::boxed::Box as Box_;
6use std::os::unix::io::{IntoRawFd, RawFd};
7
8use io_lifetimes::OwnedFd;
9
10#[cfg(feature = "v0_70")]
11#[cfg_attr(docsrs, doc(cfg(feature = "v0_70")))]
12use crate::Regex;
13use crate::{prelude::*, PtyFlags, Terminal};
14
15pub trait TerminalExtManual: 'static {
16 #[cfg(feature = "v0_70")]
17 #[cfg_attr(docsrs, doc(cfg(feature = "v0_70")))]
18 #[doc(alias = "vte_terminal_check_regex_array_at")]
19 #[doc(alias = "vte_terminal_check_regex_simple_at")]
20 #[doc(alias = "check_regex_array_at")]
21 fn check_regex_simple_at(
22 &self,
23 x: f64,
24 y: f64,
25 regexes: &[&Regex],
26 match_flags: u32,
27 ) -> Vec<glib::GString>;
28
29 #[doc(alias = "vte_terminal_set_colors")]
30 fn set_colors(
31 &self,
32 foreground: Option<&gdk::RGBA>,
33 background: Option<&gdk::RGBA>,
34 palette: &[&gdk::RGBA],
35 );
36
37 #[doc(alias = "vte_terminal_watch_child")]
38 fn watch_child(&self, child_pid: glib::Pid);
39
40 #[doc(alias = "vte_terminal_spawn_async")]
41 #[allow(clippy::too_many_arguments)]
42 fn spawn_async<P: FnOnce(Result<glib::Pid, glib::Error>) + 'static, Q: Fn() + 'static>(
43 &self,
44 pty_flags: PtyFlags,
45 working_directory: Option<&str>,
46 argv: &[&str],
47 envv: &[&str],
48 spawn_flags: glib::SpawnFlags,
49 child_setup: Q,
50 timeout: i32,
51 cancellable: Option<&impl IsA<gio::Cancellable>>,
52 callback: P,
53 );
54
55 #[doc(alias = "vte_terminal_spawn_async")]
56 #[allow(clippy::too_many_arguments)]
57 fn spawn_future<Q: Fn() + 'static>(
58 &self,
59 pty_flags: PtyFlags,
60 working_directory: Option<&str>,
61 argv: &[&str],
62 envv: &[&str],
63 spawn_flags: glib::SpawnFlags,
64 child_setup: Q,
65 timeout: i32,
66 ) -> std::pin::Pin<
67 Box_<dyn std::future::Future<Output = Result<glib::Pid, glib::Error>> + 'static>,
68 >;
69
70 #[doc(alias = "vte_terminal_spawn_with_fds_async")]
71 #[allow(clippy::too_many_arguments)]
72 unsafe fn spawn_with_fds_async<
73 P: FnOnce(Result<glib::Pid, glib::Error>) + 'static,
74 Q: Fn() + 'static,
75 >(
76 &self,
77 pty_flags: PtyFlags,
78 working_directory: Option<&str>,
79 argv: &[&str],
80 envv: &[&str],
81 fds: Vec<OwnedFd>,
82 map_fds: &[RawFd],
83 spawn_flags: glib::SpawnFlags,
84 child_setup: Q,
85 timeout: i32,
86 cancellable: Option<&impl IsA<gio::Cancellable>>,
87 callback: P,
88 );
89
90 #[doc(alias = "vte_terminal_spawn_async")]
91 #[allow(clippy::too_many_arguments)]
92 unsafe fn spawn_with_fds_future<Q: Fn() + 'static>(
93 &self,
94 pty_flags: PtyFlags,
95 working_directory: Option<&str>,
96 argv: &[&str],
97 envv: &[&str],
98 fds: Vec<OwnedFd>,
99 map_fds: &[RawFd],
100 spawn_flags: glib::SpawnFlags,
101 child_setup: Q,
102 timeout: i32,
103 ) -> std::pin::Pin<
104 Box_<dyn std::future::Future<Output = Result<glib::Pid, glib::Error>> + 'static>,
105 >;
106}
107
108impl<O: IsA<Terminal>> TerminalExtManual for O {
109 #[cfg(feature = "v0_70")]
110 #[cfg_attr(docsrs, doc(cfg(feature = "v0_70")))]
111 fn check_regex_simple_at(
112 &self,
113 x: f64,
114 y: f64,
115 regexes: &[&Regex],
116 match_flags: u32,
117 ) -> Vec<glib::GString> {
118 let n_regexes = regexes.len() as _;
119 unsafe {
120 let mut n_matches = std::mem::MaybeUninit::uninit();
121 let ret = FromGlibContainer::from_glib_full_num(
122 ffi::vte_terminal_check_regex_array_at(
123 self.as_ref().to_glib_none().0,
124 x,
125 y,
126 regexes.as_ptr() as *mut _,
127 n_regexes,
128 match_flags,
129 n_matches.as_mut_ptr(),
130 ),
131 n_matches.assume_init() as _,
132 );
133 ret
134 }
135 }
136
137 #[doc(alias = "vte_terminal_spawn_async")]
138 fn spawn_future<Q: Fn() + 'static>(
139 &self,
140 pty_flags: PtyFlags,
141 working_directory: Option<&str>,
142 argv: &[&str],
143 envv: &[&str],
144 spawn_flags: glib::SpawnFlags,
145 child_setup: Q,
146 timeout: i32,
147 ) -> std::pin::Pin<
148 Box_<dyn std::future::Future<Output = Result<glib::Pid, glib::Error>> + 'static>,
149 > {
150 let working_directory = working_directory.map(ToOwned::to_owned);
151 let argv: Vec<String> = argv.iter().map(|p| p.to_string()).collect();
152 let envv: Vec<String> = envv.iter().map(|p| p.to_string()).collect();
153 Box_::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
154 let argv: Vec<&str> = argv.iter().map(|s| s.as_str()).collect();
155 let envv: Vec<&str> = envv.iter().map(|s| s.as_str()).collect();
156 obj.spawn_async(
157 pty_flags,
158 working_directory.as_deref(),
159 &argv,
160 &envv,
161 spawn_flags,
162 child_setup,
163 timeout,
164 Some(cancellable),
165 move |res| {
166 send.resolve(res);
167 },
168 );
169 }))
170 }
171
172 #[doc(alias = "vte_terminal_spawn_async")]
176 unsafe fn spawn_with_fds_future<Q: Fn() + 'static>(
177 &self,
178 pty_flags: PtyFlags,
179 working_directory: Option<&str>,
180 argv: &[&str],
181 envv: &[&str],
182 fds: Vec<OwnedFd>,
183 map_fds: &[RawFd],
184 spawn_flags: glib::SpawnFlags,
185 child_setup: Q,
186 timeout: i32,
187 ) -> std::pin::Pin<
188 Box_<dyn std::future::Future<Output = Result<glib::Pid, glib::Error>> + 'static>,
189 > {
190 let working_directory = working_directory.map(ToOwned::to_owned);
191 let argv: Vec<String> = argv.iter().map(|p| p.to_string()).collect();
192 let envv: Vec<String> = envv.iter().map(|p| p.to_string()).collect();
193 let map_fds: Vec<RawFd> = map_fds.to_vec();
194 Box_::pin(gio::GioFuture::new(self, move |obj, cancellable, send| {
195 let argv: Vec<&str> = argv.iter().map(|s| s.as_str()).collect();
196 let envv: Vec<&str> = envv.iter().map(|s| s.as_str()).collect();
197 obj.spawn_with_fds_async(
198 pty_flags,
199 working_directory.as_deref(),
200 &argv,
201 &envv,
202 fds,
203 &map_fds,
204 spawn_flags,
205 child_setup,
206 timeout,
207 Some(cancellable),
208 move |res| {
209 send.resolve(res);
210 },
211 );
212 }))
213 }
214
215 #[doc(alias = "vte_terminal_set_colors")]
216 fn set_colors(
217 &self,
218 foreground: Option<&gdk::RGBA>,
219 background: Option<&gdk::RGBA>,
220 palette: &[&gdk::RGBA],
221 ) {
222 let palette_size = palette.len();
223
224 let palette_vector = palette
225 .iter()
226 .map(|item| unsafe { *item.to_glib_none().0 })
227 .collect::<Vec<gdk::ffi::GdkRGBA>>();
228
229 unsafe {
230 ffi::vte_terminal_set_colors(
231 self.as_ref().to_glib_none().0,
232 foreground.to_glib_none().0,
233 background.to_glib_none().0,
234 palette_vector.as_ptr(),
235 palette_size,
236 );
237 }
238 }
239
240 fn watch_child(&self, child_pid: glib::Pid) {
241 unsafe {
242 ffi::vte_terminal_watch_child(self.as_ref().to_glib_none().0, child_pid.into_glib());
243 }
244 }
245
246 fn spawn_async<P: FnOnce(Result<glib::Pid, glib::Error>) + 'static, Q: Fn() + 'static>(
247 &self,
248 pty_flags: PtyFlags,
249 working_directory: Option<&str>,
250 argv: &[&str],
251 envv: &[&str],
252 spawn_flags: glib::SpawnFlags,
253 child_setup: Q,
254 timeout: i32,
255 cancellable: Option<&impl IsA<gio::Cancellable>>,
256 callback: P,
257 ) {
258 assert_initialized_main_thread!();
259 let main_context = glib::MainContext::ref_thread_default();
260 let is_main_context_owner = main_context.is_owner();
261 let has_acquired_main_context = (!is_main_context_owner)
262 .then(|| main_context.acquire().ok())
263 .flatten();
264 assert!(
265 is_main_context_owner || has_acquired_main_context.is_some(),
266 "Async operations only allowed if the thread is owning the MainContext"
267 );
268 assert!(argv.first().is_some(), "Need to pass an argument");
269 let child_setup_data: Box_<glib::thread_guard::ThreadGuard<Q>> =
270 Box_::new(glib::thread_guard::ThreadGuard::new(child_setup));
271 unsafe extern "C" fn child_setup_func<Q: Fn() + 'static>(user_data: glib::ffi::gpointer) {
272 let callback: Box_<glib::thread_guard::ThreadGuard<Q>> =
273 Box_::from_raw(user_data as *mut _);
274 let callback = callback.into_inner();
275 callback()
276 }
277 let child_setup = Some(child_setup_func::<Q> as _);
278 let callback_data: Box_<glib::thread_guard::ThreadGuard<P>> =
279 Box_::new(glib::thread_guard::ThreadGuard::new(callback));
280 unsafe extern "C" fn spawn_async_trampoline<
281 P: FnOnce(Result<glib::Pid, glib::Error>) + 'static,
282 >(
283 _terminal: *mut ffi::VteTerminal,
284 pid: glib::ffi::GPid,
285 error: *mut glib::ffi::GError,
286 user_data: glib::ffi::gpointer,
287 ) {
288 let pid = from_glib(pid);
289 let result = if let Some(err) = Option::<glib::Error>::from_glib_none(error) {
290 Err(err)
291 } else {
292 Ok(pid)
293 };
294 let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
295 Box_::from_raw(user_data as *mut _);
296 let callback = callback.into_inner();
297 callback(result)
298 }
299 let callback = Some(spawn_async_trampoline::<P> as _);
300 unsafe extern "C" fn child_setup_data_destroy_func<Q: Fn() + 'static>(
301 data: glib::ffi::gpointer,
302 ) {
303 let _callback: Box_<Q> = Box_::from_raw(data as *mut _);
304 }
305 let destroy_call8 = Some(child_setup_data_destroy_func::<Q> as _);
306 let super_callback0: Box_<glib::thread_guard::ThreadGuard<Q>> = child_setup_data;
307 let super_callback1: Box_<glib::thread_guard::ThreadGuard<P>> = callback_data;
308 unsafe {
309 ffi::vte_terminal_spawn_async(
310 self.as_ref().to_glib_none().0,
311 pty_flags.into_glib(),
312 working_directory.to_glib_none().0,
313 argv.to_glib_none().0,
314 envv.to_glib_none().0,
315 spawn_flags.into_glib(),
316 child_setup,
317 Box_::into_raw(super_callback0) as *mut _,
318 destroy_call8,
319 timeout,
320 cancellable.map(|p| p.as_ref()).to_glib_none().0,
321 callback,
322 Box_::into_raw(super_callback1) as *mut _,
323 );
324 }
325 }
326
327 unsafe fn spawn_with_fds_async<
331 P: FnOnce(Result<glib::Pid, glib::Error>) + 'static,
332 Q: Fn() + 'static,
333 >(
334 &self,
335 pty_flags: PtyFlags,
336 working_directory: Option<&str>,
337 argv: &[&str],
338 envv: &[&str],
339 fds: Vec<OwnedFd>,
340 map_fds: &[RawFd],
341 spawn_flags: glib::SpawnFlags,
342 child_setup: Q,
343 timeout: i32,
344 cancellable: Option<&impl IsA<gio::Cancellable>>,
345 callback: P,
346 ) {
347 assert_initialized_main_thread!();
348 let main_context = glib::MainContext::ref_thread_default();
349 let is_main_context_owner = main_context.is_owner();
350 let has_acquired_main_context = (!is_main_context_owner)
351 .then(|| main_context.acquire().ok())
352 .flatten();
353 assert!(
354 is_main_context_owner || has_acquired_main_context.is_some(),
355 "Async operations only allowed if the thread is owning the MainContext"
356 );
357 assert!(argv.first().is_some(), "Need to pass an argument");
358 let n_fds = fds.len() as _;
359 let n_map_fds = map_fds.len() as _;
360 let child_setup_data: Box_<glib::thread_guard::ThreadGuard<Q>> =
361 Box_::new(glib::thread_guard::ThreadGuard::new(child_setup));
362 unsafe extern "C" fn child_setup_func<Q: Fn() + 'static>(user_data: glib::ffi::gpointer) {
363 let callback: Box_<glib::thread_guard::ThreadGuard<Q>> =
364 Box_::from_raw(user_data as *mut _);
365 let callback = callback.into_inner();
366 callback()
367 }
368
369 let child_setup = Some(child_setup_func::<Q> as _);
370
371 let callback_data: Box_<glib::thread_guard::ThreadGuard<P>> =
372 Box_::new(glib::thread_guard::ThreadGuard::new(callback));
373 unsafe extern "C" fn spawn_with_fds_async_trampoline<
374 P: FnOnce(Result<glib::Pid, glib::Error>) + 'static,
375 >(
376 _terminal: *mut ffi::VteTerminal,
377 pid: glib::ffi::GPid,
378 error: *mut glib::ffi::GError,
379 user_data: glib::ffi::gpointer,
380 ) {
381 let pid = from_glib(pid);
382 let result = if let Some(err) = Option::<glib::Error>::from_glib_none(error) {
383 Err(err)
384 } else {
385 Ok(pid)
386 };
387 let callback: Box_<glib::thread_guard::ThreadGuard<P>> =
388 Box_::from_raw(user_data as *mut _);
389 let callback = callback.into_inner();
390 callback(result)
391 }
392 let callback = Some(spawn_with_fds_async_trampoline::<P> as _);
393 unsafe extern "C" fn child_setup_data_destroy_func<Q: Fn() + 'static>(
394 data: glib::ffi::gpointer,
395 ) {
396 let _callback: Box_<Q> = Box_::from_raw(data as *mut _);
397 }
398 let destroy_call12 = Some(child_setup_data_destroy_func::<Q> as _);
399 let super_callback0: Box_<glib::thread_guard::ThreadGuard<Q>> = child_setup_data;
400 let super_callback1: Box_<glib::thread_guard::ThreadGuard<P>> = callback_data;
401 let fds: Vec<RawFd> = fds.into_iter().map(|x| x.into_raw_fd()).collect();
402 unsafe {
403 ffi::vte_terminal_spawn_with_fds_async(
404 self.as_ref().to_glib_none().0,
405 pty_flags.into_glib(),
406 working_directory.to_glib_none().0,
407 argv.to_glib_none().0,
408 envv.to_glib_none().0,
409 fds.to_glib_none().0,
410 n_fds,
411 map_fds.to_glib_none().0,
412 n_map_fds,
413 spawn_flags.into_glib(),
414 child_setup,
415 Box_::into_raw(super_callback0) as *mut _,
416 destroy_call12,
417 timeout,
418 cancellable.map(|p| p.as_ref()).to_glib_none().0,
419 callback,
420 Box_::into_raw(super_callback1) as *mut _,
421 );
422 }
423 }
424}