Allow the usage of scancodes in the config

This change should allow the usage of scancodes in the configuration
file.

When a VirtualKeyCode for glutin is not present, this should now allow
the user to use the scancodes instead. If the user specifiecs a key with
its scancode even though the key has a VirtualKeyCode, it should still
work.

The behavior of directly specifying a VirtualKeyCode should be unchanged
by this.

This fixes #1265.
This commit is contained in:
Christian Duerr 2018-10-16 18:46:26 +00:00 committed by GitHub
parent 15e0deae2b
commit a727801f60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 219 additions and 184 deletions

View file

@ -10,8 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add support for windows
- Add terminfo capabilities advertising support for changing the window title
- Allow using scancodes in the key_bindings section
### Fixed
- Fixed erroneous results when using the `indexed_colors` config option
## Version 0.2.1

View file

@ -324,6 +324,12 @@ live_config_reload: true
# A full list with available key codes can be found here:
# https://docs.rs/glutin/*/glutin/enum.VirtualKeyCode.html#variants
#
# Instead of using the name of the keys, the `key` field also supports using
# the scancode of the desired key. Scancodes have to be specified as a
# decimal number.
# This command will allow you to display the hex scancodes for certain keys:
# `showkey --scancodes`
#
# Values for `mods`:
# - Command
# - Control

View file

@ -321,6 +321,12 @@ live_config_reload: true
# A full list with available key codes can be found here:
# https://docs.rs/glutin/*/glutin/enum.VirtualKeyCode.html#variants
#
# Instead of using the name of the keys, the `key` field also supports using
# the scancode of the desired key. Scancodes have to be specified as a
# decimal number.
# This command will allow you to display the hex scancodes for certain keys:
# `showkey --scancodes`
#
# Values for `mods`:
# - Command
# - Control

View file

