132 lines
3.5 KiB
Go
132 lines
3.5 KiB
Go
package input
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type KeyKind uint8
|
|
|
|
const (
|
|
KeyUnknown KeyKind = iota
|
|
KeyModifier
|
|
KeyFunction
|
|
KeyAction
|
|
KeyNavigation
|
|
KeyDiacritical
|
|
)
|
|
|
|
type Event struct {
|
|
Timestamp uint32 // X server milliseconds, for ordering independent listeners.
|
|
Keycode uint8
|
|
Pressed bool
|
|
Repeat bool // X11 autorepeat, distinct from a new physical key press.
|
|
Name string
|
|
Text string
|
|
Kind KeyKind
|
|
}
|
|
|
|
type EchoSettings struct {
|
|
EnableKeyEcho bool
|
|
EnableModifierKeys bool
|
|
EnableFunctionKeys bool
|
|
EnableActionKeys bool
|
|
EnableNavigationKeys bool
|
|
EnableDiacriticalKeys bool
|
|
CthulhuModifierKeys []string
|
|
}
|
|
|
|
func NewKeyEvent(keycode uint8, pressed bool, name string) Event {
|
|
kind, text := classifyKey(name)
|
|
return Event{Keycode: keycode, Pressed: pressed, Name: name, Text: text, Kind: kind}
|
|
}
|
|
|
|
func EchoText(event Event, settings EchoSettings) string {
|
|
if !event.Pressed || !settings.EnableKeyEcho {
|
|
return ""
|
|
}
|
|
var enabled bool
|
|
switch event.Kind {
|
|
case KeyModifier:
|
|
enabled = settings.EnableModifierKeys
|
|
case KeyFunction:
|
|
enabled = settings.EnableFunctionKeys
|
|
case KeyAction:
|
|
enabled = settings.EnableActionKeys
|
|
case KeyNavigation:
|
|
enabled = settings.EnableNavigationKeys
|
|
case KeyDiacritical:
|
|
enabled = settings.EnableDiacriticalKeys
|
|
}
|
|
if !enabled {
|
|
return ""
|
|
}
|
|
return event.Text
|
|
}
|
|
|
|
func classifyKey(name string) (KeyKind, string) {
|
|
if text, exists := map[string]string{
|
|
"Shift_L": "left shift", "Shift_R": "right shift",
|
|
"Control_L": "left control", "Control_R": "right control",
|
|
"Alt_L": "left alt", "Alt_R": "right alt",
|
|
"Meta_L": "left meta", "Meta_R": "right meta",
|
|
"ISO_Level3_Shift": "Alt Gr",
|
|
}[name]; exists {
|
|
return KeyModifier, text
|
|
}
|
|
if strings.HasPrefix(name, "F") {
|
|
number, err := strconv.Atoi(strings.TrimPrefix(name, "F"))
|
|
if err == nil && number >= 1 && number <= 12 {
|
|
return KeyFunction, strings.ToLower(name)
|
|
}
|
|
}
|
|
if text, exists := map[string]string{
|
|
"Return": "return", "KP_Enter": "enter", "Escape": "escape",
|
|
"Tab": "tab", "ISO_Left_Tab": "left tab", "BackSpace": "backspace",
|
|
"Delete": "delete", "KP_Delete": "delete",
|
|
"Page_Up": "page up", "KP_Page_Up": "page up", "Prior": "page up", "KP_Prior": "page up",
|
|
"Page_Down": "page down", "KP_Page_Down": "page down", "Next": "page down", "KP_Next": "page down",
|
|
}[name]; exists {
|
|
return KeyAction, text
|
|
}
|
|
if text, exists := map[string]string{
|
|
"Left": "left", "KP_Left": "left", "Right": "right", "KP_Right": "right",
|
|
"Up": "up", "KP_Up": "up", "Down": "down", "KP_Down": "down",
|
|
"Home": "home", "KP_Home": "home", "End": "end", "KP_End": "end",
|
|
}[name]; exists {
|
|
return KeyNavigation, text
|
|
}
|
|
if strings.HasPrefix(name, "dead_") {
|
|
text := strings.ReplaceAll(strings.TrimPrefix(name, "dead_"), "_", " ")
|
|
if text == "abovering" {
|
|
text = "ring"
|
|
}
|
|
return KeyDiacritical, text
|
|
}
|
|
return KeyUnknown, ""
|
|
}
|
|
|
|
type ModifierKeycodes [256]bool
|
|
|
|
func NewModifierKeycodes(keycodes ...uint8) ModifierKeycodes {
|
|
var modifiers ModifierKeycodes
|
|
for _, keycode := range keycodes {
|
|
modifiers[keycode] = true
|
|
}
|
|
return modifiers
|
|
}
|
|
|
|
func (modifiers ModifierKeycodes) Contains(keycode uint8) bool {
|
|
return modifiers[keycode]
|
|
}
|
|
|
|
func ShouldInterruptSpeech(event Event) bool {
|
|
return event.Pressed
|
|
}
|
|
|
|
// Automatic titles survive held-key repeats, but fresh input and explicit
|
|
// stop keys cancel both scheduled and already queued title speech.
|
|
func ShouldInterruptTitle(event Event) bool {
|
|
return event.Pressed && (!event.Repeat || event.Name == "Control_L" || event.Name == "Control_R" || event.Name == "Escape")
|
|
}
|