pty wrapper

This commit is contained in:
Michael Sippel 2021-09-15 17:47:40 +02:00
parent 7e6aa9bbe7
commit 5ec840e87c
Signed by: senvas
GPG key ID: F96CF119C34B64A6
3 changed files with 165 additions and 68 deletions

View file

@ -3,6 +3,7 @@ extern crate portable_pty;
mod monstera; mod monstera;
mod process; mod process;
mod pty;
use{ use{
std::sync::{Arc, RwLock}, std::sync::{Arc, RwLock},
@ -12,9 +13,11 @@ use{
core::{ core::{
View, View,
ViewPort, ViewPort,
InnerViewPort,
OuterViewPort, OuterViewPort,
Observer, Observer,
ObserverExt, ObserverExt,
ObserverBroadcast,
context::{ReprTree, Object, MorphismType, MorphismMode, Context}, context::{ReprTree, Object, MorphismType, MorphismMode, Context},
port::{UpdateTask}}, port::{UpdateTask}},
index::{IndexView}, index::{IndexView},
@ -43,7 +46,40 @@ use{
struct AsciiBox { struct AsciiBox {
content: Option<Arc<dyn TerminalView>>, content: Option<Arc<dyn TerminalView>>,
extent: Vector2<i16> extent: Vector2<i16>,
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>
}
impl AsciiBox {
pub fn new(
extent: Vector2<i16>,
content_port: OuterViewPort<dyn TerminalView>,
output_port: InnerViewPort<dyn TerminalView>
) -> Arc<RwLock<Self>> {
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<dyn TerminalView> for AsciiBox {
fn reset(&mut self, new_content: Option<Arc<dyn TerminalView>>) {
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<i16>) {
self.cast.notify(&(pt + Vector2::new(1, 1)));
}
} }
impl View for AsciiBox { impl View for AsciiBox {
@ -130,8 +166,8 @@ async fn main() {
(Point2::new(0, 2), magic.clone()), (Point2::new(0, 2), magic.clone()),
]); ]);
//compositor.write().unwrap().push(monstera::make_monstera()); compositor.write().unwrap().push(monstera::make_monstera());
compositor.write().unwrap().push(table_port.outer().flatten());//.offset(Vector2::new(40, 2))); compositor.write().unwrap().push(table_port.outer().flatten().offset(Vector2::new(40, 2)));
let mut y = 4; let mut y = 4;
@ -142,14 +178,34 @@ async fn main() {
leaf_mode: ListCursorMode::Insert, leaf_mode: ListCursorMode::Insert,
tree_addr: vec![ 0 ] tree_addr: vec![ 0 ]
}); });
/*
let mut last_box = Arc::new(RwLock::new(AsciiBox{
})); let mut pty : Option<pty::PTY> = None;
*/
loop { loop {
term_port.update(); 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) => { TerminalEvent::Resize(new_size) => {
cur_size.set(new_size); cur_size.set(new_size);
term_port.inner().get_broadcast().notify_each( term_port.inner().get_broadcast().notify_each(
@ -168,9 +224,13 @@ async fn main() {
TerminalEvent::Input(Event::Key(Key::Right)) => { TerminalEvent::Input(Event::Key(Key::Right)) => {
process_launcher.nexd(); process_launcher.nexd();
} }
TerminalEvent::Input(Event::Key(Key::Up)) => { process_launcher.up(); } TerminalEvent::Input(Event::Key(Key::Up)) => {
TerminalEvent::Input(Event::Key(Key::Down)) => { if process_launcher.up() == TreeNavResult::Exit {
//process_launcher.dn(); //process_launcher.dn();
//process_launcher.goto_home();
}
}
TerminalEvent::Input(Event::Key(Key::Down)) => {
if process_launcher.dn() == TreeNavResult::Continue { if process_launcher.dn() == TreeNavResult::Continue {
process_launcher.goto_home(); process_launcher.goto_home();
} }
@ -182,30 +242,25 @@ async fn main() {
process_launcher.goto_end(); process_launcher.goto_end();
} }
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => { 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 box_port = ViewPort::new();
let test_box = Arc::new(RwLock::new(AsciiBox { let test_box = AsciiBox::new(
content: Some(output_view.map_item(|_,a| a.add_style_back(TerminalStyle::fg_color((230, 230, 230)))).get_view().unwrap()), Vector2::new(81, 26),
extent: Vector2::new(120,30) 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<dyn TerminalView>)); table_buf.remove(Point2::new(0, y-1));
table_buf.insert(Point2::new(0, y-1), ViewPort::new().outer()); let mut p = box_port.outer().map_item(|_idx, x| x.add_style_back(TerminalStyle::fg_color((90, 120, 100))))
y += 1; .offset(Vector2::new(0, -1));
table_buf.insert(Point2::new(0, y), box_port.outer() table_port.update_hooks.write().unwrap().push(Arc::new(p.clone().0));
.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 ]
});
y += 1; y += 1;
table_buf.insert(Point2::new(0, y), process_launcher.get_term_view()); table_buf.insert(Point2::new(0, y), p.clone());
} }
ev => { ev => {
@ -226,6 +281,7 @@ async fn main() {
} }
} }
} }
}
status_chars.clear(); status_chars.clear();
let cur = process_launcher.get_cursor(); let cur = process_launcher.get_cursor();

View file

@ -8,7 +8,7 @@ use {
termion::event::{Key, Event}, termion::event::{Key, Event},
cgmath::Point2, cgmath::Point2,
nested::{ nested::{
core::{ViewPort, OuterViewPort, Observer}, core::{OuterViewPort, InnerViewPort, Observer},
singleton::{SingletonView, SingletonBuffer}, singleton::{SingletonView, SingletonBuffer},
sequence::{SequenceView, SequenceViewExt}, sequence::{SequenceView, SequenceViewExt},
index::buffer::IndexBuffer, index::buffer::IndexBuffer,
@ -19,7 +19,6 @@ use {
string_editor::CharEditor, string_editor::CharEditor,
} }
}; };
use portable_pty::{CommandBuilder, PtySize, native_pty_system, PtySystem};
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
@ -96,7 +95,7 @@ impl ProcessLauncher {
} }
} }
pub fn launch(&mut self) -> (OuterViewPort<dyn TerminalView>) { pub fn launch_pty(&mut self, port: InnerViewPort<dyn TerminalView>) -> Option<crate::pty::PTY> {
self.up(); self.up();
self.up(); self.up();
@ -109,45 +108,13 @@ impl ProcessLauncher {
} }
if strings.len() > 0 { 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 // 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..]); cmd.args(&strings[1..]);
if let Ok(child) = pair.slave.spawn_command(cmd) { crate::pty::PTY::new(cmd, port)
// 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 { } else {
make_label("invalid command") None
.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))))
} }
} }
} }

74
shell/src/pty.rs Normal file
View file

@ -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<dyn portable_pty::MasterPty + Send>,
child: Box<dyn portable_pty::Child + Send + Sync>
}
impl PTY {
pub fn new(
cmd: portable_pty::CommandBuilder,
port: InnerViewPort<dyn TerminalView>
) -> Option<Self> {
// 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);
}
_ => {
}
}
}
}