opentitanlib/util/
raw_tty.rs1use std::io::{IsTerminal, Read, Result};
6use std::ops::{Deref, DerefMut};
7use std::os::fd::{AsFd, BorrowedFd};
8
9use rustix::termios::{self, Termios};
10use tokio::io::AsyncRead;
11
12pub struct RawTty<T: AsFd> {
17 termios: Option<Termios>,
18 fd: T,
19}
20
21impl<T: AsFd> Deref for RawTty<T> {
22 type Target = T;
23
24 fn deref(&self) -> &T {
25 &self.fd
26 }
27}
28
29impl<T: AsFd> DerefMut for RawTty<T> {
30 fn deref_mut(&mut self) -> &mut T {
31 &mut self.fd
32 }
33}
34
35impl<T: AsFd> AsFd for RawTty<T> {
36 fn as_fd(&self) -> BorrowedFd<'_> {
37 self.fd.as_fd()
38 }
39}
40
41impl<T: AsFd + Read> Read for RawTty<T> {
42 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
43 self.fd.read(buf)
44 }
45}
46
47impl<T: AsFd + AsyncRead> AsyncRead for RawTty<T> {
48 fn poll_read(
49 self: std::pin::Pin<&mut Self>,
50 cx: &mut std::task::Context<'_>,
51 buf: &mut tokio::io::ReadBuf<'_>,
52 ) -> std::task::Poll<Result<()>> {
53 unsafe { self.map_unchecked_mut(|x| &mut x.fd) }.poll_read(cx, buf)
55 }
56}
57
58impl<T: AsFd> RawTty<T> {
59 pub fn new(fd: T) -> Result<Self> {
60 let termios = if fd.as_fd().is_terminal() {
61 let termios = termios::tcgetattr(&fd)?;
62
63 let mut raw_termios = termios.clone();
64 raw_termios.make_raw();
65 termios::tcsetattr(&fd, termios::OptionalActions::Now, &raw_termios)?;
66
67 Some(termios)
68 } else {
69 None
70 };
71
72 Ok(Self { termios, fd })
73 }
74}
75
76impl<T: AsFd> Drop for RawTty<T> {
77 fn drop(&mut self) {
78 if let Some(termios) = self.termios.as_ref() {
79 termios::tcsetattr(&self.fd, termios::OptionalActions::Now, termios).unwrap();
80 }
81 }
82}