use fltk::{app, text::*, window::*};
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::process::{Command, Stdio};
#[derive(Debug, Clone)]
struct Term {
pub term: TextDisplay,
current_dir: String,
cmd: String,
}
impl Term {
pub fn new(buf: TextBuffer) -> Term {
let mut current_dir = std::env::current_dir()
.unwrap()
.to_string_lossy()
.to_string();
current_dir.push_str("$ ");
let mut t = Term {
term: TextDisplay::new(5, 5, 630, 470, ""),
current_dir,
cmd: String::from(""),
};
t.set_buffer(Some(buf));
t
}
pub fn style(&mut self) {
self.term.set_color(Color::Black);
self.term.set_text_color(Color::Green);
self.term.set_text_font(Font::Courier);
self.term.set_cursor_color(Color::Green);
self.term.set_cursor_style(TextCursor::Block);
self.term.show_cursor(true);
}
fn append(&mut self, txt: &str) {
self.term.buffer().unwrap().append(txt);
self.term
.set_insert_position(self.term.buffer().unwrap().length());
self.term.scroll(
self.term
.count_lines(0, self.term.buffer().unwrap().length(), true),
0,
);
}
fn run_command(&mut self) -> String {
let args = self.cmd.clone();
let args: Vec<&str> = args.split_whitespace().collect();
if !args.is_empty() {
let mut cmd = Command::new(args[0]);
if args.len() > 1 {
if args[0] == "cd" {
let path = args[1];
return self.change_dir(&Path::new(path));
} else {
cmd.args(&args[1..]);
}
}
let out = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).output();
if let Ok(out) = out {
let stdout = out.stdout;
String::from_utf8_lossy(&stdout).to_string()
} else {
let msg = format!("{}: command not found!\n", self.cmd);
msg
}
} else {
String::from("")
}
}
pub fn change_dir(&mut self, path: &Path) -> String {
if path.exists() && path.is_dir() {
std::env::set_current_dir(path).unwrap();
let mut current_dir = std::env::current_dir()
.unwrap()
.to_string_lossy()
.to_string();
current_dir.push_str("$ ");
self.current_dir = current_dir;
String::from("")
} else {
String::from("Path does not exist!\n")
}
}
}
impl Deref for Term {
type Target = TextDisplay;
fn deref(&self) -> &Self::Target {
&self.term
}
}
impl DerefMut for Term {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.term
}
}
fn main() {
let app = app::App::default().with_scheme(app::Scheme::Plastic);
let mut wind = Window::new(100, 100, 640, 480, "Rusty Terminal");
let buf = TextBuffer::default();
let mut term = Term::new(buf);
term.style();
let dir = term.current_dir.clone();
term.append(&dir);
wind.make_resizable(true);
wind.end();
wind.show();
let mut term_c = term.clone();
term_c.handle(Box::new(move |ev| {
match ev {
Event::KeyDown => match app::event_key() {
Key::Enter => {
term.append("\n");
let out = term.run_command();
term.append(&out);
let current_dir = term.current_dir.clone();
term.append(¤t_dir);
term.cmd.clear();
true
}
Key::BackSpace => {
if !term.cmd.is_empty() {
let text_len = term.buffer().unwrap().text().len() as u32;
term.term
.buffer()
.unwrap()
.remove(text_len - 1, text_len as u32);
term.cmd.pop().unwrap();
true
} else {
false
}
}
_ => {
let temp = app::event_text();
term.cmd.push_str(&temp);
term.append(&temp);
true
}
},
_ => false,
}
}));
app.run().unwrap();
}