Restructure to library

Implemented io.Writer interface. Might want to use a different interface
add a delay character
This commit is contained in:
lordwelch 2018-06-22 20:28:21 -07:00
parent 394b44e9df
commit 9d04ddc8be
3 changed files with 285 additions and 217 deletions

43
cmd/main.go Normal file
View File

@ -0,0 +1,43 @@
package main
import (
"fmt"
"os"
"github.com/alexflint/go-arg"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
"timmy.narnian.us/git/timmy/hid"
)
func main() {
var (
err error
args struct {
SHORTCUT string `arg:"-S,help:Keymap cycle shortcut"`
PATH string `arg:"-P,help:Path to config dir default: $XDG_CONFIG_HOME"`
ORDER []string `arg:"required,positional,help:Order of keymaps"`
}
)
args.PATH = os.ExpandEnv("$XDG_CONFIG_HOME")
arg.MustParse(&args)
hid.KeymapOrder = args.ORDER
hid.KeymapPath = args.PATH
fmt.Println(hid.KeymapPath)
hid.Hidg0, err = os.OpenFile("hidg0", os.O_APPEND|os.O_WRONLY, 0755)
if err != nil {
panic(err)
}
hid.Write(transform.NewReader(os.Stdin, unicode.BOMOverride(unicode.UTF8.NewDecoder())))
// if err != nil {
// panic(err)
// }
fmt.Println("Success!")
}

242
keyboard.go Normal file
View File

@ -0,0 +1,242 @@
package hid
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"path"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
)
type syncer interface {
Sync() error
}
type Key struct {
Modifier []string `json:"modifier"`
Decimal byte `json:"decimal"`
DelayDelimiter bool `json:"delayDelimiter,omitempty"`
}
type Keymap map[string]Key
type Keyboard struct{}
const NONE = 0
const (
LCTRL byte = 1 << iota
LSHIFT
LALT
LSUPER
RCTRL
RSHIFT
RALT
RSUPER
)
const delayDelimiter = "⏲"
var (
currentKeyMap int
DefaultDelay time.Duration
KeymapOrder []string = []string{"qwerty"}
KeymapShortcut [8]byte
ErrOnUnknownKey bool
KeymapPath string
keys = make(map[string]Keymap)
flags = map[string]byte{
"LSHIFT": LSHIFT,
"LCTRL": LCTRL,
"LALT": LALT,
"LSUPER": LSUPER,
"RSHIFT": RSHIFT,
"RCTRL": RCTRL,
"RALT": RALT,
"RSUPER": RSUPER,
"NONE": NONE,
}
Hidg0 io.WriteCloser
)
func (k Keyboard) Write(p []byte) (n int, err error) {
return write(p)
}
func Write(r io.Reader) error {
_, err := io.Copy(Keyboard{}, r)
if closeCheck, ok := r.(io.Closer); ok {
closeCheck.Close()
}
return err
}
// io.writer probably isn't the best interface to use for this
func write(p []byte) (n int, err error) {
var index int
for index < len(p) {
var (
r rune
s int
flag byte
report [8]byte
)
press:
for i := 2; i < 8 && index < len(p); i++ {
var (
mod byte
)
r, s = utf8.DecodeRune(p[index:])
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
}
cur, ok := CurrentKeymap()[string(r)]
if !ok {
if i == 2 {
if !changeKeymap(r) && ErrOnUnknownKey {
return index, fmt.Errorf("rune not in keymap: %c", r)
}
} else {
break press // I should make a temp var to hold this Usage id to reduce unneeded processing
}
}
// Check if this is a delay
if cur.DelayDelimiter {
index += s + parseDelay(p[index:])
break press // I should make a temp var to hold this Usage id to reduce unneeded processing
}
// Calculate next modifier byte
for _, v := range cur.Modifier {
mod = mod | flags[v]
}
// Set the modifier if it is the firs key otherwise
// check if the next modifier byte is the same
if i == 2 {
flag = mod
} else if flag != mod {
break press // I should make a temp var to hold this Usage id to reduce unneeded processing
}
// Check for duplicate key press. You can't press a key if it is already pressed.
for u := 2; u < i; u++ {
if cur.Decimal == report[u] {
break press // I should make a temp var to hold this Usage id to reduce unneeded processing
}
}
report[i] = cur.Decimal
index += s
}
report[0] = flag
Press(report, Hidg0)
delay()
}
keymapto0() // To make it reproducible
return index, nil
}
func parseDelay(buffer []byte) int {
var index int
sb := strings.Builder{}
for index < len(buffer) {
r, s := utf8.DecodeRune(buffer[index:])
if unicode.IsDigit(r) {
sb.WriteRune(r)
index += s
} else {
break
}
}
i, err := strconv.Atoi(sb.String())
if err == nil || err == strconv.ErrRange {
DefaultDelay = time.Millisecond * time.Duration(i)
return index
}
return 0
}
func delay() {
if DefaultDelay > 0 {
if syncCheck, ok := Hidg0.(syncer); ok {
syncCheck.Sync()
}
time.Sleep(DefaultDelay)
DefaultDelay = 0
}
}
func Press(press [8]byte, file io.Writer) {
file.Write(press[:])
file.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
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
}
}
func changeKeymap(r rune) bool {
buf := bytes.NewBuffer(make([]byte, 0, 8*len(KeymapOrder))) // To batch shortcut presses
for i := 0; i < len(KeymapOrder); i++ {
if _, ok := CurrentKeymap()[string(r)]; ok {
Hidg0.Write(buf.Bytes())
return true
} else {
Press(KeymapShortcut, buf)
if currentKeyMap == len(KeymapOrder)-1 {
currentKeyMap = 0
} else {
currentKeyMap++
}
}
}
return false
}
func CurrentKeymap() Keymap {
if keymap, ok := keys[KeymapOrder[currentKeyMap]]; ok {
return keymap
} else {
return LoadKeymap(KeymapOrder[currentKeyMap])
}
}
func LoadKeymap(keymapName string) Keymap {
var (
err error
content []byte
tmp = make(Keymap, 0)
)
fmt.Println(path.Join(path.Join(KeymapPath, "hid"), keymapName+".json"))
content, err = ioutil.ReadFile(path.Join(path.Join(KeymapPath, "hid"), keymapName+".json"))
if err != nil {
return nil
}
err = json.Unmarshal(content, &tmp)
if err != nil {
return nil
}
//fmt.Println(strings.TrimSuffix(file.Name(), ext))
keys[keymapName] = tmp
return keys[keymapName]
}

