summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock69
-rw-r--r--Cargo.toml8
-rw-r--r--flake.lock87
-rw-r--r--flake.nix23
-rw-r--r--package.nix38
-rw-r--r--src/main.rs98
7 files changed, 325 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b2ae984
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+/result*
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..ade81cc
--- /dev/null
+++ b/Cargo.lock
@@ -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",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..f4b7be2
--- /dev/null
+++ b/Cargo.toml
@@ -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"
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..54f02b5
--- /dev/null
+++ b/flake.lock
@@ -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
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..6a49e38
--- /dev/null
+++ b/flake.nix
@@ -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 {};
+ };
+ };
+ };
+}
diff --git a/package.nix b/package.nix
new file mode 100644
index 0000000..84aac79
--- /dev/null
+++ b/package.nix
@@ -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";
+ };
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..05fb64e
--- /dev/null
+++ b/src/main.rs
@@ -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,
+ };
+}