summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-08 20:58:03 -0500
committerJiayi Zhao <jeff.no.zhao@gmail.com>2020-02-08 20:58:55 -0500
commit656582a6c867c25667661be9b327b4cc73859d7d (patch)
treefe03a1822b752a747da099aeb33a610fb3590067 /src
parent7a603efb9b3024438bdce8899fd21bcf353e3832 (diff)
change to using termion's keyboard handling
- user input is now on a seperate thread - this allows for other threads to be added as well - keymap configs have changed to be more user friendly
Diffstat (limited to 'src')
-rw-r--r--src/config/keymap.rs84
-rw-r--r--src/main.rs8
-rw-r--r--src/run.rs107
-rw-r--r--src/ui.rs1
-rw-r--r--src/util/event.rs83
-rw-r--r--src/util/key_mapping.rs64
-rw-r--r--src/util/mod.rs78
7 files changed, 336 insertions, 89 deletions
diff --git a/src/config/keymap.rs b/src/config/keymap.rs
index 10d6be7..e8b4120 100644
--- a/src/config/keymap.rs
+++ b/src/config/keymap.rs
@@ -1,14 +1,18 @@
-use serde_derive::Deserialize;
use std::collections::{hash_map::Entry, HashMap};
use std::process::exit;
+use serde_derive::Deserialize;
+
+use termion::event::Key;
+
use super::{parse_to_config_file, ConfigStructure, Flattenable};
use crate::commands::{self, CommandKeybind, JoshutoCommand};
+use crate::util::key_mapping::str_to_key;
use crate::KEYMAP_FILE;
pub const ESCAPE: i32 = 0x1B;
-/* #define KEY_ALT(x) KEY_F(60) + (x - 'A') */
+pub type JoshutoCommandMapping = HashMap<Key, CommandKeybind>;
const fn default_up() -> i32 {
ncurses::KEY_UP
@@ -59,7 +63,7 @@ struct JoshutoMapCommand {
pub command: String,
#[serde(default)]
pub args: Vec<String>,
- pub keys: Vec<i32>,
+ pub keys: Vec<String>,
}
#[derive(Debug, Deserialize)]
@@ -146,7 +150,15 @@ impl Flattenable<JoshutoCommandMapping> for JoshutoRawCommandMapping {
let mut keymaps = JoshutoCommandMapping::new();
for m in self.mapcommand {
match commands::from_args(m.command, m.args) {
- Ok(command) => insert_keycommand(&mut keymaps, command, &m.keys[..]),
+ Ok(command) => {
+ let keycodes: Vec<&str> = m.keys.iter().map(|s| s.as_str()).collect();
+
+ let result = insert_keycommand(&mut keymaps, command, &keycodes);
+ match result {
+ Ok(_) => {},
+ Err(e) => eprintln!("{}", e),
+ }
+ }
Err(e) => eprintln!("{}", e.cause()),
}
}
@@ -154,8 +166,6 @@ impl Flattenable<JoshutoCommandMapping> for JoshutoRawCommandMapping {
}
}
-pub type JoshutoCommandMapping = HashMap<i32, CommandKeybind>;
-
impl ConfigStructure for JoshutoCommandMapping {
fn get_config() -> Self {
parse_to_config_file::<JoshutoRawCommandMapping, JoshutoCommandMapping>(KEYMAP_FILE)
@@ -166,35 +176,45 @@ impl ConfigStructure for JoshutoCommandMapping {
fn insert_keycommand(
keymap: &mut JoshutoCommandMapping,
keycommand: Box<JoshutoCommand>,
- keycodes: &[i32],
-) {
- match keycodes.len() {
- 0 => {}
- 1 => match keymap.entry(keycodes[0]) {
- Entry::Occupied(_) => {
- eprintln!("Error: Keybindings ambiguous for {}", keycommand);
- exit(1);
- }
- Entry::Vacant(entry) => {
- entry.insert(CommandKeybind::SimpleKeybind(keycommand));
+ keycodes: &[&str],
+) -> Result<(), String> {
+ let keycode_len = keycodes.len();
+
+ if keycode_len == 0 {
+ return Ok(());
+ }
+
+ let key = match str_to_key(keycodes[0]) {
+ Some(k) => k,
+ None => return Err(format!("Unknown keycode: {}", keycodes[0]))
+ };
+
+ if keycode_len == 1 {
+ match keymap.entry(key) {
+ Entry::Occupied(_) => return Err(format!("Error: Keybindings ambiguous for {}", keycommand)),
+ Entry::Vacant(entry) => entry.insert(CommandKeybind::SimpleKeybind(keycommand)),
+ };
+ return Ok(());
+ }
+
+ match keymap.entry(key) {
+ Entry::Occupied(mut entry) => match entry.get_mut() {
+ CommandKeybind::CompositeKeybind(ref mut m) => {
+ return insert_keycommand(m, keycommand, &keycodes[1..])
}
+ _ => return Err(format!("Error: Keybindings ambiguous for {}", keycommand)),
},
- _ => match keymap.entry(keycodes[0]) {
- Entry::Occupied(mut entry) => match entry.get_mut() {
- CommandKeybind::CompositeKeybind(ref mut m) => {
- insert_keycommand(m, keycommand, &keycodes[1..])
+ Entry::Vacant(entry) => {
+ let mut new_map = JoshutoCommandMapping::new();
+ let result = insert_keycommand(&mut new_map, keycommand, &keycodes[1..]);
+ match result {
+ Ok(_) => {
+ let composite_command = CommandKeybind::CompositeKeybind(new_map);
+ entry.insert(composite_command);
}
- _ => {
- eprintln!("Error: Keybindings ambiguous for {}", keycommand);
- exit(1);
- }
- },
- Entry::Vacant(entry) => {
- let mut new_map = JoshutoCommandMapping::new();
- insert_keycommand(&mut new_map, keycommand, &keycodes[1..]);
- let composite_command = CommandKeybind::CompositeKeybind(new_map);
- entry.insert(composite_command);
+ _ => {}
}
- },
+ return result;
+ }
}
}
diff --git a/src/main.rs b/src/main.rs
index 75985e5..13f5ca0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ mod sort;
mod tab;
mod textfield;
mod ui;
+mod util;
mod unix;
mod window;
@@ -67,9 +68,10 @@ fn main() {
let keymap = JoshutoCommandMapping::get_config();
if args.debug {
- eprintln!("config: {:#?}", config);
- eprintln!("theme config: {:#?}", *THEME_T);
- eprintln!("mimetype config: {:#?}", *MIMETYPE_T);
+ eprintln!("keymaps: {:#?}", keymap);
+ // eprintln!("config: {:#?}", config);
+ // eprintln!("theme config: {:#?}", *THEME_T);
+ // eprintln!("mimetype config: {:#?}", *MIMETYPE_T);
}
run(config, keymap);
diff --git a/src/run.rs b/src/run.rs
index 1f6b0cf..a1b9414 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -1,11 +1,14 @@
use std::process;
use std::time;
+use termion::event::Key;
+
use crate::commands::{CommandKeybind, FileOperationThread, JoshutoCommand, ReloadDirList};
use crate::config::{self, JoshutoCommandMapping, JoshutoConfig};
use crate::context::JoshutoContext;
use crate::tab::JoshutoTab;
use crate::ui;
+use crate::util::event::{Event, Events};
use crate::window::JoshutoPanel;
use crate::window::JoshutoView;
@@ -13,7 +16,8 @@ fn recurse_get_keycommand(keymap: &JoshutoCommandMapping) -> Option<&JoshutoComm
let (term_rows, term_cols) = ui::getmaxyx();
ncurses::timeout(-1);
- let ch: i32 = {
+ let events = Events::new();
+ let event = {
let keymap_len = keymap.len();
let win = JoshutoPanel::new(
keymap_len as i32 + 1,
@@ -23,7 +27,7 @@ fn recurse_get_keycommand(keymap: &JoshutoCommandMapping) -> Option<&JoshutoComm
let mut display_vec: Vec<String> = keymap
.iter()
- .map(|(k, v)| format!(" {}\t{}", *k as u8 as char, v))
+ .map(|(k, v)| format!(" {:?}\t{}", k, v))
.collect();
display_vec.sort();
@@ -31,18 +35,27 @@ fn recurse_get_keycommand(keymap: &JoshutoCommandMapping) -> Option<&JoshutoComm
ui::display_menu(&win, &display_vec);
ncurses::doupdate();
- ncurses::wgetch(win.win)
+ events.next()
};
ncurses::doupdate();
- if ch == config::keymap::ESCAPE {
- None
- } else {
- match keymap.get(&ch) {
- Some(CommandKeybind::CompositeKeybind(m)) => recurse_get_keycommand(&m),
- Some(CommandKeybind::SimpleKeybind(s)) => Some(s.as_ref()),
- _ => None,
+ match event {
+ Ok(Event::Input(input)) => match input {
+ Key::Esc => {
+ None
+ }
+ key @ Key::Char(_) => {
+ match keymap.get(&key) {
+ Some(CommandKeybind::CompositeKeybind(m)) => recurse_get_keycommand(&m),
+ Some(CommandKeybind::SimpleKeybind(s)) => Some(s.as_ref()),
+ _ => None,
+ }
+ }
+ _ => {
+ None
+ }
}
+ _ => None,
}
}
@@ -147,53 +160,41 @@ pub fn run(config_t: JoshutoConfig, keymap_t: JoshutoCommandMapping) {
let mut view = JoshutoView::new(context.config_t.column_ratio);
init_context(&mut context, &view);
+ let events = Events::new();
while !context.exit {
- if !context.threads.is_empty() {
- ncurses::timeout(0);
- match process_threads(&mut context, &view) {
- Ok(_) => {}
- Err(e) => ui::wprint_err(&view.bot_win, e.to_string().as_str()),
- }
- ncurses::doupdate();
- } else {
- ncurses::timeout(-1);
- }
-
- if let Some(ch) = ncurses::get_wch() {
- let ch = match ch {
- ncurses::WchResult::Char(s) => s as i32,
- ncurses::WchResult::KeyCode(s) => s,
- };
-
- if ch == ncurses::KEY_RESIZE {
- view.resize_views();
- resize_handler(&mut context, &view);
- continue;
- }
-
- let keycommand;
-
- match keymap_t.get(&ch) {
- Some(CommandKeybind::CompositeKeybind(m)) => match recurse_get_keycommand(&m) {
- Some(s) => keycommand = s,
- None => continue,
- },
- Some(CommandKeybind::SimpleKeybind(s)) => {
- keycommand = s.as_ref();
- }
- None => {
- ui::wprint_err(&view.bot_win, &format!("Unknown keycode: {}", ch));
- ncurses::doupdate();
- continue;
- }
- }
- match keycommand.execute(&mut context, &view) {
- Ok(()) => {}
- Err(e) => {
- ui::wprint_err(&view.bot_win, e.cause());
+ let event = events.next();
+ if let Ok(event) = event {
+ match event {
+ Event::Input(key) => {
+ let keycommand = match keymap_t.get(&key) {
+ Some(CommandKeybind::CompositeKeybind(m)) => match recurse_get_keycommand(&m) {
+ Some(s) => s,
+ None => {
+ ui::wprint_err(&view.bot_win, &format!("Unknown keycode: {:?}", key));
+ ncurses::doupdate();
+ continue;
+ }
+ },
+ Some(CommandKeybind::SimpleKeybind(s)) => {
+ s.as_ref()
+ }
+ None => {
+ ui::wprint_err(&view.bot_win, &format!("Unknown keycode: {:?}", key));
+ ncurses::doupdate();
+ continue;
+ }
+ };
+ match keycommand.execute(&mut context, &view) {
+ Err(e) => {
+ ui::wprint_err(&view.bot_win, e.cause());
+ }
+ _ => {}
+ }
ncurses::doupdate();
}
+ event => ui::wprint_err(&view.bot_win, &format!("Unknown keycode: {:?}", event)),
}
+ ncurses::doupdate();
}
}
ui::end_ncurses();
diff --git a/src/ui.rs b/src/ui.rs
index 71898cc..f7f6ba3 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -32,7 +32,6 @@ pub fn init_ncurses() {
ncurses::initscr();
ncurses::cbreak();
- ncurses::keypad(ncurses::stdscr(), true);
ncurses::start_color();
ncurses::use_default_colors();
ncurses::noecho();
diff --git a/src/util/event.rs b/src/util/event.rs
new file mode 100644
index 0000000..c6559cf
--- /dev/null
+++ b/src/util/event.rs
@@ -0,0 +1,83 @@
+use std::io;
+use std::sync::mpsc;
+use std::thread;
+use std::time::Duration;
+
+use termion::event::Key;
+use termion::input::TermRead;
+
+#[derive(Debug)]
+pub enum Event<I> {
+ Input(I),
+ Tick,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct Config {
+ pub tick_rate: Duration,
+}
+
+impl Default for Config {
+ fn default() -> Config {
+ Config {
+ tick_rate: Duration::from_millis(250),
+ }
+ }
+}
+
+/// A small event handler that wrap termion input and tick events. Each event
+/// type is handled in its own thread and returned to a common `Receiver`
+pub struct Events {
+ rx: mpsc::Receiver<Event<Key>>,
+ input_handle: thread::JoinHandle<()>,
+// tick_handle: thread::JoinHandle<()>,
+}
+
+impl Events {
+ pub fn new() -> Events {
+ Events::with_config(Config::default())
+ }
+
+ pub fn with_config(config: Config) -> Events {
+ let (tx, rx) = mpsc::channel();
+ let input_handle = {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let stdin = io::stdin();
+ for evt in stdin.keys() {
+ match evt {
+ Ok(key) => {
+ if let Err(e) = tx.send(Event::Input(key)) {
+ eprintln!("Err: {:#?}", e);
+ return;
+ }
+ }
+ Err(e) => {}
+ }
+ }
+ })
+ };
+/*
+ let tick_handle = {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ let tx = tx.clone();
+ loop {
+ tx.send(Event::Tick).unwrap();
+ thread::sleep(config.tick_rate);
+ }
+ })
+ };
+*/
+ Events {
+ rx,
+ input_handle,
+// tick_handle,
+ }
+ }
+
+ pub fn next(&self) -> Result<Event<Key>, mpsc::RecvError> {
+ self.rx.recv()
+ }
+}
+
diff --git a/src/util/key_mapping.rs b/src/util/key_mapping.rs
new file mode 100644
index 0000000..9d7c182
--- /dev/null
+++ b/src/util/key_mapping.rs
@@ -0,0 +1,64 @@
+use termion::event::Key;
+
+pub fn str_to_key(s: &str) -> Option<Key> {
+ if s.len() == 0 {
+ return None;
+ }
+
+ let key = match s {
+ "backspace" => Some(Key::Backspace),
+ "left" => Some(Key::Left),
+ "right" => Some(Key::Right),
+ "up" => Some(Key::Up),
+ "down" => Some(Key::Down),
+ "home" => Some(Key::Home),
+ "end" => Some(Key::End),
+ "page_up" => Some(Key::PageUp),
+ "page_down" => Some(Key::PageDown),
+ "delete" => Some(Key::Delete),
+ "insert" => Some(Key::Insert),
+ "escape" => Some(Key::Esc),
+ "tab" => Some(Key::BackTab),
+ "f1" => Some(Key::F(1)),
+ "f2" => Some(Key::F(2)),
+ "f3" => Some(Key::F(3)),
+ "f4" => Some(Key::F(4)),
+ "f5" => Some(Key::F(5)),
+ "f6" => Some(Key::F(6)),
+ "f7" => Some(Key::F(7)),
+ "f8" => Some(Key::F(8)),
+ "f9" => Some(Key::F(9)),
+ "f10" => Some(Key::F(10)),
+ "f11" => Some(Key::F(11)),
+ "f12" => Some(Key::F(12)),
+ _ => None,
+ };
+
+ if let Some(a) = key {
+ return key;
+ }
+
+ if s.starts_with("ctrl+") {
+ let ch = s.chars().skip("ctrl+".len()).next();
+ let key = match ch {
+ Some(ch) => Some(Key::Ctrl(ch)),
+ None => None,
+ };
+ return key;
+ } else if s.starts_with("alt+") {
+ let ch = s.chars().skip("alt+".len()).next();
+ let key = match ch {
+ Some(ch) => Some(Key::Alt(ch)),
+ None => None,
+ };
+ return key;
+ } else if s.len() == 1 {
+ let ch = s.chars().next();
+ let key = match ch {
+ Some(ch) => Some(Key::Char(ch)),
+ None => None,
+ };
+ return key;
+ }
+ return None;
+}
diff --git a/src/util/mod.rs b/src/util/mod.rs
new file mode 100644
index 0000000..94ead10
--- /dev/null
+++ b/src/util/mod.rs
@@ -0,0 +1,78 @@
+pub mod event;
+pub mod key_mapping;
+
+use rand::distributions::{Distribution, Uniform};
+use rand::rngs::ThreadRng;
+
+#[derive(Clone)]
+pub struct RandomSignal {
+ distribution: Uniform<u64>,
+ rng: ThreadRng,
+}
+
+impl RandomSignal {
+ pub fn new(lower: u64, upper: u64) -> RandomSignal {
+ RandomSignal {
+ distribution: Uniform::new(lower, upper),
+ rng: rand::thread_rng(),
+ }
+ }
+}
+
+impl Iterator for RandomSignal {
+ type Item = u64;
+ fn next(&mut self) -> Option<u64> {
+ Some(self.distribution.sample(&mut self.rng))
+ }
+}
+
+#[derive(Clone)]
+pub struct SinSignal {
+ x: f64,
+ interval: f64,
+ period: f64,
+ scale: f64,
+}
+
+impl SinSignal {
+ pub fn new(interval: f64, period: f64, scale: f64) -> SinSignal {
+ SinSignal {
+ x: 0.0,
+ interval,
+ period,
+ scale,
+ }
+ }
+}
+
+impl Iterator for SinSignal {
+ type Item = (f64, f64);
+ fn next(&mut self) -> Option<Self::Item> {
+ let point = (self.x, (self.x * 1.0 / self.period).sin() * self.scale);
+ self.x += self.interval;
+ Some(point)
+ }
+}
+
+pub struct TabsState<'a> {
+ pub titles: Vec<&'a str>,
+ pub index: usize,
+}
+
+impl<'a> TabsState<'a> {
+ pub fn new(titles: Vec<&'a str>) -> TabsState {
+ TabsState { titles, index: 0 }
+ }
+ pub fn next(&mut self) {
+ self.index = (self.index + 1) % self.titles.len();
+ }
+
+ pub fn previous(&mut self) {
+ if self.index > 0 {
+ self.index -= 1;
+ } else {
+ self.index = self.titles.len() - 1;
+ }
+ }
+}
+