aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/input/handler.rs
diff options
context:
space:
mode:
authorKunal Mohan <kunalmohan99@gmail.com>2021-05-16 16:42:50 +0530
committerKunal Mohan <kunalmohan99@gmail.com>2021-05-16 21:41:56 +0530
commit2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch)
tree5a332470e32b380816098594a89d3c3db1f6eb53 /src/common/input/handler.rs
parenthandle error on termios initialization (#511) (diff)
downloadsl0ppij-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.rs268
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!")
- }
- }
-}