feat(input): port keyboard, keybinding and mouse input handling
Ports ZIG/src/input (except macOS keymaps) into ghostty-input: - key, key_mods (incl. RemapSet), the physical keycode table - key_encode: legacy, modifyOtherKeys and kitty keyboard protocol encoders, function key tables - Binding: all keybinding actions with their user-visible docs (for +list-actions), triggers, sets with sequences, chains, leader keys and key tables, and formatting back to config strings - command palette defaults, mouse, paste safety checks, kitty helpers, links, helpgen action docs Deviations (docs/rewrite/notes/ghostty-input.md): Mods.super is named super_; case folding uses char::to_lowercase plus Ghostty's special cases instead of uucode tables; chain parents are trigger paths instead of pointers; a Zig bug where chained leaves inside sequences corrupted the shared prefix when formatting is fixed (covered by a test). Integration left: building encode/paste Options from terminal modes, and implementing the EntryFormatter trait in ghostty-config. Test Plan: - cargo test -p ghostty-input (220 tests; 8 macOS-only skipped) - cargo clippy -p ghostty-input --all-targets -- -D warnings
This commit is contained in:
1 parent
3323c3f525
commit
3afe938b95
21 files changed
+13413
-1
No files matched your search
@@ -0,0 +1,616 @@
|
||||
//! This is the list of "PC style function keys" that xterm supports for
|
||||
//! the legacy keyboard protocols. These always take priority since at the
|
||||
//! time of writing this, even the most modern keyboard protocols still
|
||||
//! are backwards compatible with regards to these sequences.
|
||||
//!
|
||||
//! This is based on a variety of sources cross-referenced but mostly
|
||||
//! based on foot's keymap.h: https://codeberg.org/dnkl/foot/src/branch/master/keymap.h
|
||||
//! (ZIG/src/input/function_keys.zig)
|
||||
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use crate::key::{Key, Mods};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CursorMode {
|
||||
Any,
|
||||
Normal,
|
||||
Application,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum KeypadMode {
|
||||
Any,
|
||||
Normal,
|
||||
Application,
|
||||
}
|
||||
|
||||
/// A bit confusing so I'll document this one: this is the "modify other keys"
|
||||
/// setting. We only change behavior for "set_other" which is ESC [ > 4; 2 m.
|
||||
/// So this can be "any" which means we don't care what's going on. Or it
|
||||
/// can be "set" which means modify keys must be set EXCEPT FOR "other keys"
|
||||
/// mode, and "set_other" which means modify keys must be set to "other keys"
|
||||
/// mode.
|
||||
///
|
||||
/// See: https://invisible-island.net/xterm/modified-keys.html
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ModifyKeys {
|
||||
Any,
|
||||
Set,
|
||||
SetOther,
|
||||
}
|
||||
|
||||
/// A single entry in the table of keys.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Entry {
|
||||
/// The exact set of modifiers that must be active for this entry to match.
|
||||
/// If mods_empty_is_any is true then empty mods means any set of mods can
|
||||
/// match. Otherwise, empty mods means no mods must be active.
|
||||
pub mods: Mods,
|
||||
pub mods_empty_is_any: bool,
|
||||
|
||||
/// The state required for cursor/keypad mode.
|
||||
pub cursor: CursorMode,
|
||||
pub keypad: KeypadMode,
|
||||
|
||||
/// Whether or not this entry should be used
|
||||
pub modify_other_keys: ModifyKeys,
|
||||
|
||||
/// The sequence to send to the pty if this entry matches.
|
||||
pub sequence: String,
|
||||
}
|
||||
|
||||
impl Entry {
|
||||
fn new(sequence: impl Into<String>) -> Entry {
|
||||
Entry {
|
||||
mods: Mods::default(),
|
||||
mods_empty_is_any: true,
|
||||
cursor: CursorMode::Any,
|
||||
keypad: KeypadMode::Any,
|
||||
modify_other_keys: ModifyKeys::Any,
|
||||
sequence: sequence.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn m(shift: bool, ctrl: bool, alt: bool, super_: bool) -> Mods {
|
||||
Mods {
|
||||
shift,
|
||||
ctrl,
|
||||
alt,
|
||||
super_,
|
||||
caps_lock: false,
|
||||
num_lock: false,
|
||||
sides: crate::key_mods::Side {
|
||||
shift: crate::key_mods::ModSide::Left,
|
||||
ctrl: crate::key_mods::ModSide::Left,
|
||||
alt: crate::key_mods::ModSide::Left,
|
||||
super_: crate::key_mods::ModSide::Left,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The list of modifier combinations for modify other key sequences.
|
||||
/// The mode value is index + 2.
|
||||
pub const MODIFIERS: &[Mods] = &[
|
||||
m(true, false, false, false),
|
||||
m(false, false, true, false),
|
||||
m(true, false, true, false),
|
||||
m(false, true, false, false),
|
||||
m(true, true, false, false),
|
||||
m(false, true, true, false),
|
||||
m(true, true, true, false),
|
||||
m(false, false, false, true),
|
||||
m(true, false, false, true),
|
||||
m(false, false, true, true),
|
||||
m(true, false, true, true),
|
||||
m(false, true, false, true),
|
||||
m(true, true, false, true),
|
||||
m(false, true, true, true),
|
||||
m(true, true, true, true),
|
||||
];
|
||||
|
||||
fn ent(mods: Mods, modify_other_keys: ModifyKeys, sequence: &str) -> Entry {
|
||||
Entry {
|
||||
mods,
|
||||
modify_other_keys,
|
||||
..Entry::new(sequence)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the entries for a key. Not every key has any entries. It is
|
||||
/// expected that you'll just use a for loop through the entry values, there
|
||||
/// are never more than a couple dozen or so.
|
||||
pub fn keys(k: Key) -> &'static [Entry] {
|
||||
&KEYS[k as usize]
|
||||
}
|
||||
|
||||
/// Iterate over the whole table (all keys, in `Key` order).
|
||||
pub fn all_keys() -> impl Iterator<Item = &'static [Entry]> {
|
||||
KEYS.iter().map(Vec::as_slice)
|
||||
}
|
||||
|
||||
static KEYS: LazyLock<Vec<Vec<Entry>>> = LazyLock::new(build_keys);
|
||||
|
||||
fn cat(mut a: Vec<Entry>, b: Vec<Entry>) -> Vec<Entry> {
|
||||
a.extend(b);
|
||||
a
|
||||
}
|
||||
|
||||
fn one(seq: &str) -> Vec<Entry> {
|
||||
vec![Entry::new(seq)]
|
||||
}
|
||||
|
||||
fn build_keys() -> Vec<Vec<Entry>> {
|
||||
let mut result: Vec<Vec<Entry>> = vec![Vec::new(); Key::ALL.len()];
|
||||
let mut set = |k: Key, v: Vec<Entry>| result[k as usize] = v;
|
||||
|
||||
set(
|
||||
Key::ArrowUp,
|
||||
cat(pc_style("\x1b[1;{}A"), cursor_key("\x1b[A", "\x1bOA")),
|
||||
);
|
||||
set(
|
||||
Key::ArrowDown,
|
||||
cat(pc_style("\x1b[1;{}B"), cursor_key("\x1b[B", "\x1bOB")),
|
||||
);
|
||||
set(
|
||||
Key::ArrowRight,
|
||||
cat(pc_style("\x1b[1;{}C"), cursor_key("\x1b[C", "\x1bOC")),
|
||||
);
|
||||
set(
|
||||
Key::ArrowLeft,
|
||||
cat(pc_style("\x1b[1;{}D"), cursor_key("\x1b[D", "\x1bOD")),
|
||||
);
|
||||
set(
|
||||
Key::Home,
|
||||
cat(pc_style("\x1b[1;{}H"), cursor_key("\x1b[H", "\x1bOH")),
|
||||
);
|
||||
set(
|
||||
Key::End,
|
||||
cat(pc_style("\x1b[1;{}F"), cursor_key("\x1b[F", "\x1bOF")),
|
||||
);
|
||||
set(Key::Insert, cat(pc_style("\x1b[2;{}~"), one("\x1B[2~")));
|
||||
set(Key::Delete, cat(pc_style("\x1b[3;{}~"), one("\x1B[3~")));
|
||||
set(Key::PageUp, cat(pc_style("\x1b[5;{}~"), one("\x1B[5~")));
|
||||
set(Key::PageDown, cat(pc_style("\x1b[6;{}~"), one("\x1B[6~")));
|
||||
|
||||
// Function Keys. todo: f13-f35 but we need to add to input.Key
|
||||
set(Key::F1, cat(pc_style("\x1b[1;{}P"), one("\x1BOP")));
|
||||
set(Key::F2, cat(pc_style("\x1b[1;{}Q"), one("\x1BOQ")));
|
||||
set(Key::F3, cat(pc_style("\x1b[13;{}~"), one("\x1BOR")));
|
||||
set(Key::F4, cat(pc_style("\x1b[1;{}S"), one("\x1BOS")));
|
||||
set(Key::F5, cat(pc_style("\x1b[15;{}~"), one("\x1B[15~")));
|
||||
set(Key::F6, cat(pc_style("\x1b[17;{}~"), one("\x1B[17~")));
|
||||
set(Key::F7, cat(pc_style("\x1b[18;{}~"), one("\x1B[18~")));
|
||||
set(Key::F8, cat(pc_style("\x1b[19;{}~"), one("\x1B[19~")));
|
||||
set(Key::F9, cat(pc_style("\x1b[20;{}~"), one("\x1B[20~")));
|
||||
set(Key::F10, cat(pc_style("\x1b[21;{}~"), one("\x1B[21~")));
|
||||
set(Key::F11, cat(pc_style("\x1b[23;{}~"), one("\x1B[23~")));
|
||||
set(Key::F12, cat(pc_style("\x1b[24;{}~"), one("\x1B[24~")));
|
||||
|
||||
// Keypad keys
|
||||
set(Key::Numpad0, kp_keys("p"));
|
||||
set(Key::Numpad1, kp_keys("q"));
|
||||
set(Key::Numpad2, kp_keys("r"));
|
||||
set(Key::Numpad3, kp_keys("s"));
|
||||
set(Key::Numpad4, kp_keys("t"));
|
||||
set(Key::Numpad5, kp_keys("u"));
|
||||
set(Key::Numpad6, kp_keys("v"));
|
||||
set(Key::Numpad7, kp_keys("w"));
|
||||
set(Key::Numpad8, kp_keys("x"));
|
||||
set(Key::Numpad9, kp_keys("y"));
|
||||
set(Key::NumpadDecimal, kp_keys("n"));
|
||||
set(Key::NumpadDivide, kp_keys("o"));
|
||||
set(Key::NumpadMultiply, kp_keys("j"));
|
||||
set(Key::NumpadSubtract, kp_keys("m"));
|
||||
set(Key::NumpadAdd, kp_keys("k"));
|
||||
set(Key::NumpadEnter, cat(kp_keys("M"), one("\r")));
|
||||
set(
|
||||
Key::NumpadUp,
|
||||
cat(pc_style("\x1b[1;{}A"), cursor_key("\x1b[A", "\x1bOA")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadDown,
|
||||
cat(pc_style("\x1b[1;{}B"), cursor_key("\x1b[B", "\x1bOB")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadRight,
|
||||
cat(pc_style("\x1b[1;{}C"), cursor_key("\x1b[C", "\x1bOC")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadLeft,
|
||||
cat(pc_style("\x1b[1;{}D"), cursor_key("\x1b[D", "\x1bOD")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadBegin,
|
||||
cat(pc_style("\x1b[1;{}E"), cursor_key("\x1b[E", "\x1bOE")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadHome,
|
||||
cat(pc_style("\x1b[1;{}H"), cursor_key("\x1b[H", "\x1bOH")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadEnd,
|
||||
cat(pc_style("\x1b[1;{}F"), cursor_key("\x1b[F", "\x1bOF")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadInsert,
|
||||
cat(pc_style("\x1b[2;{}~"), one("\x1B[2~")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadDelete,
|
||||
cat(pc_style("\x1b[3;{}~"), one("\x1B[3~")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadPageUp,
|
||||
cat(pc_style("\x1b[5;{}~"), one("\x1B[5~")),
|
||||
);
|
||||
set(
|
||||
Key::NumpadPageDown,
|
||||
cat(pc_style("\x1b[6;{}~"), one("\x1B[6~")),
|
||||
);
|
||||
|
||||
set(
|
||||
Key::Backspace,
|
||||
vec![
|
||||
// Modify Keys Normal / Other
|
||||
ent(m(true, false, false, false), ModifyKeys::Set, "\x7f"),
|
||||
ent(m(false, false, true, false), ModifyKeys::Set, "\x1b\x7f"),
|
||||
ent(m(true, false, true, false), ModifyKeys::Set, "\x1b\x7f"),
|
||||
ent(m(true, true, false, false), ModifyKeys::Set, "\x08"),
|
||||
ent(m(false, true, true, false), ModifyKeys::Set, "\x1b\x08"),
|
||||
ent(m(false, false, false, true), ModifyKeys::Set, "\x7f"),
|
||||
ent(m(true, false, false, true), ModifyKeys::Set, "\x7f"),
|
||||
ent(m(false, false, true, true), ModifyKeys::Set, "\x1b\x7f"),
|
||||
ent(m(true, false, true, true), ModifyKeys::Set, "\x1b\x7f"),
|
||||
ent(m(false, true, false, true), ModifyKeys::Set, "\x08"),
|
||||
ent(m(true, true, false, true), ModifyKeys::Set, "\x08"),
|
||||
ent(m(false, true, true, true), ModifyKeys::Set, "\x1b\x08"),
|
||||
ent(m(true, true, true, true), ModifyKeys::Set, "\x1b\x08"),
|
||||
ent(
|
||||
m(true, false, false, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;2;127~",
|
||||
),
|
||||
ent(
|
||||
m(false, false, true, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;3;127~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, true, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;4;127~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, false, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;6;127~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, true, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;7;127~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, true, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;8;127~",
|
||||
),
|
||||
ent(
|
||||
m(false, false, false, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;9;127~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, false, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;10;127~",
|
||||
),
|
||||
ent(
|
||||
m(false, false, true, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;11;127~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, true, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;12;127~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, false, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;13;127~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, false, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;14;127~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, true, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;15;127~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, true, true),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;16;127~",
|
||||
),
|
||||
ent(m(false, true, false, false), ModifyKeys::Any, "\x08"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\x7f"),
|
||||
ent(m(false, true, false, false), ModifyKeys::Any, "\x08"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\x7f"),
|
||||
],
|
||||
);
|
||||
|
||||
set(
|
||||
Key::Tab,
|
||||
vec![
|
||||
ent(m(true, false, false, false), ModifyKeys::Set, "\x1b[Z"),
|
||||
ent(m(false, false, true, false), ModifyKeys::Set, "\x1b\t"),
|
||||
ent(
|
||||
m(true, false, false, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;2;9~",
|
||||
),
|
||||
ent(
|
||||
m(false, false, true, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;3;9~",
|
||||
),
|
||||
ent(m(true, false, true, false), ModifyKeys::Any, "\x1b[27;4;9~"),
|
||||
ent(
|
||||
m(false, true, false, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;5;9~",
|
||||
),
|
||||
ent(m(true, true, false, false), ModifyKeys::Any, "\x1b[27;6;9~"),
|
||||
ent(m(false, true, true, false), ModifyKeys::Any, "\x1b[27;7;9~"),
|
||||
ent(m(true, true, true, false), ModifyKeys::Any, "\x1b[27;8;9~"),
|
||||
ent(
|
||||
m(false, false, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;9;9~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;10;9~",
|
||||
),
|
||||
ent(
|
||||
m(false, false, true, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;11;9~",
|
||||
),
|
||||
ent(m(true, false, true, true), ModifyKeys::Any, "\x1b[27;12;9~"),
|
||||
ent(
|
||||
m(false, true, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;13;9~",
|
||||
),
|
||||
ent(m(true, true, false, true), ModifyKeys::Any, "\x1b[27;14;9~"),
|
||||
ent(m(false, true, true, true), ModifyKeys::Any, "\x1b[27;15;9~"),
|
||||
ent(m(true, true, true, true), ModifyKeys::Any, "\x1b[27;16;9~"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\t"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\t"),
|
||||
],
|
||||
);
|
||||
|
||||
set(
|
||||
Key::Enter,
|
||||
vec![
|
||||
ent(
|
||||
m(true, false, false, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;2;13~",
|
||||
),
|
||||
ent(m(false, false, true, false), ModifyKeys::Set, "\x1b\r"),
|
||||
ent(
|
||||
m(false, false, true, false),
|
||||
ModifyKeys::SetOther,
|
||||
"\x1b[27;3;13~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, true, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;4;13~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, false, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;5;13~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, false, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;6;13~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, true, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;7;13~",
|
||||
),
|
||||
ent(m(true, true, true, false), ModifyKeys::Any, "\x1b[27;8;13~"),
|
||||
ent(
|
||||
m(false, false, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;9;13~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;10;13~",
|
||||
),
|
||||
ent(
|
||||
m(false, false, true, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;11;13~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, true, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;12;13~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;13;13~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;14;13~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, true, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;15;13~",
|
||||
),
|
||||
ent(m(true, true, true, true), ModifyKeys::Any, "\x1b[27;16;13~"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\r"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\r"),
|
||||
],
|
||||
);
|
||||
|
||||
set(
|
||||
Key::Escape,
|
||||
vec![
|
||||
ent(
|
||||
m(true, false, false, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;2;27~",
|
||||
),
|
||||
ent(m(false, false, true, false), ModifyKeys::Any, "\x1b\x1b"),
|
||||
ent(
|
||||
m(true, false, true, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;4;27~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, false, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;5;27~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, false, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;6;27~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, true, false),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;7;27~",
|
||||
),
|
||||
ent(m(true, true, true, false), ModifyKeys::Any, "\x1b[27;8;27~"),
|
||||
ent(
|
||||
m(false, false, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;9;27~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;10;27~",
|
||||
),
|
||||
ent(
|
||||
m(false, false, true, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;11;27~",
|
||||
),
|
||||
ent(
|
||||
m(true, false, true, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;12;27~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;13;27~",
|
||||
),
|
||||
ent(
|
||||
m(true, true, false, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;14;27~",
|
||||
),
|
||||
ent(
|
||||
m(false, true, true, true),
|
||||
ModifyKeys::Any,
|
||||
"\x1b[27;15;27~",
|
||||
),
|
||||
ent(m(true, true, true, true), ModifyKeys::Any, "\x1b[27;16;27~"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\x1b"),
|
||||
ent(m(false, false, false, false), ModifyKeys::Any, "\x1b"),
|
||||
],
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Returns the default keypad application mode entry.
|
||||
fn kp_default(suffix: &str) -> Vec<Entry> {
|
||||
vec![Entry {
|
||||
mods_empty_is_any: false,
|
||||
keypad: KeypadMode::Application,
|
||||
..Entry::new(format!("\x1bO{suffix}"))
|
||||
}]
|
||||
}
|
||||
|
||||
/// Returns the entries for a keypad key. The suffix is the final character
|
||||
/// of the sent sequence, such as "r" for kp_2.
|
||||
fn kp_keys(suffix: &str) -> Vec<Entry> {
|
||||
let mut pc = pc_style(&format!("\x1bO{{}}{suffix}"));
|
||||
for entry in &mut pc {
|
||||
entry.keypad = KeypadMode::Application;
|
||||
}
|
||||
cat(kp_default(suffix), pc)
|
||||
}
|
||||
|
||||
/// Returns entries that are dependent on cursor key settings.
|
||||
fn cursor_key(normal: &str, application: &str) -> Vec<Entry> {
|
||||
vec![
|
||||
Entry {
|
||||
cursor: CursorMode::Normal,
|
||||
..Entry::new(normal)
|
||||
},
|
||||
Entry {
|
||||
cursor: CursorMode::Application,
|
||||
..Entry::new(application)
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Constructs a set of pcStyle function keys using the given format. The
|
||||
/// format should have exactly one "hole" (`{}`) for the mods code.
|
||||
/// Example: "\x1b[11;{}~" for F1.
|
||||
fn pc_style(fmt: &str) -> Vec<Entry> {
|
||||
MODIFIERS
|
||||
.iter()
|
||||
.zip(2..)
|
||||
.map(|(mods, code)| Entry {
|
||||
mods: *mods,
|
||||
..Entry::new(fmt.replace("{}", &code.to_string()))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn keys_table() {
|
||||
// All key sequences must fit into a termio array.
|
||||
// TODO(integration): termio's `Message.WriteReq.Small.Max` isn't
|
||||
// available here; use its documented limit (38 bytes).
|
||||
const MAX: usize = 38;
|
||||
let mut n = 0;
|
||||
for entries in all_keys() {
|
||||
for entry in entries {
|
||||
assert!(entry.sequence.len() <= MAX, "{:?}", entry.sequence);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
assert!(n > 0);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user