vte4/
terminal.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use 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    // # Safety
173    //
174    // The map_fds have to make sense.
175    #[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    // # Safety
328    //
329    // The map_fds have to make sense.
330    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}