alacritty/alacritty_terminal/src/config/bindings.rs

1024 lines
34 KiB
Rust
Raw Normal View History

// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt;
use std::str::FromStr;
2019-03-30 16:48:36 +00:00
use glutin::{ModifiersState, MouseButton};
use serde::de::Error as SerdeError;
use serde::de::{self, MapAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer};
use crate::input::{Action, Binding, KeyBinding, MouseBinding};
2019-03-30 16:48:36 +00:00
use crate::term::TermMode;
macro_rules! bindings {
(
$ty:ident;
$(
$key:path
$(,[$($mod:ident: $enabled:expr),*])*
$(,+$mode:expr)*
$(,~$notmode:expr)*
;$action:expr
);*
$(;)*
) => {{
let mut v = Vec::new();
$(
let mut _mods = ModifiersState {
$($($mod: $enabled),*,)*
..Default::default()
};
let mut _mode = TermMode::empty();
$(_mode = $mode;)*
let mut _notmode = TermMode::empty();
$(_notmode = $notmode;)*
v.push($ty {
trigger: $key,
mods: _mods,
mode: _mode,
notmode: _notmode,
action: $action,
});
)*
v
}}
}
pub fn default_mouse_bindings() -> Vec<MouseBinding> {
bindings!(
MouseBinding;
MouseButton::Middle; Action::PasteSelection;
)
}
pub fn default_key_bindings() -> Vec<KeyBinding> {
let mut bindings = bindings!(
KeyBinding;
Key::Paste; Action::Paste;
Key::Copy; Action::Copy;
Key::L, [ctrl: true]; Action::ClearLogNotice;
Key::L, [ctrl: true]; Action::Esc("\x0c".into());
Key::PageUp, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollPageUp;
Key::PageDown, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollPageDown;
Key::Home, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollToTop;
Key::End, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollToBottom;
Key::Home, +TermMode::APP_CURSOR; Action::Esc("\x1bOH".into());
Key::Home, ~TermMode::APP_CURSOR; Action::Esc("\x1b[H".into());
Key::Home, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[1;2H".into());
Key::End, +TermMode::APP_CURSOR; Action::Esc("\x1bOF".into());
Key::End, ~TermMode::APP_CURSOR; Action::Esc("\x1b[F".into());
Key::End, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[1;2F".into());
Key::PageUp; Action::Esc("\x1b[5~".into());
Key::PageUp, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[5;2~".into());
Key::PageDown; Action::Esc("\x1b[6~".into());
Key::PageDown, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[6;2~".into());
Key::Tab, [shift: true]; Action::Esc("\x1b[Z".into());
Key::Back; Action::Esc("\x7f".into());
Key::Back, [alt: true]; Action::Esc("\x1b\x7f".into());
Key::Insert; Action::Esc("\x1b[2~".into());
Key::Delete; Action::Esc("\x1b[3~".into());
Key::Up, +TermMode::APP_CURSOR; Action::Esc("\x1bOA".into());
Key::Up, ~TermMode::APP_CURSOR; Action::Esc("\x1b[A".into());
Key::Down, +TermMode::APP_CURSOR; Action::Esc("\x1bOB".into());
Key::Down, ~TermMode::APP_CURSOR; Action::Esc("\x1b[B".into());
Key::Right, +TermMode::APP_CURSOR; Action::Esc("\x1bOC".into());
Key::Right, ~TermMode::APP_CURSOR; Action::Esc("\x1b[C".into());
Key::Left, +TermMode::APP_CURSOR; Action::Esc("\x1bOD".into());
Key::Left, ~TermMode::APP_CURSOR; Action::Esc("\x1b[D".into());
Key::F1; Action::Esc("\x1bOP".into());
Key::F2; Action::Esc("\x1bOQ".into());
Key::F3; Action::Esc("\x1bOR".into());
Key::F4; Action::Esc("\x1bOS".into());
Key::F5; Action::Esc("\x1b[15~".into());
Key::F6; Action::Esc("\x1b[17~".into());
Key::F7; Action::Esc("\x1b[18~".into());
Key::F8; Action::Esc("\x1b[19~".into());
Key::F9; Action::Esc("\x1b[20~".into());
Key::F10; Action::Esc("\x1b[21~".into());
Key::F11; Action::Esc("\x1b[23~".into());
Key::F12; Action::Esc("\x1b[24~".into());
Key::F13; Action::Esc("\x1b[25~".into());
Key::F14; Action::Esc("\x1b[26~".into());
Key::F15; Action::Esc("\x1b[28~".into());
Key::F16; Action::Esc("\x1b[29~".into());
Key::F17; Action::Esc("\x1b[31~".into());
Key::F18; Action::Esc("\x1b[32~".into());
Key::F19; Action::Esc("\x1b[33~".into());
Key::F20; Action::Esc("\x1b[34~".into());
Key::NumpadEnter; Action::Esc("\n".into());
);
// Code Modifiers
// ---------+---------------------------
// 2 | Shift
// 3 | Alt
// 4 | Shift + Alt
// 5 | Control
// 6 | Shift + Control
// 7 | Alt + Control
// 8 | Shift + Alt + Control
// ---------+---------------------------
//
// from: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
let modifiers = vec![
ModifiersState { shift: true, ..ModifiersState::default() },
ModifiersState { alt: true, ..ModifiersState::default() },
ModifiersState { shift: true, alt: true, ..ModifiersState::default() },
ModifiersState { ctrl: true, ..ModifiersState::default() },
ModifiersState { shift: true, ctrl: true, ..ModifiersState::default() },
ModifiersState { alt: true, ctrl: true, ..ModifiersState::default() },
ModifiersState { shift: true, alt: true, ctrl: true, ..ModifiersState::default() },
];
for (index, mods) in modifiers.iter().enumerate() {
let modifiers_code = index + 2;
bindings.extend(bindings!(
KeyBinding;
Key::Up, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}A", modifiers_code));
Key::Down, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}B", modifiers_code));
Key::Right, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}C", modifiers_code));
Key::Left, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}D", modifiers_code));
Key::F1, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}P", modifiers_code));
Key::F2, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}Q", modifiers_code));
Key::F3, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}R", modifiers_code));
Key::F4, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}S", modifiers_code));
Key::F5, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[15;{}~", modifiers_code));
Key::F6, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[17;{}~", modifiers_code));
Key::F7, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[18;{}~", modifiers_code));
Key::F8, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[19;{}~", modifiers_code));
Key::F9, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[20;{}~", modifiers_code));
Key::F10, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[21;{}~", modifiers_code));
Key::F11, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[23;{}~", modifiers_code));
Key::F12, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[24;{}~", modifiers_code));
Key::F13, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[25;{}~", modifiers_code));
Key::F14, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[26;{}~", modifiers_code));
Key::F15, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[28;{}~", modifiers_code));
Key::F16, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[29;{}~", modifiers_code));
Key::F17, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[31;{}~", modifiers_code));
Key::F18, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[32;{}~", modifiers_code));
Key::F19, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[33;{}~", modifiers_code));
Key::F20, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[34;{}~", modifiers_code));
));
// We're adding the following bindings with `Shift` manually above, so skipping them here
// modifiers_code != Shift
if modifiers_code != 2 {
bindings.extend(bindings!(
KeyBinding;
Key::PageUp, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[5;{}~", modifiers_code));
Key::PageDown, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[6;{}~", modifiers_code));
Key::End, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}F", modifiers_code));
Key::Home, [shift: mods.shift, alt: mods.alt, ctrl: mods.ctrl];
Action::Esc(format!("\x1b[1;{}H", modifiers_code));
));
}
}
bindings.extend(platform_key_bindings());
bindings
}
#[cfg(not(any(target_os = "macos", test)))]
fn common_keybindings() -> Vec<KeyBinding> {
bindings!(
KeyBinding;
Key::V, [ctrl: true, shift: true]; Action::Paste;
Key::C, [ctrl: true, shift: true]; Action::Copy;
Key::Insert, [shift: true]; Action::PasteSelection;
Key::Key0, [ctrl: true]; Action::ResetFontSize;
Key::Equals, [ctrl: true]; Action::IncreaseFontSize;
Key::Add, [ctrl: true]; Action::IncreaseFontSize;
Key::Subtract, [ctrl: true]; Action::DecreaseFontSize;
Key::Minus, [ctrl: true]; Action::DecreaseFontSize;
)
}
#[cfg(not(any(target_os = "macos", target_os = "windows", test)))]
pub fn platform_key_bindings() -> Vec<KeyBinding> {
common_keybindings()
}
#[cfg(all(target_os = "windows", not(test)))]
pub fn platform_key_bindings() -> Vec<KeyBinding> {
let mut bindings = bindings!(
KeyBinding;
Key::Return, [alt: true]; Action::ToggleFullscreen;
);
bindings.extend(common_keybindings());
bindings
}
#[cfg(all(target_os = "macos", not(test)))]
pub fn platform_key_bindings() -> Vec<KeyBinding> {
bindings!(
KeyBinding;
Key::Key0, [logo: true]; Action::ResetFontSize;
Key::Equals, [logo: true]; Action::IncreaseFontSize;
Key::Add, [logo: true]; Action::IncreaseFontSize;
Key::Minus, [logo: true]; Action::DecreaseFontSize;
Key::F, [ctrl: true, logo: true]; Action::ToggleFullscreen;
Key::K, [logo: true]; Action::ClearHistory;
Key::K, [logo: true]; Action::Esc("\x0c".into());
Key::V, [logo: true]; Action::Paste;
Key::C, [logo: true]; Action::Copy;
Key::H, [logo: true]; Action::Hide;
Key::Q, [logo: true]; Action::Quit;
Key::W, [logo: true]; Action::Quit;
)
}
// Don't return any bindings for tests since they are commented-out by default
#[cfg(test)]
pub fn platform_key_bindings() -> Vec<KeyBinding> {
vec![]
}
#[derive(Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Key {
Scancode(u32),
Key1,
Key2,
Key3,
Key4,
Key5,
Key6,
Key7,
Key8,
Key9,
Key0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
Escape,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
Snapshot,
Scroll,
Pause,
Insert,
Home,
Delete,
End,
PageDown,
PageUp,
Left,
Up,
Right,
Down,
Back,
Return,
Space,
Compose,
Numlock,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
AbntC1,
AbntC2,
Add,
Apostrophe,
Apps,
At,
Ax,
Backslash,
Calculator,
Capital,
Colon,
Comma,
Convert,
Decimal,
Divide,
Equals,
Grave,
Kana,
Kanji,
LAlt,
LBracket,
LControl,
LShift,
LWin,
Mail,
MediaSelect,
MediaStop,
Minus,
Multiply,
Mute,
MyComputer,
NavigateForward,
NavigateBackward,
NextTrack,
NoConvert,
NumpadComma,
NumpadEnter,
NumpadEquals,
OEM102,
Period,
PlayPause,
Power,
PrevTrack,
RAlt,
RBracket,
RControl,
RShift,
RWin,
Semicolon,
Slash,
Sleep,
Stop,
Subtract,
Sysrq,
Tab,
Underline,
Unlabeled,
VolumeDown,
VolumeUp,
Wake,
WebBack,
WebFavorites,
WebForward,
WebHome,
WebRefresh,
WebSearch,
WebStop,
Yen,
Caret,
Copy,
Paste,
Cut,
}
impl Key {
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,
F16 => Key::F16,
F17 => Key::F17,
F18 => Key::F18,
F19 => Key::F19,
F20 => Key::F20,
F21 => Key::F21,
F22 => Key::F22,
F23 => Key::F23,
F24 => Key::F24,
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,
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,
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,
}
}
}
struct ModeWrapper {
pub mode: TermMode,
pub not_mode: TermMode,
}
impl<'a> Deserialize<'a> for ModeWrapper {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: Deserializer<'a>,
{
struct ModeVisitor;
impl<'a> Visitor<'a> for ModeVisitor {
type Value = ModeWrapper;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Combination of AppCursor | AppKeypad, possibly with negation (~)")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<ModeWrapper, E>
where
E: de::Error,
{
let mut res = ModeWrapper { mode: TermMode::empty(), not_mode: TermMode::empty() };
for modifier in value.split('|') {
match modifier.trim().to_lowercase().as_str() {
"appcursor" => res.mode |= TermMode::APP_CURSOR,
"~appcursor" => res.not_mode |= TermMode::APP_CURSOR,
"appkeypad" => res.mode |= TermMode::APP_KEYPAD,
"~appkeypad" => res.not_mode |= TermMode::APP_KEYPAD,
"~alt" => res.not_mode |= TermMode::ALT_SCREEN,
"alt" => res.mode |= TermMode::ALT_SCREEN,
_ => error!("Unknown mode {:?}", modifier),
}
}
Ok(res)
}
}
deserializer.deserialize_str(ModeVisitor)
}
}
struct MouseButtonWrapper(MouseButton);
impl MouseButtonWrapper {
fn into_inner(self) -> MouseButton {
self.0
}
}
impl<'a> Deserialize<'a> for MouseButtonWrapper {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: Deserializer<'a>,
{
struct MouseButtonVisitor;
impl<'a> Visitor<'a> for MouseButtonVisitor {
type Value = MouseButtonWrapper;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Left, Right, Middle, or a number")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<MouseButtonWrapper, E>
where
E: de::Error,
{
match value {
"Left" => Ok(MouseButtonWrapper(MouseButton::Left)),
"Right" => Ok(MouseButtonWrapper(MouseButton::Right)),
"Middle" => Ok(MouseButtonWrapper(MouseButton::Middle)),
_ => {
if let Ok(index) = u8::from_str(value) {
Ok(MouseButtonWrapper(MouseButton::Other(index)))
} else {
Err(E::invalid_value(Unexpected::Str(value), &self))
}
},
}
}
}
deserializer.deserialize_str(MouseButtonVisitor)
}
}
/// Bindings are deserialized into a `RawBinding` before being parsed as a
/// `KeyBinding` or `MouseBinding`.
#[derive(PartialEq, Eq)]
struct RawBinding {
key: Option<Key>,
mouse: Option<MouseButton>,
mods: ModifiersState,
mode: TermMode,
notmode: TermMode,
action: Action,
}
impl RawBinding {
fn into_mouse_binding(self) -> ::std::result::Result<MouseBinding, Self> {
if let Some(mouse) = self.mouse {
Ok(Binding {
trigger: mouse,
mods: self.mods,
action: self.action,
mode: self.mode,
notmode: self.notmode,
})
} else {
Err(self)
}
}
fn into_key_binding(self) -> ::std::result::Result<KeyBinding, Self> {
if let Some(key) = self.key {
Ok(KeyBinding {
trigger: key,
mods: self.mods,
action: self.action,
mode: self.mode,
notmode: self.notmode,
})
} else {
Err(self)
}
}
}
impl<'a> Deserialize<'a> for RawBinding {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: Deserializer<'a>,
{
enum Field {
Key,
Mods,
Mode,
Action,
Chars,
Mouse,
Command,
}
impl<'a> Deserialize<'a> for Field {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Field, D::Error>
where
D: Deserializer<'a>,
{
struct FieldVisitor;
static FIELDS: &[&str] =
&["key", "mods", "mode", "action", "chars", "mouse", "command"];
impl<'a> Visitor<'a> for FieldVisitor {
type Value = Field;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("binding fields")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<Field, E>
where
E: de::Error,
{
match value {
"key" => Ok(Field::Key),
"mods" => Ok(Field::Mods),
"mode" => Ok(Field::Mode),
"action" => Ok(Field::Action),
"chars" => Ok(Field::Chars),
"mouse" => Ok(Field::Mouse),
"command" => Ok(Field::Command),
_ => Err(E::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_str(FieldVisitor)
}
}
struct RawBindingVisitor;
impl<'a> Visitor<'a> for RawBindingVisitor {
type Value = RawBinding;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("binding specification")
}
fn visit_map<V>(self, mut map: V) -> ::std::result::Result<RawBinding, V::Error>
where
V: MapAccess<'a>,
{
let mut mods: Option<ModifiersState> = None;
let mut key: Option<Key> = None;
let mut chars: Option<String> = None;
let mut action: Option<crate::input::Action> = None;
let mut mode: Option<TermMode> = None;
let mut not_mode: Option<TermMode> = None;
let mut mouse: Option<MouseButton> = None;
let mut command: Option<CommandWrapper> = None;
use ::serde::de::Error;
while let Some(struct_key) = map.next_key::<Field>()? {
match struct_key {
Field::Key => {
if key.is_some() {
return Err(<V::Error as Error>::duplicate_field("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() {
return Err(<V::Error as Error>::duplicate_field("mods"));
}
mods = Some(map.next_value::<ModsWrapper>()?.into_inner());
},
Field::Mode => {
if mode.is_some() {
return Err(<V::Error as Error>::duplicate_field("mode"));
}
let mode_deserializer = map.next_value::<ModeWrapper>()?;
mode = Some(mode_deserializer.mode);
not_mode = Some(mode_deserializer.not_mode);
},
Field::Action => {
if action.is_some() {
return Err(<V::Error as Error>::duplicate_field("action"));
}
action = Some(map.next_value::<Action>()?);
},
Field::Chars => {
if chars.is_some() {
return Err(<V::Error as Error>::duplicate_field("chars"));
}
chars = Some(map.next_value()?);
},
Field::Mouse => {
if chars.is_some() {
return Err(<V::Error as Error>::duplicate_field("mouse"));
}
mouse = Some(map.next_value::<MouseButtonWrapper>()?.into_inner());
},
Field::Command => {
if command.is_some() {
return Err(<V::Error as Error>::duplicate_field("command"));
}
command = Some(map.next_value::<CommandWrapper>()?);
},
}
}
let action = match (action, chars, command) {
(Some(action), None, None) => action,
(None, Some(chars), None) => Action::Esc(chars),
(None, None, Some(cmd)) => match cmd {
CommandWrapper::Just(program) => Action::Command(program, vec![]),
CommandWrapper::WithArgs { program, args } => {
Action::Command(program, args)
},
},
(None, None, None) => {
return Err(V::Error::custom("must specify chars, action or command"));
},
_ => {
return Err(V::Error::custom("must specify only chars, action or command"))
},
};
let mode = mode.unwrap_or_else(TermMode::empty);
let not_mode = not_mode.unwrap_or_else(TermMode::empty);
let mods = mods.unwrap_or_else(ModifiersState::default);
if mouse.is_none() && key.is_none() {
return Err(V::Error::custom("bindings require mouse button or key"));
}
Ok(RawBinding { mode, notmode: not_mode, action, key, mouse, mods })
}
}
const FIELDS: &[&str] = &["key", "mods", "mode", "action", "chars", "mouse", "command"];
deserializer.deserialize_struct("RawBinding", FIELDS, RawBindingVisitor)
}
}
impl<'a> Deserialize<'a> for MouseBinding {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: Deserializer<'a>,
{
let raw = RawBinding::deserialize(deserializer)?;
raw.into_mouse_binding().map_err(|_| D::Error::custom("expected mouse binding"))
}
}
impl<'a> Deserialize<'a> for KeyBinding {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: Deserializer<'a>,
{
let raw = RawBinding::deserialize(deserializer)?;
raw.into_key_binding().map_err(|_| D::Error::custom("expected key binding"))
}
}
#[serde(untagged)]
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
pub enum CommandWrapper {
Just(String),
WithArgs {
program: String,
#[serde(default)]
args: Vec<String>,
},
}
impl CommandWrapper {
pub fn program(&self) -> &str {
match self {
CommandWrapper::Just(program) => program,
CommandWrapper::WithArgs { program, .. } => program,
}
}
pub fn args(&self) -> &[String] {
match self {
CommandWrapper::Just(_) => &[],
CommandWrapper::WithArgs { args, .. } => args,
}
}
}
/// Newtype for implementing deserialize on glutin Mods
///
/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
/// impl below.
#[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)]
pub struct ModsWrapper(ModifiersState);
impl ModsWrapper {
pub fn into_inner(self) -> ModifiersState {
self.0
}
}
impl<'a> de::Deserialize<'a> for ModsWrapper {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: de::Deserializer<'a>,
{
struct ModsVisitor;
impl<'a> Visitor<'a> for ModsVisitor {
type Value = ModsWrapper;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Some subset of Command|Shift|Super|Alt|Option|Control")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<ModsWrapper, E>
where
E: de::Error,
{
let mut res = ModifiersState::default();
for modifier in value.split('|') {
match modifier.trim().to_lowercase().as_str() {
"command" | "super" => res.logo = true,
"shift" => res.shift = true,
"alt" | "option" => res.alt = true,
"control" => res.ctrl = true,
"none" => (),
_ => error!("Unknown modifier {:?}", modifier),
}
}
Ok(ModsWrapper(res))
}
}
deserializer.deserialize_str(ModsVisitor)
}
}