small software-rendered rust tty
bendn 10 months ago
commit e1b5326
-rw-r--r--.gitignore1
-rw-r--r--Cargo.toml15
-rw-r--r--rustfmt.toml3
-rw-r--r--src/main.rs142
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(())
+}