wasmer_wasix/os/tty/
tty_sys.rs

1use super::TtyBridge;
2use crate::WasiTtyState;
3
4/// [`TtyBridge`] implementation for Unix systems.
5#[derive(Debug, Default, Clone)]
6pub struct SysTty;
7
8impl TtyBridge for SysTty {
9    fn reset(&self) {
10        sys::reset().ok();
11    }
12
13    fn tty_get(&self) -> WasiTtyState {
14        let echo = sys::is_mode_echo();
15        let line_buffered = sys::is_mode_line_buffering();
16        let line_feeds = sys::is_mode_line_feeds();
17        let stdin_tty = sys::is_stdin_tty();
18        let stdout_tty = sys::is_stdout_tty();
19        let stderr_tty = sys::is_stderr_tty();
20        let (cols, rows) = sys_terminal_size::get_terminal_size();
21
22        WasiTtyState {
23            cols,
24            rows,
25            width: 800,
26            height: 600,
27            stdin_tty,
28            stdout_tty,
29            stderr_tty,
30            echo,
31            line_buffered,
32            line_feeds,
33        }
34    }
35
36    fn tty_set(&self, tty_state: WasiTtyState) {
37        if tty_state.echo {
38            sys::set_mode_echo().ok();
39        } else {
40            sys::set_mode_no_echo().ok();
41        }
42        if tty_state.line_buffered {
43            sys::set_mode_line_buffered().ok();
44        } else {
45            sys::set_mode_no_line_buffered().ok();
46        }
47        if tty_state.line_feeds {
48            sys::set_mode_line_feeds().ok();
49        } else {
50            sys::set_mode_no_line_feeds().ok();
51        }
52    }
53}
54
55mod sys_terminal_size {
56    static DEFAULT_SIZE: (u32, u32) = (80, 25);
57
58    #[cfg(not(target_arch = "wasm32"))]
59    pub fn get_terminal_size() -> (u32, u32) {
60        if let Some((terminal_size::Width(width), terminal_size::Height(height))) =
61            terminal_size::terminal_size()
62        {
63            (width.into(), height.into())
64        } else {
65            DEFAULT_SIZE
66        }
67    }
68
69    #[cfg(target_arch = "wasm32")]
70    pub fn get_terminal_size() -> (u32, u32) {
71        DEFAULT_SIZE
72    }
73}
74
75#[allow(unused_mut, unused_imports)]
76#[cfg(all(unix, not(target_os = "ios")))]
77mod sys {
78    use {
79        libc::{
80            ECHO, ECHOCTL, ECHOE, ECHOK, ECHONL, ICANON, ICRNL, IEXTEN, IGNCR, ISIG, IXON, ONLCR,
81            OPOST, TCSANOW, c_int, tcsetattr, termios,
82        },
83        std::mem,
84        std::os::unix::io::AsRawFd,
85    };
86
87    fn io_result(ret: libc::c_int) -> std::io::Result<()> {
88        match ret {
89            0 => Ok(()),
90            _ => Err(std::io::Error::last_os_error()),
91        }
92    }
93
94    pub fn reset() -> Result<(), anyhow::Error> {
95        let mut termios = mem::MaybeUninit::<termios>::uninit();
96        io_result(unsafe { ::libc::tcgetattr(0, termios.as_mut_ptr()) })?;
97        let mut termios = unsafe { termios.assume_init() };
98
99        termios.c_lflag |= ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL;
100
101        unsafe { tcsetattr(0, TCSANOW, &termios) };
102        Ok(())
103    }
104
105    pub fn is_stdin_tty() -> bool {
106        ::termios::Termios::from_fd(0).is_ok()
107    }
108
109    pub fn is_stdout_tty() -> bool {
110        ::termios::Termios::from_fd(1).is_ok()
111    }
112
113    pub fn is_stderr_tty() -> bool {
114        ::termios::Termios::from_fd(2).is_ok()
115    }
116
117    pub fn is_mode_echo() -> bool {
118        if let Ok(termios) = ::termios::Termios::from_fd(0) {
119            (termios.c_lflag & ::termios::ECHO) != 0
120        } else {
121            false
122        }
123    }
124
125    pub fn is_mode_line_buffering() -> bool {
126        if let Ok(termios) = ::termios::Termios::from_fd(0) {
127            (termios.c_lflag & ::termios::ICANON) != 0
128        } else {
129            false
130        }
131    }
132
133    pub fn is_mode_line_feeds() -> bool {
134        if let Ok(termios) = ::termios::Termios::from_fd(0) {
135            (termios.c_lflag & ::termios::ONLCR) != 0
136        } else {
137            false
138        }
139    }
140
141    pub fn set_mode_no_echo() -> Result<(), anyhow::Error> {
142        let mut termios = mem::MaybeUninit::<termios>::uninit();
143        io_result(unsafe { ::libc::tcgetattr(0, termios.as_mut_ptr()) })?;
144        let mut termios = unsafe { termios.assume_init() };
145
146        termios.c_lflag &= !ECHO;
147        termios.c_lflag &= !ECHOE;
148        termios.c_lflag &= !ECHOK;
149        termios.c_lflag &= !ECHOCTL;
150        termios.c_lflag &= !IEXTEN;
151        /*
152        termios.c_lflag &= !ISIG;
153        termios.c_lflag &= !IXON;
154        termios.c_lflag &= !ICRNL;
155        termios.c_lflag &= !OPOST;
156        */
157
158        unsafe { tcsetattr(0, TCSANOW, &termios) };
159        Ok(())
160    }
161
162    pub fn set_mode_echo() -> Result<(), anyhow::Error> {
163        let mut termios = mem::MaybeUninit::<termios>::uninit();
164        io_result(unsafe { ::libc::tcgetattr(0, termios.as_mut_ptr()) })?;
165        let mut termios = unsafe { termios.assume_init() };
166
167        termios.c_lflag |= ECHO;
168        termios.c_lflag |= ECHOE;
169        termios.c_lflag |= ECHOK;
170        termios.c_lflag |= ECHOCTL;
171        termios.c_lflag |= IEXTEN;
172        /*
173        termios.c_lflag |= ISIG;
174        termios.c_lflag |= IXON;
175        termios.c_lflag |= ICRNL;
176        termios.c_lflag |= OPOST;
177        */
178
179        unsafe { tcsetattr(0, TCSANOW, &termios) };
180        Ok(())
181    }
182
183    pub fn set_mode_no_line_buffered() -> Result<(), anyhow::Error> {
184        let mut termios = mem::MaybeUninit::<termios>::uninit();
185        io_result(unsafe { ::libc::tcgetattr(0, termios.as_mut_ptr()) })?;
186        let mut termios = unsafe { termios.assume_init() };
187
188        termios.c_lflag &= !ICANON;
189
190        unsafe { tcsetattr(0, TCSANOW, &termios) };
191        Ok(())
192    }
193
194    pub fn set_mode_line_buffered() -> Result<(), anyhow::Error> {
195        let mut termios = mem::MaybeUninit::<termios>::uninit();
196        io_result(unsafe { ::libc::tcgetattr(0, termios.as_mut_ptr()) })?;
197        let mut termios = unsafe { termios.assume_init() };
198
199        termios.c_lflag |= ICANON;
200
201        unsafe { tcsetattr(0, TCSANOW, &termios) };
202        Ok(())
203    }
204
205    pub fn set_mode_no_line_feeds() -> Result<(), anyhow::Error> {
206        let mut termios = mem::MaybeUninit::<termios>::uninit();
207        io_result(unsafe { ::libc::tcgetattr(0, termios.as_mut_ptr()) })?;
208        let mut termios = unsafe { termios.assume_init() };
209
210        termios.c_lflag &= !ONLCR;
211
212        unsafe { tcsetattr(0, TCSANOW, &termios) };
213        Ok(())
214    }
215
216    pub fn set_mode_line_feeds() -> Result<(), anyhow::Error> {
217        let mut termios = mem::MaybeUninit::<termios>::uninit();
218        io_result(unsafe { ::libc::tcgetattr(0, termios.as_mut_ptr()) })?;
219        let mut termios = unsafe { termios.assume_init() };
220
221        termios.c_lflag |= ONLCR;
222
223        unsafe { tcsetattr(0, TCSANOW, &termios) };
224        Ok(())
225    }
226}
227
228#[cfg(any(not(unix), target_os = "ios"))]
229mod sys {
230    pub fn reset() -> Result<(), anyhow::Error> {
231        Ok(())
232    }
233
234    pub fn is_stdin_tty() -> bool {
235        false
236    }
237
238    pub fn is_stdout_tty() -> bool {
239        false
240    }
241
242    pub fn is_stderr_tty() -> bool {
243        false
244    }
245
246    pub fn is_mode_echo() -> bool {
247        true
248    }
249
250    pub fn is_mode_line_buffering() -> bool {
251        true
252    }
253
254    pub fn is_mode_line_feeds() -> bool {
255        true
256    }
257
258    pub fn set_mode_no_echo() -> Result<(), anyhow::Error> {
259        Ok(())
260    }
261
262    pub fn set_mode_echo() -> Result<(), anyhow::Error> {
263        Ok(())
264    }
265
266    pub fn set_mode_no_line_buffered() -> Result<(), anyhow::Error> {
267        Ok(())
268    }
269
270    pub fn set_mode_line_buffered() -> Result<(), anyhow::Error> {
271        Ok(())
272    }
273
274    pub fn set_mode_no_line_feeds() -> Result<(), anyhow::Error> {
275        Ok(())
276    }
277
278    pub fn set_mode_line_feeds() -> Result<(), anyhow::Error> {
279        Ok(())
280    }
281}