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:
parent
03ec054d8b
commit
7441c3e637
174
ghid/keyboard.go
174
ghid/keyboard.go
@ -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
46
main.go
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user