small software-rendered rust tty
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.toml | 15 | ||||
| -rw-r--r-- | rustfmt.toml | 3 | ||||
| -rw-r--r-- | src/main.rs | 142 |
4 files changed, 161 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffa3bbd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Cargo.lock
\ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..eecf677 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "pattypan" +version = "0.1.0" +edition = "2024" + +[dependencies] +anstream = "0.6.18" +anyhow = "1.0.98" +ctlfun = { git = "https://git.buny.computer/lambda/ctlfun.git", version = "0.1.0" } +fontdue = "0.9.3" +implicit-fn = "0.1.0" +libc = "0.2.172" +minifb = "0.28.0" +nix = { version = "0.30.1", features = ["process", "term"] } +parking_lot = "0.12.3" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3a18f83 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +max_width = 75 +group_imports = "StdExternalCrate" +imports_granularity = "Module" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3120ea3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,142 @@ +#![feature(deadline_api)] +use std::io::Write; +use std::iter::successors; +use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd}; +use std::process::{Command, exit}; +use std::sync::mpsc; +use std::thread::sleep; +use std::time::Duration; + +use anyhow::Result; +use minifb::{InputCallback, Key, WindowOptions}; +use nix::pty::{ForkptyResult, forkpty}; + +fn spawn(shell: &str) -> Result<OwnedFd> { + let x = unsafe { forkpty(None, None)? }; + match x { + ForkptyResult::Child => { + let sh = Command::new(shell).spawn()?.wait(); + // std::thread::sleep(Duration::from_millis(5000)); + // exit(0); + + exit(0); + } + ForkptyResult::Parent { child, master } => { + use libc::{F_GETFL, F_SETFL, O_NONBLOCK, fcntl}; + unsafe { + assert_eq!( + fcntl( + master.as_raw_fd(), + F_SETFL, + fcntl(master.as_raw_fd(), F_GETFL, 0) | O_NONBLOCK, + ), + 0 + ) + }; + Ok(master) + } + } +} + +fn read(fd: BorrowedFd) -> Option<Vec<u8>> { + let mut x = [0; 1 << 16]; + let n = nix::unistd::read(fd, &mut x).ok()?; + Some(x[..n].to_vec()) +} +fn write(fd: BorrowedFd, x: &[u8]) -> Result<()> { + let n = nix::unistd::write(fd, x)?; + anyhow::ensure!(n == x.len()); + Ok(()) +} + +struct KeyPress(mpsc::Sender<Key>); +impl InputCallback for KeyPress { + fn add_char(&mut self, _: u32) {} + fn set_key_state(&mut self, key: Key, state: bool) { + if state { + self.0.send((key)).unwrap(); + } + } +} +enum Event { + Read(Vec<u8>), + Write(Key), +} +fn main() -> Result<()> { + let mut w = minifb::Window::new( + "pattypan", + 5, + 5, + WindowOptions { + borderless: true, + title: false, + resize: true, + ..Default::default() + }, + )?; + + // input + let (ktx, krx) = mpsc::channel::<Key>(); + + w.set_input_callback(Box::new(KeyPress(ktx))); + w.update(); + + let pty = spawn("fish")?; + let pty1 = pty.try_clone()?; + + std::thread::spawn(move || { + while let Ok(k) = krx.recv() { + let x = match k { + Key::Enter => b"\n", + Key::Space => b" ", + _ => &[k as u8 - 10 + b'a'], + }; + write(pty1.as_fd(), x).unwrap(); + } + }); + + // output + let (ttx, trx) = mpsc::channel(); + + std::thread::spawn(move || { + loop { + let x = successors(read(pty.as_fd()), |_| read(pty.as_fd())) + .flatten() + .collect::<Vec<u8>>(); + if !x.is_empty() { + // println!("recv"); + ttx.send(x).unwrap(); + } + sleep(Duration::from_millis(10)) + } + }); + + // let x = b"echo -e \"\x1b(0lqqqk\nx \x1b(Bx\nmqqqj"; + // let x = String::from_utf8_lossy(&x); + // println!("{}", x); + let mut s = anstream::StripStream::new(std::io::stdout()); + loop { + while let Ok(x) = trx.recv_timeout(Duration::from_millis(50)) { + s.write_all(&x)?; + } + s.flush()?; + w.update(); + sleep(Duration::from_millis(10)) + } + + // println!("-------------------"); + // let mut t = TerminalInputParser::new(); + // for char in x { + // use ctlfun::TerminalInput::*; + // match t.parse_byte(char) { + // Continue => { + // print!("-"); + // } + // Char(x) => print!("{x}"), + // Control(control_function) => println!("{:?}", control_function), + // _ => panic!(), + // } + // } + + Ok(()) +} |