diff options
| author | Jonas Maier <> | 2026-03-04 16:47:34 +0100 |
|---|---|---|
| committer | Jonas Maier <> | 2026-03-04 16:47:34 +0100 |
| commit | 15182b740d9d3d3b7e263b1690051aa06334ab0c (patch) | |
| tree | 198f73df725bce910f5df9229833756f12e1bb60 | |
| download | pish-15182b740d9d3d3b7e263b1690051aa06334ab0c.tar.gz | |
initial commit
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 25 | ||||
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | src/main.rs | 100 |
4 files changed, 133 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ce9277f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "pish" +version = "0.1.0" +dependencies = [ + "termios", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a9a594f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "pish" +version = "0.1.0" +edition = "2024" + +[dependencies] +termios = "0.3" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7f372c1 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,100 @@ +use std::io::{self, Read, Write, IsTerminal}; +use std::os::unix::io::AsRawFd; +use termios::*; + +struct ScopedRawMode { + fd: i32, + settings: Termios, +} + +impl Drop for ScopedRawMode { + fn drop(&mut self) { + self.disable(); + } +} + +impl ScopedRawMode { + fn on_fd(fd: i32) -> Self { + let mut termios = Termios::from_fd(fd).unwrap(); + let settings = termios.clone(); + cfmakeraw(&mut termios); + tcsetattr(fd, TCSANOW, &termios).unwrap(); + Self { fd, settings } + } + + fn disable(&self) { + tcsetattr(self.fd, TCSANOW, &self.settings).unwrap(); + } +} + +macro_rules! print { + ($($x:tt)*) => {{ + write!(io::stdout(), $($x)*).unwrap(); + io::stdout().flush().unwrap(); + }} +} + +fn main() { + let stdin = io::stdin(); + let stdout = io::stdout(); + + if !stdin.is_terminal() { + println!("need to run in a tty"); + return; + } + + let fd = stdin.as_raw_fd(); + let _scoped_raw = ScopedRawMode::on_fd(fd); // needs to be a var, gets dropped on scope exit, + // even if something panics + + let mut stdin = stdin.lock(); + let mut stdout = stdout.lock(); + + let mut buffer = [0u8; 1]; + + loop { + let Ok(_) = stdin.read_exact(&mut buffer) else { + break; + }; + + match buffer[0] { + // EOF + 4 => { + break; + } + + // Enter + b'\r' => { + println!("\r"); + } + + // Backspace (127 on most systems) + 127 => { + write!(stdout, "\x08 \x08").unwrap(); + stdout.flush().unwrap(); + } + + // Escape sequence + 27 => { + let mut seq = [0u8; 2]; + stdin.read_exact(&mut seq).unwrap(); + + if seq[0] == b'[' { + match seq[1] { + b'A' => print!("Up"), + b'B' => print!("Down"), + b'C' => print!("Right"), + b'D' => print!("Left"), + _ => {} + } + } + } + + // Normal character + x => { + stdout.write(&[x]).unwrap(); + stdout.flush().unwrap(); + } + } + } +} |
