From 2fee948de10d28a4b09ac4188d6967373bac5cac Mon Sep 17 00:00:00 2001 From: andromeda Date: Sat, 17 Jan 2026 00:40:22 +0100 Subject: [PATCH] nonblocking, good stuff w/ vte :) --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + src/main.rs | 47 +++++++++++++++++++++++++++++------------------ 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ade81cc..4527a6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,11 +50,21 @@ dependencies = [ "libc", ] +[[package]] +name = "nonblock" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c7a4f22e5f2e2bd805d6ab56f1ae87eb1815673e1b452048896fb687a8a3d4" +dependencies = [ + "libc", +] + [[package]] name = "rustty" version = "0.1.0" dependencies = [ "nix", + "nonblock", "vte", ] diff --git a/Cargo.toml b/Cargo.toml index f4b7be2..f282520 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] nix = {default-features = false, features=["term","process","fs"], version="0.30.1"} +nonblock = "0.2.0" vte = "0.15.0" diff --git a/src/main.rs b/src/main.rs index 05fb64e..6e680f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,16 @@ +use nix::fcntl::{fcntl, F_GETFL, F_SETFL, OFlag}; use nix::pty::{ForkptyResult, forkpty, PtyMaster}; -use nix::unistd::read; +use nix::unistd::{execv, read, write}; -use std::vec::Vec; -use std::io::{self, Read, Write}; -use std::process::Command; -use std::thread; -use std::time::Duration; +use nonblock::NonBlockingReader; + +use std::vec; +use std::ffi::CString; +use std::io; use vte::{Params, Parser, Perform}; +// yoinked form vte docs struct Performer; impl Perform for Performer { fn print(&mut self, c: char) { @@ -54,25 +56,35 @@ impl Perform for Performer { } fn main() { - // TODO get rid of hard coded `sh` path - let mut pty = spawn_pty("/home/andromeda/.nix-profile/bin/sh").unwrap(); + let pty = spawn_pty("/home/andromeda/.nix-profile/bin/sh").unwrap(); println!("{:?}", &pty); + let pty_flags = fcntl(&pty, F_GETFL).unwrap(); + fcntl(&pty, F_SETFL(OFlag::from_bits_truncate(pty_flags) | OFlag::O_NONBLOCK)).unwrap(); - let input = io::stdin(); - let mut handle = input.lock(); + let stdin = io::stdin(); + let mut nb_stdin = NonBlockingReader::from_fd(stdin).unwrap(); let mut statemachine = Parser::new(); let mut performer = Performer; let mut buf = [0u8; 2048]; + let mut vec: Vec = vec!(); - while true { + loop { match read(&pty, &mut buf) { - Ok(0) => break, + Ok(0) => (), + Ok(n) => statemachine.advance(&mut performer, &buf[..n]), + Err(_e) => (), + }; + vec.clear(); + match &nb_stdin.read_available(&mut vec) { + Ok(0) => (), Ok(n) => { - statemachine.advance(&mut performer, &buf[..n]); + let un = *n as usize; + buf[..un].copy_from_slice(&vec[..un]); + let _ = write(&pty, &buf[..un]); }, - Err(_e) => break, + Err(e) => println!("error: {:?}", e), }; }; } @@ -82,14 +94,13 @@ fn spawn_pty(shell: &str) -> Option { // SAFETY safe unless os out of PTYs; incredibly unlikely match unsafe {forkpty(None, None)} { Ok(fork_pty_res) => match fork_pty_res { - ForkptyResult::Parent {master, ..} => { + ForkptyResult::Parent {child:_, master} => { + // SAFETY `master` is a valid PtyMaster return Some(unsafe{PtyMaster::from_owned_fd(master)}); } ForkptyResult::Child =>{ - Command::new(shell).spawn(); - // TODO come up with an elegant solution - thread::sleep(Duration::MAX); + let _ = execv::(&CString::new(shell).unwrap(), &[]); return None; } },