@ -837,7 +837,7 @@ impl<'a> de::Deserialize<'a> for MouseButton {
/// Bindings are deserialized into a `RawBinding` before being parsed as a
/// `KeyBinding` or `MouseBinding`.
struct RawBinding {
key: Option<::glutin::VirtualKeyCode>,
key: Option<Key>,
mouse: Option<::glutin::MouseButton>,
mods: ModifiersState,
mode: TermMode,
@ -941,7 +941,7 @@ impl<'a> de::Deserialize<'a> for RawBinding {
where V: MapAccess<'a>,
{
let mut mods: Option<ModifiersState> = None;
let mut key: Option<::glutin::VirtualKeyCode> = None;
let mut key: Option<Key> = None;
let mut chars: Option<String> = None;
let mut action: Option<::input::Action> = None;
let mut mode: Option<TermMode> = None;
@ -958,8 +958,21 @@ impl<'a> de::Deserialize<'a> for RawBinding {
return Err(<V::Error as Error>::duplicate_field("key"));
}
let coherent_key = map.next_value::<Key>()?;
key = Some(coherent_key.to_glutin_key());
let val = map.next_value::<serde_yaml::Value>()?;
if val.is_u64() {
let scancode = val.as_u64().unwrap();
if scancode > u64::from(::std::u32::MAX) {
return Err(<V::Error as Error>::custom(format!(
"invalid key binding, scancode too big: {}",
scancode
)));
}
key = Some(Key::Scancode(scancode as u32));
} else {
let k = Key::deserialize(val)
.map_err(V::Error::custom)?;
key = Some(k);
}
},
Field::Mods => {
if mods.is_some() {
@ -2059,8 +2072,9 @@ mod tests {
}
#[cfg_attr(feature = "cargo-clippy", allow(enum_variant_names))]
#[derive(Deserialize, Copy, Clone)]
enum Key {
#[derive(Deserialize, Copy, Clone, Debug, Eq, PartialEq)]
pub enum Key {
Scancode(u32),
Key1,
Key2,
Key3,
@ -2218,164 +2232,164 @@ enum Key {
}
impl Key {
fn to_glutin_key(self) -> ::glutin::VirtualKeyCode {
use ::glutin::VirtualKeyCode::*;
// Thank you, vim macros!
match self {
Key::Key1 => Key1,
Key::Key2 => Key2,
Key::Key3 => Key3,
Key::Key4 => Key4,
Key::Key5 => Key5,
Key::Key6 => Key6,
Key::Key7 => Key7,
Key::Key8 => Key8,
Key::Key9 => Key9,
Key::Key0 => Key0,
Key::A => A,
Key::B => B,
Key::C => C,
Key::D => D,
Key::E => E,
Key::F => F,
Key::G => G,
Key::H => H,
Key::I => I,
Key::J => J,
Key::K => K,
Key::L => L,
Key::M => M,
Key::N => N,
Key::O => O,
Key::P => P,
Key::Q => Q,
Key::R => R,
Key::S => S,
Key::T => T,
Key::U => U,
Key::V => V,
Key::W => W,
Key::X => X,
Key::Y => Y,
Key::Z => Z,
Key::Escape => Escape,
Key::F1 => F1,
Key::F2 => F2,
Key::F3 => F3,
Key::F4 => F4,
Key::F5 => F5,
Key::F6 => F6,
Key::F7 => F7,
Key::F8 => F8,
Key::F9 => F9,
Key::F10 => F10,
Key::F11 => F11,
Key::F12 => F12,
Key::F13 => F13,
Key::F14 => F14,
Key::F15 => F15,
Key::Snapshot => Snapshot,
Key::Scroll => Scroll,
Key::Pause => Pause,
Key::Insert => Insert,
Key::Home => Home,
Key::Delete => Delete,
Key::End => End,
Key::PageDown => PageDown,
Key::PageUp => PageUp,
Key::Left => Left,
Key::Up => Up,
Key::Right => Right,
Key::Down => Down,
Key::Back => Back,
Key::Return => Return,
Key::Space => Space,
Key::Compose => Compose,
Key::Numlock => Numlock,
Key::Numpad0 => Numpad0,
Key::Numpad1 => Numpad1,
Key::Numpad2 => Numpad2,
Key::Numpad3 => Numpad3,
Key::Numpad4 => Numpad4,
Key::Numpad5 => Numpad5,
Key::Numpad6 => Numpad6,
Key::Numpad7 => Numpad7,
Key::Numpad8 => Numpad8,
Key::Numpad9 => Numpad9,
Key::AbntC1 => AbntC1,
Key::AbntC2 => AbntC2,
Key::Add => Add,
Key::Apostrophe => Apostrophe,
Key::Apps => Apps,
Key::At => At,
Key::Ax => Ax,
Key::Backslash => Backslash,
Key::Calculator => Calculator,
Key::Capital => Capital,
Key::Colon => Colon,
Key::Comma => Comma,
Key::Convert => Convert,
Key::Decimal => Decimal,
Key::Divide => Divide,
Key::Equals => Equals,
Key::Grave => Grave,
Key::Kana => Kana,
Key::Kanji => Kanji,
Key::LAlt => LAlt,
Key::LBracket => LBracket,
Key::LControl => LControl,
Key::LMenu => LMenu,
Key::LShift => LShift,
Key::LWin => LWin,
Key::Mail => Mail,
Key::MediaSelect => MediaSelect,
Key::MediaStop => MediaStop,
Key::Minus => Minus,
Key::Multiply => Multiply,
Key::Mute => Mute,
Key::MyComputer => MyComputer,
Key::NavigateForward => NavigateForward,
Key::NavigateBackward => NavigateBackward,
Key::NextTrack => NextTrack,
Key::NoConvert => NoConvert,
Key::NumpadComma => NumpadComma,
Key::NumpadEnter => NumpadEnter,
Key::NumpadEquals => NumpadEquals,
Key::OEM102 => OEM102,
Key::Period => Period,
Key::PlayPause => PlayPause,
Key::Power => Power,
Key::PrevTrack => PrevTrack,
Key::RAlt => RAlt,
Key::RBracket => RBracket,
Key::RControl => RControl,
Key::RMenu => RMenu,
Key::RShift => RShift,
Key::RWin => RWin,
Key::Semicolon => Semicolon,
Key::Slash => Slash,
Key::Sleep => Sleep,
Key::Stop => Stop,
Key::Subtract => Subtract,
Key::Sysrq => Sysrq,
Key::Tab => Tab,
Key::Underline => Underline,
Key::Unlabeled => Unlabeled,
Key::VolumeDown => VolumeDown,
Key::VolumeUp => VolumeUp,
Key::Wake => Wake,
Key::WebBack => WebBack,
Key::WebFavorites => WebFavorites,
Key::WebForward => WebForward,
Key::WebHome => WebHome,
Key::WebRefresh => WebRefresh,
Key::WebSearch => WebSearch,
Key::WebStop => WebStop,
Key::Yen => Yen,
Key::Caret => Caret,
Key::Copy => Copy,
Key::Paste => Paste,
Key::Cut => Cut,
pub fn from_glutin_input(key: ::glutin::VirtualKeyCode) -> Self {
use glutin::VirtualKeyCode::*;
// Thank you, vim macros and regex!
match key {
Key1 => Key::Key1,
Key2 => Key::Key2,
Key3 => Key::Key3,
Key4 => Key::Key4,
Key5 => Key::Key5,
Key6 => Key::Key6,
Key7 => Key::Key7,
Key8 => Key::Key8,
Key9 => Key::Key9,
Key0 => Key::Key0,
A => Key::A,
B => Key::B,
C => Key::C,
D => Key::D,
E => Key::E,
F => Key::F,
G => Key::G,
H => Key::H,
I => Key::I,
J => Key::J,
K => Key::K,
L => Key::L,
M => Key::M,
N => Key::N,
O => Key::O,
P => Key::P,
Q => Key::Q,
R => Key::R,
S => Key::S,
T => Key::T,
U => Key::U,
V => Key::V,
W => Key::W,
X => Key::X,
Y => Key::Y,
Z => Key::Z,
Escape => Key::Escape,
F1 => Key::F1,
F2 => Key::F2,
F3 => Key::F3,
F4 => Key::F4,
F5 => Key::F5,
F6 => Key::F6,
F7 => Key::F7,
F8 => Key::F8,
F9 => Key::F9,
F10 => Key::F10,
F11 => Key::F11,
F12 => Key::F12,
F13 => Key::F13,
F14 => Key::F14,
F15 => Key::F15,
Snapshot => Key::Snapshot,
Scroll => Key::Scroll,
Pause => Key::Pause,
Insert => Key::Insert,
Home => Key::Home,
Delete => Key::Delete,
End => Key::End,
PageDown => Key::PageDown,
PageUp => Key::PageUp,
Left => Key::Left,
Up => Key::Up,
Right => Key::Right,
Down => Key::Down,
Back => Key::Back,
Return => Key::Return,
Space => Key::Space,
Compose => Key::Compose,
Numlock => Key::Numlock,
Numpad0 => Key::Numpad0,
Numpad1 => Key::Numpad1,
Numpad2 => Key::Numpad2,
Numpad3 => Key::Numpad3,
Numpad4 => Key::Numpad4,
Numpad5 => Key::Numpad5,
Numpad6 => Key::Numpad6,
Numpad7 => Key::Numpad7,
Numpad8 => Key::Numpad8,
Numpad9 => Key::Numpad9,
AbntC1 => Key::AbntC1,
AbntC2 => Key::AbntC2,
Add => Key::Add,
Apostrophe => Key::Apostrophe,
Apps => Key::Apps,
At => Key::At,
Ax => Key::Ax,
Backslash => Key::Backslash,
Calculator => Key::Calculator,
Capital => Key::Capital,
Colon => Key::Colon,
Comma => Key::Comma,
Convert => Key::Convert,
Decimal => Key::Decimal,
Divide => Key::Divide,
Equals => Key::Equals,
Grave => Key::Grave,
Kana => Key::Kana,
Kanji => Key::Kanji,
LAlt => Key::LAlt,
LBracket => Key::LBracket,
LControl => Key::LControl,
LMenu => Key::LMenu,
LShift => Key::LShift,
LWin => Key::LWin,
Mail => Key::Mail,
MediaSelect => Key::MediaSelect,
MediaStop => Key::MediaStop,
Minus => Key::Minus,
Multiply => Key::Multiply,
Mute => Key::Mute,
MyComputer => Key::MyComputer,
NavigateForward => Key::NavigateForward,
NavigateBackward => Key::NavigateBackward,
NextTrack => Key::NextTrack,
NoConvert => Key::NoConvert,
NumpadComma => Key::NumpadComma,
NumpadEnter => Key::NumpadEnter,
NumpadEquals => Key::NumpadEquals,
OEM102 => Key::OEM102,
Period => Key::Period,
PlayPause => Key::PlayPause,
Power => Key::Power,
PrevTrack => Key::PrevTrack,
RAlt => Key::RAlt,
RBracket => Key::RBracket,
RControl => Key::RControl,
RMenu => Key::RMenu,
RShift => Key::RShift,
RWin => Key::RWin,
Semicolon => Key::Semicolon,
Slash => Key::Slash,
Sleep => Key::Sleep,
Stop => Key::Stop,
Subtract => Key::Subtract,
Sysrq => Key::Sysrq,
Tab => Key::Tab,
Underline => Key::Underline,
Unlabeled => Key::Unlabeled,
VolumeDown => Key::VolumeDown,
VolumeUp => Key::VolumeUp,
Wake => Key::Wake,
WebBack => Key::WebBack,
WebFavorites => Key::WebFavorites,
WebForward => Key::WebForward,
WebHome => Key::WebHome,
WebRefresh => Key::WebRefresh,
WebSearch => Key::WebSearch,
WebStop => Key::WebStop,
Yen => Key::Yen,
Caret => Key::Caret,
Copy => Key::Copy,
Paste => Key::Paste,
Cut => Key::Cut,
}
}
}

View file

@ -334,9 +334,8 @@ impl<N: Notify> Processor<N> {
processor.ctx.terminal.dirty = true;
},
KeyboardInput { input, .. } => {
let glutin::KeyboardInput { state, virtual_keycode, modifiers, .. } = input;
processor.process_key(state, virtual_keycode, modifiers);
if state == ElementState::Pressed {
processor.process_key(input);
if input.state == ElementState::Pressed {
// Hide cursor while typing
*hide_cursor = true;
}

View file

@ -26,9 +26,9 @@ use std::time::Instant;
use std::os::unix::process::CommandExt;
use copypasta::{Clipboard, Load, Buffer as ClipboardBuffer};
use glutin::{ElementState, VirtualKeyCode, MouseButton, TouchPhase, MouseScrollDelta, ModifiersState};
use glutin::{ElementState, MouseButton, TouchPhase, MouseScrollDelta, ModifiersState, KeyboardInput};
use config;
use config::{self, Key};
use grid::Scroll;
use event::{ClickState, Mouse};
use index::{Line, Column, Side, Point};
@ -100,7 +100,7 @@ pub struct Binding<T> {
}
/// Bindings that are triggered by a keyboard key
pub type KeyBinding = Binding<VirtualKeyCode>;
pub type KeyBinding = Binding<Key>;
/// Bindings that are triggered by a mouse button
pub type MouseBinding = Binding<MouseButton>;
@ -618,24 +618,18 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
/// 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: ModifiersState,
) {
match (key, state) {
(Some(key), ElementState::Pressed) => {
*self.ctx.last_modifiers() = mods;
pub fn process_key(&mut self, input: KeyboardInput) {
match input.state {
ElementState::Pressed => {
*self.ctx.last_modifiers() = input.modifiers;
*self.ctx.received_count() = 0;
*self.ctx.suppress_chars() = false;
if self.process_key_bindings(mods, key) {
if self.process_key_bindings(input) {
*self.ctx.suppress_chars() = true;
}
},
(_, ElementState::Released) => *self.ctx.suppress_chars() = false,
_ => ()
ElementState::Released => *self.ctx.suppress_chars() = false,
}
}
@ -668,10 +662,24 @@ 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: ModifiersState, key: VirtualKeyCode) -> bool {
fn process_key_bindings(&mut self, input: KeyboardInput) -> bool {
let mut has_binding = false;
for binding in self.key_bindings {
if binding.is_triggered_by(self.ctx.terminal_mode(), mods, &key) {
let is_triggered = match binding.trigger {
Key::Scancode(_) => binding.is_triggered_by(
self.ctx.terminal_mode(),
input.modifiers,
&Key::Scancode(input.scancode),
),
_ => if let Some(key) = input.virtual_keycode {
let key = Key::from_glutin_input(key);
binding.is_triggered_by(self.ctx.terminal_mode(), input.modifiers, &key)
} else {
false
},
};
if is_triggered {
// binding was triggered; run the action
binding.execute(&mut self.ctx);
has_binding = true;