From 5ec840e87c6b63adcdb3b6789e6c69c69907335f Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Wed, 15 Sep 2021 17:47:40 +0200 Subject: [PATCH] pty wrapper --- shell/src/main.rs | 116 ++++++++++++++++++++++++++++++++----------- shell/src/process.rs | 43 ++-------------- shell/src/pty.rs | 74 +++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 68 deletions(-) create mode 100644 shell/src/pty.rs diff --git a/shell/src/main.rs b/shell/src/main.rs index 79a433e..d6a9533 100644 --- a/shell/src/main.rs +++ b/shell/src/main.rs @@ -3,6 +3,7 @@ extern crate portable_pty; mod monstera; mod process; +mod pty; use{ std::sync::{Arc, RwLock}, @@ -12,9 +13,11 @@ use{ core::{ View, ViewPort, + InnerViewPort, OuterViewPort, Observer, ObserverExt, + ObserverBroadcast, context::{ReprTree, Object, MorphismType, MorphismMode, Context}, port::{UpdateTask}}, index::{IndexView}, @@ -43,7 +46,40 @@ use{ struct AsciiBox { content: Option>, - extent: Vector2 + extent: Vector2, + + cast: Arc>> +} + +impl AsciiBox { + pub fn new( + extent: Vector2, + content_port: OuterViewPort, + output_port: InnerViewPort + ) -> Arc> { + let ascii_box = Arc::new(RwLock::new(AsciiBox { + content: None, + extent, + cast: output_port.get_broadcast() + })); + + output_port.0.update_hooks.write().unwrap().push(Arc::new(content_port.0.clone())); + output_port.set_view(Some(ascii_box.clone())); + content_port.add_observer(ascii_box.clone()); + + ascii_box + } +} + +impl Observer for AsciiBox { + fn reset(&mut self, new_content: Option>) { + self.content = new_content; + self.notify_each(GridWindowIterator::from(Point2::new(0, 0) ..= Point2::new(self.extent.x, self.extent.y))); + } + + fn notify(&mut self, pt: &Point2) { + self.cast.notify(&(pt + Vector2::new(1, 1))); + } } impl View for AsciiBox { @@ -130,8 +166,8 @@ async fn main() { (Point2::new(0, 2), magic.clone()), ]); - //compositor.write().unwrap().push(monstera::make_monstera()); - compositor.write().unwrap().push(table_port.outer().flatten());//.offset(Vector2::new(40, 2))); + compositor.write().unwrap().push(monstera::make_monstera()); + compositor.write().unwrap().push(table_port.outer().flatten().offset(Vector2::new(40, 2))); let mut y = 4; @@ -142,14 +178,34 @@ async fn main() { leaf_mode: ListCursorMode::Insert, tree_addr: vec![ 0 ] }); -/* - let mut last_box = Arc::new(RwLock::new(AsciiBox{ - - })); -*/ + + let mut pty : Option = None; + loop { term_port.update(); - match term.next_event().await { + if let Some(p) = pty.as_mut() { + if p.get_status() { + pty = None; + + 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()); + } + } + term_port.update(); + + let ev = term.next_event().await; + + if let Some(pty) = pty.as_mut() { + pty.handle_terminal_event(&ev); + } else { + + match ev { TerminalEvent::Resize(new_size) => { cur_size.set(new_size); term_port.inner().get_broadcast().notify_each( @@ -168,9 +224,13 @@ async fn main() { TerminalEvent::Input(Event::Key(Key::Right)) => { process_launcher.nexd(); } - TerminalEvent::Input(Event::Key(Key::Up)) => { process_launcher.up(); } + TerminalEvent::Input(Event::Key(Key::Up)) => { + if process_launcher.up() == TreeNavResult::Exit { + //process_launcher.dn(); + //process_launcher.goto_home(); + } + } TerminalEvent::Input(Event::Key(Key::Down)) => { - //process_launcher.dn(); if process_launcher.dn() == TreeNavResult::Continue { process_launcher.goto_home(); } @@ -182,30 +242,25 @@ async fn main() { process_launcher.goto_end(); } TerminalEvent::Input(Event::Key(Key::Char('\n'))) => { - let output_view = process_launcher.launch(); + let mut output_port = ViewPort::new(); + pty = process_launcher.launch_pty(output_port.inner()); - let box_port = ViewPort::new(); - let test_box = Arc::new(RwLock::new(AsciiBox { - content: Some(output_view.map_item(|_,a| a.add_style_back(TerminalStyle::fg_color((230, 230, 230)))).get_view().unwrap()), - extent: Vector2::new(120,30) - })); + let box_port = ViewPort::new(); + let test_box = AsciiBox::new( + Vector2::new(81, 26), + output_port.outer() + .map_item(|_,a| a.add_style_back(TerminalStyle::fg_color((230, 230, 230)))), + box_port.inner() + ); - box_port.inner().set_view(Some(test_box.clone() as Arc)); + table_buf.remove(Point2::new(0, y-1)); - table_buf.insert(Point2::new(0, y-1), ViewPort::new().outer()); - y += 1; - table_buf.insert(Point2::new(0, y), box_port.outer() - .map_item(|_idx, x| x.add_style_back(TerminalStyle::fg_color((90, 120, 100)))) - .offset(Vector2::new(0, -1))); - - process_launcher = ProcessLauncher::new(); - process_launcher.goto(TreeCursor { - leaf_mode: ListCursorMode::Insert, - tree_addr: vec![ 0 ] - }); + let mut p = box_port.outer().map_item(|_idx, x| x.add_style_back(TerminalStyle::fg_color((90, 120, 100)))) + .offset(Vector2::new(0, -1)); + table_port.update_hooks.write().unwrap().push(Arc::new(p.clone().0)); y += 1; - table_buf.insert(Point2::new(0, y), process_launcher.get_term_view()); + table_buf.insert(Point2::new(0, y), p.clone()); } ev => { @@ -226,6 +281,7 @@ async fn main() { } } } + } status_chars.clear(); let cur = process_launcher.get_cursor(); diff --git a/shell/src/process.rs b/shell/src/process.rs index 25e4996..dd8148c 100644 --- a/shell/src/process.rs +++ b/shell/src/process.rs @@ -8,7 +8,7 @@ use { termion::event::{Key, Event}, cgmath::Point2, nested::{ - core::{ViewPort, OuterViewPort, Observer}, + core::{OuterViewPort, InnerViewPort, Observer}, singleton::{SingletonView, SingletonBuffer}, sequence::{SequenceView, SequenceViewExt}, index::buffer::IndexBuffer, @@ -19,7 +19,6 @@ use { string_editor::CharEditor, } }; -use portable_pty::{CommandBuilder, PtySize, native_pty_system, PtySystem}; //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> @@ -96,7 +95,7 @@ impl ProcessLauncher { } } - pub fn launch(&mut self) -> (OuterViewPort) { + pub fn launch_pty(&mut self, port: InnerViewPort) -> Option { self.up(); self.up(); @@ -109,45 +108,13 @@ impl ProcessLauncher { } if strings.len() > 0 { - // Create a new pty - let mut pair = native_pty_system().openpty(PtySize { - rows: 30, - cols: 120, - // Not all systems support pixel_width, pixel_height, - // but it is good practice to set it to something - // that matches the size of the selected font. That - // is more complex than can be shown here in this - // brief example though! - pixel_width: 0, - pixel_height: 0, - }).unwrap(); - // Spawn a shell into the pty - let mut cmd = CommandBuilder::new(strings[0].as_str()); + let mut cmd = crate::pty::CommandBuilder::new(strings[0].as_str()); cmd.args(&strings[1..]); - if let Ok(child) = pair.slave.spawn_command(cmd) { - // Read and parse output from the pty with reader - let mut reader = pair.master.try_clone_reader().unwrap(); - - // Send data to the pty by writing to the master - //writeln!(pair.master, "ls -l\r\n"); - - let port = ViewPort::new(); - let p = port.inner(); - async_std::task::spawn_blocking( - move || { - nested::terminal::ansi_parser::read_ansi_from(&mut reader, p); - }); - - port.into_outer() - } else { - make_label("invalid command") - .map_item(|idx, a| a.add_style_back(TerminalStyle::fg_color((200,0,0)))) - } + crate::pty::PTY::new(cmd, port) } else { - make_label("no command") - .map_item(|idx, a| a.add_style_back(TerminalStyle::fg_color((200,0,0)))) + None } } } diff --git a/shell/src/pty.rs b/shell/src/pty.rs new file mode 100644 index 0000000..74a5f67 --- /dev/null +++ b/shell/src/pty.rs @@ -0,0 +1,74 @@ + +use { + termion::event::{Key, Event}, + nested::{ + core::{InnerViewPort}, + terminal::{TerminalView, TerminalEvent} + } +}; + +pub use portable_pty::CommandBuilder; + +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub struct PTY { + master: Box, + child: Box +} + +impl PTY { + pub fn new( + cmd: portable_pty::CommandBuilder, + port: InnerViewPort + ) -> Option { + + // Create a new pty + let mut pair = portable_pty::native_pty_system().openpty(portable_pty::PtySize { + rows: 25, + cols: 80, + + // Not all systems support pixel_width, pixel_height, + // but it is good practice to set it to something + // that matches the size of the selected font. That + // is more complex than can be shown here in this + // brief example though! + pixel_width: 0, + pixel_height: 0, + }).unwrap(); + + if let Ok(child) = pair.slave.spawn_command(cmd) { + let mut reader = pair.master.try_clone_reader().unwrap(); + + async_std::task::spawn_blocking( + move || { + nested::terminal::ansi_parser::read_ansi_from(&mut reader, port); + }); + + Some(PTY { + master: pair.master, + child + }) + } else { + None + } + } + + pub fn get_status(&mut self) -> bool { + if let Ok(Some(status)) = self.child.try_wait() { + true + } else { + false + } + } + + pub fn handle_terminal_event(&mut self, event: &TerminalEvent) { + match event { + TerminalEvent::Input(Event::Key(Key::Char(c))) => { + write!(self.master, "{}", c); + } + _ => { + } + } + } +} +