Add raw mode module

This commit is contained in:
Michael Zhang 2023-01-27 03:15:02 -06:00
parent c555b2e006
commit 250793b330
3 changed files with 57 additions and 3 deletions

View file

@ -7,6 +7,7 @@ extern crate tracing;
pub mod asciicast;
pub mod recorder;
mod raw_term;
// pub mod recorder;
// pub mod term;
// pub mod writer;

38
src/raw_term.rs Normal file
View file

@ -0,0 +1,38 @@
use std::os::unix::prelude::RawFd;
use anyhow::Result;
use nix::sys::termios::Termios;
/// Wraps a RawFd in order to enable raw mode.
pub struct RawTerm(RawFd, Termios);
impl RawTerm {
pub fn init(fd: RawFd) -> Result<Self> {
use nix::sys::termios::*;
let saved_mode = tcgetattr(fd)?;
let mut mode = saved_mode.clone();
mode.input_flags &= !(InputFlags::BRKINT
| InputFlags::ICRNL
| InputFlags::INPCK
| InputFlags::ISTRIP
| InputFlags::IXON);
mode.output_flags &= !OutputFlags::OPOST;
mode.control_flags &= !(ControlFlags::CSIZE | ControlFlags::PARENB);
mode.control_flags |= ControlFlags::CS8;
mode.local_flags &= !(LocalFlags::ECHO
| LocalFlags::ICANON
| LocalFlags::IEXTEN
| LocalFlags::ISIG);
mode.control_chars[libc::VMIN] = 1;
mode.control_chars[libc::VTIME] = 0;
tcsetattr(fd, SetArg::TCSAFLUSH, &mode)?;
Ok(RawTerm(fd, saved_mode))
}
}
impl Drop for RawTerm {
fn drop(&mut self) {
use nix::sys::termios::*;
tcsetattr(self.0, SetArg::TCSAFLUSH, &self.1).unwrap();
}
}

View file

@ -2,7 +2,8 @@
use std::env;
use std::ffi::CString;
use std::os::unix::prelude::{AsRawFd, OsStrExt, RawFd};
use std::io::stdin;
use std::os::unix::prelude::{AsFd, AsRawFd, OsStrExt, RawFd};
use std::process::{Command, Stdio};
use std::sync::mpsc::{self, Receiver, Sender};
use std::time::Instant;
@ -11,14 +12,21 @@ use anyhow::{Context, Result};
use libc::{O_RDWR, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
use nix::fcntl::{open, OFlag};
use nix::pty::{openpty, OpenptyResult};
use nix::sys::select::{select, FdSet};
use nix::sys::stat::Mode;
use nix::sys::termios::{
ControlFlags, InputFlags, LocalFlags, OutputFlags, SetArg,
};
use nix::sys::{
select::{select, FdSet},
stat::Mode,
termios,
};
use nix::unistd::{
close, dup, dup2, execvp, execvpe, fork, fsync, read, setsid, ttyname, write,
ForkResult,
};
use crate::asciicast::{Event, EventKind};
use crate::raw_term::RawTerm;
pub struct Terminal {
event_tx: Sender<Event>,
@ -56,6 +64,10 @@ impl Terminal {
command.env(key, val);
}
// Set raw mode
let stdin = stdin();
let _term = RawTerm::init(stdin.as_raw_fd())?;
// Open a pty
let pty = openpty(None, None)?;
let parent_stdout_dup = dup(STDOUT_FILENO)?;
@ -135,6 +147,9 @@ impl Terminal {
// Master is ready for read, which means child process stdout has data
// (child process stdout) -> (pty.slave) -> (pty.master)
if read_fd_set.contains(pty.master) {
// TODO: This line trips if the child process dies from, say, Ctrl+D. Need to catch this
// somehow and gracefully react to it.
// Possibly signals would be a good solution here?
let bytes_read =
read(pty.master, &mut buf).context("Read from master")?;
if bytes_read == 0 {