1 #![allow(clippy::enum_glob_use)]
2
3 use std::fmt::{self, Debug, Display};
4
5 use bitflags::bitflags;
6 use glutin::event::VirtualKeyCode::*;
7 use glutin::event::{ModifiersState, MouseButton, VirtualKeyCode};
8 use serde::de::Error as SerdeError;
9 use serde::de::{self, MapAccess, Unexpected, Visitor};
10 use serde::{Deserialize, Deserializer};
11 use serde_yaml::Value as SerdeValue;
12
13 use alacritty_config_derive::ConfigDeserialize;
14
15 use alacritty_terminal::config::Program;
16 use alacritty_terminal::term::TermMode;
17 use alacritty_terminal::vi_mode::ViMotion;
18
19 use crate::config::ui_config::Hint;
20
21 /// Describes a state and action to take in that state.
22 ///
23 /// This is the shared component of `MouseBinding` and `KeyBinding`.
default() -> Self24 #[derive(Debug, Clone, PartialEq, Eq)]
25 pub struct Binding<T> {
26 /// Modifier keys required to activate binding.
27 pub mods: ModifiersState,
28
29 /// String to send to PTY if mods and mode match.
30 pub action: Action,
31
32 /// Binding mode required to activate binding.
33 pub mode: BindingMode,
34
35 /// Excluded binding modes where the binding won't be activated.
36 pub notmode: BindingMode,
37
38 /// This property is used as part of the trigger detection code.
39 ///
40 /// For example, this might be a key like "G", or a mouse button.
41 pub trigger: T,
42 }
43
44 /// Bindings that are triggered by a keyboard key.
45 pub type KeyBinding = Binding<Key>;
46
47 /// Bindings that are triggered by a mouse button.
48 pub type MouseBinding = Binding<MouseButton>;
49
50 impl<T: Eq> Binding<T> {
51 #[inline]
52 pub fn is_triggered_by(&self, mode: BindingMode, mods: ModifiersState, input: &T) -> bool {
53 // Check input first since bindings are stored in one big list. This is
54 // the most likely item to fail so prioritizing it here allows more
55 // checks to be short circuited.
56 self.trigger == *input
57 && self.mods == mods
58 && mode.contains(self.mode)
59 && !mode.intersects(self.notmode)
60 }
61
62 #[inline]
63 pub fn triggers_match(&self, binding: &Binding<T>) -> bool {
64 // Check the binding's key and modifiers.
65 if self.trigger != binding.trigger || self.mods != binding.mods {
66 return false;
default() -> Self67 }
68
69 let selfmode = if self.mode.is_empty() { BindingMode::all() } else { self.mode };
70 let bindingmode = if binding.mode.is_empty() { BindingMode::all() } else { binding.mode };
71
72 if !selfmode.intersects(bindingmode) {
73 return false;
74 }
75
76 // The bindings are never active at the same time when the required modes of one binding
77 // are part of the forbidden bindings of the other.
78 if self.mode.intersects(binding.notmode) || binding.mode.intersects(self.notmode) {
79 return false;
80 }
81
82 true
83 }
84 }
85
86 #[derive(ConfigDeserialize, Debug, Clone, PartialEq, Eq)]
87 pub enum Action {
88 /// Write an escape sequence.
89 #[config(skip)]
90 Esc(String),
91
92 /// Run given command.
93 #[config(skip)]
94 Command(Program),
95
96 /// Regex keyboard hints.
97 #[config(skip)]
98 Hint(Hint),
99
100 /// Move vi mode cursor.
101 #[config(skip)]
102 ViMotion(ViMotion),
103
104 /// Perform vi mode action.
105 #[config(skip)]
106 Vi(ViAction),
107
108 /// Perform search mode action.
109 #[config(skip)]
110 Search(SearchAction),
111
112 /// Paste contents of system clipboard.
113 Paste,
114
115 /// Store current selection into clipboard.
116 Copy,
117
118 #[cfg(not(any(target_os = "macos", windows)))]
119 /// Store current selection into selection buffer.
120 CopySelection,
121
122 /// Paste contents of selection buffer.
123 PasteSelection,
124
125 /// Increase font size.
126 IncreaseFontSize,
127
128 /// Decrease font size.
129 DecreaseFontSize,
130
131 /// Reset font size to the config value.
132 ResetFontSize,
133
134 /// Scroll exactly one page up.
135 ScrollPageUp,
136
137 /// Scroll exactly one page down.
138 ScrollPageDown,
139
140 /// Scroll half a page up.
141 ScrollHalfPageUp,
142
143 /// Scroll half a page down.
144 ScrollHalfPageDown,
145
146 /// Scroll one line up.
147 ScrollLineUp,
148
149 /// Scroll one line down.
150 ScrollLineDown,
151
152 /// Scroll all the way to the top.
153 ScrollToTop,
154
155 /// Scroll all the way to the bottom.
156 ScrollToBottom,
157
158 /// Clear the display buffer(s) to remove history.
159 ClearHistory,
160
161 /// Hide the Alacritty window.
162 Hide,
163
164 /// Hide all windows other than Alacritty on macOS.
165 #[cfg(target_os = "macos")]
166 HideOtherApplications,
167
168 /// Minimize the Alacritty window.
169 Minimize,
170
171 /// Quit Alacritty.
172 Quit,
173
174 /// Clear warning and error notices.
175 ClearLogNotice,
176
177 /// Spawn a new instance of Alacritty.
178 SpawnNewInstance,
179
180 /// Toggle fullscreen.
181 ToggleFullscreen,
182
183 /// Toggle simple fullscreen on macOS.
184 #[cfg(target_os = "macos")]
185 ToggleSimpleFullscreen,
186
187 /// Clear active selection.
188 ClearSelection,
189
190 /// Toggle vi mode.
191 ToggleViMode,
192
193 /// Allow receiving char input.
194 ReceiveChar,
195
196 /// Start a forward buffer search.
197 SearchForward,
198
199 /// Start a backward buffer search.
200 SearchBackward,
201
202 /// No action.
203 None,
204 }
205
206 impl From<&'static str> for Action {
207 fn from(s: &'static str) -> Action {
208 Action::Esc(s.into())
209 }
210 }
211
212 impl From<ViAction> for Action {
213 fn from(action: ViAction) -> Self {
214 Self::Vi(action)
215 }
216 }
217
218 impl From<ViMotion> for Action {
219 fn from(motion: ViMotion) -> Self {
220 Self::ViMotion(motion)
221 }
222 }
223
224 impl From<SearchAction> for Action {
225 fn from(action: SearchAction) -> Self {
226 Self::Search(action)
227 }
228 }
229
230 /// Display trait used for error logging.
231 impl Display for Action {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 match self {
234 Action::ViMotion(motion) => motion.fmt(f),
235 Action::Vi(action) => action.fmt(f),
236 _ => write!(f, "{:?}", self),
237 }
238 }
239 }
240
241 /// Vi mode specific actions.
242 #[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
243 pub enum ViAction {
244 /// Toggle normal vi selection.
245 ToggleNormalSelection,
246 /// Toggle line vi selection.
247 ToggleLineSelection,
248 /// Toggle block vi selection.
249 ToggleBlockSelection,
250 /// Toggle semantic vi selection.
251 ToggleSemanticSelection,
252 /// Jump to the beginning of the next match.
253 SearchNext,
254 /// Jump to the beginning of the previous match.
255 SearchPrevious,
256 /// Jump to the next start of a match to the left of the origin.
257 SearchStart,
258 /// Jump to the next end of a match to the right of the origin.
259 SearchEnd,
260 /// Launch the URL below the vi mode cursor.
261 Open,
262 }
263
264 /// Search mode specific actions.
265 #[allow(clippy::enum_variant_names)]
266 #[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
267 pub enum SearchAction {
268 /// Move the focus to the next search match.
269 SearchFocusNext,
270 /// Move the focus to the previous search match.
271 SearchFocusPrevious,
272 /// Confirm the active search.
273 SearchConfirm,
274 /// Cancel the active search.
275 SearchCancel,
276 /// Reset the search regex.
277 SearchClear,
278 /// Delete the last word in the search regex.
279 SearchDeleteWord,
280 /// Go to the previous regex in the search history.
281 SearchHistoryPrevious,
282 /// Go to the next regex in the search history.
283 SearchHistoryNext,
284 }
285
286 macro_rules! bindings {
287 (
288 KeyBinding;
289 $(
290 $key:ident
291 $(,$mods:expr)*
292 $(,+$mode:expr)*
293 $(,~$notmode:expr)*
294 ;$action:expr
295 );*
296 $(;)*
297 ) => {{
298 bindings!(
299 KeyBinding;
300 $(
301 Key::Keycode($key)
302 $(,$mods)*
303 $(,+$mode)*
304 $(,~$notmode)*
305 ;$action
306 );*
307 )
308 }};
309 (
310 $ty:ident;
311 $(
312 $key:expr
313 $(,$mods:expr)*
314 $(,+$mode:expr)*
315 $(,~$notmode:expr)*
316 ;$action:expr
317 );*
318 $(;)*
319 ) => {{
320 let mut v = Vec::new();
321
322 $(
323 let mut _mods = ModifiersState::empty();
324 $(_mods = $mods;)*
325 let mut _mode = BindingMode::empty();
326 $(_mode.insert($mode);)*
327 let mut _notmode = BindingMode::empty();
328 $(_notmode.insert($notmode);)*
329
330 v.push($ty {
331 trigger: $key,
332 mods: _mods,
333 mode: _mode,
334 notmode: _notmode,
335 action: $action.into(),
336 });
337 )*
338
339 v
340 }};
341 }
342
343 pub fn default_mouse_bindings() -> Vec<MouseBinding> {
344 bindings!(
345 MouseBinding;
346 MouseButton::Middle, ~BindingMode::VI; Action::PasteSelection;
347 )
348 }
349
350 pub fn default_key_bindings() -> Vec<KeyBinding> {
351 let mut bindings = bindings!(
352 KeyBinding;
353 Copy; Action::Copy;
354 Copy, +BindingMode::VI; Action::ClearSelection;
355 Paste, ~BindingMode::VI; Action::Paste;
356 L, ModifiersState::CTRL; Action::ClearLogNotice;
357 L, ModifiersState::CTRL, ~BindingMode::VI, ~BindingMode::SEARCH;
358 Action::Esc("\x0c".into());
359 Tab, ModifiersState::SHIFT, ~BindingMode::VI, ~BindingMode::SEARCH;
360 Action::Esc("\x1b[Z".into());
361 Back, ModifiersState::ALT, ~BindingMode::VI, ~BindingMode::SEARCH;
362 Action::Esc("\x1b\x7f".into());
363 Back, ModifiersState::SHIFT, ~BindingMode::VI, ~BindingMode::SEARCH;
364 Action::Esc("\x7f".into());
365 Home, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollToTop;
366 End, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollToBottom;
367 PageUp, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollPageUp;
368 PageDown, ModifiersState::SHIFT, ~BindingMode::ALT_SCREEN; Action::ScrollPageDown;
369 Home, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN,
370 ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[1;2H".into());
371 End, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN,
372 ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[1;2F".into());
373 PageUp, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN,
374 ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[5;2~".into());
375 PageDown, ModifiersState::SHIFT, +BindingMode::ALT_SCREEN,
376 ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[6;2~".into());
377 Home, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
378 Action::Esc("\x1bOH".into());
379 Home, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
380 Action::Esc("\x1b[H".into());
381 End, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
382 Action::Esc("\x1bOF".into());
383 End, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
384 Action::Esc("\x1b[F".into());
385 Up, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
386 Action::Esc("\x1bOA".into());
387 Up, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
388 Action::Esc("\x1b[A".into());
389 Down, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
390 Action::Esc("\x1bOB".into());
391 Down, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
392 Action::Esc("\x1b[B".into());
393 Right, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
394 Action::Esc("\x1bOC".into());
395 Right, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
396 Action::Esc("\x1b[C".into());
397 Left, +BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
398 Action::Esc("\x1bOD".into());
399 Left, ~BindingMode::APP_CURSOR, ~BindingMode::VI, ~BindingMode::SEARCH;
400 Action::Esc("\x1b[D".into());
401 Back, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x7f".into());
402 Insert, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[2~".into());
403 Delete, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[3~".into());
404 PageUp, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[5~".into());
405 PageDown, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[6~".into());
406 F1, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOP".into());
407 F2, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOQ".into());
408 F3, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOR".into());
409 F4, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1bOS".into());
410 F5, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[15~".into());
411 F6, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[17~".into());
412 F7, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[18~".into());
413 F8, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[19~".into());
414 F9, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[20~".into());
415 F10, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[21~".into());
416 F11, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[23~".into());
417 F12, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[24~".into());
418 F13, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[25~".into());
419 F14, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[26~".into());
420 F15, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[28~".into());
421 F16, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[29~".into());
422 F17, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[31~".into());
423 F18, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[32~".into());
424 F19, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[33~".into());
425 F20, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\x1b[34~".into());
426 NumpadEnter, ~BindingMode::VI, ~BindingMode::SEARCH; Action::Esc("\n".into());
427 Space, ModifiersState::SHIFT | ModifiersState::CTRL, ~BindingMode::SEARCH;
428 Action::ToggleViMode;
429 Space, ModifiersState::SHIFT | ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
430 Action::ScrollToBottom;
431 Escape, +BindingMode::VI, ~BindingMode::SEARCH;
432 Action::ClearSelection;
433 I, +BindingMode::VI, ~BindingMode::SEARCH;
434 Action::ToggleViMode;
435 I, +BindingMode::VI, ~BindingMode::SEARCH;
436 Action::ScrollToBottom;
437 C, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
438 Action::ToggleViMode;
439 Y, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
440 Action::ScrollLineUp;
441 E, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
442 Action::ScrollLineDown;
443 G, +BindingMode::VI, ~BindingMode::SEARCH;
444 Action::ScrollToTop;
445 G, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
446 Action::ScrollToBottom;
447 B, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
448 Action::ScrollPageUp;
449 F, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
450 Action::ScrollPageDown;
451 U, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
452 Action::ScrollHalfPageUp;
453 D, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
454 Action::ScrollHalfPageDown;
455 Y, +BindingMode::VI, ~BindingMode::SEARCH; Action::Copy;
456 Y, +BindingMode::VI, ~BindingMode::SEARCH;
457 Action::ClearSelection;
458 Slash, +BindingMode::VI, ~BindingMode::SEARCH;
459 Action::SearchForward;
460 Slash, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
461 Action::SearchBackward;
462 V, +BindingMode::VI, ~BindingMode::SEARCH;
463 ViAction::ToggleNormalSelection;
464 V, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
465 ViAction::ToggleLineSelection;
466 V, ModifiersState::CTRL, +BindingMode::VI, ~BindingMode::SEARCH;
467 ViAction::ToggleBlockSelection;
468 V, ModifiersState::ALT, +BindingMode::VI, ~BindingMode::SEARCH;
469 ViAction::ToggleSemanticSelection;
470 N, +BindingMode::VI, ~BindingMode::SEARCH;
471 ViAction::SearchNext;
472 N, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
473 ViAction::SearchPrevious;
474 Return, +BindingMode::VI, ~BindingMode::SEARCH;
475 ViAction::Open;
476 K, +BindingMode::VI, ~BindingMode::SEARCH;
477 ViMotion::Up;
478 J, +BindingMode::VI, ~BindingMode::SEARCH;
479 ViMotion::Down;
480 H, +BindingMode::VI, ~BindingMode::SEARCH;
481 ViMotion::Left;
482 L, +BindingMode::VI, ~BindingMode::SEARCH;
483 ViMotion::Right;
484 Up, +BindingMode::VI, ~BindingMode::SEARCH;
485 ViMotion::Up;
486 Down, +BindingMode::VI, ~BindingMode::SEARCH;
487 ViMotion::Down;
488 Left, +BindingMode::VI, ~BindingMode::SEARCH;
489 ViMotion::Left;
490 Right, +BindingMode::VI, ~BindingMode::SEARCH;
491 ViMotion::Right;
492 Key0, +BindingMode::VI, ~BindingMode::SEARCH;
493 ViMotion::First;
494 Key4, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
495 ViMotion::Last;
496 Key6, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
497 ViMotion::FirstOccupied;
498 H, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
499 ViMotion::High;
500 M, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
501 ViMotion::Middle;
502 L, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
503 ViMotion::Low;
504 B, +BindingMode::VI, ~BindingMode::SEARCH;
505 ViMotion::SemanticLeft;
506 W, +BindingMode::VI, ~BindingMode::SEARCH;
507 ViMotion::SemanticRight;
508 E, +BindingMode::VI, ~BindingMode::SEARCH;
509 ViMotion::SemanticRightEnd;
510 B, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
511 ViMotion::WordLeft;
512 W, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
513 ViMotion::WordRight;
514 E, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
515 ViMotion::WordRightEnd;
516 Key5, ModifiersState::SHIFT, +BindingMode::VI, ~BindingMode::SEARCH;
517 ViMotion::Bracket;
518 Return, +BindingMode::SEARCH, +BindingMode::VI;
519 SearchAction::SearchConfirm;
520 Escape, +BindingMode::SEARCH; SearchAction::SearchCancel;
521 C, ModifiersState::CTRL, +BindingMode::SEARCH; SearchAction::SearchCancel;
522 U, ModifiersState::CTRL, +BindingMode::SEARCH; SearchAction::SearchClear;
523 W, ModifiersState::CTRL, +BindingMode::SEARCH; SearchAction::SearchDeleteWord;
524 P, ModifiersState::CTRL, +BindingMode::SEARCH; SearchAction::SearchHistoryPrevious;
525 N, ModifiersState::CTRL, +BindingMode::SEARCH; SearchAction::SearchHistoryNext;
526 Up, +BindingMode::SEARCH; SearchAction::SearchHistoryPrevious;
527 Down, +BindingMode::SEARCH; SearchAction::SearchHistoryNext;
528 Return, +BindingMode::SEARCH, ~BindingMode::VI;
529 SearchAction::SearchFocusNext;
530 Return, ModifiersState::SHIFT, +BindingMode::SEARCH, ~BindingMode::VI;
531 SearchAction::SearchFocusPrevious;
532 );
533
534 // Code Modifiers
535 // ---------+---------------------------
536 // 2 | Shift
537 // 3 | Alt
538 // 4 | Shift + Alt
539 // 5 | Control
540 // 6 | Shift + Control
541 // 7 | Alt + Control
542 // 8 | Shift + Alt + Control
543 // ---------+---------------------------
544 //
545 // from: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
546 let mut modifiers = vec![
547 ModifiersState::SHIFT,
548 ModifiersState::ALT,
549 ModifiersState::SHIFT | ModifiersState::ALT,
550 ModifiersState::CTRL,
551 ModifiersState::SHIFT | ModifiersState::CTRL,
552 ModifiersState::ALT | ModifiersState::CTRL,
553 ModifiersState::SHIFT | ModifiersState::ALT | ModifiersState::CTRL,
554 ];
555
556 for (index, mods) in modifiers.drain(..).enumerate() {
557 let modifiers_code = index + 2;
558 bindings.extend(bindings!(
559 KeyBinding;
560 Delete, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
561 Action::Esc(format!("\x1b[3;{}~", modifiers_code));
562 Up, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
563 Action::Esc(format!("\x1b[1;{}A", modifiers_code));
564 Down, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
565 Action::Esc(format!("\x1b[1;{}B", modifiers_code));
566 Right, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
567 Action::Esc(format!("\x1b[1;{}C", modifiers_code));
568 Left, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
569 Action::Esc(format!("\x1b[1;{}D", modifiers_code));
570 F1, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
571 Action::Esc(format!("\x1b[1;{}P", modifiers_code));
572 F2, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
573 Action::Esc(format!("\x1b[1;{}Q", modifiers_code));
574 F3, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
575 Action::Esc(format!("\x1b[1;{}R", modifiers_code));
576 F4, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
577 Action::Esc(format!("\x1b[1;{}S", modifiers_code));
578 F5, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
579 Action::Esc(format!("\x1b[15;{}~", modifiers_code));
580 F6, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
581 Action::Esc(format!("\x1b[17;{}~", modifiers_code));
582 F7, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
583 Action::Esc(format!("\x1b[18;{}~", modifiers_code));
584 F8, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
585 Action::Esc(format!("\x1b[19;{}~", modifiers_code));
586 F9, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
587 Action::Esc(format!("\x1b[20;{}~", modifiers_code));
588 F10, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
589 Action::Esc(format!("\x1b[21;{}~", modifiers_code));
590 F11, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
591 Action::Esc(format!("\x1b[23;{}~", modifiers_code));
592 F12, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
593 Action::Esc(format!("\x1b[24;{}~", modifiers_code));
594 F13, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
595 Action::Esc(format!("\x1b[25;{}~", modifiers_code));
596 F14, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
597 Action::Esc(format!("\x1b[26;{}~", modifiers_code));
598 F15, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
599 Action::Esc(format!("\x1b[28;{}~", modifiers_code));
600 F16, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
601 Action::Esc(format!("\x1b[29;{}~", modifiers_code));
602 F17, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
603 Action::Esc(format!("\x1b[31;{}~", modifiers_code));
604 F18, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
605 Action::Esc(format!("\x1b[32;{}~", modifiers_code));
606 F19, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
607 Action::Esc(format!("\x1b[33;{}~", modifiers_code));
608 F20, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
609 Action::Esc(format!("\x1b[34;{}~", modifiers_code));
610 ));
611
612 // We're adding the following bindings with `Shift` manually above, so skipping them here.
613 if modifiers_code != 2 {
614 bindings.extend(bindings!(
615 KeyBinding;
616 Insert, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
617 Action::Esc(format!("\x1b[2;{}~", modifiers_code));
618 PageUp, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
619 Action::Esc(format!("\x1b[5;{}~", modifiers_code));
620 PageDown, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
621 Action::Esc(format!("\x1b[6;{}~", modifiers_code));
622 End, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
623 Action::Esc(format!("\x1b[1;{}F", modifiers_code));
624 Home, mods, ~BindingMode::VI, ~BindingMode::SEARCH;
625 Action::Esc(format!("\x1b[1;{}H", modifiers_code));
626 ));
627 }
628 }
629
630 bindings.extend(platform_key_bindings());
631
632 bindings
633 }
634
635 #[cfg(not(any(target_os = "macos", test)))]
636 fn common_keybindings() -> Vec<KeyBinding> {
637 bindings!(
638 KeyBinding;
639 V, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::VI; Action::Paste;
640 C, ModifiersState::CTRL | ModifiersState::SHIFT; Action::Copy;
641 F, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::SEARCH;
642 Action::SearchForward;
643 B, ModifiersState::CTRL | ModifiersState::SHIFT, ~BindingMode::SEARCH;
644 Action::SearchBackward;
645 C, ModifiersState::CTRL | ModifiersState::SHIFT,
646 +BindingMode::VI, ~BindingMode::SEARCH; Action::ClearSelection;
647 Insert, ModifiersState::SHIFT, ~BindingMode::VI; Action::PasteSelection;
648 Key0, ModifiersState::CTRL; Action::ResetFontSize;
649 Equals, ModifiersState::CTRL; Action::IncreaseFontSize;
650 Plus, ModifiersState::CTRL; Action::IncreaseFontSize;
651 NumpadAdd, ModifiersState::CTRL; Action::IncreaseFontSize;
652 Minus, ModifiersState::CTRL; Action::DecreaseFontSize;
653 NumpadSubtract, ModifiersState::CTRL; Action::DecreaseFontSize;
654 )
655 }
656
657 #[cfg(not(any(target_os = "macos", target_os = "windows", test)))]
658 pub fn platform_key_bindings() -> Vec<KeyBinding> {
659 common_keybindings()
660 }
661
662 #[cfg(all(target_os = "windows", not(test)))]
663 pub fn platform_key_bindings() -> Vec<KeyBinding> {
664 let mut bindings = bindings!(
665 KeyBinding;
666 Return, ModifiersState::ALT; Action::ToggleFullscreen;
667 );
668 bindings.extend(common_keybindings());
669 bindings
670 }
671
672 #[cfg(all(target_os = "macos", not(test)))]
673 pub fn platform_key_bindings() -> Vec<KeyBinding> {
674 bindings!(
675 KeyBinding;
676 Key0, ModifiersState::LOGO; Action::ResetFontSize;
677 Equals, ModifiersState::LOGO; Action::IncreaseFontSize;
678 Plus, ModifiersState::LOGO; Action::IncreaseFontSize;
679 NumpadAdd, ModifiersState::LOGO; Action::IncreaseFontSize;
680 Minus, ModifiersState::LOGO; Action::DecreaseFontSize;
681 NumpadSubtract, ModifiersState::LOGO; Action::DecreaseFontSize;
682 Insert, ModifiersState::SHIFT, ~BindingMode::VI, ~BindingMode::SEARCH;
683 Action::Esc("\x1b[2;2~".into());
684 K, ModifiersState::LOGO, ~BindingMode::VI, ~BindingMode::SEARCH;
685 Action::Esc("\x0c".into());
686 K, ModifiersState::LOGO, ~BindingMode::VI, ~BindingMode::SEARCH; Action::ClearHistory;
687 V, ModifiersState::LOGO, ~BindingMode::VI; Action::Paste;
688 N, ModifiersState::LOGO; Action::SpawnNewInstance;
689 F, ModifiersState::CTRL | ModifiersState::LOGO; Action::ToggleFullscreen;
690 C, ModifiersState::LOGO; Action::Copy;
691 C, ModifiersState::LOGO, +BindingMode::VI, ~BindingMode::SEARCH; Action::ClearSelection;
692 H, ModifiersState::LOGO; Action::Hide;
693 H, ModifiersState::LOGO | ModifiersState::ALT; Action::HideOtherApplications;
694 M, ModifiersState::LOGO; Action::Minimize;
695 Q, ModifiersState::LOGO; Action::Quit;
696 W, ModifiersState::LOGO; Action::Quit;
697 F, ModifiersState::LOGO, ~BindingMode::SEARCH; Action::SearchForward;
698 B, ModifiersState::LOGO, ~BindingMode::SEARCH; Action::SearchBackward;
699 )
700 }
701
702 // Don't return any bindings for tests since they are commented-out by default.
703 #[cfg(test)]
704 pub fn platform_key_bindings() -> Vec<KeyBinding> {
705 vec![]
706 }
707
708 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
709 pub enum Key {
710 Scancode(u32),
711 Keycode(VirtualKeyCode),
712 }
713
714 impl<'a> Deserialize<'a> for Key {
715 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
716 where
717 D: Deserializer<'a>,
718 {
719 let value = SerdeValue::deserialize(deserializer)?;
720 match u32::deserialize(value.clone()) {
721 Ok(scancode) => Ok(Key::Scancode(scancode)),
722 Err(_) => {
723 let keycode = VirtualKeyCode::deserialize(value).map_err(D::Error::custom)?;
724 Ok(Key::Keycode(keycode))
725 },
726 }
727 }
728 }
729
730 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
731 pub struct ModeWrapper {
732 pub mode: BindingMode,
733 pub not_mode: BindingMode,
734 }
735
736 bitflags! {
737 /// Modes available for key bindings.
738 pub struct BindingMode: u8 {
739 const APP_CURSOR = 0b0000_0001;
740 const APP_KEYPAD = 0b0000_0010;
741 const ALT_SCREEN = 0b0000_0100;
742 const VI = 0b0000_1000;
743 const SEARCH = 0b0001_0000;
744 }
745 }
746
747 impl BindingMode {
748 pub fn new(mode: &TermMode, search: bool) -> BindingMode {
749 let mut binding_mode = BindingMode::empty();
750 binding_mode.set(BindingMode::APP_CURSOR, mode.contains(TermMode::APP_CURSOR));
751 binding_mode.set(BindingMode::APP_KEYPAD, mode.contains(TermMode::APP_KEYPAD));
752 binding_mode.set(BindingMode::ALT_SCREEN, mode.contains(TermMode::ALT_SCREEN));
753 binding_mode.set(BindingMode::VI, mode.contains(TermMode::VI));
754 binding_mode.set(BindingMode::SEARCH, search);
755 binding_mode
756 }
757 }
758
759 impl Default for ModeWrapper {
760 fn default() -> Self {
761 Self { mode: BindingMode::empty(), not_mode: BindingMode::empty() }
762 }
763 }
764
765 impl<'a> Deserialize<'a> for ModeWrapper {
766 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
767 where
768 D: Deserializer<'a>,
769 {
770 struct ModeVisitor;
771
772 impl<'a> Visitor<'a> for ModeVisitor {
773 type Value = ModeWrapper;
774
775 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
776 f.write_str(
777 "a combination of AppCursor | AppKeypad | Alt | Vi, possibly with negation (~)",
778 )
779 }
780
781 fn visit_str<E>(self, value: &str) -> Result<ModeWrapper, E>
782 where
783 E: de::Error,
784 {
785 let mut res =
786 ModeWrapper { mode: BindingMode::empty(), not_mode: BindingMode::empty() };
787
788 for modifier in value.split('|') {
789 match modifier.trim().to_lowercase().as_str() {
790 "appcursor" => res.mode |= BindingMode::APP_CURSOR,
791 "~appcursor" => res.not_mode |= BindingMode::APP_CURSOR,
792 "appkeypad" => res.mode |= BindingMode::APP_KEYPAD,
793 "~appkeypad" => res.not_mode |= BindingMode::APP_KEYPAD,
794 "alt" => res.mode |= BindingMode::ALT_SCREEN,
795 "~alt" => res.not_mode |= BindingMode::ALT_SCREEN,
796 "vi" => res.mode |= BindingMode::VI,
797 "~vi" => res.not_mode |= BindingMode::VI,
798 "search" => res.mode |= BindingMode::SEARCH,
799 "~search" => res.not_mode |= BindingMode::SEARCH,
800 _ => return Err(E::invalid_value(Unexpected::Str(modifier), &self)),
801 }
802 }
803
804 Ok(res)
805 }
806 }
807 deserializer.deserialize_str(ModeVisitor)
808 }
809 }
810
811 struct MouseButtonWrapper(MouseButton);
812
813 impl MouseButtonWrapper {
814 fn into_inner(self) -> MouseButton {
815 self.0
816 }
817 }
818
819 impl<'a> Deserialize<'a> for MouseButtonWrapper {
820 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
821 where
822 D: Deserializer<'a>,
823 {
824 struct MouseButtonVisitor;
825
826 impl<'a> Visitor<'a> for MouseButtonVisitor {
827 type Value = MouseButtonWrapper;
828
829 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
830 f.write_str("Left, Right, Middle, or a number from 0 to 65536")
831 }
832
833 fn visit_u64<E>(self, value: u64) -> Result<MouseButtonWrapper, E>
834 where
835 E: de::Error,
836 {
837 match value {
838 0..=65536 => Ok(MouseButtonWrapper(MouseButton::Other(value as u16))),
839 _ => Err(E::invalid_value(Unexpected::Unsigned(value), &self)),
840 }
841 }
842
843 fn visit_str<E>(self, value: &str) -> Result<MouseButtonWrapper, E>
844 where
845 E: de::Error,
846 {
847 match value {
848 "Left" => Ok(MouseButtonWrapper(MouseButton::Left)),
849 "Right" => Ok(MouseButtonWrapper(MouseButton::Right)),
850 "Middle" => Ok(MouseButtonWrapper(MouseButton::Middle)),
851 _ => Err(E::invalid_value(Unexpected::Str(value), &self)),
852 }
853 }
854 }
855
856 deserializer.deserialize_any(MouseButtonVisitor)
857 }
858 }
859
860 /// Bindings are deserialized into a `RawBinding` before being parsed as a
861 /// `KeyBinding` or `MouseBinding`.
862 #[derive(PartialEq, Eq)]
863 struct RawBinding {
864 key: Option<Key>,
865 mouse: Option<MouseButton>,
866 mods: ModifiersState,
867 mode: BindingMode,
868 notmode: BindingMode,
869 action: Action,
870 }
871
872 impl RawBinding {
873 fn into_mouse_binding(self) -> Result<MouseBinding, Self> {
874 if let Some(mouse) = self.mouse {
875 Ok(Binding {
876 trigger: mouse,
877 mods: self.mods,
878 action: self.action,
879 mode: self.mode,
880 notmode: self.notmode,
881 })
882 } else {
883 Err(self)
884 }
885 }
886
887 fn into_key_binding(self) -> Result<KeyBinding, Self> {
888 if let Some(key) = self.key {
889 Ok(KeyBinding {
890 trigger: key,
891 mods: self.mods,
892 action: self.action,
893 mode: self.mode,
894 notmode: self.notmode,
895 })
896 } else {
897 Err(self)
898 }
899 }
900 }
901
902 impl<'a> Deserialize<'a> for RawBinding {
903 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
904 where
905 D: Deserializer<'a>,
906 {
907 const FIELDS: &[&str] = &["key", "mods", "mode", "action", "chars", "mouse", "command"];
908
909 enum Field {
910 Key,
911 Mods,
912 Mode,
913 Action,
914 Chars,
915 Mouse,
916 Command,
917 }
918
919 impl<'a> Deserialize<'a> for Field {
920 fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
921 where
922 D: Deserializer<'a>,
923 {
924 struct FieldVisitor;
925
926 impl<'a> Visitor<'a> for FieldVisitor {
927 type Value = Field;
928
929 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
930 f.write_str("binding fields")
931 }
932
933 fn visit_str<E>(self, value: &str) -> Result<Field, E>
934 where
935 E: de::Error,
936 {
937 match value.to_ascii_lowercase().as_str() {
938 "key" => Ok(Field::Key),
939 "mods" => Ok(Field::Mods),
940 "mode" => Ok(Field::Mode),
941 "action" => Ok(Field::Action),
942 "chars" => Ok(Field::Chars),
943 "mouse" => Ok(Field::Mouse),
944 "command" => Ok(Field::Command),
945 _ => Err(E::unknown_field(value, FIELDS)),
946 }
947 }
948 }
949
950 deserializer.deserialize_str(FieldVisitor)
951 }
952 }
953
954 struct RawBindingVisitor;
955 impl<'a> Visitor<'a> for RawBindingVisitor {
956 type Value = RawBinding;
957
958 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
959 f.write_str("binding specification")
960 }
961
962 fn visit_map<V>(self, mut map: V) -> Result<RawBinding, V::Error>
963 where
964 V: MapAccess<'a>,
965 {
966 let mut mods: Option<ModifiersState> = None;
967 let mut key: Option<Key> = None;
968 let mut chars: Option<String> = None;
969 let mut action: Option<Action> = None;
970 let mut mode: Option<BindingMode> = None;
971 let mut not_mode: Option<BindingMode> = None;
972 let mut mouse: Option<MouseButton> = None;
973 let mut command: Option<Program> = None;
974
975 use de::Error;
976
977 while let Some(struct_key) = map.next_key::<Field>()? {
978 match struct_key {
979 Field::Key => {
980 if key.is_some() {
981 return Err(<V::Error as Error>::duplicate_field("key"));
982 }
983
984 let val = map.next_value::<SerdeValue>()?;
985 if val.is_u64() {
986 let scancode = val.as_u64().unwrap();
987 if scancode > u64::from(std::u32::MAX) {
988 return Err(<V::Error as Error>::custom(format!(
989 "Invalid key binding, scancode too big: {}",
990 scancode
991 )));
992 }
993 key = Some(Key::Scancode(scancode as u32));
994 } else {
995 let k = Key::deserialize(val).map_err(V::Error::custom)?;
996 key = Some(k);
997 }
998 },
999 Field::Mods => {
1000 if mods.is_some() {
1001 return Err(<V::Error as Error>::duplicate_field("mods"));
1002 }
1003
1004 mods = Some(map.next_value::<ModsWrapper>()?.into_inner());
1005 },
1006 Field::Mode => {
1007 if mode.is_some() {
1008 return Err(<V::Error as Error>::duplicate_field("mode"));
1009 }
1010
1011 let mode_deserializer = map.next_value::<ModeWrapper>()?;
1012 mode = Some(mode_deserializer.mode);
1013 not_mode = Some(mode_deserializer.not_mode);
1014 },
1015 Field::Action => {
1016 if action.is_some() {
1017 return Err(<V::Error as Error>::duplicate_field("action"));
1018 }
1019
1020 let value = map.next_value::<SerdeValue>()?;
1021
1022 action = if let Ok(vi_action) = ViAction::deserialize(value.clone()) {
1023 Some(vi_action.into())
1024 } else if let Ok(vi_motion) = ViMotion::deserialize(value.clone()) {
1025 Some(vi_motion.into())
1026 } else if let Ok(search_action) =
1027 SearchAction::deserialize(value.clone())
1028 {
1029 Some(search_action.into())
1030 } else {
1031 match Action::deserialize(value.clone()).map_err(V::Error::custom) {
1032 Ok(action) => Some(action),
1033 Err(err) => {
1034 let value = match value {
1035 SerdeValue::String(string) => string,
1036 SerdeValue::Mapping(map) if map.len() == 1 => {
1037 match map.into_iter().next() {
1038 Some((
1039 SerdeValue::String(string),
1040 SerdeValue::Null,
1041 )) => string,
1042 _ => return Err(err),
1043 }
1044 },
1045 _ => return Err(err),
1046 };
1047 return Err(V::Error::custom(format!(
1048 "unknown keyboard action `{}`",
1049 value
1050 )));
1051 },
1052 }
1053 };
1054 },
1055 Field::Chars => {
1056 if chars.is_some() {
1057 return Err(<V::Error as Error>::duplicate_field("chars"));
1058 }
1059
1060 chars = Some(map.next_value()?);
1061 },
1062 Field::Mouse => {
1063 if chars.is_some() {
1064 return Err(<V::Error as Error>::duplicate_field("mouse"));
1065 }
1066
1067 mouse = Some(map.next_value::<MouseButtonWrapper>()?.into_inner());
1068 },
1069 Field::Command => {
1070 if command.is_some() {
1071 return Err(<V::Error as Error>::duplicate_field("command"));
1072 }
1073
1074 command = Some(map.next_value::<Program>()?);
1075 },
1076 }
1077 }
1078
1079 let mode = mode.unwrap_or_else(BindingMode::empty);
1080 let not_mode = not_mode.unwrap_or_else(BindingMode::empty);
1081 let mods = mods.unwrap_or_else(ModifiersState::default);
1082
1083 let action = match (action, chars, command) {
1084 (Some(action @ Action::ViMotion(_)), None, None)
1085 | (Some(action @ Action::Vi(_)), None, None) => {
1086 if !mode.intersects(BindingMode::VI) || not_mode.intersects(BindingMode::VI)
1087 {
1088 return Err(V::Error::custom(format!(
1089 "action `{}` is only available in vi mode, try adding `mode: Vi`",
1090 action,
1091 )));
1092 }
1093 action
1094 },
1095 (Some(action @ Action::Search(_)), None, None) => {
1096 if !mode.intersects(BindingMode::SEARCH) {
1097 return Err(V::Error::custom(format!(
1098 "action `{}` is only available in search mode, try adding `mode: \
1099 Search`",
1100 action,
1101 )));
1102 }
1103 action
1104 },
1105 (Some(action), None, None) => action,
1106 (None, Some(chars), None) => Action::Esc(chars),
1107 (None, None, Some(cmd)) => Action::Command(cmd),
1108 _ => {
1109 return Err(V::Error::custom(
1110 "must specify exactly one of chars, action or command",
1111 ))
1112 },
1113 };
1114
1115 if mouse.is_none() && key.is_none() {
1116 return Err(V::Error::custom("bindings require mouse button or key"));
1117 }
1118
1119 Ok(RawBinding { mode, notmode: not_mode, action, key, mouse, mods })
1120 }
1121 }
1122
1123 deserializer.deserialize_struct("RawBinding", FIELDS, RawBindingVisitor)
1124 }
1125 }
1126
1127 impl<'a> Deserialize<'a> for MouseBinding {
1128 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1129 where
1130 D: Deserializer<'a>,
1131 {
1132 let raw = RawBinding::deserialize(deserializer)?;
1133 raw.into_mouse_binding()
1134 .map_err(|_| D::Error::custom("expected mouse binding, got key binding"))
1135 }
1136 }
1137
1138 impl<'a> Deserialize<'a> for KeyBinding {
1139 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1140 where
1141 D: Deserializer<'a>,
1142 {
1143 let raw = RawBinding::deserialize(deserializer)?;
1144 raw.into_key_binding()
1145 .map_err(|_| D::Error::custom("expected key binding, got mouse binding"))
1146 }
1147 }
1148
1149 /// Newtype for implementing deserialize on glutin Mods.
1150 ///
1151 /// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
1152 /// impl below.
1153 #[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)]
1154 pub struct ModsWrapper(pub ModifiersState);
1155
1156 impl ModsWrapper {
1157 pub fn into_inner(self) -> ModifiersState {
1158 self.0
1159 }
1160 }
1161
1162 impl<'a> de::Deserialize<'a> for ModsWrapper {
1163 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1164 where
1165 D: de::Deserializer<'a>,
1166 {
1167 struct ModsVisitor;
1168
1169 impl<'a> Visitor<'a> for ModsVisitor {
1170 type Value = ModsWrapper;
1171
1172 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1173 f.write_str("None or a subset of Shift|Control|Super|Command|Alt|Option")
1174 }
1175
1176 fn visit_str<E>(self, value: &str) -> Result<ModsWrapper, E>
1177 where
1178 E: de::Error,
1179 {
1180 let mut res = ModifiersState::empty();
1181 for modifier in value.split('|') {
1182 match modifier.trim().to_lowercase().as_str() {
1183 "command" | "super" => res.insert(ModifiersState::LOGO),
1184 "shift" => res.insert(ModifiersState::SHIFT),
1185 "alt" | "option" => res.insert(ModifiersState::ALT),
1186 "control" => res.insert(ModifiersState::CTRL),
1187 "none" => (),
1188 _ => return Err(E::invalid_value(Unexpected::Str(modifier), &self)),
1189 }
1190 }
1191
1192 Ok(ModsWrapper(res))
1193 }
1194 }
1195
1196 deserializer.deserialize_str(ModsVisitor)
1197 }
1198 }
1199
1200 #[cfg(test)]
1201 mod tests {
1202 use super::*;
1203
1204 use glutin::event::ModifiersState;
1205
1206 type MockBinding = Binding<usize>;
1207
1208 impl Default for MockBinding {
1209 fn default() -> Self {
1210 Self {
1211 mods: Default::default(),
1212 action: Action::None,
1213 mode: BindingMode::empty(),
1214 notmode: BindingMode::empty(),
1215 trigger: Default::default(),
1216 }
1217 }
1218 }
1219
1220 #[test]
1221 fn binding_matches_itself() {
1222 let binding = MockBinding::default();
1223 let identical_binding = MockBinding::default();
1224
1225 assert!(binding.triggers_match(&identical_binding));
1226 assert!(identical_binding.triggers_match(&binding));
1227 }
1228
1229 #[test]
1230 fn binding_matches_different_action() {
1231 let binding = MockBinding::default();
1232 let different_action =
1233 MockBinding { action: Action::ClearHistory, ..MockBinding::default() };
1234
1235 assert!(binding.triggers_match(&different_action));
1236 assert!(different_action.triggers_match(&binding));
1237 }
1238
1239 #[test]
1240 fn mods_binding_requires_strict_match() {
1241 let superset_mods = MockBinding { mods: ModifiersState::all(), ..MockBinding::default() };
1242 let subset_mods = MockBinding { mods: ModifiersState::ALT, ..MockBinding::default() };
1243
1244 assert!(!superset_mods.triggers_match(&subset_mods));
1245 assert!(!subset_mods.triggers_match(&superset_mods));
1246 }
1247
1248 #[test]
1249 fn binding_matches_identical_mode() {
1250 let b1 = MockBinding { mode: BindingMode::ALT_SCREEN, ..MockBinding::default() };
1251 let b2 = MockBinding { mode: BindingMode::ALT_SCREEN, ..MockBinding::default() };
1252
1253 assert!(b1.triggers_match(&b2));
1254 assert!(b2.triggers_match(&b1));
1255 }
1256
1257 #[test]
1258 fn binding_without_mode_matches_any_mode() {
1259 let b1 = MockBinding::default();
1260 let b2 = MockBinding {
1261 mode: BindingMode::APP_KEYPAD,
1262 notmode: BindingMode::ALT_SCREEN,
1263 ..MockBinding::default()
1264 };
1265
1266 assert!(b1.triggers_match(&b2));
1267 }
1268
1269 #[test]
1270 fn binding_with_mode_matches_empty_mode() {
1271 let b1 = MockBinding {
1272 mode: BindingMode::APP_KEYPAD,
1273 notmode: BindingMode::ALT_SCREEN,
1274 ..MockBinding::default()
1275 };
1276 let b2 = MockBinding::default();
1277
1278 assert!(b1.triggers_match(&b2));
1279 assert!(b2.triggers_match(&b1));
1280 }
1281
1282 #[test]
1283 fn binding_matches_modes() {
1284 let b1 = MockBinding {
1285 mode: BindingMode::ALT_SCREEN | BindingMode::APP_KEYPAD,
1286 ..MockBinding::default()
1287 };
1288 let b2 = MockBinding { mode: BindingMode::APP_KEYPAD, ..MockBinding::default() };
1289
1290 assert!(b1.triggers_match(&b2));
1291 assert!(b2.triggers_match(&b1));
1292 }
1293
1294 #[test]
1295 fn binding_matches_partial_intersection() {
1296 let b1 = MockBinding {
1297 mode: BindingMode::ALT_SCREEN | BindingMode::APP_KEYPAD,
1298 ..MockBinding::default()
1299 };
1300 let b2 = MockBinding {
1301 mode: BindingMode::APP_KEYPAD | BindingMode::APP_CURSOR,
1302 ..MockBinding::default()
1303 };
1304
1305 assert!(b1.triggers_match(&b2));
1306 assert!(b2.triggers_match(&b1));
1307 }
1308
1309 #[test]
1310 fn binding_mismatches_notmode() {
1311 let b1 = MockBinding { mode: BindingMode::ALT_SCREEN, ..MockBinding::default() };
1312 let b2 = MockBinding { notmode: BindingMode::ALT_SCREEN, ..MockBinding::default() };
1313
1314 assert!(!b1.triggers_match(&b2));
1315 assert!(!b2.triggers_match(&b1));
1316 }
1317
1318 #[test]
1319 fn binding_mismatches_unrelated() {
1320 let b1 = MockBinding { mode: BindingMode::ALT_SCREEN, ..MockBinding::default() };
1321 let b2 = MockBinding { mode: BindingMode::APP_KEYPAD, ..MockBinding::default() };
1322
1323 assert!(!b1.triggers_match(&b2));
1324 assert!(!b2.triggers_match(&b1));
1325 }
1326
1327 #[test]
1328 fn binding_matches_notmodes() {
1329 let subset_notmodes = MockBinding {
1330 notmode: BindingMode::VI | BindingMode::APP_CURSOR,
1331 ..MockBinding::default()
1332 };
1333 let superset_notmodes =
1334 MockBinding { notmode: BindingMode::APP_CURSOR, ..MockBinding::default() };
1335
1336 assert!(subset_notmodes.triggers_match(&superset_notmodes));
1337 assert!(superset_notmodes.triggers_match(&subset_notmodes));
1338 }
1339
1340 #[test]
1341 fn binding_matches_mode_notmode() {
1342 let b1 = MockBinding {
1343 mode: BindingMode::VI,
1344 notmode: BindingMode::APP_CURSOR,
1345 ..MockBinding::default()
1346 };
1347 let b2 = MockBinding { notmode: BindingMode::APP_CURSOR, ..MockBinding::default() };
1348
1349 assert!(b1.triggers_match(&b2));
1350 assert!(b2.triggers_match(&b1));
1351 }
1352
1353 #[test]
1354 fn binding_trigger_input() {
1355 let binding = MockBinding { trigger: 13, ..MockBinding::default() };
1356
1357 let mods = binding.mods;
1358 let mode = binding.mode;
1359
1360 assert!(binding.is_triggered_by(mode, mods, &13));
1361 assert!(!binding.is_triggered_by(mode, mods, &32));
1362 }
1363
1364 #[test]
1365 fn binding_trigger_mods() {
1366 let binding = MockBinding {
1367 mods: ModifiersState::ALT | ModifiersState::LOGO,
1368 ..MockBinding::default()
1369 };
1370
1371 let superset_mods = ModifiersState::all();
1372 let subset_mods = ModifiersState::empty();
1373
1374 let t = binding.trigger;
1375 let mode = binding.mode;
1376
1377 assert!(binding.is_triggered_by(mode, binding.mods, &t));
1378 assert!(!binding.is_triggered_by(mode, superset_mods, &t));
1379 assert!(!binding.is_triggered_by(mode, subset_mods, &t));
1380 }
1381
1382 #[test]
1383 fn binding_trigger_modes() {
1384 let binding = MockBinding { mode: BindingMode::ALT_SCREEN, ..MockBinding::default() };
1385
1386 let t = binding.trigger;
1387 let mods = binding.mods;
1388
1389 assert!(!binding.is_triggered_by(BindingMode::VI, mods, &t));
1390 assert!(binding.is_triggered_by(BindingMode::ALT_SCREEN, mods, &t));
1391 assert!(binding.is_triggered_by(BindingMode::ALT_SCREEN | BindingMode::VI, mods, &t));
1392 }
1393
1394 #[test]
1395 fn binding_trigger_notmodes() {
1396 let binding = MockBinding { notmode: BindingMode::ALT_SCREEN, ..MockBinding::default() };
1397
1398 let t = binding.trigger;
1399 let mods = binding.mods;
1400
1401 assert!(binding.is_triggered_by(BindingMode::VI, mods, &t));
1402 assert!(!binding.is_triggered_by(BindingMode::ALT_SCREEN, mods, &t));
1403 assert!(!binding.is_triggered_by(BindingMode::ALT_SCREEN | BindingMode::VI, mods, &t));
1404 }
1405 }
1406