Files

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")
}