Overhaul input thread
Remove raw/non raw distinction. Use a pipe for input thread commands and poll stdin/pipe for eventsmaster
parent
839c1b1eb5
commit
b8261ee36a
|
@ -199,6 +199,13 @@ impl From<std::ffi::NulError> for MeliError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<nix::Error> for MeliError {
|
||||||
|
#[inline]
|
||||||
|
fn from(kind: nix::Error) -> MeliError {
|
||||||
|
MeliError::new(format!("{}", kind)).set_source(Some(Arc::new(kind)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&str> for MeliError {
|
impl From<&str> for MeliError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(kind: &str) -> MeliError {
|
fn from(kind: &str) -> MeliError {
|
||||||
|
|
25
src/bin.rs
25
src/bin.rs
|
@ -429,33 +429,28 @@ fn run_app() -> Result<()> {
|
||||||
_ => {debug!(&r);}
|
_ => {debug!(&r);}
|
||||||
}
|
}
|
||||||
match r.unwrap() {
|
match r.unwrap() {
|
||||||
ThreadEvent::Input(Key::Ctrl('z')) => {
|
ThreadEvent::Input((Key::Ctrl('z'), _)) if state.mode != UIMode::Embed => {
|
||||||
state.switch_to_main_screen();
|
state.switch_to_main_screen();
|
||||||
//_thread_handler.join().expect("Couldn't join on the associated thread");
|
//_thread_handler.join().expect("Couldn't join on the associated thread");
|
||||||
let self_pid = nix::unistd::Pid::this();
|
let self_pid = nix::unistd::Pid::this();
|
||||||
nix::sys::signal::kill(self_pid, nix::sys::signal::Signal::SIGSTOP).unwrap();
|
nix::sys::signal::kill(self_pid, nix::sys::signal::Signal::SIGSTOP).unwrap();
|
||||||
state.switch_to_alternate_screen();
|
state.switch_to_alternate_screen();
|
||||||
state.restore_input();
|
|
||||||
// BUG: thread sends input event after one received key
|
// BUG: thread sends input event after one received key
|
||||||
state.update_size();
|
state.update_size();
|
||||||
state.render();
|
state.render();
|
||||||
state.redraw();
|
state.redraw();
|
||||||
},
|
},
|
||||||
ThreadEvent::Input(Key::Ctrl('l')) => {
|
ThreadEvent::Input(raw_input @ (Key::Ctrl('l'), _)) => {
|
||||||
/* Manual screen redraw */
|
|
||||||
state.update_size();
|
|
||||||
state.render();
|
|
||||||
state.redraw();
|
|
||||||
},
|
|
||||||
ThreadEvent::InputRaw(raw_input @ (Key::Ctrl('l'), _)) => {
|
|
||||||
/* Manual screen redraw */
|
/* Manual screen redraw */
|
||||||
state.update_size();
|
state.update_size();
|
||||||
state.render();
|
state.render();
|
||||||
state.redraw();
|
state.redraw();
|
||||||
|
if state.mode == UIMode::Embed {
|
||||||
state.rcv_event(UIEvent::EmbedInput(raw_input));
|
state.rcv_event(UIEvent::EmbedInput(raw_input));
|
||||||
state.redraw();
|
state.redraw();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ThreadEvent::Input(k) => {
|
ThreadEvent::Input((k, r)) => {
|
||||||
match state.mode {
|
match state.mode {
|
||||||
UIMode::Normal => {
|
UIMode::Normal => {
|
||||||
match k {
|
match k {
|
||||||
|
@ -504,17 +499,15 @@ fn run_app() -> Result<()> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UIMode::Embed => state.redraw(),
|
UIMode::Embed => {
|
||||||
|
state.rcv_event(UIEvent::EmbedInput((k,r)));
|
||||||
|
state.redraw();
|
||||||
|
},
|
||||||
UIMode::Fork => {
|
UIMode::Fork => {
|
||||||
break 'inner; // `goto` 'reap loop, and wait on child.
|
break 'inner; // `goto` 'reap loop, and wait on child.
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ThreadEvent::InputRaw(raw_input) => {
|
|
||||||
state.rcv_event(UIEvent::EmbedInput(raw_input));
|
|
||||||
state.redraw();
|
|
||||||
},
|
|
||||||
ThreadEvent::RefreshMailbox(event) => {
|
ThreadEvent::RefreshMailbox(event) => {
|
||||||
state.refresh_event(*event);
|
state.refresh_event(*event);
|
||||||
state.redraw();
|
state.redraw();
|
||||||
|
|
|
@ -176,9 +176,6 @@ impl MailcapEntry {
|
||||||
a => a.to_string(),
|
a => a.to_string(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
{
|
|
||||||
context.input_kill();
|
|
||||||
}
|
|
||||||
let cmd_string = format!("{} {}", cmd, args.join(" "));
|
let cmd_string = format!("{} {}", cmd, args.join(" "));
|
||||||
melib::log(
|
melib::log(
|
||||||
format!("Executing: sh -c \"{}\"", cmd_string.replace("\"", "\\\"")),
|
format!("Executing: sh -c \"{}\"", cmd_string.replace("\"", "\\\"")),
|
||||||
|
@ -236,7 +233,6 @@ impl MailcapEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.replies.push_back(UIEvent::Fork(ForkType::Finished));
|
context.replies.push_back(UIEvent::Fork(ForkType::Finished));
|
||||||
context.restore_input();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
39
src/state.rs
39
src/state.rs
|
@ -37,6 +37,7 @@ use smallvec::SmallVec;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use termion::raw::IntoRawMode;
|
use termion::raw::IntoRawMode;
|
||||||
use termion::screen::AlternateScreen;
|
use termion::screen::AlternateScreen;
|
||||||
|
@ -45,6 +46,7 @@ use termion::{clear, cursor};
|
||||||
pub type StateStdout = termion::screen::AlternateScreen<termion::raw::RawTerminal<std::io::Stdout>>;
|
pub type StateStdout = termion::screen::AlternateScreen<termion::raw::RawTerminal<std::io::Stdout>>;
|
||||||
|
|
||||||
struct InputHandler {
|
struct InputHandler {
|
||||||
|
pipe: (RawFd, RawFd),
|
||||||
rx: Receiver<InputCommand>,
|
rx: Receiver<InputCommand>,
|
||||||
tx: Sender<InputCommand>,
|
tx: Sender<InputCommand>,
|
||||||
}
|
}
|
||||||
|
@ -54,36 +56,27 @@ impl InputHandler {
|
||||||
/* Clear channel without blocking. switch_to_main_screen() issues a kill when
|
/* Clear channel without blocking. switch_to_main_screen() issues a kill when
|
||||||
* returning from a fork and there's no input thread, so the newly created thread will
|
* returning from a fork and there's no input thread, so the newly created thread will
|
||||||
* receive it and die. */
|
* receive it and die. */
|
||||||
let _ = self.rx.try_iter().count();
|
//let _ = self.rx.try_iter().count();
|
||||||
let rx = self.rx.clone();
|
let rx = self.rx.clone();
|
||||||
|
let pipe = self.pipe.0;
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("input-thread".to_string())
|
.name("input-thread".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
get_events(
|
get_events(
|
||||||
|k| {
|
|
||||||
tx.send(ThreadEvent::Input(k)).unwrap();
|
|
||||||
},
|
|
||||||
|i| {
|
|i| {
|
||||||
tx.send(ThreadEvent::InputRaw(i)).unwrap();
|
tx.send(ThreadEvent::Input(i)).unwrap();
|
||||||
},
|
},
|
||||||
&rx,
|
&rx,
|
||||||
None,
|
pipe,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kill(&self) {
|
fn kill(&self) {
|
||||||
|
let _ = nix::unistd::write(self.pipe.1, &[1]);
|
||||||
self.tx.send(InputCommand::Kill).unwrap();
|
self.tx.send(InputCommand::Kill).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn switch_to_raw(&self) {
|
|
||||||
self.tx.send(InputCommand::Raw).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn switch_from_raw(&self) {
|
|
||||||
self.tx.send(InputCommand::NoRaw).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A context container for loaded settings, accounts, UI changes, etc.
|
/// A context container for loaded settings, accounts, UI changes, etc.
|
||||||
|
@ -117,14 +110,6 @@ impl Context {
|
||||||
self.input.kill();
|
self.input.kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_from_raw(&self) {
|
|
||||||
self.input.switch_from_raw();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn input_to_raw(&self) {
|
|
||||||
self.input.switch_to_raw();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn restore_input(&self) {
|
pub fn restore_input(&self) {
|
||||||
self.input.restore(self.sender.clone());
|
self.input.restore(self.sender.clone());
|
||||||
}
|
}
|
||||||
|
@ -225,6 +210,8 @@ impl State {
|
||||||
* stdin, see get_events() for details
|
* stdin, see get_events() for details
|
||||||
* */
|
* */
|
||||||
let input_thread = unbounded();
|
let input_thread = unbounded();
|
||||||
|
let input_thread_pipe = nix::unistd::pipe()
|
||||||
|
.map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)?;
|
||||||
let mut backends = Backends::new();
|
let mut backends = Backends::new();
|
||||||
let settings = Settings::new()?;
|
let settings = Settings::new()?;
|
||||||
let mut plugin_manager = PluginManager::new();
|
let mut plugin_manager = PluginManager::new();
|
||||||
|
@ -338,6 +325,7 @@ impl State {
|
||||||
sender,
|
sender,
|
||||||
receiver,
|
receiver,
|
||||||
input: InputHandler {
|
input: InputHandler {
|
||||||
|
pipe: input_thread_pipe,
|
||||||
rx: input_thread.1,
|
rx: input_thread.1,
|
||||||
tx: input_thread.0,
|
tx: input_thread.0,
|
||||||
},
|
},
|
||||||
|
@ -423,7 +411,6 @@ impl State {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.flush();
|
self.flush();
|
||||||
self.stdout = None;
|
self.stdout = None;
|
||||||
self.context.input.kill();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_to_alternate_screen(&mut self) {
|
pub fn switch_to_alternate_screen(&mut self) {
|
||||||
|
@ -937,16 +924,10 @@ impl State {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UIEvent::ChangeMode(m) => {
|
UIEvent::ChangeMode(m) => {
|
||||||
if self.mode == UIMode::Embed {
|
|
||||||
self.context.input_from_raw();
|
|
||||||
}
|
|
||||||
self.context
|
self.context
|
||||||
.sender
|
.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::ChangeMode(m)))
|
.send(ThreadEvent::UIEvent(UIEvent::ChangeMode(m)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if m == UIMode::Embed {
|
|
||||||
self.context.input_to_raw();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
UIEvent::Timer(id) if id == self.draw_rate_limit.id() => {
|
UIEvent::Timer(id) if id == self.draw_rate_limit.id() => {
|
||||||
self.draw_rate_limit.reset();
|
self.draw_rate_limit.reset();
|
||||||
|
|
|
@ -20,8 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::terminal::position::*;
|
use crate::terminal::position::*;
|
||||||
use melib::log;
|
use melib::{log, ERROR, error::*};
|
||||||
use melib::ERROR;
|
|
||||||
|
|
||||||
use nix::fcntl::{open, OFlag};
|
use nix::fcntl::{open, OFlag};
|
||||||
use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
|
use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
|
||||||
|
@ -29,8 +28,8 @@ use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt, Winsize};
|
||||||
use nix::sys::stat;
|
use nix::sys::stat;
|
||||||
use nix::unistd::{dup2, fork, ForkResult};
|
use nix::unistd::{dup2, fork, ForkResult};
|
||||||
use nix::{ioctl_none_bad, ioctl_write_ptr_bad};
|
use nix::{ioctl_none_bad, ioctl_write_ptr_bad};
|
||||||
use std::ffi::CString;
|
use std::ffi::{CString, OsStr};
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
|
use std::os::unix::{ffi::OsStrExt, io::{AsRawFd, FromRawFd, IntoRawFd}};
|
||||||
|
|
||||||
mod grid;
|
mod grid;
|
||||||
|
|
||||||
|
@ -56,7 +55,7 @@ pub fn create_pty(
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
command: String,
|
command: String,
|
||||||
) -> nix::Result<Arc<Mutex<EmbedGrid>>> {
|
) -> Result<Arc<Mutex<EmbedGrid>>> {
|
||||||
// Open a new PTY master
|
// Open a new PTY master
|
||||||
let master_fd = posix_openpt(OFlag::O_RDWR)?;
|
let master_fd = posix_openpt(OFlag::O_RDWR)?;
|
||||||
|
|
||||||
|
@ -115,17 +114,31 @@ pub fn create_pty(
|
||||||
std::process::exit(-1);
|
std::process::exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = nix::unistd::execv(
|
/* Find posix sh location, because POSIX shell is not always at /bin/sh */
|
||||||
&CString::new("sh").unwrap(),
|
let path_var = std::process::Command::new("getconf").args(&["PATH"]).output()?.stdout;
|
||||||
&[
|
for mut p in std::env::split_paths(&OsStr::from_bytes(&path_var[..])) {
|
||||||
&CString::new("-c").unwrap(),
|
p.push("sh");
|
||||||
&CString::new(command.as_bytes()).unwrap(),
|
if p.exists() {
|
||||||
],
|
if let Err(e) = nix::unistd::execv(
|
||||||
) {
|
&CString::new(p.as_os_str().as_bytes()).unwrap(),
|
||||||
log(format!("Could not execute `{}`: {}", command, e,), ERROR);
|
&[
|
||||||
std::process::exit(-1);
|
&CString::new("sh").unwrap(),
|
||||||
|
&CString::new("-c").unwrap(),
|
||||||
|
&CString::new(command.as_bytes()).unwrap(),
|
||||||
|
],
|
||||||
|
) {
|
||||||
|
log(format!("Could not execute `{}`: {}", command, e,), ERROR);
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* This path shouldn't be executed. */
|
log(
|
||||||
|
format!(
|
||||||
|
"Could not execute `{}`: did not find the standard POSIX sh shell in PATH = {}",
|
||||||
|
command, String::from_utf8_lossy(&path_var),
|
||||||
|
),
|
||||||
|
ERROR,
|
||||||
|
);
|
||||||
std::process::exit(-1);
|
std::process::exit(-1);
|
||||||
}
|
}
|
||||||
Ok(ForkResult::Parent { child }) => child,
|
Ok(ForkResult::Parent { child }) => child,
|
||||||
|
|
|
@ -137,8 +137,7 @@ impl PartialEq<Key> for &Key {
|
||||||
/// Keep track of whether we're accepting normal user input or a pasted string.
|
/// Keep track of whether we're accepting normal user input or a pasted string.
|
||||||
enum InputMode {
|
enum InputMode {
|
||||||
Normal,
|
Normal,
|
||||||
Paste,
|
Paste(Vec<u8>),
|
||||||
PasteRaw(Vec<u8>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -146,12 +145,10 @@ enum InputMode {
|
||||||
pub enum InputCommand {
|
pub enum InputCommand {
|
||||||
/// Exit thread
|
/// Exit thread
|
||||||
Kill,
|
Kill,
|
||||||
/// Send raw bytes as well
|
|
||||||
Raw,
|
|
||||||
/// Ignore raw bytes
|
|
||||||
NoRaw,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use nix::poll::{poll, PollFd, PollFlags};
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use termion::input::TermReadEventsAndRaw;
|
use termion::input::TermReadEventsAndRaw;
|
||||||
/*
|
/*
|
||||||
* If we fork (for example start $EDITOR) we want the input-thread to stop reading from stdin. The
|
* If we fork (for example start $EDITOR) we want the input-thread to stop reading from stdin. The
|
||||||
|
@ -163,111 +160,68 @@ use termion::input::TermReadEventsAndRaw;
|
||||||
*/
|
*/
|
||||||
/// The thread function that listens for user input and forwards it to the main event loop.
|
/// The thread function that listens for user input and forwards it to the main event loop.
|
||||||
pub fn get_events(
|
pub fn get_events(
|
||||||
mut closure: impl FnMut(Key),
|
|
||||||
closure_raw: impl FnMut((Key, Vec<u8>)),
|
|
||||||
rx: &Receiver<InputCommand>,
|
|
||||||
input: Option<(TermionEvent, Vec<u8>)>,
|
|
||||||
) -> () {
|
|
||||||
let stdin = std::io::stdin();
|
|
||||||
let mut input_mode = InputMode::Normal;
|
|
||||||
let mut paste_buf = String::with_capacity(256);
|
|
||||||
for c in input
|
|
||||||
.map(|v| Ok(v))
|
|
||||||
.into_iter()
|
|
||||||
.chain(stdin.events_and_raw())
|
|
||||||
{
|
|
||||||
select! {
|
|
||||||
default => {},
|
|
||||||
recv(rx) -> cmd => {
|
|
||||||
match cmd.unwrap() {
|
|
||||||
InputCommand::Kill => return,
|
|
||||||
InputCommand::Raw => {
|
|
||||||
get_events_raw(closure, closure_raw, rx, c.ok());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InputCommand::NoRaw => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match c {
|
|
||||||
Ok((TermionEvent::Key(k), _)) if input_mode == InputMode::Normal => {
|
|
||||||
closure(Key::from(k));
|
|
||||||
}
|
|
||||||
Ok((TermionEvent::Key(TermionKey::Char(k)), _)) if input_mode == InputMode::Paste => {
|
|
||||||
paste_buf.push(k);
|
|
||||||
}
|
|
||||||
Ok((TermionEvent::Unsupported(ref k), _)) if k.as_slice() == BRACKET_PASTE_START => {
|
|
||||||
input_mode = InputMode::Paste;
|
|
||||||
}
|
|
||||||
Ok((TermionEvent::Unsupported(ref k), _)) if k.as_slice() == BRACKET_PASTE_END => {
|
|
||||||
input_mode = InputMode::Normal;
|
|
||||||
let ret = Key::from(&paste_buf);
|
|
||||||
paste_buf.clear();
|
|
||||||
closure(ret);
|
|
||||||
}
|
|
||||||
_ => {} // Mouse events or errors.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as `get_events` but also forwards the raw bytes of the input as well
|
|
||||||
pub fn get_events_raw(
|
|
||||||
closure_nonraw: impl FnMut(Key),
|
|
||||||
mut closure: impl FnMut((Key, Vec<u8>)),
|
mut closure: impl FnMut((Key, Vec<u8>)),
|
||||||
rx: &Receiver<InputCommand>,
|
rx: &Receiver<InputCommand>,
|
||||||
input: Option<(TermionEvent, Vec<u8>)>,
|
new_command_fd: RawFd,
|
||||||
) -> () {
|
) -> () {
|
||||||
let stdin = std::io::stdin();
|
let stdin = std::io::stdin();
|
||||||
|
let stdin_fd = PollFd::new(std::io::stdin().as_raw_fd(), PollFlags::POLLIN);
|
||||||
|
let new_command_pollfd = nix::poll::PollFd::new(new_command_fd, nix::poll::PollFlags::POLLIN);
|
||||||
let mut input_mode = InputMode::Normal;
|
let mut input_mode = InputMode::Normal;
|
||||||
let mut paste_buf = String::with_capacity(256);
|
let mut paste_buf = String::with_capacity(256);
|
||||||
for c in input
|
let mut stdin_iter = stdin.events_and_raw();
|
||||||
.map(|v| Ok(v))
|
'poll_while: while let Ok(_n_raw) = poll(&mut [new_command_pollfd, stdin_fd], -1) {
|
||||||
.into_iter()
|
//debug!(_n_raw);
|
||||||
.chain(stdin.events_and_raw())
|
|
||||||
{
|
|
||||||
select! {
|
select! {
|
||||||
default => {},
|
default => {
|
||||||
|
if stdin_fd.revents().is_some() {
|
||||||
|
if let Some(c) = stdin_iter.next(){
|
||||||
|
match (c, &mut input_mode) {
|
||||||
|
(Ok((TermionEvent::Key(k), bytes)), InputMode::Normal) => {
|
||||||
|
closure((Key::from(k), bytes));
|
||||||
|
}
|
||||||
|
(
|
||||||
|
Ok((TermionEvent::Key(TermionKey::Char(k)), ref mut bytes)), InputMode::Paste(ref mut buf),
|
||||||
|
) => {
|
||||||
|
paste_buf.push(k);
|
||||||
|
let bytes = std::mem::replace(bytes, Vec::new());
|
||||||
|
buf.extend(bytes.into_iter());
|
||||||
|
}
|
||||||
|
(Ok((TermionEvent::Unsupported(ref k), _)), _) if k.as_slice() == BRACKET_PASTE_START => {
|
||||||
|
input_mode = InputMode::Paste(Vec::new());
|
||||||
|
}
|
||||||
|
(Ok((TermionEvent::Unsupported(ref k), _)), InputMode::Paste(ref mut buf))
|
||||||
|
if k.as_slice() == BRACKET_PASTE_END =>
|
||||||
|
{
|
||||||
|
let buf = std::mem::replace(buf, Vec::new());
|
||||||
|
input_mode = InputMode::Normal;
|
||||||
|
let ret = Key::from(&paste_buf);
|
||||||
|
paste_buf.clear();
|
||||||
|
closure((ret, buf));
|
||||||
|
}
|
||||||
|
_ => {} // Mouse events or errors.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
recv(rx) -> cmd => {
|
recv(rx) -> cmd => {
|
||||||
|
use nix::sys::time::TimeValLike;
|
||||||
|
let mut buf = [0;2];
|
||||||
|
//debug!("get_events_raw will nix::unistd::read");
|
||||||
|
let mut read_fd_set = nix::sys::select::FdSet::new();
|
||||||
|
read_fd_set.insert(new_command_fd);
|
||||||
|
let mut error_fd_set = nix::sys::select::FdSet::new();
|
||||||
|
error_fd_set.insert(new_command_fd);
|
||||||
|
let timeval: nix::sys::time::TimeSpec = nix::sys::time::TimeSpec::seconds(2);
|
||||||
|
if nix::sys::select::pselect(None, Some(&mut read_fd_set), None, Some(&mut error_fd_set), Some(&timeval), None).is_err() || error_fd_set.highest() == Some(new_command_fd) || read_fd_set.highest() != Some(new_command_fd) {
|
||||||
|
continue 'poll_while;
|
||||||
|
};
|
||||||
|
let _ = nix::unistd::read(new_command_fd, buf.as_mut());
|
||||||
match cmd.unwrap() {
|
match cmd.unwrap() {
|
||||||
InputCommand::Kill => return,
|
InputCommand::Kill => return,
|
||||||
InputCommand::NoRaw => {
|
|
||||||
get_events(closure_nonraw, closure, rx, c.ok());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InputCommand::Raw => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match (c, &mut input_mode) {
|
|
||||||
(Ok((TermionEvent::Key(k), bytes)), InputMode::Normal) => {
|
|
||||||
closure((Key::from(k), bytes));
|
|
||||||
}
|
|
||||||
(
|
|
||||||
Ok((TermionEvent::Key(TermionKey::Char(k)), ref mut bytes)),
|
|
||||||
InputMode::PasteRaw(ref mut buf),
|
|
||||||
) => {
|
|
||||||
paste_buf.push(k);
|
|
||||||
let bytes = std::mem::replace(bytes, Vec::new());
|
|
||||||
buf.extend(bytes.into_iter());
|
|
||||||
}
|
|
||||||
(Ok((TermionEvent::Unsupported(ref k), _)), _)
|
|
||||||
if k.as_slice() == BRACKET_PASTE_START =>
|
|
||||||
{
|
|
||||||
input_mode = InputMode::PasteRaw(Vec::new());
|
|
||||||
}
|
|
||||||
(Ok((TermionEvent::Unsupported(ref k), _)), InputMode::PasteRaw(ref mut buf))
|
|
||||||
if k.as_slice() == BRACKET_PASTE_END =>
|
|
||||||
{
|
|
||||||
let buf = std::mem::replace(buf, Vec::new());
|
|
||||||
input_mode = InputMode::Normal;
|
|
||||||
let ret = Key::from(&paste_buf);
|
|
||||||
paste_buf.clear();
|
|
||||||
closure((ret, buf));
|
|
||||||
}
|
|
||||||
_ => {} // Mouse events or errors.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,9 +60,8 @@ pub enum StatusEvent {
|
||||||
pub enum ThreadEvent {
|
pub enum ThreadEvent {
|
||||||
NewThread(thread::ThreadId, String),
|
NewThread(thread::ThreadId, String),
|
||||||
/// User input.
|
/// User input.
|
||||||
Input(Key),
|
Input((Key, Vec<u8>)),
|
||||||
/// User input and input as raw bytes.
|
/// User input and input as raw bytes.
|
||||||
InputRaw((Key, Vec<u8>)),
|
|
||||||
/// A watched Mailbox has been refreshed.
|
/// A watched Mailbox has been refreshed.
|
||||||
RefreshMailbox(Box<RefreshEvent>),
|
RefreshMailbox(Box<RefreshEvent>),
|
||||||
UIEvent(UIEvent),
|
UIEvent(UIEvent),
|
||||||
|
|
Loading…
Reference in New Issue