This commit is contained in:
andromeda
2026-01-16 21:31:03 +01:00
commit eedc84d871
7 changed files with 325 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target
/result*

69
Cargo.lock generated Normal file
View File

@@ -0,0 +1,69 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "libc"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "rustty"
version = "0.1.0"
dependencies = [
"nix",
"vte",
]
[[package]]
name = "vte"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5924018406ce0063cd67f8e008104968b74b563ee1b85dde3ed1f7cb87d3dbd"
dependencies = [
"arrayvec",
"memchr",
]

8
Cargo.toml Normal file
View File

@@ -0,0 +1,8 @@
[package]
name = "rustty"
version = "0.1.0"
edition = "2024"
[dependencies]
nix = {default-features = false, features=["term","process","fs"], version="0.30.1"}
vte = "0.15.0"

87
flake.lock generated Normal file
View File

@@ -0,0 +1,87 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"naersk",
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1752475459,
"narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=",
"owner": "nix-community",
"repo": "fenix",
"rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"naersk": {
"inputs": {
"fenix": "fenix",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1763384566,
"narHash": "sha256-r+wgI+WvNaSdxQmqaM58lVNvJYJ16zoq+tKN20cLst4=",
"owner": "nix-community",
"repo": "naersk",
"rev": "d4155d6ebb70fbe2314959842f744aa7cabbbf6a",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1768305791,
"narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"naersk": "naersk",
"nixpkgs": "nixpkgs"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1752428706,
"narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "591e3b7624be97e4443ea7b5542c191311aa141d",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

23
flake.nix Normal file
View File

@@ -0,0 +1,23 @@
{
inputs = {
nixpkgs.url = "nixpkgs/nixos-unstable";
naersk = {
url = "github:nix-community/naersk";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
nixpkgs,
naersk,
...
}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.${system} = {
default = pkgs.callPackage ./package.nix {
naersk = pkgs.callPackage naersk {};
};
};
};
}

38
package.nix Normal file
View File

@@ -0,0 +1,38 @@
{
fontconfig,
freetype,
lib,
libGL,
libxkbcommon,
makeWrapper,
naersk,
pkg-config,
wayland,
...
}:
naersk.buildPackage rec {
name = "rustty";
src = ./.;
buildInputs = [
# fonts
fontconfig
freetype
freetype.dev
# wayland graphics
libGL
libxkbcommon
wayland
];
nativeBuildInputs = [
pkg-config
makeWrapper
];
postInstall = ''
wrapProgram "$out/bin/${meta.mainProgram}" --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath buildInputs}"
'';
meta = {
mainProgram = "rustty";
homepage = "https://mtgmonkey.net";
};
}

98
src/main.rs Normal file
View File

@@ -0,0 +1,98 @@
use nix::pty::{ForkptyResult, forkpty, PtyMaster};
use nix::unistd::read;
use std::vec::Vec;
use std::io::{self, Read, Write};
use std::process::Command;
use std::thread;
use std::time::Duration;
use vte::{Params, Parser, Perform};
struct Performer;
impl Perform for Performer {
fn print(&mut self, c: char) {
println!("[print] {:?}", c);
}
fn execute(&mut self, byte: u8) {
println!("[execute] {:02x}", byte);
}
fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
println!(
"[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
params, intermediates, ignore, c
);
}
fn put(&mut self, byte: u8) {
println!("[put] {:02x}", byte);
}
fn unhook(&mut self) {
println!("[unhook]");
}
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
println!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
}
fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
println!(
"[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}",
params, intermediates, ignore, c
);
}
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
println!(
"[esc_dispatch] intermediates={:?}, ignore={:?}, byte={:02x}",
intermediates, ignore, byte
);
}
}
fn main() {
// TODO get rid of hard coded `sh` path
let mut pty = spawn_pty("/home/andromeda/.nix-profile/bin/sh").unwrap();
println!("{:?}", &pty);
let input = io::stdin();
let mut handle = input.lock();
let mut statemachine = Parser::new();
let mut performer = Performer;
let mut buf = [0u8; 2048];
while true {
match read(&pty, &mut buf) {
Ok(0) => break,
Ok(n) => {
statemachine.advance(&mut performer, &buf[..n]);
},
Err(_e) => break,
};
};
}
// forks a new pty and returns file descriptor of the master
fn spawn_pty(shell: &str) -> Option<PtyMaster> {
// 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, ..} => {
// 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);
return None;
}
},
Err(_e) => return None,
};
}