395 lines
10 KiB
Go
395 lines
10 KiB
Go
package terminal
|
|
|
|
// Key represents a parsed input key
|
|
type Key uint16
|
|
|
|
// Key constants - designed for expansion
|
|
const (
|
|
KeyNone Key = iota
|
|
KeyRune // Printable character (check Event.Rune)
|
|
|
|
// Control keys
|
|
KeyEscape
|
|
KeyEnter
|
|
KeyTab
|
|
KeyBacktab // Shift+Tab
|
|
KeyShiftTab // Same as KeyBacktab,for clarity
|
|
KeyBackspace
|
|
KeyDelete
|
|
KeySpace
|
|
|
|
// Navigation
|
|
KeyUp
|
|
KeyDown
|
|
KeyLeft
|
|
KeyRight
|
|
KeyHome
|
|
KeyEnd
|
|
KeyPageUp
|
|
KeyPageDown
|
|
KeyInsert
|
|
|
|
// Function keys
|
|
KeyF1
|
|
KeyF2
|
|
KeyF3
|
|
KeyF4
|
|
KeyF5
|
|
KeyF6
|
|
KeyF7
|
|
KeyF8
|
|
KeyF9
|
|
KeyF10
|
|
KeyF11
|
|
KeyF12
|
|
|
|
// Ctrl+letter (Ctrl+A = 0x01, Ctrl+Z = 0x1A)
|
|
KeyCtrlA
|
|
KeyCtrlB
|
|
KeyCtrlC
|
|
KeyCtrlD
|
|
KeyCtrlE
|
|
KeyCtrlF
|
|
KeyCtrlG
|
|
KeyCtrlH // Often same as Backspace
|
|
KeyCtrlI // Often same as Tab
|
|
KeyCtrlJ // Often same as Enter
|
|
KeyCtrlK
|
|
KeyCtrlL
|
|
KeyCtrlM // Often same as Enter
|
|
KeyCtrlN
|
|
KeyCtrlO
|
|
KeyCtrlP
|
|
KeyCtrlQ
|
|
KeyCtrlR
|
|
KeyCtrlS
|
|
KeyCtrlT
|
|
KeyCtrlU
|
|
KeyCtrlV
|
|
KeyCtrlW
|
|
KeyCtrlX
|
|
KeyCtrlY
|
|
KeyCtrlZ
|
|
|
|
// Ctrl+special
|
|
KeyCtrlSpace // Ctrl+@ / Ctrl+Space (Ctrl+@ produces NUL byte 0x00)
|
|
KeyCtrlBackslash
|
|
KeyCtrlBracketLeft
|
|
KeyCtrlBracketRight
|
|
KeyCtrlCaret
|
|
KeyCtrlUnderscore
|
|
)
|
|
|
|
// Modifier flags
|
|
type Modifier uint8
|
|
|
|
const (
|
|
ModNone Modifier = 0
|
|
ModShift Modifier = 1 << 0
|
|
ModAlt Modifier = 1 << 1
|
|
ModCtrl Modifier = 1 << 2
|
|
)
|
|
|
|
// escapeSequence maps escape sequences to keys
|
|
// Key: sequence after ESC [ (e.g., "A" for up arrow)
|
|
type escapeSequence struct {
|
|
seq string
|
|
key Key
|
|
mod Modifier
|
|
}
|
|
|
|
// Known escape sequences (CSI sequences: ESC [ ...)
|
|
var csiSequences = []escapeSequence{
|
|
// Arrow keys
|
|
{"A", KeyUp, ModNone},
|
|
{"B", KeyDown, ModNone},
|
|
{"C", KeyRight, ModNone},
|
|
{"D", KeyLeft, ModNone},
|
|
{"Z", KeyBacktab, ModShift}, // Shift+Tab
|
|
|
|
// Arrow keys with modifiers (xterm style: ESC [ 1 ; mod X)
|
|
{"1;2A", KeyUp, ModShift},
|
|
{"1;2B", KeyDown, ModShift},
|
|
{"1;2C", KeyRight, ModShift},
|
|
{"1;2D", KeyLeft, ModShift},
|
|
{"1;3A", KeyUp, ModAlt},
|
|
{"1;3B", KeyDown, ModAlt},
|
|
{"1;3C", KeyRight, ModAlt},
|
|
{"1;3D", KeyLeft, ModAlt},
|
|
{"1;5A", KeyUp, ModCtrl},
|
|
{"1;5B", KeyDown, ModCtrl},
|
|
{"1;5C", KeyRight, ModCtrl},
|
|
{"1;5D", KeyLeft, ModCtrl},
|
|
|
|
// Navigation
|
|
{"H", KeyHome, ModNone},
|
|
{"F", KeyEnd, ModNone},
|
|
{"1~", KeyHome, ModNone},
|
|
{"4~", KeyEnd, ModNone},
|
|
{"5~", KeyPageUp, ModNone},
|
|
{"6~", KeyPageDown, ModNone},
|
|
{"2~", KeyInsert, ModNone},
|
|
{"3~", KeyDelete, ModNone},
|
|
{"7~", KeyHome, ModNone},
|
|
{"8~", KeyEnd, ModNone},
|
|
|
|
// Function keys (xterm)
|
|
{"11~", KeyF1, ModNone},
|
|
{"12~", KeyF2, ModNone},
|
|
{"13~", KeyF3, ModNone},
|
|
{"14~", KeyF4, ModNone},
|
|
{"15~", KeyF5, ModNone},
|
|
{"17~", KeyF6, ModNone},
|
|
{"18~", KeyF7, ModNone},
|
|
{"19~", KeyF8, ModNone},
|
|
{"20~", KeyF9, ModNone},
|
|
{"21~", KeyF10, ModNone},
|
|
{"23~", KeyF11, ModNone},
|
|
{"24~", KeyF12, ModNone},
|
|
|
|
// Function keys (vt style)
|
|
{"[A", KeyF1, ModNone},
|
|
{"[B", KeyF2, ModNone},
|
|
{"[C", KeyF3, ModNone},
|
|
{"[D", KeyF4, ModNone},
|
|
{"[E", KeyF5, ModNone},
|
|
|
|
// Shift+Navigation (mod=2)
|
|
{"1;2H", KeyHome, ModShift},
|
|
{"1;2F", KeyEnd, ModShift},
|
|
{"2;2~", KeyInsert, ModShift},
|
|
{"3;2~", KeyDelete, ModShift},
|
|
{"5;2~", KeyPageUp, ModShift},
|
|
{"6;2~", KeyPageDown, ModShift},
|
|
|
|
// Alt+Arrows (mod=3) - already have 1;3A-D
|
|
// Alt+Navigation (mod=3)
|
|
{"1;3H", KeyHome, ModAlt},
|
|
{"1;3F", KeyEnd, ModAlt},
|
|
{"2;3~", KeyInsert, ModAlt},
|
|
{"3;3~", KeyDelete, ModAlt},
|
|
{"5;3~", KeyPageUp, ModAlt},
|
|
{"6;3~", KeyPageDown, ModAlt},
|
|
|
|
// Shift+Alt (mod=4)
|
|
{"1;4A", KeyUp, ModShift | ModAlt},
|
|
{"1;4B", KeyDown, ModShift | ModAlt},
|
|
{"1;4C", KeyRight, ModShift | ModAlt},
|
|
{"1;4D", KeyLeft, ModShift | ModAlt},
|
|
{"1;4H", KeyHome, ModShift | ModAlt},
|
|
{"1;4F", KeyEnd, ModShift | ModAlt},
|
|
{"2;4~", KeyInsert, ModShift | ModAlt},
|
|
{"3;4~", KeyDelete, ModShift | ModAlt},
|
|
{"5;4~", KeyPageUp, ModShift | ModAlt},
|
|
{"6;4~", KeyPageDown, ModShift | ModAlt},
|
|
|
|
// Ctrl+Arrows (mod=5) - already have 1;5A-D
|
|
// Ctrl+Navigation (mod=5)
|
|
{"1;5H", KeyHome, ModCtrl},
|
|
{"1;5F", KeyEnd, ModCtrl},
|
|
{"2;5~", KeyInsert, ModCtrl},
|
|
{"3;5~", KeyDelete, ModCtrl},
|
|
{"5;5~", KeyPageUp, ModCtrl},
|
|
{"6;5~", KeyPageDown, ModCtrl},
|
|
|
|
// Shift+Ctrl (mod=6)
|
|
{"1;6A", KeyUp, ModShift | ModCtrl},
|
|
{"1;6B", KeyDown, ModShift | ModCtrl},
|
|
{"1;6C", KeyRight, ModShift | ModCtrl},
|
|
{"1;6D", KeyLeft, ModShift | ModCtrl},
|
|
{"1;6H", KeyHome, ModShift | ModCtrl},
|
|
{"1;6F", KeyEnd, ModShift | ModCtrl},
|
|
{"2;6~", KeyInsert, ModShift | ModCtrl},
|
|
{"3;6~", KeyDelete, ModShift | ModCtrl},
|
|
{"5;6~", KeyPageUp, ModShift | ModCtrl},
|
|
{"6;6~", KeyPageDown, ModShift | ModCtrl},
|
|
|
|
// Alt+Ctrl (mod=7)
|
|
{"1;7A", KeyUp, ModAlt | ModCtrl},
|
|
{"1;7B", KeyDown, ModAlt | ModCtrl},
|
|
{"1;7C", KeyRight, ModAlt | ModCtrl},
|
|
{"1;7D", KeyLeft, ModAlt | ModCtrl},
|
|
{"1;7H", KeyHome, ModAlt | ModCtrl},
|
|
{"1;7F", KeyEnd, ModAlt | ModCtrl},
|
|
{"2;7~", KeyInsert, ModAlt | ModCtrl},
|
|
{"3;7~", KeyDelete, ModAlt | ModCtrl},
|
|
{"5;7~", KeyPageUp, ModAlt | ModCtrl},
|
|
{"6;7~", KeyPageDown, ModAlt | ModCtrl},
|
|
|
|
// Shift+Alt+Ctrl (mod=8)
|
|
{"1;8A", KeyUp, ModShift | ModAlt | ModCtrl},
|
|
{"1;8B", KeyDown, ModShift | ModAlt | ModCtrl},
|
|
{"1;8C", KeyRight, ModShift | ModAlt | ModCtrl},
|
|
{"1;8D", KeyLeft, ModShift | ModAlt | ModCtrl},
|
|
{"1;8H", KeyHome, ModShift | ModAlt | ModCtrl},
|
|
{"1;8F", KeyEnd, ModShift | ModAlt | ModCtrl},
|
|
{"2;8~", KeyInsert, ModShift | ModAlt | ModCtrl},
|
|
{"3;8~", KeyDelete, ModShift | ModAlt | ModCtrl},
|
|
{"5;8~", KeyPageUp, ModShift | ModAlt | ModCtrl},
|
|
{"6;8~", KeyPageDown, ModShift | ModAlt | ModCtrl},
|
|
|
|
// F-keys with modifiers (CSI style: ESC [ 1 ; mod P/Q/R/S for F1-F4)
|
|
// F1-F4 with Shift (mod=2)
|
|
{"1;2P", KeyF1, ModShift},
|
|
{"1;2Q", KeyF2, ModShift},
|
|
{"1;2R", KeyF3, ModShift},
|
|
{"1;2S", KeyF4, ModShift},
|
|
// F1-F4 with Alt (mod=3)
|
|
{"1;3P", KeyF1, ModAlt},
|
|
{"1;3Q", KeyF2, ModAlt},
|
|
{"1;3R", KeyF3, ModAlt},
|
|
{"1;3S", KeyF4, ModAlt},
|
|
// F1-F4 with Ctrl (mod=5)
|
|
{"1;5P", KeyF1, ModCtrl},
|
|
{"1;5Q", KeyF2, ModCtrl},
|
|
{"1;5R", KeyF3, ModCtrl},
|
|
{"1;5S", KeyF4, ModCtrl},
|
|
// F1-F4 with Shift+Alt (mod=4)
|
|
{"1;4P", KeyF1, ModShift | ModAlt},
|
|
{"1;4Q", KeyF2, ModShift | ModAlt},
|
|
{"1;4R", KeyF3, ModShift | ModAlt},
|
|
{"1;4S", KeyF4, ModShift | ModAlt},
|
|
// F1-F4 with Shift+Ctrl (mod=6)
|
|
{"1;6P", KeyF1, ModShift | ModCtrl},
|
|
{"1;6Q", KeyF2, ModShift | ModCtrl},
|
|
{"1;6R", KeyF3, ModShift | ModCtrl},
|
|
{"1;6S", KeyF4, ModShift | ModCtrl},
|
|
// F1-F4 with Alt+Ctrl (mod=7)
|
|
{"1;7P", KeyF1, ModAlt | ModCtrl},
|
|
{"1;7Q", KeyF2, ModAlt | ModCtrl},
|
|
{"1;7R", KeyF3, ModAlt | ModCtrl},
|
|
{"1;7S", KeyF4, ModAlt | ModCtrl},
|
|
// F1-F4 with Shift+Alt+Ctrl (mod=8)
|
|
{"1;8P", KeyF1, ModShift | ModAlt | ModCtrl},
|
|
{"1;8Q", KeyF2, ModShift | ModAlt | ModCtrl},
|
|
{"1;8R", KeyF3, ModShift | ModAlt | ModCtrl},
|
|
{"1;8S", KeyF4, ModShift | ModAlt | ModCtrl},
|
|
|
|
// F5-F12 with modifiers (ESC [ N ; mod ~)
|
|
// F5 (15~)
|
|
{"15;2~", KeyF5, ModShift},
|
|
{"15;3~", KeyF5, ModAlt},
|
|
{"15;4~", KeyF5, ModShift | ModAlt},
|
|
{"15;5~", KeyF5, ModCtrl},
|
|
{"15;6~", KeyF5, ModShift | ModCtrl},
|
|
{"15;7~", KeyF5, ModAlt | ModCtrl},
|
|
{"15;8~", KeyF5, ModShift | ModAlt | ModCtrl},
|
|
// F6 (17~)
|
|
{"17;2~", KeyF6, ModShift},
|
|
{"17;3~", KeyF6, ModAlt},
|
|
{"17;4~", KeyF6, ModShift | ModAlt},
|
|
{"17;5~", KeyF6, ModCtrl},
|
|
{"17;6~", KeyF6, ModShift | ModCtrl},
|
|
{"17;7~", KeyF6, ModAlt | ModCtrl},
|
|
{"17;8~", KeyF6, ModShift | ModAlt | ModCtrl},
|
|
// F7 (18~)
|
|
{"18;2~", KeyF7, ModShift},
|
|
{"18;3~", KeyF7, ModAlt},
|
|
{"18;4~", KeyF7, ModShift | ModAlt},
|
|
{"18;5~", KeyF7, ModCtrl},
|
|
{"18;6~", KeyF7, ModShift | ModCtrl},
|
|
{"18;7~", KeyF7, ModAlt | ModCtrl},
|
|
{"18;8~", KeyF7, ModShift | ModAlt | ModCtrl},
|
|
// F8 (19~)
|
|
{"19;2~", KeyF8, ModShift},
|
|
{"19;3~", KeyF8, ModAlt},
|
|
{"19;4~", KeyF8, ModShift | ModAlt},
|
|
{"19;5~", KeyF8, ModCtrl},
|
|
{"19;6~", KeyF8, ModShift | ModCtrl},
|
|
{"19;7~", KeyF8, ModAlt | ModCtrl},
|
|
{"19;8~", KeyF8, ModShift | ModAlt | ModCtrl},
|
|
// F9 (20~)
|
|
{"20;2~", KeyF9, ModShift},
|
|
{"20;3~", KeyF9, ModAlt},
|
|
{"20;4~", KeyF9, ModShift | ModAlt},
|
|
{"20;5~", KeyF9, ModCtrl},
|
|
{"20;6~", KeyF9, ModShift | ModCtrl},
|
|
{"20;7~", KeyF9, ModAlt | ModCtrl},
|
|
{"20;8~", KeyF9, ModShift | ModAlt | ModCtrl},
|
|
// F10 (21~)
|
|
{"21;2~", KeyF10, ModShift},
|
|
{"21;3~", KeyF10, ModAlt},
|
|
{"21;4~", KeyF10, ModShift | ModAlt},
|
|
{"21;5~", KeyF10, ModCtrl},
|
|
{"21;6~", KeyF10, ModShift | ModCtrl},
|
|
{"21;7~", KeyF10, ModAlt | ModCtrl},
|
|
{"21;8~", KeyF10, ModShift | ModAlt | ModCtrl},
|
|
// F11 (23~)
|
|
{"23;2~", KeyF11, ModShift},
|
|
{"23;3~", KeyF11, ModAlt},
|
|
{"23;4~", KeyF11, ModShift | ModAlt},
|
|
{"23;5~", KeyF11, ModCtrl},
|
|
{"23;6~", KeyF11, ModShift | ModCtrl},
|
|
{"23;7~", KeyF11, ModAlt | ModCtrl},
|
|
{"23;8~", KeyF11, ModShift | ModAlt | ModCtrl},
|
|
// F12 (24~)
|
|
{"24;2~", KeyF12, ModShift},
|
|
{"24;3~", KeyF12, ModAlt},
|
|
{"24;4~", KeyF12, ModShift | ModAlt},
|
|
{"24;5~", KeyF12, ModCtrl},
|
|
{"24;6~", KeyF12, ModShift | ModCtrl},
|
|
{"24;7~", KeyF12, ModAlt | ModCtrl},
|
|
{"24;8~", KeyF12, ModShift | ModAlt | ModCtrl},
|
|
}
|
|
|
|
// SS3 sequences (ESC O ...)
|
|
var ss3Sequences = []escapeSequence{
|
|
{"A", KeyUp, ModNone},
|
|
{"B", KeyDown, ModNone},
|
|
{"C", KeyRight, ModNone},
|
|
{"D", KeyLeft, ModNone},
|
|
{"H", KeyHome, ModNone},
|
|
{"F", KeyEnd, ModNone},
|
|
{"P", KeyF1, ModNone},
|
|
{"Q", KeyF2, ModNone},
|
|
{"R", KeyF3, ModNone},
|
|
{"S", KeyF4, ModNone},
|
|
|
|
// Numeric Keypad (Application Mode)
|
|
{"M", KeyEnter, ModNone}, // Keypad Enter
|
|
{"X", KeyRune, ModNone}, // Keypad = (some terminals)
|
|
{"j", KeyRune, ModNone}, // Keypad *
|
|
{"k", KeyRune, ModNone}, // Keypad +
|
|
{"l", KeyRune, ModNone}, // Keypad ,
|
|
{"m", KeyRune, ModNone}, // Keypad -
|
|
{"n", KeyRune, ModNone}, // Keypad .
|
|
{"o", KeyRune, ModNone}, // Keypad /
|
|
{"p", KeyRune, ModNone}, // Keypad 0
|
|
{"q", KeyRune, ModNone}, // Keypad 1
|
|
{"r", KeyRune, ModNone}, // Keypad 2
|
|
{"s", KeyRune, ModNone}, // Keypad 3
|
|
{"t", KeyRune, ModNone}, // Keypad 4
|
|
{"u", KeyRune, ModNone}, // Keypad 5
|
|
{"v", KeyRune, ModNone}, // Keypad 6
|
|
{"w", KeyRune, ModNone}, // Keypad 7
|
|
{"x", KeyRune, ModNone}, // Keypad 8
|
|
{"y", KeyRune, ModNone}, // Keypad 9
|
|
}
|
|
|
|
var csiMap = buildSequenceMap(csiSequences)
|
|
var ss3Map = buildSequenceMap(ss3Sequences)
|
|
|
|
func buildSequenceMap(seqs []escapeSequence) map[string]escapeSequence {
|
|
m := make(map[string]escapeSequence, len(seqs))
|
|
for _, s := range seqs {
|
|
m[s.seq] = s
|
|
}
|
|
return m
|
|
}
|
|
|
|
// lookupCSI performs zero-alloc map lookup via compiler optimization
|
|
// The string([]byte) conversion inline in map access does not allocate
|
|
func lookupCSI(seq []byte) (Key, Modifier, bool) {
|
|
if s, ok := csiMap[string(seq)]; ok {
|
|
return s.key, s.mod, true
|
|
}
|
|
return KeyNone, ModNone, false
|
|
}
|
|
|
|
// lookupSS3 performs zero-alloc map lookup
|
|
func lookupSS3(seq []byte) (Key, Modifier, bool) {
|
|
if s, ok := ss3Map[string(seq)]; ok {
|
|
return s.key, s.mod, true
|
|
}
|
|
return KeyNone, ModNone, false
|
|
} |