Add child forking functionality
parent
ac334b09b1
commit
b35407bc7f
|
@ -10,6 +10,7 @@ path = "src/bin.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chan = "0.1.21"
|
chan = "0.1.21"
|
||||||
chan-signal = "0.3.1"
|
chan-signal = "0.3.1"
|
||||||
|
nix = "*"
|
||||||
melib = { path = "melib", version = "*" }
|
melib = { path = "melib", version = "*" }
|
||||||
|
|
||||||
ui = { path = "ui", version = "*" }
|
ui = { path = "ui", version = "*" }
|
||||||
|
|
85
src/bin.rs
85
src/bin.rs
|
@ -31,7 +31,7 @@ use ui::*;
|
||||||
pub use melib::*;
|
pub use melib::*;
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::io::{stdout, stdin, };
|
use std::io::{stdout,};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate chan;
|
extern crate chan;
|
||||||
|
@ -39,13 +39,24 @@ extern crate chan_signal;
|
||||||
|
|
||||||
use chan_signal::Signal;
|
use chan_signal::Signal;
|
||||||
|
|
||||||
|
fn make_input_thread(sx: chan::Sender<ThreadEvent>, rx: chan::Receiver<bool>) -> () {
|
||||||
|
let stdin = std::io::stdin();
|
||||||
|
thread::Builder::new().name("input-thread".to_string()).spawn(move || {
|
||||||
|
|
||||||
|
get_events(stdin,
|
||||||
|
|k| {
|
||||||
|
sx.send(ThreadEvent::Input(k));
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
sx.send(ThreadEvent::UIEventType(UIEventType::ChangeMode(UIMode::Fork)));
|
||||||
|
}, rx)}).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
fn main() {
|
fn main() {
|
||||||
/* Lock all stdios */
|
/* Lock all stdio outs */
|
||||||
let _stdout = stdout();
|
let _stdout = stdout();
|
||||||
let mut _stdout = _stdout.lock();
|
let mut _stdout = _stdout.lock();
|
||||||
let stdin = stdin();
|
|
||||||
let stdin = stdin;
|
|
||||||
/*
|
/*
|
||||||
let _stderr = stderr();
|
let _stderr = stderr();
|
||||||
let mut _stderr = _stderr.lock();
|
let mut _stderr = _stderr.lock();
|
||||||
|
@ -58,15 +69,17 @@ fn main() {
|
||||||
* */
|
* */
|
||||||
let (sender, receiver) = chan::sync(::std::mem::size_of::<ThreadEvent>());
|
let (sender, receiver) = chan::sync(::std::mem::size_of::<ThreadEvent>());
|
||||||
|
|
||||||
{
|
|
||||||
let sender = sender.clone();
|
/*
|
||||||
thread::Builder::new().name("input-thread".to_string()).spawn(move || {
|
* Create async channel to block the input-thread if we need to fork and stop it from reading
|
||||||
get_events(stdin, move | k| { sender.send(ThreadEvent::Input(k));
|
* stdin, see get_events() for details
|
||||||
})}).unwrap();
|
* */
|
||||||
}
|
let (tx, rx) = chan::async();
|
||||||
|
/* Get input thread handle to kill it if we need to */
|
||||||
|
make_input_thread(sender.clone(), rx.clone());
|
||||||
|
|
||||||
/* Create the application State. This is the 'System' part of an ECS architecture */
|
/* Create the application State. This is the 'System' part of an ECS architecture */
|
||||||
let mut state = State::new(_stdout, sender);
|
let mut state = State::new(_stdout, sender.clone(), tx );
|
||||||
|
|
||||||
/* Register some reasonably useful interfaces */
|
/* Register some reasonably useful interfaces */
|
||||||
let menu = Entity {component: Box::new(AccountMenu::new(&state.context.accounts)) };
|
let menu = Entity {component: Box::new(AccountMenu::new(&state.context.accounts)) };
|
||||||
|
@ -81,23 +94,27 @@ fn main() {
|
||||||
state.register_entity(xdg_notifications);
|
state.register_entity(xdg_notifications);
|
||||||
|
|
||||||
/* Keep track of the input mode. See ui::UIMode for details */
|
/* Keep track of the input mode. See ui::UIMode for details */
|
||||||
let mut mode: UIMode = UIMode::Normal;
|
|
||||||
'main: loop {
|
'main: loop {
|
||||||
state.render();
|
state.render();
|
||||||
|
eprintln!("entered main loop");
|
||||||
|
|
||||||
'inner: loop {
|
'inner: loop {
|
||||||
|
eprintln!("entered inner loop");
|
||||||
/* Check if any entities have sent reply events to State. */
|
/* Check if any entities have sent reply events to State. */
|
||||||
let events: Vec<UIEvent> = state.context.replies();
|
let events: Vec<UIEvent> = state.context.replies();
|
||||||
for e in events {
|
for e in events {
|
||||||
state.rcv_event(e);
|
state.rcv_event(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.redraw();
|
state.redraw();
|
||||||
/* Poll on all channels. Currently we have the input channel for stdin, watching events and the signal watcher. */
|
/* Poll on all channels. Currently we have the input channel for stdin, watching events and the signal watcher. */
|
||||||
chan_select! {
|
chan_select! {
|
||||||
receiver.recv() -> r => {
|
receiver.recv() -> r => {
|
||||||
|
eprintln!("received {:?}", r);
|
||||||
match r.unwrap() {
|
match r.unwrap() {
|
||||||
ThreadEvent::Input(k) => {
|
ThreadEvent::Input(k) => {
|
||||||
match mode {
|
eprintln!(" match input");
|
||||||
|
match state.mode {
|
||||||
UIMode::Normal => {
|
UIMode::Normal => {
|
||||||
match k {
|
match k {
|
||||||
Key::Char('q') | Key::Char('Q') => {
|
Key::Char('q') | Key::Char('Q') => {
|
||||||
|
@ -105,8 +122,8 @@ fn main() {
|
||||||
break 'main;
|
break 'main;
|
||||||
},
|
},
|
||||||
Key::Char(';') => {
|
Key::Char(';') => {
|
||||||
mode = UIMode::Execute;
|
state.mode = UIMode::Execute;
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ChangeMode(mode)});
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ChangeMode(UIMode::Execute)});
|
||||||
state.redraw();
|
state.redraw();
|
||||||
}
|
}
|
||||||
key => {
|
key => {
|
||||||
|
@ -118,8 +135,8 @@ fn main() {
|
||||||
UIMode::Execute => {
|
UIMode::Execute => {
|
||||||
match k {
|
match k {
|
||||||
Key::Char('\n') | Key::Esc => {
|
Key::Char('\n') | Key::Esc => {
|
||||||
mode = UIMode::Normal;
|
state.mode = UIMode::Normal;
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ChangeMode(mode)});
|
state.rcv_event(UIEvent { id: 0, event_type: UIEventType::ChangeMode(UIMode::Normal)});
|
||||||
state.redraw();
|
state.redraw();
|
||||||
},
|
},
|
||||||
k @ Key::Char(_) => {
|
k @ Key::Char(_) => {
|
||||||
|
@ -129,6 +146,10 @@ fn main() {
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
UIMode::Fork => {
|
||||||
|
eprintln!("UIMODE FORK");
|
||||||
|
break 'inner; // `goto` 'reap loop, and wait on child.
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ThreadEvent::RefreshMailbox { name : n } => {
|
ThreadEvent::RefreshMailbox { name : n } => {
|
||||||
|
@ -137,20 +158,50 @@ fn main() {
|
||||||
/* Don't handle this yet. */
|
/* Don't handle this yet. */
|
||||||
eprintln!("Refresh mailbox {}", n);
|
eprintln!("Refresh mailbox {}", n);
|
||||||
},
|
},
|
||||||
|
ThreadEvent::UIEventType(UIEventType::ChangeMode(f)) => {
|
||||||
|
state.mode = f;
|
||||||
|
break 'inner; // `goto` 'reap loop, and wait on child.
|
||||||
|
}
|
||||||
ThreadEvent::UIEventType(e) => {
|
ThreadEvent::UIEventType(e) => {
|
||||||
|
eprintln!(" match event");
|
||||||
state.rcv_event(UIEvent { id: 0, event_type: e});
|
state.rcv_event(UIEvent { id: 0, event_type: e});
|
||||||
state.render();
|
state.render();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
signal.recv() -> signal => {
|
signal.recv() -> signal => {
|
||||||
|
if state.mode != UIMode::Fork {
|
||||||
if let Some(Signal::WINCH) = signal {
|
if let Some(Signal::WINCH) = signal {
|
||||||
|
eprintln!("resize, mode is {:?}", state.mode);
|
||||||
state.update_size();
|
state.update_size();
|
||||||
state.render();
|
state.render();
|
||||||
state.redraw();
|
state.redraw();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
} // end of 'inner
|
||||||
|
|
||||||
|
'reap: loop {
|
||||||
|
eprintln!("reached reap loop");
|
||||||
|
match state.try_wait_on_child() {
|
||||||
|
Some(true) => {
|
||||||
|
make_input_thread(sender.clone(), rx.clone());
|
||||||
|
state.mode = UIMode::Normal;
|
||||||
|
state.render();
|
||||||
|
},
|
||||||
|
Some(false) => {
|
||||||
|
use std::{thread, time};
|
||||||
|
|
||||||
|
let ten_millis = time::Duration::from_millis(500);
|
||||||
|
|
||||||
|
thread::sleep(ten_millis);
|
||||||
|
continue 'reap;
|
||||||
|
},
|
||||||
|
None => {break 'reap;},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,3 +11,4 @@ chan = "0.1.21"
|
||||||
notify = "4.0.1"
|
notify = "4.0.1"
|
||||||
notify-rust = "^3"
|
notify-rust = "^3"
|
||||||
nom = "3.2.0"
|
nom = "3.2.0"
|
||||||
|
chan-signal = "0.3.1"
|
||||||
|
|
|
@ -476,7 +476,42 @@ impl Component for MailListing {
|
||||||
UIEventType::Input(Key::Char('\n')) if self.unfocused == false => {
|
UIEventType::Input(Key::Char('\n')) if self.unfocused == false => {
|
||||||
self.unfocused = true;
|
self.unfocused = true;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
},
|
||||||
|
UIEventType::Input(Key::Char('m')) if self.unfocused == false => {
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
/* Kill input thread so that spawned command can be sole receiver of stdin */
|
||||||
|
{
|
||||||
|
/* I tried thread::park() here but for some reason it never blocked and always
|
||||||
|
* returned. Spinlocks are also useless because you have to keep the mutex
|
||||||
|
* guard alive til the child process exits, which requires some effort.
|
||||||
|
*
|
||||||
|
* The only problem with this approach is tht the user has to send some input
|
||||||
|
* in order for the input-thread to wake up and realise it should kill itself.
|
||||||
|
*
|
||||||
|
* I tried writing to stdin/tty manually but for some reason rustty didn't
|
||||||
|
* acknowledge it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* tx sends to input-thread and it kills itself.
|
||||||
|
*/
|
||||||
|
let tx = context.input_thread();
|
||||||
|
tx.send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = Command::new("vim")
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.spawn()
|
||||||
|
.expect("failed to execute process") ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main loop will wait on children and when they reap them the loop spawns a new
|
||||||
|
* input-thread
|
||||||
|
*/
|
||||||
|
context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::Fork(output) });
|
||||||
|
context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::ChangeMode(UIMode::Fork) });
|
||||||
|
return;
|
||||||
},
|
},
|
||||||
UIEventType::Input(Key::Esc) | UIEventType::Input(Key::Char('i')) if self.unfocused == true => {
|
UIEventType::Input(Key::Esc) | UIEventType::Input(Key::Char('i')) if self.unfocused == true => {
|
||||||
self.unfocused = false;
|
self.unfocused = false;
|
||||||
|
|
|
@ -328,14 +328,15 @@ impl Component for StatusBar {
|
||||||
(set_y(upper_left, get_y(bottom_right) - height + 1), set_y(bottom_right, get_y(bottom_right) - height+1)),
|
(set_y(upper_left, get_y(bottom_right) - height + 1), set_y(bottom_right, get_y(bottom_right) - height+1)),
|
||||||
context);
|
context);
|
||||||
},
|
},
|
||||||
|
_ => {},
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {
|
fn process_event(&mut self, event: &UIEvent, context: &mut Context) {
|
||||||
self.container.rcv_event(event, context);
|
self.container.rcv_event(event, context);
|
||||||
match event.event_type {
|
match &event.event_type {
|
||||||
UIEventType::RefreshMailbox((idx_a, idx_f)) => {
|
UIEventType::RefreshMailbox((ref idx_a, ref idx_f)) => {
|
||||||
let m = &context.accounts[idx_a][idx_f].as_ref().unwrap().as_ref().unwrap();
|
let m = &context.accounts[*idx_a][*idx_f].as_ref().unwrap().as_ref().unwrap();
|
||||||
self.status = format!("{} |Mailbox: {}, Messages: {}, New: {}", self.mode, m.folder.name(), m.collection.len(), m.collection.iter().filter(|e| !e.is_seen()).count());
|
self.status = format!("{} |Mailbox: {}, Messages: {}, New: {}", self.mode, m.folder.name(), m.collection.len(), m.collection.iter().filter(|e| !e.is_seen()).count());
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
|
@ -344,7 +345,7 @@ impl Component for StatusBar {
|
||||||
let offset = self.status.find('|').unwrap_or(self.status.len());
|
let offset = self.status.find('|').unwrap_or(self.status.len());
|
||||||
self.status.replace_range(..offset, &format!("{} ", m));
|
self.status.replace_range(..offset, &format!("{} ", m));
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.mode = m;
|
self.mode = m.clone();
|
||||||
match m {
|
match m {
|
||||||
UIMode::Normal => {
|
UIMode::Normal => {
|
||||||
self.height = 1;
|
self.height = 1;
|
||||||
|
@ -354,11 +355,12 @@ impl Component for StatusBar {
|
||||||
UIMode::Execute => {
|
UIMode::Execute => {
|
||||||
self.height = 2;
|
self.height = 2;
|
||||||
},
|
},
|
||||||
|
_ => {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
UIEventType::ExInput(Key::Char(c)) => {
|
UIEventType::ExInput(Key::Char(c)) => {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.ex_buffer.push(c);
|
self.ex_buffer.push(*c);
|
||||||
},
|
},
|
||||||
UIEventType::Resize => {
|
UIEventType::Resize => {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*! A parser module for user commands passed through the Ex mode.
|
/*! A parser module for user commands passed through the Ex mode.
|
||||||
*/
|
*/
|
||||||
use std;
|
use std;
|
||||||
use nom::digit;
|
use nom::{digit, alpha};
|
||||||
|
|
||||||
|
|
||||||
named!(usize_c<usize>,
|
named!(usize_c<usize>,
|
||||||
|
@ -11,3 +11,8 @@ named!(pub goto<usize>,
|
||||||
preceded!(tag!("b "),
|
preceded!(tag!("b "),
|
||||||
call!(usize_c))
|
call!(usize_c))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
named!(pub sort<&str>,
|
||||||
|
preceded!(tag!("sort "),
|
||||||
|
map_res!(call!(alpha), std::str::from_utf8))
|
||||||
|
);
|
||||||
|
|
|
@ -42,6 +42,9 @@ pub use self::components::*;
|
||||||
|
|
||||||
extern crate melib;
|
extern crate melib;
|
||||||
extern crate notify_rust;
|
extern crate notify_rust;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate chan;
|
||||||
|
extern crate chan_signal;
|
||||||
use melib::*;
|
use melib::*;
|
||||||
|
|
||||||
use std::io::{Write, };
|
use std::io::{Write, };
|
||||||
|
@ -54,13 +57,13 @@ use termion::event::{Key as TermionKey, };
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
use termion::screen::AlternateScreen;
|
use termion::screen::AlternateScreen;
|
||||||
|
|
||||||
extern crate chan;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate nom;
|
extern crate nom;
|
||||||
use chan::Sender;
|
use chan::Sender;
|
||||||
|
|
||||||
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads
|
/// `ThreadEvent` encapsulates all of the possible values we need to transfer between our threads
|
||||||
/// to the main process.
|
/// to the main process.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ThreadEvent {
|
pub enum ThreadEvent {
|
||||||
/// User input.
|
/// User input.
|
||||||
Input(Key),
|
Input(Key),
|
||||||
|
@ -84,6 +87,8 @@ pub enum UIEventType {
|
||||||
RefreshMailbox((usize,usize)),
|
RefreshMailbox((usize,usize)),
|
||||||
//Quit?
|
//Quit?
|
||||||
Resize,
|
Resize,
|
||||||
|
/// Force redraw.
|
||||||
|
Fork(std::process::Child),
|
||||||
ChangeMailbox(usize),
|
ChangeMailbox(usize),
|
||||||
ChangeMode(UIMode),
|
ChangeMode(UIMode),
|
||||||
Command(String),
|
Command(String),
|
||||||
|
@ -98,10 +103,11 @@ pub struct UIEvent {
|
||||||
pub event_type: UIEventType,
|
pub event_type: UIEventType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub enum UIMode {
|
pub enum UIMode {
|
||||||
Normal,
|
Normal,
|
||||||
Execute,
|
Execute,
|
||||||
|
Fork,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for UIMode {
|
impl fmt::Display for UIMode {
|
||||||
|
@ -109,6 +115,7 @@ impl fmt::Display for UIMode {
|
||||||
write!(f, "{}", match *self {
|
write!(f, "{}", match *self {
|
||||||
UIMode::Normal => { "NORMAL" },
|
UIMode::Normal => { "NORMAL" },
|
||||||
UIMode::Execute => { "EX" },
|
UIMode::Execute => { "EX" },
|
||||||
|
UIMode::Fork => { "FORK" },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,12 +138,17 @@ pub struct Context {
|
||||||
/// Events queue that components send back to the state
|
/// Events queue that components send back to the state
|
||||||
replies: VecDeque<UIEvent>,
|
replies: VecDeque<UIEvent>,
|
||||||
backends: Backends,
|
backends: Backends,
|
||||||
|
|
||||||
|
input_thread: chan::Sender<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn replies(&mut self) -> Vec<UIEvent> {
|
pub fn replies(&mut self) -> Vec<UIEvent> {
|
||||||
self.replies.drain(0..).collect()
|
self.replies.drain(0..).collect()
|
||||||
}
|
}
|
||||||
|
pub fn input_thread(&mut self) -> &mut chan::Sender<bool> {
|
||||||
|
&mut self.input_thread
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,9 +160,12 @@ pub struct State<W: Write> {
|
||||||
|
|
||||||
grid: CellBuffer,
|
grid: CellBuffer,
|
||||||
stdout: termion::screen::AlternateScreen<termion::raw::RawTerminal<W>>,
|
stdout: termion::screen::AlternateScreen<termion::raw::RawTerminal<W>>,
|
||||||
|
child: Option<std::process::Child>,
|
||||||
|
pub mode: UIMode,
|
||||||
sender: Sender<ThreadEvent>,
|
sender: Sender<ThreadEvent>,
|
||||||
entities: Vec<Entity>,
|
entities: Vec<Entity>,
|
||||||
pub context: Context,
|
pub context: Context,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> Drop for State<W> {
|
impl<W: Write> Drop for State<W> {
|
||||||
|
@ -162,7 +177,7 @@ impl<W: Write> Drop for State<W> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> State<W> {
|
impl<W: Write> State<W> {
|
||||||
pub fn new(stdout: W, sender: Sender<ThreadEvent>) -> Self {
|
pub fn new(stdout: W, sender: Sender<ThreadEvent>, input_thread: chan::Sender<bool>) -> Self {
|
||||||
let settings = Settings::new();
|
let settings = Settings::new();
|
||||||
let backends = Backends::new();
|
let backends = Backends::new();
|
||||||
let stdout = AlternateScreen::from(stdout.into_raw_mode().unwrap());
|
let stdout = AlternateScreen::from(stdout.into_raw_mode().unwrap());
|
||||||
|
@ -179,15 +194,20 @@ impl<W: Write> State<W> {
|
||||||
rows: rows,
|
rows: rows,
|
||||||
grid: CellBuffer::new(cols, rows, Cell::with_char(' ')),
|
grid: CellBuffer::new(cols, rows, Cell::with_char(' ')),
|
||||||
stdout: stdout,
|
stdout: stdout,
|
||||||
|
child: None,
|
||||||
|
mode: UIMode::Normal,
|
||||||
sender: sender,
|
sender: sender,
|
||||||
entities: Vec::with_capacity(1),
|
entities: Vec::with_capacity(1),
|
||||||
|
|
||||||
|
|
||||||
context: Context {
|
context: Context {
|
||||||
accounts: accounts,
|
accounts: accounts,
|
||||||
backends: backends,
|
backends: backends,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
dirty_areas: VecDeque::with_capacity(5),
|
dirty_areas: VecDeque::with_capacity(5),
|
||||||
replies: VecDeque::with_capacity(5),
|
replies: VecDeque::with_capacity(5),
|
||||||
|
|
||||||
|
input_thread: input_thread,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
write!(s.stdout, "{}{}{}", cursor::Hide, clear::All, cursor::Goto(1,1)).unwrap();
|
write!(s.stdout, "{}{}{}", cursor::Hide, clear::All, cursor::Goto(1,1)).unwrap();
|
||||||
|
@ -298,6 +318,12 @@ impl<W: Write> State<W> {
|
||||||
self.parse_command(cmd);
|
self.parse_command(cmd);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
|
UIEventType::Fork(child) => {
|
||||||
|
self.mode = UIMode::Fork;
|
||||||
|
self.child = Some(child);
|
||||||
|
self.stdout.flush().unwrap();
|
||||||
|
return;
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
/* inform each entity */
|
/* inform each entity */
|
||||||
|
@ -320,6 +346,25 @@ impl<W: Write> State<W> {
|
||||||
self.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((account_idx, folder_idx)) });
|
self.rcv_event(UIEvent { id: 0, event_type: UIEventType::RefreshMailbox((account_idx, folder_idx)) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn try_wait_on_child(&mut self) -> Option<bool> {
|
||||||
|
if {
|
||||||
|
if let Some(ref mut c) = self.child {
|
||||||
|
let mut w = c.try_wait();
|
||||||
|
match w {
|
||||||
|
Ok(Some(_)) => { true },
|
||||||
|
Ok(None) => { false },
|
||||||
|
Err(_) => { return None; },
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
self.child = None;
|
||||||
|
return Some(true);
|
||||||
|
}
|
||||||
|
Some(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -391,10 +436,30 @@ impl From<TermionKey> for Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_events(stdin: std::io::Stdin, mut closure: impl FnMut(Key)) -> (){
|
|
||||||
let stdin = stdin.lock();
|
/*
|
||||||
|
* If we fork (for example start $EDITOR) we want the input-thread to stop reading from stdin. The
|
||||||
|
* best way I came up with right now is to send a signal to the thread that is read in the first
|
||||||
|
* input in stdin after the fork, and then the thread kills itself. The parent process spawns a new
|
||||||
|
* input-thread when the child returns.
|
||||||
|
*
|
||||||
|
* The main loop uses try_wait_on_child() to check if child has exited.
|
||||||
|
*/
|
||||||
|
pub fn get_events(stdin: std::io::Stdin, mut closure: impl FnMut(Key), mut exit: impl FnMut(), rx: chan::Receiver<bool>) -> (){
|
||||||
for c in stdin.keys() {
|
for c in stdin.keys() {
|
||||||
|
chan_select! {
|
||||||
|
default => {},
|
||||||
|
rx.recv() -> val => {
|
||||||
|
if let Some(true) = val {
|
||||||
|
exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
if let Ok(k) = c {
|
if let Ok(k) = c {
|
||||||
|
eprintln!("rcvd {:?}", k);
|
||||||
closure(Key::from(k));
|
closure(Key::from(k));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue