Restructure to library
Implemented io.Writer interface. Might want to use a different interface add a delay character
This commit is contained in:
parent
394b44e9df
commit
9d04ddc8be
43
cmd/main.go
Normal file
43
cmd/main.go
Normal 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
242
keyboard.go
Normal 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
217
main.go
@ -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()
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user