mirror of
https://github.com/qmk/qmk_firmware.git
synced 2024-12-15 06:10:53 +00:00
299 lines
6.1 KiB
Go
299 lines
6.1 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"io/ioutil"
|
||
|
"fmt"
|
||
|
"encoding/json"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"sort"
|
||
|
"hash/crc64"
|
||
|
//"encoding/base64"
|
||
|
)
|
||
|
|
||
|
func main() {
|
||
|
// Show Usage
|
||
|
if len(os.Args) < 3 {
|
||
|
fmt.Println("Usage: ./keymap-gen src-dir out-dir")
|
||
|
fmt.Println("Outputs c files in out-dir")
|
||
|
fmt.Println("Make sure the dirs exist.")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
files, err := filepath.Glob(os.Args[1] + "/*.json")
|
||
|
if err != nil {
|
||
|
fmt.Printf("Could not open src-dir: %v\n", err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for _, fname := range(files) {
|
||
|
fmt.Println("Processing: ", fname)
|
||
|
// Read the source
|
||
|
data, err := ioutil.ReadFile(fname)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
|
||
|
// Unbundle Data
|
||
|
var FullDict map[string]Entry
|
||
|
json.Unmarshal(data, &FullDict)
|
||
|
|
||
|
// Loop over entries and store
|
||
|
var output []string
|
||
|
for i,v := range FullDict {
|
||
|
if i == "0" {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Special handling for numbermap
|
||
|
var entry string
|
||
|
if strings.Contains(fname, "num") {
|
||
|
entry = v.toKeymap("NUM|")
|
||
|
} else {
|
||
|
entry = v.toKeymap("")
|
||
|
}
|
||
|
|
||
|
if entry != "" {
|
||
|
output = append(output, entry)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sort by length, then amount of whitespace lol
|
||
|
sort.Slice(output, func (i,j int) bool {
|
||
|
var maxLen int
|
||
|
if len(output[i]) > len(output[j]) {
|
||
|
maxLen = len(output[i])
|
||
|
} else {
|
||
|
maxLen = len(output[j])
|
||
|
}
|
||
|
|
||
|
return maxLen-strings.Count(output[i][:40], " ") < maxLen-strings.Count(output[j][:40], " ")
|
||
|
})
|
||
|
|
||
|
// Whack a disclaimer
|
||
|
output = append([]string{"// This file is automatically generated. Do not edit it!\n\n"}, output...)
|
||
|
|
||
|
// Write all data out
|
||
|
outName := filepath.Base(fname)
|
||
|
outName = os.Args[2] + outName[:len(outName)-5]+".def"
|
||
|
fmt.Println("Saving: ", outName)
|
||
|
ioutil.WriteFile(outName, []byte(strings.Join(output, "")), 0755)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (e Entry) toKeymap(prefix string) (string) {
|
||
|
// storage for parts
|
||
|
var command, chord, arg string
|
||
|
wordInfo := parseWords(e)
|
||
|
|
||
|
// Handle prefix
|
||
|
if prefix != "" {
|
||
|
chord = prefix
|
||
|
}
|
||
|
|
||
|
// Format the chord
|
||
|
keys := []string{"AA", "AS", "AE", "AT", "AN", "AI", "AO", "AP"}
|
||
|
for i, v := range e.Input {
|
||
|
chord += keys[v-1]
|
||
|
|
||
|
if i != len(e.Input)-1 {
|
||
|
chord += "|"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Handle specials/base first
|
||
|
var ok bool
|
||
|
var v []string
|
||
|
|
||
|
if e.Special != "" {
|
||
|
v, ok = QMKLookup[e.Special]
|
||
|
}
|
||
|
if !ok && e.Base != "" {
|
||
|
v, ok = QMKLookup[e.Base]
|
||
|
}
|
||
|
|
||
|
if ok {
|
||
|
// Determine way to send key
|
||
|
if len(v) == 1 {
|
||
|
command = "PRES("
|
||
|
} else {
|
||
|
command = "KEYS("
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ok {
|
||
|
if len(v) > 1 {
|
||
|
arg += "{"
|
||
|
}
|
||
|
|
||
|
// String together args
|
||
|
for ii, vv := range(v) {
|
||
|
arg += vv
|
||
|
if ii == len(v)-1 && len(v) > 1 {
|
||
|
arg += ", COMBO_END}"
|
||
|
} else if ii != len(v)-1 {
|
||
|
arg += ", "
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
hash := crc64.Checksum([]byte(fmt.Sprintf("%v%v", arg, chord)), crc64.MakeTable(crc64.ECMA))
|
||
|
hashStr := fmt.Sprintf("cmb_%x", hash)
|
||
|
wordSpacer := strings.Repeat(" ", 42-len(arg))
|
||
|
if command == "KEYS(" {
|
||
|
arg = fmt.Sprintf("%v, %v %v", hashStr, wordSpacer, arg)
|
||
|
} else {
|
||
|
arg = fmt.Sprintf("%65v", arg)
|
||
|
}
|
||
|
|
||
|
goto Found
|
||
|
}
|
||
|
|
||
|
// Parse out word info
|
||
|
if wordInfo.LRank == 0 && wordInfo.RRank == 0 {
|
||
|
goto Blank
|
||
|
}
|
||
|
|
||
|
if wordInfo.LRank != 0 || wordInfo.RRank != 0 {
|
||
|
if wordInfo.LRank != 0 && wordInfo.RRank != 0 {
|
||
|
// Just blank the structure and recall
|
||
|
left, right := e, e
|
||
|
left.Trw = nil
|
||
|
right.Tlw = nil
|
||
|
return fmt.Sprintf("%v%v", left.toKeymap("LFT|"), right.toKeymap("RGT|"))
|
||
|
}
|
||
|
|
||
|
var word string
|
||
|
if wordInfo.LRank > wordInfo.RRank {
|
||
|
word = wordInfo.LWord
|
||
|
} else {
|
||
|
word = wordInfo.RWord
|
||
|
}
|
||
|
|
||
|
// Add in thumb
|
||
|
chord = "AR|" + chord
|
||
|
|
||
|
|
||
|
// generate function name
|
||
|
hash := crc64.Checksum([]byte(word), crc64.MakeTable(crc64.ECMA))
|
||
|
hashStr := fmt.Sprintf("str_%016X", hash)
|
||
|
command = "SUBS("
|
||
|
wordSpacer := strings.Repeat(" ", 40-len(word))
|
||
|
arg = fmt.Sprintf("%v, %v \"%v \"", hashStr, wordSpacer, word)
|
||
|
goto Found
|
||
|
}
|
||
|
|
||
|
panic(e.String())
|
||
|
|
||
|
Found:
|
||
|
chord += ","
|
||
|
return fmt.Sprintf("%v%-35v%v)\n", command, chord, arg)
|
||
|
|
||
|
Blank:
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
type Entry struct {
|
||
|
Input []int
|
||
|
Base string
|
||
|
Tlw []interface{}
|
||
|
Trw []interface{}
|
||
|
Special string
|
||
|
}
|
||
|
type Word struct {
|
||
|
LWord string
|
||
|
LRank float64
|
||
|
RWord string
|
||
|
RRank float64
|
||
|
}
|
||
|
|
||
|
func parseWords(e Entry) (ret Word) {
|
||
|
if len(e.Tlw) > 0 {
|
||
|
ret.LWord = e.Tlw[0].(string)
|
||
|
ret.LRank= e.Tlw[1].(float64)
|
||
|
}
|
||
|
if len(e.Trw) > 0 {
|
||
|
ret.RWord = e.Trw[0].(string)
|
||
|
ret.RRank= e.Trw[1].(float64)
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
func (e Entry) String() (ret string) {
|
||
|
ret = fmt.Sprintln("Input: ", e.Input)
|
||
|
ret += fmt.Sprintln("Base: ", e.Base)
|
||
|
ret += fmt.Sprintln("Left: ", e.Tlw)
|
||
|
ret += fmt.Sprintln("Right: ", e.Trw)
|
||
|
ret += fmt.Sprintln("Special: ", e.Special)
|
||
|
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
var QMKLookup = map[string][]string {
|
||
|
"!":[]string{"KC_LSFT", "KC_1"},
|
||
|
"'":[]string{"KC_QUOT"},
|
||
|
"(":[]string{"KC_LSFT", "KC_9"},
|
||
|
")":[]string{"KC_LSFT", "KC_0"},
|
||
|
",":[]string{"KC_COMM"},
|
||
|
"-":[]string{"KC_MINS"},
|
||
|
".":[]string{"KC_DOT"},
|
||
|
";":[]string{"KC_SCLN"},
|
||
|
"?":[]string{"KC_QUOT"},
|
||
|
"a":[]string{"KC_A"},
|
||
|
"b":[]string{"KC_B"},
|
||
|
"c":[]string{"KC_C"},
|
||
|
"d":[]string{"KC_D"},
|
||
|
"e":[]string{"KC_E"},
|
||
|
"f":[]string{"KC_F"},
|
||
|
"g":[]string{"KC_G"},
|
||
|
"h":[]string{"KC_H"},
|
||
|
"i":[]string{"KC_I"},
|
||
|
"j":[]string{"KC_J"},
|
||
|
"k":[]string{"KC_K"},
|
||
|
"l":[]string{"KC_L"},
|
||
|
"m":[]string{"KC_M"},
|
||
|
"n":[]string{"KC_N"},
|
||
|
"o":[]string{"KC_O"},
|
||
|
"p":[]string{"KC_P"},
|
||
|
"q":[]string{"KC_Q"},
|
||
|
"r":[]string{"KC_R"},
|
||
|
"s":[]string{"KC_S"},
|
||
|
"t":[]string{"KC_T"},
|
||
|
"u":[]string{"KC_U"},
|
||
|
"v":[]string{"KC_V"},
|
||
|
"w":[]string{"KC_W"},
|
||
|
"x":[]string{"KC_X"},
|
||
|
"y":[]string{"KC_Y"},
|
||
|
"z":[]string{"KC_Z"},
|
||
|
|
||
|
//specials
|
||
|
"bksp":[]string{"KC_BSPC"},
|
||
|
"enter":[]string{"KC_ENT"},
|
||
|
//"numsym":[]string{"NUM)"}, //TODO: Sticky
|
||
|
//"LETTERS":[]string{"KC_SPC"},
|
||
|
|
||
|
//symbols
|
||
|
"[":[]string{"KC_LBRC"},
|
||
|
"]":[]string{"KC_RBRC"},
|
||
|
" ":[]string{"KC_SPC"},
|
||
|
"1":[]string{"KC_1"},
|
||
|
"2":[]string{"KC_2"},
|
||
|
"3":[]string{"KC_3"},
|
||
|
"4":[]string{"KC_4"},
|
||
|
"5":[]string{"KC_5"},
|
||
|
"6":[]string{"KC_6"},
|
||
|
"7":[]string{"KC_7"},
|
||
|
"8":[]string{"KC_8"},
|
||
|
"9":[]string{"KC_9"},
|
||
|
"0":[]string{"KC_0"},
|
||
|
"=":[]string{"KC_EQL"},
|
||
|
"Fn":[]string{"KC_NO"},
|
||
|
"SPACE":[]string{"KC_SPC"},
|
||
|
"Home":[]string{"KC_HOME"},
|
||
|
"End":[]string{"KC_END"},
|
||
|
" ":[]string{"KC_TAB"},
|
||
|
" ":[]string{"KC_TAB"},
|
||
|
"\t":[]string{"KC_TAB"},
|
||
|
"`":[]string{"KC_GRV"},
|
||
|
}
|