From 9d04ddc8beef976db6c598b80803a762273ca67b Mon Sep 17 00:00:00 2001 From: lordwelch Date: Fri, 22 Jun 2018 20:28:21 -0700 Subject: [PATCH] Restructure to library Implemented io.Writer interface. Might want to use a different interface add a delay character --- cmd/main.go | 43 ++++++++++ keyboard.go | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 217 ---------------------------------------------- 3 files changed, 285 insertions(+), 217 deletions(-) create mode 100644 cmd/main.go create mode 100644 keyboard.go delete mode 100644 main.go diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..23389e0 --- /dev/null +++ b/cmd/main.go @@ -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!") +} diff --git a/keyboard.go b/keyboard.go new file mode 100644 index 0000000..eb705a4 --- /dev/null +++ b/keyboard.go @@ -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] +} diff --git a/main.go b/main.go deleted file mode 100644 index e7a4de8..0000000 --- a/main.go +++ /dev/null @@ -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, ¤tKeyMap) - 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, ¤tKeyMap) - Press([8]byte{flag, 0, report[0], report[1], report[2], report[3], report[4], report[5]}, hidg0) - flag = 0 - cnt++ - } - keymapto0(args, hidg0, ¤tKeyMap) - fmt.Println("Success!") - hidg0.Close() - -}