Use a struct as a base instead of bare functions in a library

Added error reporting for Keyboard type
Added a file flag to read the input from
Fixed odd keymap path handling
This commit is contained in:
lordwelch 2020-03-05 12:52:27 -08:00
parent 03ec054d8b
commit 7441c3e637
2 changed files with 131 additions and 89 deletions

View File

@ -30,8 +30,18 @@ type Key struct {
// A Keymap is a json representation of the unicode rune mapped to its USB HID value // A Keymap is a json representation of the unicode rune mapped to its USB HID value
type Keymap map[string]Key type Keymap map[string]Key
// Keyboard is a type to attach the methods to if someone wants to use it type Keyboard struct {
type Keyboard struct{} PressDelay time.Duration // PressDelay is the time in ms to delay before sending a press event
ReleaseDelay time.Duration // ReleaseDelay is the time in ms to wait before sending the release event
KeymapOrder []string // Keymap Order is the order in which the specified keymaps cycle on the computer
KeymapShortcut [8]byte // KeymapShortcut is the key combo that will cycle the current keymap by one
ErrOnUnknownKey bool // ErrOnUnknownKey whether or not to fail if the unicode rune is invalid or is not in the specified keymaps
KeymapPath string // KeymapPath is the pathe to where the keymap files are stored
currentKeyMap int
keymaps map[string]Keymap
flags map[string]byte
Hidg0 io.Writer
}
// bit flag of modifier keys // bit flag of modifier keys
const ( const (
@ -47,16 +57,7 @@ const (
) )
var ( var (
PressDelay time.Duration // PressDelay is the time in ms to delay before sending a press event Modifiers = map[string]byte{
ReleaseDelay time.Duration // ReleaseDelay is the time in ms to wait before sending the release event
KeymapOrder []string // Keymap Order is the order in which the specified keymaps cycle on the computer
KeymapShortcut [8]byte // KeymapShortcut is the key combo that will cycle the current keymap by one
ErrOnUnknownKey bool // ErrOnUnknownKey whether or not to fail if the unicode rune is invalid or is not in the specified keymaps
KeymapPath string // KeymapPath is the pathe to where the keymap files are stored
currentKeyMap int
keys = make(map[string]Keymap)
flags = map[string]byte{
"LSHIFT": LSHIFT, "LSHIFT": LSHIFT,
"LCTRL": LCTRL, "LCTRL": LCTRL,
"LALT": LALT, "LALT": LALT,
@ -67,21 +68,23 @@ var (
"RSUPER": RSUPER, "RSUPER": RSUPER,
"NONE": NONE, "NONE": NONE,
} }
Hidg0 io.Writer
) )
func (k Keyboard) Write(p []byte) (n int, err error) { func NewKeyboard(Modifiers map[string]byte, kemapOrder []string, KeymapPath string, hidg0 io.Writer) *Keyboard {
return write(p) return &Keyboard{
} flags: Modifiers,
KeymapOrder: kemapOrder,
func Write(r io.Reader) error { KeymapPath: KeymapPath,
_, err := io.Copy(Keyboard{}, r) Hidg0: hidg0,
return err }
} }
// io.writer probably isn't the best interface to use for this // io.writer probably isn't the best interface to use for this
func write(p []byte) (n int, err error) { func (k *Keyboard) Write(p []byte) (int, error) {
var index int var (
index int
err error
)
for index < len(p) { for index < len(p) {
var ( var (
r rune r rune
@ -98,10 +101,14 @@ func write(p []byte) (n int, err error) {
if r == utf8.RuneError { if r == utf8.RuneError {
return index, fmt.Errorf("invalid rune: 0x%X", p[index]) // This probably screws things up if the last rune in 'p' is incomplete return index, fmt.Errorf("invalid rune: 0x%X", p[index]) // This probably screws things up if the last rune in 'p' is incomplete
} }
cur, ok := CurrentKeymap()[string(r)] cur, ok := k.CurrentKeymap()[string(r)]
if !ok { if !ok {
if i == 2 { // can't press two keys from different keymaps if i == 3 { // can't press two keys from different keymaps
if !changeKeymap(r) && ErrOnUnknownKey { ok, err = k.changeKeymap(r)
if !ok && k.ErrOnUnknownKey {
if err != nil {
return index, err
}
return index, fmt.Errorf("rune not in keymap: %c", r) return index, fmt.Errorf("rune not in keymap: %c", r)
} }
} else { } else {
@ -112,13 +119,13 @@ func write(p []byte) (n int, err error) {
switch { switch {
case cur.PressDelayDelimiter: case cur.PressDelayDelimiter:
var n int var n int
n, PressDelay = parseDelay(p[index+s:]) n, k.PressDelay = parseDelay(p[index+s:])
index += s + n index += s + n
break press break press
case cur.ReleaseDelayDelimiter: case cur.ReleaseDelayDelimiter:
var n int var n int
n, ReleaseDelay = parseDelay(p[index+s:]) n, k.ReleaseDelay = parseDelay(p[index+s:])
index += s + n index += s + n
break press break press
@ -134,7 +141,7 @@ func write(p []byte) (n int, err error) {
default: default:
// Calculate next modifier byte // Calculate next modifier byte
for _, v := range cur.Modifier { for _, v := range cur.Modifier {
mod = mod | flags[v] mod |= k.flags[v]
} }
// Set the modifier if it is the first key otherwise // Set the modifier if it is the first key otherwise
@ -151,20 +158,24 @@ func write(p []byte) (n int, err error) {
break press break press
} }
} }
} }
report[i] = cur.Decimal report[i] = cur.Decimal
index += s index += s
if PressDelay > 0 { if k.PressDelay > 0 {
break press break press
} }
} }
report[0] = flag report[0] = flag
r, _ = utf8.DecodeRune(p[index-1:]) err = k.Press(report, k.Hidg0)
Press(report, Hidg0) if err != nil {
delay(PressDelay) return index, err
}
k.delay(k.PressDelay)
}
err = k.keymapto0() // To make it reproducible
if err != nil {
return index, err
} }
keymapto0() // To make it reproducible
return index, nil return index, nil
} }
@ -194,69 +205,84 @@ func parseDelay(buffer []byte) (count int, delay time.Duration) {
return 0, 0 return 0, 0
} }
func delay(Delay time.Duration) { func (k *Keyboard) delay(Delay time.Duration) {
if Delay > 0 { if Delay > 0 {
if syncCheck, ok := Hidg0.(syncer); ok { if syncCheck, ok := k.Hidg0.(syncer); ok {
syncCheck.Sync() _ = syncCheck.Sync()
} }
time.Sleep(Delay) time.Sleep(Delay)
} }
} }
func Press(press [8]byte, file io.Writer) { func (k *Keyboard) Press(press [8]byte, file io.Writer) error {
file.Write(press[:]) _, err1 := file.Write(press[:])
delay(ReleaseDelay) k.delay(k.ReleaseDelay)
file.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) _, err2 := file.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
} if err1 != nil {
return err1
func Hold(press [8]byte, file io.Writer) {
file.Write(press[:])
}
func keymapto0() {
if len(KeymapOrder) > 1 {
for i := 0; i < len(KeymapOrder)-(currentKeyMap); i++ {
Press([8]byte{LALT, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00}, Hidg0)
}
currentKeyMap = 0
} }
if err2 != nil {
return err2
}
return nil
} }
func changeKeymap(r rune) bool { func (k *Keyboard) Hold(press [8]byte, file io.Writer) error {
buf := bytes.NewBuffer(make([]byte, 0, 8*len(KeymapOrder))) // To batch shortcut presses _, err := file.Write(press[:])
return err
}
for i := 0; i < len(KeymapOrder); i++ { func (k *Keyboard) keymapto0() error {
_, ok := CurrentKeymap()[string(r)] if len(k.KeymapOrder) > 1 {
for i := 0; i < len(k.KeymapOrder)-(k.currentKeyMap); i++ {
err := k.Press([8]byte{LALT, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00}, k.Hidg0)
if err != nil {
return err
}
}
k.currentKeyMap = 0
}
return nil
}
func (k *Keyboard) changeKeymap(r rune) (bool, error) {
var err error
buf := bytes.NewBuffer(make([]byte, 0, 8*len(k.KeymapOrder))) // To batch shortcut presses
for i := 0; i < len(k.KeymapOrder); i++ {
_, ok := k.CurrentKeymap()[string(r)]
if ok { if ok {
Hidg0.Write(buf.Bytes()) _, err = k.Hidg0.Write(buf.Bytes())
return true return true, err
} }
Press(KeymapShortcut, buf) err = k.Press(k.KeymapShortcut, buf)
if currentKeyMap == len(KeymapOrder)-1 { if err != nil {
currentKeyMap = 0 return false, err
}
if k.currentKeyMap == len(k.KeymapOrder)-1 {
k.currentKeyMap = 0
} else { } else {
currentKeyMap++ k.currentKeyMap++
} }
} }
return false return false, nil
} }
func CurrentKeymap() Keymap { func (k *Keyboard) CurrentKeymap() Keymap {
keymap, ok := keys[KeymapOrder[currentKeyMap]] keymap, ok := k.keymaps[k.KeymapOrder[k.currentKeyMap]]
if ok { if ok {
return keymap return keymap
} }
return LoadKeymap(KeymapOrder[currentKeyMap]) k.keymaps[k.KeymapOrder[k.currentKeyMap]] = LoadKeymap(k.KeymapOrder[k.currentKeyMap], k.KeymapPath)
return k.keymaps[k.KeymapOrder[k.currentKeyMap]]
} }
func LoadKeymap(keymapName string) Keymap { func LoadKeymap(keymapName string, KeymapPath string) Keymap {
var ( var (
err error err error
content []byte content []byte
file = path.Join(path.Join(KeymapPath, "hid"), keymapName+".json") file = path.Join(KeymapPath, keymapName+".json")
tmp = make(Keymap, 0) tmp = make(Keymap)
) )
fmt.Println(file) fmt.Println(file)
content, err = ioutil.ReadFile(file) content, err = ioutil.ReadFile(file)
@ -268,7 +294,5 @@ func LoadKeymap(keymapName string) Keymap {
if err != nil { if err != nil {
return nil return nil
} }
return tmp
keys[keymapName] = tmp
return keys[keymapName]
} }

46
main.go
View File

@ -3,34 +3,52 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"io"
"os" "os"
"path"
"timmy.narnian.us/hid/ghid" hid "timmy.narnian.us/hid/ghid"
) )
func main() { func main() {
var ( var (
SHORTCUT string Shortcut string
filePath string
keymapPath string
err error
ghid0 *os.File
tmp *os.File
keyboard *hid.Keyboard
) )
if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists {
flag.StringVar(&SHORTCUT, "shortcut", "", "Keymap cycle shortcut") _ = os.Setenv("XDG_CONFIG_HOME", path.Join(os.ExpandEnv("$HOME"), ".config"))
flag.StringVar(&SHORTCUT, "s", "", "Keymap cycle shortcut") }
flag.StringVar(&hid.KeymapPath, "path", os.ExpandEnv("$XDG_CONFIG_HOME"), "Path to config dir default: $XDG_CONFIG_HOME") flag.StringVar(&Shortcut, "shortcut", "", "Keymap cycle shortcut")
flag.StringVar(&hid.KeymapPath, "p", os.ExpandEnv("$XDG_CONFIG_HOME"), "Path to config dir default: $XDG_CONFIG_HOME") flag.StringVar(&Shortcut, "s", "", "Keymap cycle shortcut")
flag.StringVar(&keymapPath, "path", path.Join(os.ExpandEnv("$XDG_CONFIG_HOME"), "hid"), "Path to config dir default: $XDG_CONFIG_HOME")
flag.StringVar(&keymapPath, "p", path.Join(os.ExpandEnv("$XDG_CONFIG_HOME"), "hid"), "Path to config dir default: $XDG_CONFIG_HOME")
flag.StringVar(&filePath, "f", "-", "The file to read content from. Defaults to stdin")
flag.StringVar(&filePath, "file", "-", "The file to read content from. Defaults to stdin")
flag.Parse() flag.Parse()
fmt.Println(keymapPath)
hid.KeymapOrder = flag.Args() if filePath != "-" {
tmp, err = os.Open(path.Clean(filePath))
if err == nil {
_ = os.Stdin.Close()
os.Stdin = tmp
}
}
fmt.Println(hid.KeymapPath) ghid0, err = os.OpenFile("/dev/hidg0", os.O_APPEND|os.O_WRONLY, 0600)
file, err := os.OpenFile("/dev/hidg0", os.O_APPEND|os.O_WRONLY, 0755)
if err != nil { if err != nil {
panic(err) panic(err)
} }
hid.Hidg0 = file defer ghid0.Close()
defer file.Close()
hid.Write(os.Stdin) keyboard = hid.NewKeyboard(hid.Modifiers, flag.Args(), keymapPath, ghid0)
_, err = io.Copy(keyboard, os.Stdin)
if err != nil { if err != nil {
panic(err) panic(err)