process launcher: PTYStatus & kill process
This commit is contained in:
parent
c9c6958a62
commit
f3ebc7b919
2 changed files with 56 additions and 20 deletions
|
@ -17,7 +17,8 @@ use {
|
||||||
tree_nav::{TreeNav, TreeNavResult, TerminalTreeEditor, TreeCursor},
|
tree_nav::{TreeNav, TreeNavResult, TerminalTreeEditor, TreeCursor},
|
||||||
list::{ListCursorMode, ListEditor, ListEditorStyle, sexpr::ListDecoration},
|
list::{ListCursorMode, ListEditor, ListEditorStyle, sexpr::ListDecoration},
|
||||||
string_editor::CharEditor,
|
string_editor::CharEditor,
|
||||||
}
|
},
|
||||||
|
crate::pty::{PTY, PTYStatus}
|
||||||
};
|
};
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
@ -77,7 +78,7 @@ pub struct ProcessLauncher {
|
||||||
suspended: bool,
|
suspended: bool,
|
||||||
|
|
||||||
pty_port: ViewPort<dyn TerminalView>,
|
pty_port: ViewPort<dyn TerminalView>,
|
||||||
status_port: ViewPort<dyn SingletonView<Item = Option<portable_pty::ExitStatus>>>,
|
status_port: ViewPort<dyn SingletonView<Item = PTYStatus>>,
|
||||||
|
|
||||||
comp_port: ViewPort<dyn TerminalView>,
|
comp_port: ViewPort<dyn TerminalView>,
|
||||||
compositor: Arc<RwLock<nested::terminal::TerminalCompositor>>
|
compositor: Arc<RwLock<nested::terminal::TerminalCompositor>>
|
||||||
|
@ -156,7 +157,7 @@ impl ProcessLauncher {
|
||||||
tree_addr: vec![]
|
tree_addr: vec![]
|
||||||
});
|
});
|
||||||
|
|
||||||
self.pty = crate::pty::PTY::new(cmd, self.pty_port.inner(), self.status_port.inner());
|
self.pty = PTY::new(cmd, cgmath::Vector2::new(120, 40), self.pty_port.inner(), self.status_port.inner());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,13 +174,18 @@ impl TerminalEditor for ProcessLauncher {
|
||||||
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
|
|
||||||
// todo: move to observer of status view
|
// todo: move to observer of status view
|
||||||
if let Some(status) = self.status_port.outer().get_view().get() {
|
if let PTYStatus::Done{ status } = self.status_port.outer().get_view().get() {
|
||||||
self.pty = None;
|
self.pty = None;
|
||||||
self.suspended = false;
|
self.suspended = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => {
|
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => {
|
||||||
|
// todo: sigterm instead of kill?
|
||||||
|
if let Some(mut pty) = self.pty.as_mut() {
|
||||||
|
pty.kill();
|
||||||
|
}
|
||||||
|
|
||||||
self.pty = None;
|
self.pty = None;
|
||||||
self.suspended = false;
|
self.suspended = false;
|
||||||
self.cmd_editor.goto(TreeCursor {
|
self.cmd_editor.goto(TreeCursor {
|
||||||
|
@ -222,7 +228,7 @@ impl TreeNav for ProcessLauncher {
|
||||||
|
|
||||||
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult {
|
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult {
|
||||||
self.suspended = false;
|
self.suspended = false;
|
||||||
if let Some(status) = self.status_port.outer().get_view().get() {
|
if let PTYStatus::Done{status} = self.status_port.outer().get_view().get() {
|
||||||
self.pty = None;
|
self.pty = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
std::sync::{Arc, Mutex},
|
||||||
termion::event::{Key, Event},
|
termion::event::{Key, Event},
|
||||||
std::sync::Mutex,
|
cgmath::Vector2,
|
||||||
nested::{
|
nested::{
|
||||||
core::{InnerViewPort},
|
core::{InnerViewPort},
|
||||||
singleton::{SingletonView, SingletonBuffer},
|
singleton::{SingletonView, SingletonBuffer},
|
||||||
|
@ -13,21 +14,39 @@ pub use portable_pty::CommandBuilder;
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum PTYStatus {
|
||||||
|
Running{ pid: u32 },
|
||||||
|
Done{ status: portable_pty::ExitStatus }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PTYStatus {
|
||||||
|
fn default() -> Self {
|
||||||
|
PTYStatus::Running {
|
||||||
|
pid: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
pub struct PTY {
|
pub struct PTY {
|
||||||
master: Mutex<Box<dyn portable_pty::MasterPty + Send>>
|
master: Mutex<Box<dyn portable_pty::MasterPty + Send>>,
|
||||||
|
child: Arc<Mutex<Box<dyn portable_pty::Child + Send + Sync>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PTY {
|
impl PTY {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cmd: portable_pty::CommandBuilder,
|
cmd: portable_pty::CommandBuilder,
|
||||||
|
max_size: Vector2<i16>,
|
||||||
term_port: InnerViewPort<dyn TerminalView>,
|
term_port: InnerViewPort<dyn TerminalView>,
|
||||||
status_port: InnerViewPort<dyn SingletonView<Item = Option<portable_pty::ExitStatus>>>
|
status_port: InnerViewPort<dyn SingletonView<Item = PTYStatus>>
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
|
|
||||||
// Create a new pty
|
// Create a new pty
|
||||||
let mut pair = portable_pty::native_pty_system().openpty(portable_pty::PtySize {
|
let mut pair = portable_pty::native_pty_system().openpty(portable_pty::PtySize {
|
||||||
rows: 25,
|
rows: max_size.y as u16,
|
||||||
cols: 120,
|
cols: max_size.x as u16,
|
||||||
|
|
||||||
// Not all systems support pixel_width, pixel_height,
|
// Not all systems support pixel_width, pixel_height,
|
||||||
// but it is good practice to set it to something
|
// but it is good practice to set it to something
|
||||||
|
@ -40,29 +59,40 @@ impl PTY {
|
||||||
|
|
||||||
if let Ok(mut child) = pair.slave.spawn_command(cmd) {
|
if let Ok(mut child) = pair.slave.spawn_command(cmd) {
|
||||||
let mut reader = pair.master.try_clone_reader().unwrap();
|
let mut reader = pair.master.try_clone_reader().unwrap();
|
||||||
|
let mut status_buf = SingletonBuffer::new(PTYStatus::Running{ pid: child.process_id().expect("") }, status_port);
|
||||||
|
|
||||||
|
let child = Arc::new(Mutex::new(child));
|
||||||
|
|
||||||
async_std::task::spawn_blocking(
|
async_std::task::spawn_blocking(
|
||||||
move || {
|
move || {
|
||||||
nested::terminal::ansi_parser::read_ansi_from(&mut reader, term_port);
|
nested::terminal::ansi_parser::read_ansi_from(&mut reader, term_port);
|
||||||
});
|
});
|
||||||
|
|
||||||
async_std::task::spawn_blocking(
|
async_std::task::spawn_blocking({
|
||||||
|
let child = child.clone();
|
||||||
move || {
|
move || {
|
||||||
let mut status_buf = SingletonBuffer::new(None, status_port);
|
loop {
|
||||||
if let Ok(status) = child.wait() {
|
if let Ok(Some(status)) = child.lock().unwrap().try_wait() {
|
||||||
status_buf.set(Some(status));
|
status_buf.set(PTYStatus::Done{ status });
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
Some(PTY {
|
Some(PTY {
|
||||||
master: Mutex::new(pair.master)
|
master: Mutex::new(pair.master),
|
||||||
|
child
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kill(&mut self) {
|
||||||
|
self.child.lock().unwrap().kill();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
pub fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
match event {
|
match event {
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
||||||
|
@ -94,19 +124,19 @@ impl PTY {
|
||||||
TerminalEditorResult::Continue
|
TerminalEditorResult::Continue
|
||||||
}
|
}
|
||||||
TerminalEvent::Input(Event::Key(Key::Up)) => {
|
TerminalEvent::Input(Event::Key(Key::Up)) => {
|
||||||
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'A']).unwrap();
|
self.master.lock().unwrap().write(&[b'\x1B', b'[', b'A']).unwrap();
|
||||||
TerminalEditorResult::Continue
|
TerminalEditorResult::Continue
|
||||||
}
|
}
|
||||||
TerminalEvent::Input(Event::Key(Key::Down)) => {
|
TerminalEvent::Input(Event::Key(Key::Down)) => {
|
||||||
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'B']).unwrap();
|
self.master.lock().unwrap().write(&[b'\x1B', b'[', b'B']).unwrap();
|
||||||
TerminalEditorResult::Continue
|
TerminalEditorResult::Continue
|
||||||
}
|
}
|
||||||
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
||||||
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'C']).unwrap();
|
self.master.lock().unwrap().write(&[b'\x1B', b'[', b'C']).unwrap();
|
||||||
TerminalEditorResult::Continue
|
TerminalEditorResult::Continue
|
||||||
}
|
}
|
||||||
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
||||||
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'D']).unwrap();
|
self.master.lock().unwrap().write(&[b'\x1B', b'[', b'D']).unwrap();
|
||||||
TerminalEditorResult::Continue
|
TerminalEditorResult::Continue
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
Loading…
Reference in a new issue