moa/emulator/frontends/common/src/tty.rs

98 lines
2.5 KiB
Rust

use std::thread;
use std::sync::mpsc;
use std::time::Duration;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use nix::fcntl::OFlag;
use nix::pty::{self, PtyMaster};
use nix::fcntl::{fcntl, FcntlArg};
use moa_host::Tty;
#[derive(Debug, PartialEq, Eq)]
pub enum SimplePtyError {
Open,
PtsName,
}
pub struct SimplePty {
pub name: String,
input: mpsc::Receiver<u8>,
output: mpsc::Sender<u8>,
}
impl SimplePty {
pub fn new(name: String, input: mpsc::Receiver<u8>, output: mpsc::Sender<u8>) -> SimplePty {
SimplePty {
name,
input,
output,
}
}
pub fn open() -> Result<SimplePty, SimplePtyError> {
let pty = pty::posix_openpt(OFlag::O_RDWR)
.and_then(|pty| {
pty::grantpt(&pty)?;
pty::unlockpt(&pty)?;
Ok(pty)
})
.map_err(|_| SimplePtyError::Open)?;
let name = unsafe { pty::ptsname(&pty).map_err(|_| SimplePtyError::PtsName)? };
let (input_tx, input_rx) = mpsc::channel();
let (output_tx, output_rx) = mpsc::channel();
let shared = SimplePty::new(name.clone(), input_rx, output_tx);
SimplePty::spawn_poller(pty, name, input_tx, output_rx);
Ok(shared)
}
fn spawn_poller(mut pty: PtyMaster, name: String, input_tx: mpsc::Sender<u8>, output_rx: mpsc::Receiver<u8>) {
thread::spawn(move || {
println!("pty: spawned reader for {}", name);
fcntl(pty.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).unwrap();
let mut buf = [0; 1];
loop {
match pty.read(&mut buf) {
Ok(_) => {
input_tx.send(buf[0]).unwrap();
},
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {},
Err(err) => {
println!("ERROR: {:?}", err);
},
}
while let Ok(data) = output_rx.try_recv() {
pty.write_all(&[data]).unwrap();
}
thread::sleep(Duration::from_millis(10));
}
});
}
}
impl Tty for SimplePty {
fn device_name(&self) -> String {
self.name.clone()
}
fn read(&mut self) -> Option<u8> {
match self.input.try_recv() {
Ok(data) => Some(data),
_ => None,
}
}
fn write(&mut self, output: u8) -> bool {
self.output.send(output).unwrap();
true
}
}