mvp
This commit is contained in:
parent
0a2ec5f358
commit
883b94f1eb
5 changed files with 203 additions and 55 deletions
|
@ -5,3 +5,4 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nix = { version = "0.30.1", features = ["term", "process", "fs"], default-features = false }
|
nix = { version = "0.30.1", features = ["term", "process", "fs"], default-features = false }
|
||||||
|
iced = "0.13.1"
|
||||||
|
|
6
flake.lock
generated
6
flake.lock
generated
|
@ -22,11 +22,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751011381,
|
"lastModified": 1751271578,
|
||||||
"narHash": "sha256-krGXKxvkBhnrSC/kGBmg5MyupUUT5R6IBCLEzx9jhMM=",
|
"narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "30e2e2857ba47844aa71991daa6ed1fc678bcbb7",
|
"rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
18
package.nix
18
package.nix
|
@ -1,9 +1,16 @@
|
||||||
{
|
{
|
||||||
busybox-sandbox-shell,
|
busybox-sandbox-shell,
|
||||||
|
expat,
|
||||||
|
fontconfig,
|
||||||
|
freetype,
|
||||||
lib,
|
lib,
|
||||||
|
libGL,
|
||||||
|
libxkbcommon,
|
||||||
makeWrapper,
|
makeWrapper,
|
||||||
naersk,
|
naersk,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
|
wayland,
|
||||||
|
xorg,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
naersk.buildPackage rec {
|
naersk.buildPackage rec {
|
||||||
|
@ -11,6 +18,17 @@ naersk.buildPackage rec {
|
||||||
src = ./.;
|
src = ./.;
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
busybox-sandbox-shell
|
busybox-sandbox-shell
|
||||||
|
expat
|
||||||
|
fontconfig
|
||||||
|
freetype
|
||||||
|
freetype.dev
|
||||||
|
libGL
|
||||||
|
libxkbcommon
|
||||||
|
wayland
|
||||||
|
xorg.libX11
|
||||||
|
xorg.libXcursor
|
||||||
|
xorg.libXi
|
||||||
|
xorg.libXrandr
|
||||||
];
|
];
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
146
src/lib.rs
Normal file
146
src/lib.rs
Normal file
|
@ -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<Vec<u8>> {
|
||||||
|
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<u8>) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
87
src/main.rs
87
src/main.rs
|
@ -1,56 +1,39 @@
|
||||||
use nix::pty::{ForkptyResult, forkpty};
|
use iced::Element;
|
||||||
use nix::unistd::{read, write};
|
use iced::widget::{button, text};
|
||||||
|
|
||||||
|
use nix::unistd::write;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::Read;
|
||||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd};
|
use std::io::Write;
|
||||||
use std::process::Command;
|
use std::os::unix::io::{AsFd, OwnedFd};
|
||||||
|
|
||||||
fn main() {
|
use rust_term::*;
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_pty_with_shell(default_shell: String) -> OwnedFd {
|
fn main() -> iced::Result {
|
||||||
match (unsafe { forkpty(None, None) }) {
|
iced::run("test", Model::update, Model::view)
|
||||||
Ok(fork_pty_res) => match fork_pty_res {
|
/* let default_shell = "/home/mtgmonkey/.nix-profile/bin/dash".to_string();
|
||||||
ForkptyResult::Parent { master, .. } => master,
|
let fd = spawn_pty_with_shell(default_shell);
|
||||||
ForkptyResult::Child => {
|
let mut write_buffer = "tty\n".as_bytes().to_vec();
|
||||||
Command::new(&default_shell).spawn().unwrap();
|
write(fd.as_fd(), &mut write_buffer);
|
||||||
std::thread::sleep(std::time::Duration::MAX);
|
loop {
|
||||||
std::process::exit(0);
|
let red = read_from_fd(&fd);
|
||||||
}
|
match red {
|
||||||
},
|
Some(red) => print!("{}", String::from_utf8(red).unwrap()),
|
||||||
Err(e) => panic!("failed to fork {:?}", e),
|
None => {
|
||||||
}
|
let mut read_buffer = [0; 65536];
|
||||||
}
|
let mut file = File::from(std::io::stdin().as_fd().try_clone_to_owned().unwrap());
|
||||||
|
file.flush();
|
||||||
fn read_from_fd(fd: OwnedFd) -> Option<Vec<u8>> {
|
let file = file.read(&mut read_buffer);
|
||||||
let mut read_buffer = [0; 65536];
|
println!(
|
||||||
let mut file = File::from(fd).read(&mut read_buffer);
|
"{}",
|
||||||
match file {
|
match file {
|
||||||
Ok(file) => Some(read_buffer[..file].to_vec()),
|
Ok(file) => write(fd.as_fd(), &read_buffer[..file]).unwrap(),
|
||||||
Err(_) => None,
|
Err(_) => 0,
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
|
}
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue