diff options
Diffstat (limited to 'src/input.rs')
-rw-r--r-- | src/input.rs | 163 |
1 files changed, 111 insertions, 52 deletions
diff --git a/src/input.rs b/src/input.rs index 468e9ef3..6e3d6d0d 100644 --- a/src/input.rs +++ b/src/input.rs @@ -24,9 +24,8 @@ use std::process::Command; use std::time::Instant; use copypasta::{Clipboard, Load, Buffer}; -use glutin::{ElementState, VirtualKeyCode, MouseButton}; -use glutin::{Mods, mods}; -use glutin::{TouchPhase, MouseScrollDelta}; +use glutin::{ElementState, VirtualKeyCode, MouseButton, TouchPhase, MouseScrollDelta}; +use glutin::ModifiersState; use config; use event::{ClickState, Mouse}; @@ -60,6 +59,9 @@ pub trait ActionContext { fn line_selection(&mut self, point: Point); fn mouse_mut(&mut self) -> &mut Mouse; fn mouse_coords(&self) -> Option<Point>; + fn received_count(&mut self) -> &mut usize; + fn suppress_chars(&mut self) -> &mut bool; + fn last_modifiers(&mut self) -> &mut ModifiersState; } /// Describes a state and action to take in that state @@ -68,7 +70,7 @@ pub trait ActionContext { #[derive(Debug, Clone)] pub struct Binding<T> { /// Modifier keys required to activate binding - pub mods: Mods, + pub mods: ModifiersState, /// String to send to pty if mods and mode match pub action: Action, @@ -96,7 +98,7 @@ impl<T: Eq> Binding<T> { fn is_triggered_by( &self, mode: TermMode, - mods: &Mods, + mods: &ModifiersState, input: &T ) -> bool { // Check input first since bindings are stored in one big list. This is @@ -126,9 +128,15 @@ impl<T> Binding<T> { self.notmode.is_empty() || !mode.intersects(self.notmode) } + /// Check that two mods descriptions for equivalence + /// + /// Optimized to use single check instead of four (one per modifier) #[inline] - fn mods_match(&self, mods: &Mods) -> bool { - self.mods.is_all() || *mods == self.mods + fn mods_match(&self, mods: &ModifiersState) -> bool { + debug_assert!(4 == mem::size_of::<ModifiersState>()); + unsafe { + mem::transmute_copy::<_, u32>(&self.mods) == mem::transmute_copy::<_, u32>(mods) + } } } @@ -414,37 +422,52 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { return; } - self.process_mouse_bindings(mods::NONE, button); + self.process_mouse_bindings(&ModifiersState::default(), button); } + /// Process key input + /// + /// If a keybinding was run, returns true. Otherwise returns false. pub fn process_key( &mut self, state: ElementState, key: Option<VirtualKeyCode>, - mods: Mods, - string: Option<String>, + mods: &ModifiersState, ) { - if let Some(key) = key { - // Ignore release events - if state == ElementState::Released { - return; - } + match (key, state) { + (Some(key), ElementState::Pressed) => { + *self.ctx.last_modifiers() = *mods; + *self.ctx.received_count() = 0; + *self.ctx.suppress_chars() = false; + + if self.process_key_bindings(&mods, key) { + *self.ctx.suppress_chars() = true; + } + }, + (_, ElementState::Released) => *self.ctx.suppress_chars() = false, + _ => () + } + } - if self.process_key_bindings(mods, key) { - return; - } + /// Process a received character + pub fn received_char(&mut self, c: char) { + if !*self.ctx.suppress_chars() { + self.ctx.clear_selection(); - } + let utf8_len = c.len_utf8(); + if *self.ctx.received_count() == 0 && self.ctx.last_modifiers().alt && utf8_len == 1 { + self.ctx.write_to_pty(b"\x1b".to_vec()); + } - // Didn't process a binding; print the provided character - if let Some(mut string) = string { - // from ST - if string.len() == 1 && mods.contains(mods::ALT) { - string.insert(0, '\x1b'); + let mut bytes = Vec::with_capacity(utf8_len); + unsafe { + bytes.set_len(utf8_len); + c.encode_utf8(&mut bytes[..]); } - self.ctx.write_to_pty(string.into_bytes()); - self.ctx.clear_selection(); + self.ctx.write_to_pty(bytes); + + *self.ctx.received_count() += 1; } } @@ -454,9 +477,9 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { /// for its action to be executed. /// /// Returns true if an action is executed. - fn process_key_bindings(&mut self, mods: Mods, key: VirtualKeyCode) -> bool { + fn process_key_bindings(&mut self, mods: &ModifiersState, key: VirtualKeyCode) -> bool { for binding in self.key_bindings { - if binding.is_triggered_by(self.ctx.terminal_mode(), &mods, &key) { + if binding.is_triggered_by(self.ctx.terminal_mode(), mods, &key) { // binding was triggered; run the action binding.execute(&mut self.ctx); return true; @@ -472,9 +495,9 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { /// for its action to be executed. /// /// Returns true if an action is executed. - fn process_mouse_bindings(&mut self, mods: Mods, button: MouseButton) -> bool { + fn process_mouse_bindings(&mut self, mods: &ModifiersState, button: MouseButton) -> bool { for binding in self.mouse_bindings { - if binding.is_triggered_by(self.ctx.terminal_mode(), &mods, &button) { + if binding.is_triggered_by(self.ctx.terminal_mode(), mods, &button) { // binding was triggered; run the action binding.execute(&mut self.ctx); return true; @@ -490,7 +513,7 @@ mod tests { use std::borrow::Cow; use std::time::Duration; - use glutin::{mods, VirtualKeyCode, Event, ElementState, MouseButton}; + use glutin::{VirtualKeyCode, Event, WindowEvent, ElementState, MouseButton, ModifiersState}; use term::{SizeInfo, Term, TermMode, mode}; use event::{Mouse, ClickState}; @@ -515,6 +538,9 @@ mod tests { pub size_info: &'a SizeInfo, pub mouse: &'a mut Mouse, pub last_action: MultiClick, + pub received_count: usize, + pub suppress_chars: bool, + pub last_modifiers: ModifiersState, } impl <'a>super::ActionContext for ActionContext<'a> { @@ -555,6 +581,15 @@ mod tests { fn mouse_mut(&mut self) -> &mut Mouse { self.mouse } + fn received_count(&mut self) -> &mut usize { + &mut self.received_count + } + fn suppress_chars(&mut self) -> &mut bool { + &mut self.suppress_chars + } + fn last_modifiers(&mut self) -> &mut ModifiersState { + &mut self.last_modifiers + } } macro_rules! test_clickstate { @@ -590,6 +625,9 @@ mod tests { mouse: &mut mouse, size_info: &size, last_action: MultiClick::None, + received_count: 0, + suppress_chars: false, + last_modifiers: ModifiersState::default(), }; let mut processor = Processor { @@ -606,8 +644,8 @@ mod tests { mouse_bindings: &config.mouse_bindings()[..], }; - if let Event::MouseInput(state, input) = $input { - processor.mouse_input(state, input); + if let Event::WindowEvent { event: WindowEvent::MouseInput { state, button, .. }, .. } = $input { + processor.mouse_input(state, button); }; assert!(match mouse.click_state { @@ -640,7 +678,14 @@ mod tests { test_clickstate! { name: single_click, initial_state: ClickState::None, - input: Event::MouseInput(ElementState::Pressed, MouseButton::Left), + input: Event::WindowEvent { + event: WindowEvent::MouseInput { + state: ElementState::Pressed, + button: MouseButton::Left, + device_id: unsafe { ::std::mem::transmute_copy(&0) }, + }, + window_id: unsafe { ::std::mem::transmute_copy(&0) }, + }, end_state: ClickState::Click, last_action: MultiClick::None } @@ -648,7 +693,14 @@ mod tests { test_clickstate! { name: double_click, initial_state: ClickState::Click, - input: Event::MouseInput(ElementState::Pressed, MouseButton::Left), + input: Event::WindowEvent { + event: WindowEvent::MouseInput { + state: ElementState::Pressed, + button: MouseButton::Left, + device_id: unsafe { ::std::mem::transmute_copy(&0) }, + }, + window_id: unsafe { ::std::mem::transmute_copy(&0) }, + }, end_state: ClickState::DoubleClick, last_action: MultiClick::DoubleClick } @@ -656,72 +708,79 @@ mod tests { test_clickstate! { name: triple_click, initial_state: ClickState::DoubleClick, - input: Event::MouseInput(ElementState::Pressed, MouseButton::Left), + input: Event::WindowEvent { + event: WindowEvent::MouseInput { + state: ElementState::Pressed, + button: MouseButton::Left, + device_id: unsafe { ::std::mem::transmute_copy(&0) }, + }, + window_id: unsafe { ::std::mem::transmute_copy(&0) }, + }, end_state: ClickState::TripleClick, last_action: MultiClick::TripleClick } test_process_binding! { name: process_binding_nomode_shiftmod_require_shift, - binding: Binding { trigger: KEY, mods: mods::SHIFT, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState { shift: true, ctrl: false, alt: false, logo: false }, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }, triggers: true, mode: mode::NONE, - mods: mods::SHIFT + mods: ModifiersState { shift: true, ctrl: false, alt: false, logo: false } } test_process_binding! { name: process_binding_nomode_nomod_require_shift, - binding: Binding { trigger: KEY, mods: mods::SHIFT, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState { shift: true, ctrl: false, alt: false, logo: false }, action: Action::from("\x1b[1;2D"), mode: mode::NONE, notmode: mode::NONE }, triggers: false, mode: mode::NONE, - mods: mods::NONE + mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } test_process_binding! { name: process_binding_nomode_controlmod, - binding: Binding { trigger: KEY, mods: mods::CONTROL, action: Action::from("\x1b[1;5D"), mode: mode::NONE, notmode: mode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState { ctrl: true, shift: false, alt: false, logo: false }, action: Action::from("\x1b[1;5D"), mode: mode::NONE, notmode: mode::NONE }, triggers: true, mode: mode::NONE, - mods: mods::CONTROL + mods: ModifiersState { ctrl: true, shift: false, alt: false, logo: false } } test_process_binding! { name: process_binding_nomode_nomod_require_not_appcursor, - binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1b[D"), mode: mode::NONE, notmode: mode::APP_CURSOR }, + binding: Binding { trigger: KEY, mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false }, action: Action::from("\x1b[D"), mode: mode::NONE, notmode: mode::APP_CURSOR }, triggers: true, mode: mode::NONE, - mods: mods::NONE + mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } test_process_binding! { name: process_binding_appcursormode_nomod_require_appcursor, - binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false }, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, triggers: true, mode: mode::APP_CURSOR, - mods: mods::NONE + mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } test_process_binding! { name: process_binding_nomode_nomod_require_appcursor, - binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false }, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, triggers: false, mode: mode::NONE, - mods: mods::NONE + mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } test_process_binding! { name: process_binding_appcursormode_appkeypadmode_nomod_require_appcursor, - binding: Binding { trigger: KEY, mods: mods::ANY, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false }, action: Action::from("\x1bOD"), mode: mode::APP_CURSOR, notmode: mode::NONE }, triggers: true, mode: mode::APP_CURSOR | mode::APP_KEYPAD, - mods: mods::NONE + mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: false } } test_process_binding! { name: process_binding_fail_with_extra_mods, - binding: Binding { trigger: KEY, mods: mods::SUPER, action: Action::from("arst"), mode: mode::NONE, notmode: mode::NONE }, + binding: Binding { trigger: KEY, mods: ModifiersState { shift: false, ctrl: false, alt: false, logo: true }, action: Action::from("arst"), mode: mode::NONE, notmode: mode::NONE }, triggers: false, mode: mode::NONE, - mods: mods::SUPER | mods::ALT + mods: ModifiersState { shift: false, ctrl: false, alt: true, logo: true } } } |