217
main.go
View File

@ -1,217 +0,0 @@
package main
import (
"bufio"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"github.com/alexflint/go-arg"
)
type Key struct {
Modifier []string `json:"modifier"`
Decimal int `json:"decimal"`
}
type Keys map[string]Key
type Args struct {
SHORTCUT string `arg:"-S,help:Keymap cycle shortcut"`
ORDER []string `arg:"required,positional,help:Order of keymaps"`
}
const (
LCTRL byte = 1 << iota
LSHIFT
LALT
LSUPER
RCTRL
RSHIFT
RALT
RSUPER
)
func Press(press [8]byte, file io.Writer) {
binary.Write(file, binary.BigEndian, press[:])
binary.Write(file, binary.BigEndian, [8]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
func Hold(press [8]byte, file io.Writer) {
binary.Write(file, binary.BigEndian, press[:])
}
func keymapto0(args Args, hidg0 *os.File, currentKeyMap *int) {
if len(args.ORDER) > 1 {
for i := 0; i < len(args.ORDER)-(*currentKeyMap); i++ {
Press([8]byte{LALT, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00}, hidg0)
}
}
}
func changeKeymap(r rune, keys map[string]Keys, args Args, hidg0 *os.File, currentKeyMap *int) {
if len(args.ORDER) > 1 {
for i := 0; i < len(args.ORDER); i++ {
if keys[args.ORDER[(*currentKeyMap)]][string(r)].Decimal == 0 {
Press([8]byte{LALT, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00}, hidg0)
if *currentKeyMap == len(args.ORDER)-1 {
*currentKeyMap = 0
} else {
*currentKeyMap++
}
if i == len(args.ORDER)-1 {
fmt.Println("key not in keymap: " + string(r))
}
}
}
}
}
func main() {
var (
args Args
envExists bool
env string
hidg0 *os.File
err error
keymapsF []os.FileInfo
keys = make(map[string]Keys)
cfgPath string
stdin = bufio.NewReader(os.Stdin)
currentKeyMap int
flags = map[string]byte{
"LSHIFT": LSHIFT,
"LCTRL": LCTRL,
"LALT": LALT,
"LSUPER": LSUPER,
"RSHIFT": RSHIFT,
"RCTRL": RCTRL,
"RALT": RALT,
"RSUPER": RSUPER,
"NONE": 0,
}
)
arg.MustParse(&args)
env, envExists = os.LookupEnv("XDG_CONFIG_HOME")
if !envExists {
env = os.Getenv("HOME")
}
cfgPath = path.Join(env, "hid")
keymapsF, err = ioutil.ReadDir(cfgPath)
if err != nil {
panic(err)
}
fmt.Println(cfgPath)
hidg0, err = os.OpenFile("/dev/hidg0", os.O_WRONLY, os.ModePerm)
if err != nil {
panic(err)
}
for _, file := range keymapsF {
var (
ext string
)
ext = path.Ext(file.Name())
if strings.ToLower(ext) == ".json" {
var (
tmp Keys
T *os.File
content []byte
)
T, err = os.Open(path.Join(cfgPath, file.Name()))
if err != nil {
panic(err)
}
content, err = ioutil.ReadAll(T)
if err != nil {
panic(err)
}
err = json.Unmarshal(content, &tmp)
if err != nil {
panic(err)
}
//fmt.Println(strings.TrimSuffix(file.Name(), ext))
keys[strings.TrimSuffix(file.Name(), ext)] = tmp
T.Close()
}
}
//fmt.Println(keys)
cnt := 0
for {
var (
r rune
flag byte
report [6]byte
)
r, _, err = stdin.ReadRune()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
changeKeymap(r, keys, args, hidg0, &currentKeyMap)
report[0] = uint8(keys[args.ORDER[currentKeyMap]][string(r)].Decimal)
for _, v := range keys[args.ORDER[currentKeyMap]][string(r)].Modifier {
flag = flag | flags[v]
}
for i := 1; i < 6; i++ {
var (
mod byte
rn rune
)
rn, _, err = stdin.ReadRune()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
for _, v := range keys[args.ORDER[currentKeyMap]][string(rn)].Modifier {
mod = mod | flags[v]
}
uniq := true
for u := 0; u < i; u++ {
if uint8(keys[args.ORDER[currentKeyMap]][string(rn)].Decimal) == report[u] {
uniq = false
break
}
}
if keys[args.ORDER[(currentKeyMap)]][string(rn)].Decimal != 0 && mod == flag && uniq {
report[i] = uint8(keys[args.ORDER[currentKeyMap]][string(rn)].Decimal)
} else {
stdin.UnreadRune()
break
}
}
changeKeymap(r, keys, args, hidg0, &currentKeyMap)
Press([8]byte{flag, 0, report[0], report[1], report[2], report[3], report[4], report[5]}, hidg0)
flag = 0
cnt++
}
keymapto0(args, hidg0, &currentKeyMap)
fmt.Println("Success!")
hidg0.Close()
}