diff options
| author | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 16:42:50 +0530 |
|---|---|---|
| committer | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 21:41:56 +0530 |
| commit | 2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch) | |
| tree | 5a332470e32b380816098594a89d3c3db1f6eb53 /src/common/input/handler.rs | |
| parent | handle error on termios initialization (#511) (diff) | |
| download | sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.gz sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.xz sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.zip | |
Big refactor: separate crates for client, server and utilities
Diffstat (limited to 'src/common/input/handler.rs')
| -rw-r--r-- | src/common/input/handler.rs | 268 |
1 files changed, 0 insertions, 268 deletions
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs deleted file mode 100644 index 4ac2cf83..00000000 --- a/src/common/input/handler.rs +++ /dev/null @@ -1,268 +0,0 @@ -//! Main input logic. - -use super::actions::Action; -use super::keybinds::Keybinds; -use crate::client::ClientInstruction; -use crate::common::input::config::Config; -use crate::common::ipc::ClientToServerMsg; -use crate::common::thread_bus::{SenderWithContext, OPENCALLS}; -use crate::errors::ContextType; -use crate::os_input_output::ClientOsApi; -use crate::CommandIsExecuting; - -use termion::input::{TermRead, TermReadEventsAndRaw}; -use zellij_tile::data::{InputMode, Key, ModeInfo, Palette, PluginCapabilities}; - -/// Handles the dispatching of [`Action`]s according to the current -/// [`InputMode`], and keep tracks of the current [`InputMode`]. -struct InputHandler { - /// The current input mode - mode: InputMode, - os_input: Box<dyn ClientOsApi>, - config: Config, - command_is_executing: CommandIsExecuting, - send_client_instructions: SenderWithContext<ClientInstruction>, - should_exit: bool, - pasting: bool, -} - -impl InputHandler { - /// Returns a new [`InputHandler`] with the attributes specified as arguments. - fn new( - os_input: Box<dyn ClientOsApi>, - command_is_executing: CommandIsExecuting, - config: Config, - send_client_instructions: SenderWithContext<ClientInstruction>, - ) -> Self { - InputHandler { - mode: InputMode::Normal, - os_input, - config, - command_is_executing, - send_client_instructions, - should_exit: false, - pasting: false, - } - } - - /// Main input event loop. Interprets the terminal [`Event`](termion::event::Event)s - /// as [`Action`]s according to the current [`InputMode`], and dispatches those actions. - fn handle_input(&mut self) { - let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); - err_ctx.add_call(ContextType::StdinHandler); - let alt_left_bracket = vec![27, 91]; - let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~ - let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201 - loop { - if self.should_exit { - break; - } - let stdin_buffer = self.os_input.read_from_stdin(); - for key_result in stdin_buffer.events_and_raw() { - match key_result { - Ok((event, raw_bytes)) => match event { - termion::event::Event::Key(key) => { - let key = cast_termion_key(key); - self.handle_key(&key, raw_bytes); - } - termion::event::Event::Unsupported(unsupported_key) => { - // we have to do this because of a bug in termion - // this should be a key event and not an unsupported event - if unsupported_key == alt_left_bracket { - let key = Key::Alt('['); - self.handle_key(&key, raw_bytes); - } else if unsupported_key == bracketed_paste_start { - self.pasting = true; - } else if unsupported_key == bracketed_paste_end { - self.pasting = false; - } else { - // this is a hack because termion doesn't recognize certain keys - // in this case we just forward it to the terminal - self.handle_unknown_key(raw_bytes); - } - } - termion::event::Event::Mouse(_) => { - // Mouse events aren't implemented yet, - // use a NoOp untill then. - } - }, - Err(err) => panic!("Encountered read error: {:?}", err), - } - } - } - } - fn handle_unknown_key(&mut self, raw_bytes: Vec<u8>) { - if self.mode == InputMode::Normal || self.mode == InputMode::Locked { - let action = Action::Write(raw_bytes); - self.dispatch_action(action); - } - } - fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>) { - let keybinds = &self.config.keybinds; - if self.pasting { - // we're inside a paste block, if we're in a mode that allows sending text to the - // terminal, send all text directly without interpreting it - // otherwise, just discard the input - if self.mode == InputMode::Normal || self.mode == InputMode::Locked { - let action = Action::Write(raw_bytes); - self.dispatch_action(action); - } - } else { - for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, keybinds) { - let should_exit = self.dispatch_action(action); - if should_exit { - self.should_exit = true; - } - } - } - } - - /// Dispatches an [`Action`]. - /// - /// This function's body dictates what each [`Action`] actually does when - /// dispatched. - /// - /// # Return value - /// Currently, this function returns a boolean that indicates whether - /// [`Self::handle_input()`] should break after this action is dispatched. - /// This is a temporary measure that is only necessary due to the way that the - /// framework works, and shouldn't be necessary anymore once the test framework - /// is revised. See [issue#183](https://github.com/zellij-org/zellij/issues/183). - fn dispatch_action(&mut self, action: Action) -> bool { - let mut should_break = false; - - match action { - Action::Quit => { - self.exit(); - should_break = true; - } - Action::SwitchToMode(mode) => { - self.mode = mode; - self.os_input - .send_to_server(ClientToServerMsg::Action(action)); - } - Action::CloseFocus - | Action::NewPane(_) - | Action::NewTab - | Action::GoToNextTab - | Action::GoToPreviousTab - | Action::CloseTab - | Action::GoToTab(_) - | Action::MoveFocusOrTab(_) => { - self.command_is_executing.blocking_input_thread(); - self.os_input - .send_to_server(ClientToServerMsg::Action(action)); - self.command_is_executing - .wait_until_input_thread_is_unblocked(); - } - _ => self - .os_input - .send_to_server(ClientToServerMsg::Action(action)), - } - - should_break - } - - /// Routine to be called when the input handler exits (at the moment this is the - /// same as quitting Zellij). - fn exit(&mut self) { - self.send_client_instructions - .send(ClientInstruction::Exit) - .unwrap(); - } -} - -/// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds -/// (as pairs of [`String`]s). -// TODO this should probably be automatically generated in some way -pub fn get_mode_info( - mode: InputMode, - palette: Palette, - capabilities: PluginCapabilities, -) -> ModeInfo { - let mut keybinds: Vec<(String, String)> = vec![]; - match mode { - InputMode::Normal | InputMode::Locked => {} - InputMode::Resize => { - keybinds.push(("←↓↑→".to_string(), "Resize".to_string())); - } - InputMode::Pane => { - keybinds.push(("←↓↑→".to_string(), "Move focus".to_string())); - keybinds.push(("p".to_string(), "Next".to_string())); - keybinds.push(("n".to_string(), "New".to_string())); - keybinds.push(("d".to_string(), "Down split".to_string())); - keybinds.push(("r".to_string(), "Right split".to_string())); - keybinds.push(("x".to_string(), "Close".to_string())); - keybinds.push(("f".to_string(), "Fullscreen".to_string())); - } - InputMode::Tab => { - keybinds.push(("←↓↑→".to_string(), "Move focus".to_string())); - keybinds.push(("n".to_string(), "New".to_string())); - keybinds.push(("x".to_string(), "Close".to_string())); - keybinds.push(("r".to_string(), "Rename".to_string())); - keybinds.push(("s".to_string(), "Sync".to_string())); - } - InputMode::Scroll => { - keybinds.push(("↓↑".to_string(), "Scroll".to_string())); - keybinds.push(("PgUp/PgDn".to_string(), "Scroll Page".to_string())); - } - InputMode::RenameTab => { - keybinds.push(("Enter".to_string(), "when done".to_string())); - } - } - ModeInfo { - mode, - keybinds, - palette, - capabilities, - } -} - -/// Entry point to the module. Instantiates an [`InputHandler`] and starts -/// its [`InputHandler::handle_input()`] loop. -pub fn input_loop( - os_input: Box<dyn ClientOsApi>, - config: Config, - command_is_executing: CommandIsExecuting, - send_client_instructions: SenderWithContext<ClientInstruction>, -) { - let _handler = InputHandler::new( - os_input, - command_is_executing, - config, - send_client_instructions, - ) - .handle_input(); -} - -pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> { - input_bytes.keys().flatten().map(cast_termion_key).collect() -} - -// FIXME: This is an absolutely cursed function that should be destroyed as soon -// as an alternative that doesn't touch zellij-tile can be developed... -fn cast_termion_key(event: termion::event::Key) -> Key { - match event { - termion::event::Key::Backspace => Key::Backspace, - termion::event::Key::Left => Key::Left, - termion::event::Key::Right => Key::Right, - termion::event::Key::Up => Key::Up, - termion::event::Key::Down => Key::Down, - termion::event::Key::Home => Key::Home, - termion::event::Key::End => Key::End, - termion::event::Key::PageUp => Key::PageUp, - termion::event::Key::PageDown => Key::PageDown, - termion::event::Key::BackTab => Key::BackTab, - termion::event::Key::Delete => Key::Delete, - termion::event::Key::Insert => Key::Insert, - termion::event::Key::F(n) => Key::F(n), - termion::event::Key::Char(c) => Key::Char(c), - termion::event::Key::Alt(c) => Key::Alt(c), - termion::event::Key::Ctrl(c) => Key::Ctrl(c), - termion::event::Key::Null => Key::Null, - termion::event::Key::Esc => Key::Esc, - _ => { - unimplemented!("Encountered an unknown key!") - } - } -} |
