process editor: write keyboard input to pty
This commit is contained in:
parent
b9e285acb4
commit
c9c6958a62
5 changed files with 263 additions and 117 deletions
|
@ -14,15 +14,15 @@ use {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SingletonBufferView<T: Clone + Eq + Send + Sync + 'static>(Arc<RwLock<T>>);
|
pub struct SingletonBufferView<T: Clone + Send + Sync + 'static>(Arc<RwLock<T>>);
|
||||||
|
|
||||||
impl<T> View for SingletonBufferView<T>
|
impl<T> View for SingletonBufferView<T>
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
where T: Clone + Send + Sync + 'static {
|
||||||
type Msg = ();
|
type Msg = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SingletonView for SingletonBufferView<T>
|
impl<T> SingletonView for SingletonBufferView<T>
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
where T: Clone + Send + Sync + 'static {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
|
||||||
fn get(&self) -> Self::Item {
|
fn get(&self) -> Self::Item {
|
||||||
|
@ -32,13 +32,13 @@ where T: Clone + Eq + Send + Sync + 'static {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SingletonBuffer<T>
|
pub struct SingletonBuffer<T>
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
where T: Clone + Send + Sync + 'static {
|
||||||
value: Arc<RwLock<T>>,
|
value: Arc<RwLock<T>>,
|
||||||
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = T>>>>
|
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = T>>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SingletonBuffer<T>
|
impl<T> SingletonBuffer<T>
|
||||||
where T: Clone + Eq + Send + Sync + 'static {
|
where T: Clone + Send + Sync + 'static {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
value: T,
|
value: T,
|
||||||
port: InnerViewPort<dyn SingletonView<Item = T>>
|
port: InnerViewPort<dyn SingletonView<Item = T>>
|
||||||
|
@ -56,6 +56,16 @@ where T: Clone + Eq + Send + Sync + 'static {
|
||||||
self.value.read().unwrap().clone()
|
self.value.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, new_value: T) {
|
||||||
|
let mut v = self.value.write().unwrap();
|
||||||
|
*v = new_value;
|
||||||
|
drop(v);
|
||||||
|
self.cast.notify(&());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
impl<T> SingletonBuffer<T>
|
||||||
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
pub fn set(&mut self, new_value: T) {
|
pub fn set(&mut self, new_value: T) {
|
||||||
let mut v = self.value.write().unwrap();
|
let mut v = self.value.write().unwrap();
|
||||||
if *v != new_value {
|
if *v != new_value {
|
||||||
|
@ -65,6 +75,6 @@ where T: Clone + Eq + Send + Sync + 'static {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// TODO: impl Deref & DerefMut
|
// TODO: impl Deref & DerefMut
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub fn read_ansi_from<R: Read + Unpin>(ansi_reader: &mut R, port: InnerViewPort<
|
||||||
cursor: Point2::new(0, 0),
|
cursor: Point2::new(0, 0),
|
||||||
style: TerminalStyle::default(),
|
style: TerminalStyle::default(),
|
||||||
invert: false,
|
invert: false,
|
||||||
term_width: 80,
|
term_width: 120,
|
||||||
|
|
||||||
cursor_save: Point2::new(0, 0),
|
cursor_save: Point2::new(0, 0),
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ impl Perform for PerfAtom {
|
||||||
b'\t' => self.horizontal_tab(),
|
b'\t' => self.horizontal_tab(),
|
||||||
0x8 => self.backspace(),
|
0x8 => self.backspace(),
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("unhandled execute byte {:02x}", byte);
|
//eprintln!("unhandled execute byte {:02x}", byte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,9 @@ use{
|
||||||
TerminalEvent,
|
TerminalEvent,
|
||||||
make_label,
|
make_label,
|
||||||
TerminalView,
|
TerminalView,
|
||||||
TerminalEditor},
|
TerminalEditor,
|
||||||
|
TerminalEditorResult
|
||||||
|
},
|
||||||
string_editor::{StringEditor},
|
string_editor::{StringEditor},
|
||||||
tree_nav::{TreeNav, TreeNavResult, TreeCursor, TerminalTreeEditor},
|
tree_nav::{TreeNav, TreeNavResult, TreeCursor, TerminalTreeEditor},
|
||||||
list::{SExprView, ListCursorMode, ListEditor, ListEditorStyle},
|
list::{SExprView, ListCursorMode, ListEditor, ListEditorStyle},
|
||||||
|
@ -82,7 +84,11 @@ impl IndexView<Point2<i16>> for Plot {
|
||||||
if let Some(cur_val) = self.data.get(&(pt.x as usize)) {
|
if let Some(cur_val) = self.data.get(&(pt.x as usize)) {
|
||||||
if cur_val <= self.limit {
|
if cur_val <= self.limit {
|
||||||
if pt.y == (self.limit - cur_val) as i16 {
|
if pt.y == (self.limit - cur_val) as i16 {
|
||||||
return Some(TerminalAtom::from('*'));
|
return Some(TerminalAtom::from(
|
||||||
|
if cur_val < 4 { 'o' }
|
||||||
|
else if cur_val < 8 { 'O' }
|
||||||
|
else { '*' }
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pt.x > 0 {
|
if pt.x > 0 {
|
||||||
|
@ -98,7 +104,7 @@ impl IndexView<Point2<i16>> for Plot {
|
||||||
pt.y > (self.limit - cur_val) as i16
|
pt.y > (self.limit - cur_val) as i16
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return Some(TerminalAtom::from('|'));
|
return Some(TerminalAtom::from('.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,64 +280,6 @@ async fn main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let ev = term.next_event().await;
|
|
||||||
match ev {
|
|
||||||
TerminalEvent::Resize(new_size) => {
|
|
||||||
cur_size.set(new_size);
|
|
||||||
term_port.inner().get_broadcast().notify(&IndexArea::Full);
|
|
||||||
}
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) |
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Ctrl('g'))) |
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Ctrl('d'))) => break,
|
|
||||||
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
|
||||||
process_list_editor.pxev();
|
|
||||||
}
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
|
||||||
process_list_editor.nexd();
|
|
||||||
}
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Up)) => {
|
|
||||||
if process_list_editor.up() == TreeNavResult::Exit {
|
|
||||||
process_list_editor.dn();
|
|
||||||
process_list_editor.goto_home();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Down)) => {
|
|
||||||
if process_list_editor.dn() == TreeNavResult::Continue {
|
|
||||||
process_list_editor.goto_home();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Home)) => {
|
|
||||||
process_list_editor.goto_home();
|
|
||||||
}
|
|
||||||
TerminalEvent::Input(Event::Key(Key::End)) => {
|
|
||||||
process_list_editor.goto_end();
|
|
||||||
}
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
|
||||||
if let Some(launcher) = process_list_editor.get_item() {
|
|
||||||
launcher.write().unwrap().launch_pty2();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ev => {
|
|
||||||
if process_list_editor.get_cursor().leaf_mode == ListCursorMode::Select {
|
|
||||||
match ev {
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('l'))) => { process_list_editor.up(); },
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('a'))) => { process_list_editor.dn(); },
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('i'))) => { process_list_editor.pxev(); },
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('e'))) => { process_list_editor.nexd(); },
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('u'))) => { process_list_editor.goto_home(); },
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('o'))) => { process_list_editor.goto_end(); },
|
|
||||||
_ => {
|
|
||||||
process_list_editor.handle_terminal_event(&ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
process_list_editor.handle_terminal_event(&ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status_chars.clear();
|
status_chars.clear();
|
||||||
let cur = process_list_editor.get_cursor();
|
let cur = process_list_editor.get_cursor();
|
||||||
|
|
||||||
|
@ -360,6 +308,79 @@ async fn main() {
|
||||||
status_chars.push(TerminalAtom::new(c, TerminalStyle::fg_color((200, 200, 20))));
|
status_chars.push(TerminalAtom::new(c, TerminalStyle::fg_color((200, 200, 20))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ev = term.next_event().await;
|
||||||
|
|
||||||
|
if let TerminalEvent::Resize(new_size) = ev {
|
||||||
|
cur_size.set(new_size);
|
||||||
|
term_port.inner().get_broadcast().notify(&IndexArea::Full);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(process_editor) = process_list_editor.get_item() {
|
||||||
|
let mut pe = process_editor.write().unwrap();
|
||||||
|
if pe.is_captured() {
|
||||||
|
if let TerminalEditorResult::Exit = pe.handle_terminal_event(&ev) {
|
||||||
|
drop(pe);
|
||||||
|
process_list_editor.up();
|
||||||
|
process_list_editor.nexd();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match ev {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Ctrl('d'))) => break,
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Ctrl('l'))) => {
|
||||||
|
process_list_editor.goto(TreeCursor {
|
||||||
|
leaf_mode: ListCursorMode::Insert,
|
||||||
|
tree_addr: vec![ 0 ]
|
||||||
|
});
|
||||||
|
process_list_editor.data.clear();
|
||||||
|
},
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
||||||
|
process_list_editor.pxev();
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
||||||
|
process_list_editor.nexd();
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Up)) => {
|
||||||
|
if process_list_editor.up() == TreeNavResult::Exit {
|
||||||
|
process_list_editor.dn();
|
||||||
|
process_list_editor.goto_home();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Down)) => {
|
||||||
|
if process_list_editor.dn() == TreeNavResult::Continue {
|
||||||
|
process_list_editor.goto_home();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Home)) => {
|
||||||
|
process_list_editor.goto_home();
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::End)) => {
|
||||||
|
process_list_editor.goto_end();
|
||||||
|
}
|
||||||
|
ev => {
|
||||||
|
if process_list_editor.get_cursor().leaf_mode == ListCursorMode::Select {
|
||||||
|
match ev {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('l'))) => { process_list_editor.up(); },
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('a'))) => { process_list_editor.dn(); },
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('i'))) => { process_list_editor.pxev(); },
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('e'))) => { process_list_editor.nexd(); },
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('u'))) => { process_list_editor.goto_home(); },
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('o'))) => { process_list_editor.goto_end(); },
|
||||||
|
_ => {
|
||||||
|
process_list_editor.handle_terminal_event(&ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let TerminalEditorResult::Exit = process_list_editor.handle_terminal_event(&ev) {
|
||||||
|
process_list_editor.nexd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(term);
|
drop(term);
|
||||||
|
|
|
@ -15,7 +15,7 @@ use {
|
||||||
vec::VecBuffer,
|
vec::VecBuffer,
|
||||||
terminal::{TerminalAtom, TerminalStyle, TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult, make_label},
|
terminal::{TerminalAtom, TerminalStyle, TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult, make_label},
|
||||||
tree_nav::{TreeNav, TreeNavResult, TerminalTreeEditor, TreeCursor},
|
tree_nav::{TreeNav, TreeNavResult, TerminalTreeEditor, TreeCursor},
|
||||||
list::{ListEditor, ListEditorStyle, sexpr::ListDecoration},
|
list::{ListCursorMode, ListEditor, ListEditorStyle, sexpr::ListDecoration},
|
||||||
string_editor::CharEditor,
|
string_editor::CharEditor,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -74,8 +74,10 @@ pub struct ProcessLauncher {
|
||||||
>,
|
>,
|
||||||
pty: Option<crate::pty::PTY>,
|
pty: Option<crate::pty::PTY>,
|
||||||
ptybox: Arc<RwLock<crate::ascii_box::AsciiBox>>,
|
ptybox: Arc<RwLock<crate::ascii_box::AsciiBox>>,
|
||||||
|
suspended: bool,
|
||||||
|
|
||||||
pty_port: ViewPort<dyn TerminalView>,
|
pty_port: ViewPort<dyn TerminalView>,
|
||||||
|
status_port: ViewPort<dyn SingletonView<Item = Option<portable_pty::ExitStatus>>>,
|
||||||
|
|
||||||
comp_port: ViewPort<dyn TerminalView>,
|
comp_port: ViewPort<dyn TerminalView>,
|
||||||
compositor: Arc<RwLock<nested::terminal::TerminalCompositor>>
|
compositor: Arc<RwLock<nested::terminal::TerminalCompositor>>
|
||||||
|
@ -84,6 +86,7 @@ pub struct ProcessLauncher {
|
||||||
impl ProcessLauncher {
|
impl ProcessLauncher {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let pty_port = ViewPort::new();
|
let pty_port = ViewPort::new();
|
||||||
|
let status_port = ViewPort::new();
|
||||||
let comp_port = ViewPort::new();
|
let comp_port = ViewPort::new();
|
||||||
let box_port = ViewPort::<dyn TerminalView>::new();
|
let box_port = ViewPort::<dyn TerminalView>::new();
|
||||||
let compositor = nested::terminal::TerminalCompositor::new(comp_port.inner());
|
let compositor = nested::terminal::TerminalCompositor::new(comp_port.inner());
|
||||||
|
@ -126,20 +129,15 @@ impl ProcessLauncher {
|
||||||
.map_item(|_,a:&TerminalAtom| a.add_style_back(TerminalStyle::fg_color((230, 230, 230)))),
|
.map_item(|_,a:&TerminalAtom| a.add_style_back(TerminalStyle::fg_color((230, 230, 230)))),
|
||||||
box_port.inner()
|
box_port.inner()
|
||||||
),
|
),
|
||||||
|
suspended: false,
|
||||||
pty_port,
|
pty_port,
|
||||||
|
status_port,
|
||||||
comp_port,
|
comp_port,
|
||||||
compositor
|
compositor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn launch_pty2(&mut self) {
|
pub fn launch_pty(&mut self) {
|
||||||
self.launch_pty(self.pty_port.inner());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn launch_pty(&mut self, port: InnerViewPort<dyn TerminalView>) -> Option<crate::pty::PTY> {
|
|
||||||
self.up();
|
|
||||||
self.up();
|
|
||||||
|
|
||||||
let mut strings = Vec::new();
|
let mut strings = Vec::new();
|
||||||
|
|
||||||
let v = self.cmd_editor.get_data_port().get_view().unwrap();
|
let v = self.cmd_editor.get_data_port().get_view().unwrap();
|
||||||
|
@ -153,11 +151,18 @@ impl ProcessLauncher {
|
||||||
let mut cmd = crate::pty::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..]);
|
||||||
|
|
||||||
crate::pty::PTY::new(cmd, port)
|
self.cmd_editor.goto(TreeCursor {
|
||||||
} else {
|
leaf_mode: ListCursorMode::Insert,
|
||||||
None
|
tree_addr: vec![]
|
||||||
|
});
|
||||||
|
|
||||||
|
self.pty = crate::pty::PTY::new(cmd, self.pty_port.inner(), self.status_port.inner());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_captured(&self) -> bool {
|
||||||
|
self.pty.is_some() && !self.suspended
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalEditor for ProcessLauncher {
|
impl TerminalEditor for ProcessLauncher {
|
||||||
|
@ -166,27 +171,94 @@ 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
|
||||||
|
if let Some(status) = self.status_port.outer().get_view().get() {
|
||||||
|
self.pty = None;
|
||||||
|
self.suspended = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => {
|
||||||
|
self.pty = None;
|
||||||
|
self.suspended = false;
|
||||||
|
self.cmd_editor.goto(TreeCursor {
|
||||||
|
leaf_mode: ListCursorMode::Insert,
|
||||||
|
tree_addr: vec![]
|
||||||
|
});
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
},
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Ctrl('z'))) => {
|
||||||
|
self.suspended = true;
|
||||||
|
self.cmd_editor.goto(TreeCursor {
|
||||||
|
leaf_mode: ListCursorMode::Insert,
|
||||||
|
tree_addr: vec![]
|
||||||
|
});
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
event => {
|
||||||
|
if let Some(pty) = self.pty.as_mut() {
|
||||||
|
pty.handle_terminal_event(event);
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
} else {
|
||||||
match event {
|
match event {
|
||||||
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
||||||
// launch command
|
// launch command
|
||||||
self.cmd_editor.up();
|
self.launch_pty();
|
||||||
self.cmd_editor.up();
|
TerminalEditorResult::Continue
|
||||||
TerminalEditorResult::Exit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
event => self.cmd_editor.handle_terminal_event(event)
|
event => self.cmd_editor.handle_terminal_event(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TreeNav for ProcessLauncher {
|
impl TreeNav for ProcessLauncher {
|
||||||
fn get_cursor(&self) -> TreeCursor { self.cmd_editor.get_cursor() }
|
fn get_cursor(&self) -> TreeCursor {
|
||||||
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult { self.cmd_editor.goto(cur) }
|
self.cmd_editor.get_cursor()
|
||||||
fn goto_home(&mut self) -> TreeNavResult { self.cmd_editor.goto_home() }
|
}
|
||||||
fn goto_end(&mut self) -> TreeNavResult { self.cmd_editor.goto_end() }
|
|
||||||
fn pxev(&mut self) -> TreeNavResult { self.cmd_editor.pxev() }
|
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult {
|
||||||
fn nexd(&mut self) -> TreeNavResult { self.cmd_editor.nexd() }
|
self.suspended = false;
|
||||||
fn up(&mut self) -> TreeNavResult { self.cmd_editor.up() }
|
if let Some(status) = self.status_port.outer().get_view().get() {
|
||||||
fn dn(&mut self) -> TreeNavResult { self.cmd_editor.dn() }
|
self.pty = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.pty.is_none() {
|
||||||
|
self.cmd_editor.goto(cur)
|
||||||
|
} else {
|
||||||
|
self.cmd_editor.goto(TreeCursor {
|
||||||
|
leaf_mode: ListCursorMode::Select,
|
||||||
|
tree_addr: vec![]
|
||||||
|
});
|
||||||
|
TreeNavResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto_home(&mut self) -> TreeNavResult {
|
||||||
|
self.cmd_editor.goto_home()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto_end(&mut self) -> TreeNavResult {
|
||||||
|
self.cmd_editor.goto_end()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pxev(&mut self) -> TreeNavResult {
|
||||||
|
self.cmd_editor.pxev()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nexd(&mut self) -> TreeNavResult {
|
||||||
|
self.cmd_editor.nexd()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn up(&mut self) -> TreeNavResult {
|
||||||
|
self.cmd_editor.up()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dn(&mut self) -> TreeNavResult {
|
||||||
|
self.cmd_editor.dn()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ use {
|
||||||
std::sync::Mutex,
|
std::sync::Mutex,
|
||||||
nested::{
|
nested::{
|
||||||
core::{InnerViewPort},
|
core::{InnerViewPort},
|
||||||
terminal::{TerminalView, TerminalEvent}
|
singleton::{SingletonView, SingletonBuffer},
|
||||||
|
terminal::{TerminalView, TerminalEvent, TerminalEditorResult}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,20 +14,20 @@ pub use portable_pty::CommandBuilder;
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
pub struct PTY {
|
pub struct PTY {
|
||||||
master: Mutex<Box<dyn portable_pty::MasterPty + Send>>,
|
master: Mutex<Box<dyn portable_pty::MasterPty + Send>>
|
||||||
child: 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,
|
||||||
port: InnerViewPort<dyn TerminalView>
|
term_port: InnerViewPort<dyn TerminalView>,
|
||||||
|
status_port: InnerViewPort<dyn SingletonView<Item = Option<portable_pty::ExitStatus>>>
|
||||||
) -> 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: 25,
|
||||||
cols: 80,
|
cols: 120,
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -37,37 +38,79 @@ impl PTY {
|
||||||
pixel_height: 0,
|
pixel_height: 0,
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
if let Ok(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();
|
||||||
|
|
||||||
async_std::task::spawn_blocking(
|
async_std::task::spawn_blocking(
|
||||||
move || {
|
move || {
|
||||||
nested::terminal::ansi_parser::read_ansi_from(&mut reader, port);
|
nested::terminal::ansi_parser::read_ansi_from(&mut reader, term_port);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async_std::task::spawn_blocking(
|
||||||
|
move || {
|
||||||
|
let mut status_buf = SingletonBuffer::new(None, status_port);
|
||||||
|
if let Ok(status) = child.wait() {
|
||||||
|
status_buf.set(Some(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Some(PTY {
|
Some(PTY {
|
||||||
master: Mutex::new(pair.master),
|
master: Mutex::new(pair.master)
|
||||||
child
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_status(&mut self) -> bool {
|
pub fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
if let Ok(Some(status)) = self.child.try_wait() {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_terminal_event(&mut self, event: &TerminalEvent) {
|
|
||||||
match event {
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
||||||
|
self.master.lock().unwrap().write(&[13]).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
},
|
||||||
TerminalEvent::Input(Event::Key(Key::Char(c))) => {
|
TerminalEvent::Input(Event::Key(Key::Char(c))) => {
|
||||||
write!(self.master.lock().unwrap(), "{}", c);
|
write!(self.master.lock().unwrap(), "{}", c);
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
},
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Esc)) => {
|
||||||
|
self.master.lock().unwrap().write(&[0x1b]).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Backspace)) => {
|
||||||
|
self.master.lock().unwrap().write(&[0x8]).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::F(n))) => {
|
||||||
|
self.master.lock().unwrap().write(&[
|
||||||
|
0x1b,
|
||||||
|
0x0a,
|
||||||
|
match n {
|
||||||
|
11 => 133,
|
||||||
|
12 => 134,
|
||||||
|
n => 58 + n
|
||||||
|
}
|
||||||
|
]).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Up)) => {
|
||||||
|
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'A']).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Down)) => {
|
||||||
|
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'B']).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Right)) => {
|
||||||
|
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'C']).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Left)) => {
|
||||||
|
self.master.lock().unwrap().write(&[0, b'\x1B', b'[', b'D']).unwrap();
|
||||||
|
TerminalEditorResult::Continue
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
TerminalEditorResult::Exit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue