diff --git a/Cargo.toml b/Cargo.toml index 455e30f..29eb9c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] nix = { version = "0.30.1", features = ["term", "process", "fs"], default-features = false } +iced = "0.13.1" diff --git a/flake.lock b/flake.lock index 5db6fce..c8ab69c 100644 --- a/flake.lock +++ b/flake.lock @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751011381, - "narHash": "sha256-krGXKxvkBhnrSC/kGBmg5MyupUUT5R6IBCLEzx9jhMM=", + "lastModified": 1751271578, + "narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "30e2e2857ba47844aa71991daa6ed1fc678bcbb7", + "rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df", "type": "github" }, "original": { diff --git a/package.nix b/package.nix index e6b6701..62d7aa3 100644 --- a/package.nix +++ b/package.nix @@ -1,9 +1,16 @@ { busybox-sandbox-shell, + expat, + fontconfig, + freetype, lib, + libGL, + libxkbcommon, makeWrapper, naersk, pkg-config, + wayland, + xorg, ... }: naersk.buildPackage rec { @@ -11,6 +18,17 @@ naersk.buildPackage rec { src = ./.; buildInputs = [ busybox-sandbox-shell + expat + fontconfig + freetype + freetype.dev + libGL + libxkbcommon + wayland + xorg.libX11 + xorg.libXcursor + xorg.libXi + xorg.libXrandr ]; nativeBuildInputs = [ pkg-config diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1f180db --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,146 @@ +use iced::Element; +use iced::widget::{column, scrollable, text, text_input}; + +use nix::pty::{ForkptyResult, forkpty}; +use nix::unistd::write; + +use std::fs::File; +use std::io::{Read, Write}; +use std::os::unix::io::{AsFd, OwnedFd}; +use std::process::Command; + +pub fn spawn_pty_with_shell(default_shell: String) -> OwnedFd { + match unsafe { forkpty(None, None) } { + Ok(fork_pty_res) => match fork_pty_res { + ForkptyResult::Parent { master, .. } => { + set_nonblock(&master); + master + } + ForkptyResult::Child => { + Command::new(&default_shell).spawn().unwrap(); + std::thread::sleep(std::time::Duration::MAX); + std::process::exit(0); + } + }, + Err(e) => panic!("failed to fork {:?}", e), + } +} + +pub fn read_from_fd(fd: &OwnedFd) -> Option> { + let mut read_buffer = [0; 65536]; + let mut file = File::from(fd.try_clone().unwrap()); + file.flush(); + let file = file.read(&mut read_buffer); + match file { + Ok(file) => Some(read_buffer[..file].to_vec()), + Err(_) => None, + } +} + +fn set_nonblock(fd: &OwnedFd) { + let flags = nix::fcntl::fcntl(fd, nix::fcntl::FcntlArg::F_GETFL).unwrap(); + let mut flags = + nix::fcntl::OFlag::from_bits(flags & nix::fcntl::OFlag::O_ACCMODE.bits()).unwrap(); + flags.set(nix::fcntl::OFlag::O_NONBLOCK, true); + nix::fcntl::fcntl(fd, nix::fcntl::FcntlArg::F_SETFL(flags)).unwrap(); +} + +#[derive(Debug, Clone)] +pub enum Msg { + HasInput, + InputChanged(String), +} + +pub struct Model { + screen_buffer: [u8; 65536], + screen_buffer_index: usize, + fd: OwnedFd, + stdin: OwnedFd, + input: String, +} + +impl Model { + fn new( + screen_buffer: [u8; 65536], + screen_buffer_index: usize, + fd: OwnedFd, + stdin: OwnedFd, + input: String, + ) -> Self { + Model { + screen_buffer, + screen_buffer_index, + fd, + stdin, + input, + } + } + + pub fn update(&mut self, msg: Msg) { + match msg { + Msg::HasInput => { + let mut write_buffer = self.input.as_bytes().to_vec(); + write_buffer.push(b'\n'); + write(self.fd.as_fd(), &mut write_buffer); + self.input = String::new(); + let mut nored = 0; + while nored <= 2 { + let red = read_from_fd(&self.fd); + match &red { + Some(red) => { + nored += 1; + self.update_screen_buffer(red); + } + None => (), + }; + } + } + Msg::InputChanged(input) => self.input = input, + } + } + + pub fn view(&self) -> Element<'_, Msg> { + scrollable(column![ + text(String::from( + String::from_utf8(self.screen_buffer.to_vec()) + .unwrap() + .trim_end_matches('\0') + )), + text_input("", &self.input) + .on_input(Msg::InputChanged) + .on_submit(Msg::HasInput) + ]) + .into() + } + + fn update_screen_buffer(&mut self, vec: &Vec) { + let offset = self.screen_buffer.iter().position(|&c| c == b'\0').unwrap(); + for (i, chr) in vec.iter().enumerate() { + self.screen_buffer[i + offset] = chr.clone(); + } + } +} + +impl Default for Model { + fn default() -> Self { + let mut me = Self::new( + [0; 65536], + 0, + spawn_pty_with_shell("/home/mtgmonkey/.nix-profile/bin/dash".to_string()), + std::io::stdin().as_fd().try_clone_to_owned().unwrap(), + String::new(), + ); + let mut nored = true; + while nored { + let red = read_from_fd(&me.fd); + match red { + Some(red) => { + nored = false; + me.update_screen_buffer(&red); + } + None => (), + } + } + me + } +} diff --git a/src/main.rs b/src/main.rs index b2181bb..b3c3fdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,56 +1,39 @@ -use nix::pty::{ForkptyResult, forkpty}; -use nix::unistd::{read, write}; +use iced::Element; +use iced::widget::{button, text}; + +use nix::unistd::write; + use std::fs::File; -use std::io::{Read, Write}; -use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd}; -use std::process::Command; +use std::io::Read; +use std::io::Write; +use std::os::unix::io::{AsFd, OwnedFd}; -fn main() { - let default_shell = "/home/mtgmonkey/.nix-profile/bin/dash".to_string(); - let fd = spawn_pty_with_shell(default_shell); - set_nonblock(&fd); - let mut write_buffer = "tty\n".as_bytes().to_vec(); - write(fd.as_fd(), &mut write_buffer); - loop { - let red = read_from_fd(fd.try_clone().unwrap()); - match red { - Some(red) => print!("{}", String::from_utf8(red).unwrap()), - None => { - let mut write_buffer = vec![]; - std::io::stdin().read_to_end(&mut write_buffer); - write(fd.as_fd(), &mut write_buffer); - } - }; - } -} +use rust_term::*; -fn spawn_pty_with_shell(default_shell: String) -> OwnedFd { - match (unsafe { forkpty(None, None) }) { - Ok(fork_pty_res) => match fork_pty_res { - ForkptyResult::Parent { master, .. } => master, - ForkptyResult::Child => { - Command::new(&default_shell).spawn().unwrap(); - std::thread::sleep(std::time::Duration::MAX); - std::process::exit(0); - } - }, - Err(e) => panic!("failed to fork {:?}", e), - } -} - -fn read_from_fd(fd: OwnedFd) -> Option> { - let mut read_buffer = [0; 65536]; - let mut file = File::from(fd).read(&mut read_buffer); - match file { - Ok(file) => Some(read_buffer[..file].to_vec()), - Err(_) => None, - } -} - -fn set_nonblock(fd: &OwnedFd) { - let flags = nix::fcntl::fcntl(fd, nix::fcntl::FcntlArg::F_GETFL).unwrap(); - let mut flags = - nix::fcntl::OFlag::from_bits(flags & nix::fcntl::OFlag::O_ACCMODE.bits()).unwrap(); - flags.set(nix::fcntl::OFlag::O_NONBLOCK, true); - nix::fcntl::fcntl(fd, nix::fcntl::FcntlArg::F_SETFL(flags)).unwrap(); +fn main() -> iced::Result { + iced::run("test", Model::update, Model::view) + /* let default_shell = "/home/mtgmonkey/.nix-profile/bin/dash".to_string(); + let fd = spawn_pty_with_shell(default_shell); + let mut write_buffer = "tty\n".as_bytes().to_vec(); + write(fd.as_fd(), &mut write_buffer); + loop { + let red = read_from_fd(&fd); + match red { + Some(red) => print!("{}", String::from_utf8(red).unwrap()), + None => { + let mut read_buffer = [0; 65536]; + let mut file = File::from(std::io::stdin().as_fd().try_clone_to_owned().unwrap()); + file.flush(); + let file = file.read(&mut read_buffer); + println!( + "{}", + match file { + Ok(file) => write(fd.as_fd(), &read_buffer[..file]).unwrap(), + Err(_) => 0, + } + ); + } + }; + } + */ }