repo_description: Terminal session recorder, streamer and player 📹
repo_topics: asciicast asciinema cli recording rustlang streaming terminal
code: use std::fs::File;
  use std::io::{Read, Write};
  use std::os::fd::{AsFd, AsRawFd};
  use std::os::unix::fs::OpenOptionsExt;
  
  use async_trait::async_trait;
  use nix::libc;
  use nix::pty::Winsize;
  use nix::sys::termios::{self, SetArg, Termios};
  use tokio::io::unix::AsyncFd;
  use tokio::io::{self, Interest};
  
  use super::{RawTty, TtySize};
  
  pub struct DevTty {
      file: AsyncFd<File>,
      settings: libc::termios,
  }
  
  impl DevTty {
      pub async fn open() -> anyhow::Result<Self> {
          let file = File::options()
              .read(true)
              .write(true)
              .custom_flags(libc::O_NONBLOCK)
              .open("/dev/tty")?;
  
          let file = AsyncFd::new(file)?;
          let settings = super::make_raw(&file)?;
  
          Ok(Self { file, settings })
      }
  
      pub async fn resize(&mut self, size: TtySize) -> io::Result<()> {
          let xtwinops_seq = format!("\x1b[8;{};{}t", size.1, size.0);
          self.write_all(xtwinops_seq.as_bytes()).await?;
  
          Ok(())
      }
  }
  
  impl Drop for DevTty {
      fn drop(&mut self) {
          let termios = Termios::from(self.settings);
          let _ = termios::tcsetattr(self.file.as_fd(), SetArg::TCSANOW, &termios);
      }
  }
  
  #[async_trait(?Send)]
  impl RawTty for DevTty {
      fn get_size(&self) -> Winsize {
          let mut winsize = Winsize {
              ws_row: 24,
              ws_col: 80,
              ws_xpixel: 0,
              ws_ypixel: 0,
          };
  
          unsafe { libc::ioctl(self.file.as_raw_fd(), libc::TIOCGWINSZ, &mut winsize) };
  
          winsize
      }
  
      async fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
          self.file
              .async_io(Interest::READABLE, |mut file| file.read(buf))
              .await
      }
  
      async fn write(&self, buf: &[u8]) -> io::Result<usize> {
          self.file
              .async_io(Interest::WRITABLE, |mut file| file.write(buf))
              .await
      }
  }
  