diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config/keymap.rs | 84 | ||||
-rw-r--r-- | src/main.rs | 8 | ||||
-rw-r--r-- | src/run.rs | 107 | ||||
-rw-r--r-- | src/ui.rs | 1 | ||||
-rw-r--r-- | src/util/event.rs | 83 | ||||
-rw-r--r-- | src/util/key_mapping.rs | 64 | ||||
-rw-r--r-- | src/util/mod.rs | 78 |
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); @@ -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(); @@ -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; + } + } +} + |