shell: first ProcessLauncher with PTY

This commit is contained in:
Michael Sippel 2021-09-03 06:49:18 +02:00
parent bd1572c632
commit cee6e02a04
Signed by: senvas
GPG key ID: F96CF119C34B64A6
5 changed files with 261 additions and 214 deletions

View file

@ -77,5 +77,10 @@ where Key: Clone + Hash + Eq + Send + Sync + 'static,
self.insert(key, item);
}
}
pub fn remove(&mut self, key: Key) {
self.data.write().unwrap().remove(&key);
self.cast.notify(&key);
}
}

View file

@ -43,10 +43,6 @@ impl TerminalEditor for CharEditor {
} else {
"".to_string()
})
.map_item(
|_idx, atom|
atom.add_style_back(TerminalStyle::fg_color((120, 200, 10)))
)
}
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
@ -114,6 +110,10 @@ impl TerminalEditor for StringEditor {
.decorate("\"", "\"", "", 1)
.to_grid_horizontal()
.flatten()
.map_item(
|_idx, atom|
atom.add_style_back(TerminalStyle::fg_color((120, 200, 10)))
)
}
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {

View file

@ -7,6 +7,9 @@ edition = "2018"
nested = { path = "../nested" }
cgmath = "*"
termion = "*"
bincode = "*"
libc = "0.2.*"
tty = { git = "https://github.com/stemjail/tty-rs.git" }
[dependencies.async-std]
version = "1.9.0"

View file

@ -1,5 +1,8 @@
extern crate tty;
mod monstera;
mod process;
use{
std::sync::{Arc, RwLock},
@ -15,9 +18,9 @@ use{
context::{ReprTree, Object, MorphismType, MorphismMode, Context},
port::{UpdateTask}},
index::{IndexView},
sequence::{SequenceView},
sequence::{SequenceView, SequenceViewExt},
vec::{VecBuffer},
integer::{RadixProjection, DigitEditor},
integer::{RadixProjection, DigitEditor, PosIntEditor},
terminal::{
Terminal,
TerminalStyle,
@ -27,60 +30,17 @@ use{
make_label,
TerminalView,
TerminalEditor},
string_editor::{CharEditor},
string_editor::{StringEditor},
tree_nav::{TreeNav, TreeNavResult, TreeCursor, TerminalTreeEditor},
list::{SExprView, ListCursorMode, ListEditor, ListEditorStyle}
},
crate::{
process::ProcessLauncher
}
};
struct GridFill<T: Send + Sync + Clone>(T);
impl<T: Send + Sync + Clone> View for GridFill<T> {
type Msg = Point2<i16>;
}
impl<T: Send + Sync + Clone> IndexView<Point2<i16>> for GridFill<T> {
type Item = T;
fn area(&self) -> Option<Vec<Point2<i16>>> {
None
}
fn get(&self, _: &Point2<i16>) -> Option<T> {
Some(self.0.clone())
}
}
#[async_std::main]
async fn main() {
/* todo:
open::
>0:
( Path )
( Sequence ( Sequence UnicodeChar ) )
( Sequence UnicodeChar )
<1:
( FileDescriptor )
( MachineInt )
read::
>0:
( FileDescriptor )
( MachineInt )
<1:
( Sequence MachineSyllab )
( Vec MachineSyllab )
write::
>0
( FileDescriptor )
( MachineInt )
>1:
( Sequence MachineSyllab )
( Vec MachineSyllab )
*/
let term_port = ViewPort::new();
let compositor = TerminalCompositor::new(term_port.inner());
@ -107,83 +67,27 @@ write::
let cur_size_port = ViewPort::new();
let mut cur_size = nested::singleton::SingletonBuffer::new(Vector2::new(10, 10), cur_size_port.inner());
// TypeEditor
let make_char_editor = || {
std::sync::Arc::new(std::sync::RwLock::new(CharEditor::new()))
};
let make_subsub_editor = move || {
std::sync::Arc::new(std::sync::RwLock::new(ListEditor::new(make_char_editor.clone(), ListEditorStyle::String)))
};
let make_sub_editor = move || {
std::sync::Arc::new(std::sync::RwLock::new(ListEditor::new(make_subsub_editor.clone(), ListEditorStyle::HorizontalSexpr)))
};
let mut te = ListEditor::new(make_sub_editor.clone(), ListEditorStyle::VerticalSexpr);
te.goto(
TreeCursor {
leaf_mode: ListCursorMode::Insert,
tree_addr: vec![ 0 ]
}
);
let mut p = te.get_data_port().map(|sub_editor| sub_editor.read().unwrap().get_data_port());
let status_chars_port = ViewPort::new();
let mut status_chars = VecBuffer::new(status_chars_port.inner());
let help_port = ViewPort::<dyn nested::grid::GridView<Item = OuterViewPort<dyn TerminalView>>>::new();
let mut help_buf = nested::index::buffer::IndexBuffer::<Point2<i16>, OuterViewPort<dyn TerminalView>>::new(help_port.inner());
let table_style = TerminalStyle::fg_color((120, 100, 80));
let desc_style = TerminalStyle::italic(true);
help_buf.insert_iter(vec![
(Point2::new(0, 0), make_label("CTRL+{c,d,g}").map_item(|_idx, atom| atom.add_style_back(TerminalStyle::bold(true)))),
(Point2::new(1, 0), make_label(" | ").map_item(move |_idx, atom| atom.add_style_back(table_style))),
(Point2::new(2, 0), make_label("quit").map_item(move |_idx, atom| atom.add_style_back(desc_style))),
(Point2::new(0, 1), make_label("↞ ← ↑ ↓ → ↠").map_item(|_idx, atom| atom.add_style_back(TerminalStyle::bold(true)))),
(Point2::new(1, 1), make_label(" | ").map_item(move |_idx, atom| atom.add_style_back(table_style))),
(Point2::new(2, 1), make_label("move cursor").map_item(move |_idx, atom| atom.add_style_back(desc_style))),
(Point2::new(0, 3), make_label("<DEL> (Select)").map_item(|_idx, atom| atom.add_style_back(TerminalStyle::bold(true)))),
(Point2::new(1, 3), make_label(" | ").map_item(move |_idx, atom| atom.add_style_back(table_style))),
(Point2::new(2, 3), make_label("delete item at cursor position").map_item(move |_idx, atom| atom.add_style_back(desc_style))),
(Point2::new(0, 4), make_label("<DEL> (Insert)").map_item(|_idx, atom| atom.add_style_back(TerminalStyle::bold(true)))),
(Point2::new(1, 4), make_label(" | ").map_item(move |_idx, atom| atom.add_style_back(table_style))),
(Point2::new(2, 4), make_label("delete item right to cursor").map_item(move |_idx, atom| atom.add_style_back(desc_style))),
(Point2::new(0, 5), make_label("<BACKSPACE> (Insert)").map_item(|_idx, atom| atom.add_style_back(TerminalStyle::bold(true)))),
(Point2::new(1, 5), make_label(" | ").map_item(move |_idx, atom| atom.add_style_back(table_style))),
(Point2::new(2, 5), make_label("delete item left to cursor").map_item(move |_idx, atom| atom.add_style_back(desc_style))),
(Point2::new(0, 6), make_label("<TAB>").map_item(|_idx, atom| atom.add_style_back(TerminalStyle::bold(true)))),
(Point2::new(1, 6), make_label(" | ").map_item(move |_idx, atom| atom.add_style_back(table_style))),
(Point2::new(2, 6), make_label("toggle cursor mode (insert / select)").map_item(move |_idx, atom| atom.add_style_back(desc_style))),
]);
let help_head = make_label("─────────────────────┬─────────────────────").map_item(move |_idx, atom| atom.add_style_back(table_style));
table_buf.insert_iter(vec![
(Point2::new(0, 0), magic.clone()),
(Point2::new(0, 2), status_chars_port.outer().to_sequence().to_grid_horizontal()),
(Point2::new(0, 3), te.get_term_view()),
(Point2::new(0, 4), make_label(" ")),
(Point2::new(0, 5), help_head),
(Point2::new(0, 6), help_port.outer().flatten()),
(Point2::new(0, 7), magic.clone()),
(Point2::new(0, 1), status_chars_port.outer().to_sequence().to_grid_horizontal()),
]);
compositor.write().unwrap().push(monstera::make_monstera());
compositor.write().unwrap().push(table_port.outer().flatten().offset(Vector2::new(40, 2)));
/*
te.get_data_port()
.map(
|item_editor| item_editor.read().unwrap().get_data_port()
)
*/
let mut y = 2;
let mut process_launcher = ProcessLauncher::new();
table_buf.insert(Point2::new(0, y), process_launcher.get_term_view());
process_launcher.goto(TreeCursor {
leaf_mode: ListCursorMode::Insert,
tree_addr: vec![ 0 ]
});
loop {
term_port.update();
match term.next_event().await {
@ -200,122 +104,60 @@ write::
TerminalEvent::Input(Event::Key(Key::Ctrl('d'))) => break,
TerminalEvent::Input(Event::Key(Key::Left)) => {
if te.pxev() == TreeNavResult::Exit {
te.goto_home();
}
process_launcher.pxev();
}
TerminalEvent::Input(Event::Key(Key::Right)) => {
if te.nexd() == TreeNavResult::Exit {
te.goto_end();
process_launcher.nexd();
}
}
TerminalEvent::Input(Event::Key(Key::Up)) => { te.up(); }
TerminalEvent::Input(Event::Key(Key::Up)) => { process_launcher.up(); }
TerminalEvent::Input(Event::Key(Key::Down)) => {
if te.dn() == TreeNavResult::Continue {
te.goto_home();
if process_launcher.dn() == TreeNavResult::Continue {
process_launcher.goto_home();
}
}
TerminalEvent::Input(Event::Key(Key::Home)) => {
if te.goto_home() == TreeNavResult::Exit {
te.goto_home();
}
process_launcher.goto_home();
}
TerminalEvent::Input(Event::Key(Key::End)) => {
if te.goto_end() == TreeNavResult::Exit {
te.goto_end();
process_launcher.goto_end();
}
}
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
/*
let mut strings = Vec::new();
let output_view = process_launcher.launch();
let v = p.get_view().unwrap();
for i in 0 .. v.len().unwrap_or(0) {
strings.push(
v
.get(&i).unwrap()
.get_view().unwrap()
.read().unwrap()
.iter().collect::<String>()
);
}
if strings.len() == 0 { continue; }
if let Ok(output) =
std::process::Command::new(strings[0].as_str())
.args(&strings[1..])
.output()
{
// take output and update terminal view
let mut line_port = ViewPort::new();
let mut line = VecBuffer::new(line_port.inner());
for byte in output.stdout {
match byte {
b'\n' => {
compositor.write().unwrap().push(
line_port.outer()
.to_sequence()
.map(|c| TerminalAtom::new(*c, TerminalStyle::fg_color((130,90,90))))
.to_grid_horizontal()
.offset(Vector2::new(45, y))
);
y += 1;
line_port = ViewPort::new();
line = VecBuffer::new(line_port.inner());
}
byte => {
line.push(byte as char);
}
}
}
} else {
compositor.write().unwrap().push(
make_label("Command not found")
.map_item(|idx, a| a.add_style_back(TerminalStyle::fg_color((200,0,0))))
.offset(Vector2::new(45, y))
);
table_buf.insert(Point2::new(0, y), output_view);
process_launcher = ProcessLauncher::new();
process_launcher.goto(TreeCursor {
leaf_mode: ListCursorMode::Insert,
tree_addr: vec![ 0 ]
});
y += 1;
table_buf.insert(Point2::new(0, y), process_launcher.get_term_view());
}
te.up();
te.goto_home();
te = ListEditor::new(make_sub_editor.clone());
compositor.write().unwrap().push(magic.offset(Vector2::new(40, y)));
y += 1;
compositor.write().unwrap().push(
te
.horizontal_sexpr_view()
.offset(Vector2::new(40, y))
);
y += 1;
p = te.get_data_port().map(|string_editor| string_editor.read().unwrap().get_data_port());
*/
},
ev => {
if te.get_cursor().leaf_mode == ListCursorMode::Select {
if process_launcher.get_cursor().leaf_mode == ListCursorMode::Select {
match ev {
TerminalEvent::Input(Event::Key(Key::Char('l'))) => { te.up(); },
TerminalEvent::Input(Event::Key(Key::Char('a'))) => { te.dn(); },
TerminalEvent::Input(Event::Key(Key::Char('i'))) => { te.pxev(); },
TerminalEvent::Input(Event::Key(Key::Char('e'))) => { te.nexd(); },
TerminalEvent::Input(Event::Key(Key::Char('u'))) => { te.goto_home(); },
TerminalEvent::Input(Event::Key(Key::Char('o'))) => { te.goto_end(); },
TerminalEvent::Input(Event::Key(Key::Char('l'))) => { process_launcher.up(); },
TerminalEvent::Input(Event::Key(Key::Char('a'))) => { process_launcher.dn(); },
TerminalEvent::Input(Event::Key(Key::Char('i'))) => { process_launcher.pxev(); },
TerminalEvent::Input(Event::Key(Key::Char('e'))) => { process_launcher.nexd(); },
TerminalEvent::Input(Event::Key(Key::Char('u'))) => { process_launcher.goto_home(); },
TerminalEvent::Input(Event::Key(Key::Char('o'))) => { process_launcher.goto_end(); },
_ => {
te.handle_terminal_event(&ev);
process_launcher.handle_terminal_event(&ev);
}
}
} else {
te.handle_terminal_event(&ev);
process_launcher.handle_terminal_event(&ev);
}
}
}
status_chars.clear();
let cur = te.get_cursor();
let cur = process_launcher.get_cursor();
if cur.tree_addr.len() > 0 {
status_chars.push(TerminalAtom::new('@', TerminalStyle::fg_color((120, 80, 80)).add(TerminalStyle::bold(true))));

197
shell/src/process.rs Normal file
View file

@ -0,0 +1,197 @@
use {
std::{
sync::Arc,
process::Command,
os::unix::io::{FromRawFd, AsRawFd},
},
std::sync::RwLock,
tty::{FileDesc, TtyServer},
termion::event::{Key, Event},
cgmath::Point2,
nested::{
core::{ViewPort, OuterViewPort, Observer},
singleton::{SingletonView, SingletonBuffer},
sequence::{SequenceView, SequenceViewExt},
index::buffer::IndexBuffer,
vec::VecBuffer,
terminal::{TerminalAtom, TerminalStyle, TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult, make_label},
tree_nav::{TreeNav, TreeNavResult, TerminalTreeEditor, TreeCursor},
list::{ListEditor, ListEditorStyle, sexpr::ListDecoration},
string_editor::CharEditor,
}
};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
pub struct ProcessArg {
editor: ListEditor< CharEditor,
Box<dyn Fn() -> Arc<RwLock<CharEditor>> + Send + Sync + 'static> >
}
impl ProcessArg {
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = char>> {
self.editor.get_data_port()
.map(
|char_editor| char_editor.read().unwrap().get_data_port().get_view().unwrap().get().unwrap()
)
}
}
impl TerminalEditor for ProcessArg {
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
self.editor
.get_seg_seq_view()
.to_grid_horizontal()
.flatten()
}
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
match event {
TerminalEvent::Input(Event::Key(Key::Char(' '))) |
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
self.editor.up();
TerminalEditorResult::Exit
}
event => self.editor.handle_terminal_event(event)
}
}
}
impl TreeNav for ProcessArg {
fn get_cursor(&self) -> TreeCursor { self.editor.get_cursor() }
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult { self.editor.goto(cur) }
fn goto_home(&mut self) -> TreeNavResult { self.editor.goto_home() }
fn goto_end(&mut self) -> TreeNavResult { self.editor.goto_end() }
fn pxev(&mut self) -> TreeNavResult { self.editor.pxev() }
fn nexd(&mut self) -> TreeNavResult { self.editor.nexd() }
fn up(&mut self) -> TreeNavResult { self.editor.up() }
fn dn(&mut self) -> TreeNavResult { self.editor.dn() }
}
pub struct ProcessLauncher {
editor: ListEditor< ProcessArg,
Box<dyn Fn() -> Arc<RwLock<ProcessArg>> + Send + Sync + 'static> >
}
impl ProcessLauncher {
pub fn new() -> Self {
ProcessLauncher {
editor: ListEditor::new(
Box::new(
|| {
Arc::new(RwLock::new(ProcessArg {
editor: ListEditor::new(
Box::new(
|| {
Arc::new(RwLock::new(CharEditor::new()))
}
),
ListEditorStyle::Plain)
}))
}
),
ListEditorStyle::Plain
)
}
}
pub fn launch(&mut self) -> OuterViewPort<dyn TerminalView> {
self.up();
self.up();
let mut strings = Vec::new();
let v = self.editor.get_data_port().get_view().unwrap();
for i in 0 .. v.len().unwrap_or(0) {
let arg_view = v.get(&i).unwrap().read().unwrap().get_data_port().get_view().unwrap();
strings.push(arg_view.iter().collect::<String>());
}
if strings.len() > 0 {
let stdin = FileDesc::new(libc::STDIN_FILENO, false);
let mut server = match TtyServer::new(Some(&stdin)) {
Ok(s) => s,
Err(e) => { return make_label(&format!("Error TTY server: {}", e)); },
};
let mut cmd = std::process::Command::new(strings[0].as_str());
cmd.args(&strings[1..]).stdin(std::process::Stdio::null());
let process = match server.spawn(cmd) {
Ok(p) => p,
Err(e) => { return make_label(&format!("Failed to execute process: {}", e));},
};
if let Ok(mut term_view_proc) = std::process::Command::new("./target/release/ansi_parser")
.stdin(unsafe{ std::process::Stdio::from_raw_fd(server.get_master().as_raw_fd()) })
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.spawn()
{
let mut term_view_bin = term_view_proc.stdout.unwrap();
/*
//let mut term_view_bin = async_std::io::BufReader::new(unsafe { async_std::fs::File::from_raw_fd( term_view_proc.stdout.unwrap().as_raw_fd() ) } );
//let mut tv_stream = async_bincode::AsyncBincodeReader::<_, (Point2<i16>, Option<TerminalAtom>)>::from(term_view_bin);
async_std::task::spawn(
async move {
*/
let output_view_port = ViewPort::new();
let mut output_buf = IndexBuffer::new(output_view_port.inner());
while let Ok((pos, atom)) = bincode::deserialize_from(&mut term_view_bin) {
if let Some(a) = atom {
output_buf.insert(pos, a);
} else {
output_buf.remove(pos);
}
}
// });
output_view_port.outer()
} else {
make_label("Failed to spawn ansi parser process")
.map_item(|idx, a| a.add_style_back(TerminalStyle::fg_color((200,0,0))))
}
} else {
make_label("no command")
.map_item(|idx, a| a.add_style_back(TerminalStyle::fg_color((200,0,0))))
}
}
}
impl TerminalEditor for ProcessLauncher {
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
self.editor
.get_seg_seq_view()
.decorate("$(", ")", " ", 0)
.to_grid_horizontal()
.flatten()
}
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
match event {
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
// launch command
self.editor.up();
self.editor.up();
TerminalEditorResult::Exit
}
event => self.editor.handle_terminal_event(event)
}
}
}
impl TreeNav for ProcessLauncher {
fn get_cursor(&self) -> TreeCursor { self.editor.get_cursor() }
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult { self.editor.goto(cur) }
fn goto_home(&mut self) -> TreeNavResult { self.editor.goto_home() }
fn goto_end(&mut self) -> TreeNavResult { self.editor.goto_end() }
fn pxev(&mut self) -> TreeNavResult { self.editor.pxev() }
fn nexd(&mut self) -> TreeNavResult { self.editor.nexd() }
fn up(&mut self) -> TreeNavResult { self.editor.up() }
fn dn(&mut self) -> TreeNavResult { self.editor.dn() }
}