cleanup
This commit is contained in:
parent
43e1cb309d
commit
4927d4de7a
|
@ -1,423 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
|
||||||
# Released under The MIT License (MIT) (see "license.md")
|
|
||||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
"""
|
|
||||||
Generate a depiction of the layout (in html + svg)
|
|
||||||
|
|
||||||
Depends on:
|
|
||||||
- the UI info file (in JSON)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class Namespace():
|
|
||||||
pass
|
|
||||||
|
|
||||||
template = Namespace()
|
|
||||||
doc = Namespace()
|
|
||||||
info = Namespace()
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def main():
|
|
||||||
arg_parser = argparse.ArgumentParser(
|
|
||||||
description = "Generate a picture of the firmware's "
|
|
||||||
+ "keyboard layout" )
|
|
||||||
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--ui-info-file',
|
|
||||||
required = True )
|
|
||||||
|
|
||||||
args = arg_parser.parse_args(sys.argv[1:])
|
|
||||||
|
|
||||||
# constant file paths
|
|
||||||
args.template_svg_file = './build-scripts/gen_layout/template.svg'
|
|
||||||
args.template_js_file = './build-scripts/gen_layout/template.js'
|
|
||||||
|
|
||||||
# normalize paths
|
|
||||||
args.ui_info_file = os.path.abspath(args.ui_info_file)
|
|
||||||
args.template_svg_file = os.path.abspath(args.template_svg_file)
|
|
||||||
args.template_js_file = os.path.abspath(args.template_js_file)
|
|
||||||
|
|
||||||
# set vars
|
|
||||||
doc.main = '' # to store the html document we're generating
|
|
||||||
template.svg = open(args.template_svg_file).read()
|
|
||||||
template.js = open(args.template_js_file).read()
|
|
||||||
info.all = json.loads(open(args.ui_info_file).read())
|
|
||||||
|
|
||||||
info.matrix_positions = info.all['mappings']['matrix-positions']
|
|
||||||
info.matrix_layout = info.all['mappings']['matrix-layout']
|
|
||||||
|
|
||||||
# prefix
|
|
||||||
doc.prefix = ("""
|
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<script>
|
|
||||||
"""
|
|
||||||
+ template.js +
|
|
||||||
""" </script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h1>Firmware Layout</h1>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>git commit date:
|
|
||||||
<code>""" + info.all['miscellaneous']['git-commit-date'] + """</code></li>
|
|
||||||
<li>git commit id:
|
|
||||||
<code>""" + info.all['miscellaneous']['git-commit-id'] + """</code></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Notes</h2>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Layer keys are labeled e.g. <code>la 2 +- 2</code>
|
|
||||||
<ul>
|
|
||||||
<li><code>la</code> is for "layer"</li>
|
|
||||||
<li><code>2</code> indicates that the second pair of push|pop functions is
|
|
||||||
being used (differently numbered pairs won't interfere with each
|
|
||||||
other)</li>
|
|
||||||
<li><code>+</code> indicates that the layer is being "pushed" onto the
|
|
||||||
stack at some point, either when the key is pressed or when it is
|
|
||||||
released</li>
|
|
||||||
<li><code>-</code> indicates that the layer is being "popped" off of the
|
|
||||||
stack at some point</li>
|
|
||||||
<li>the last <code>2</code> indicates the layer-number that will be
|
|
||||||
activated on "push".</li>
|
|
||||||
</ul>
|
|
||||||
See the project 'readme.md' file on <a
|
|
||||||
href='https://github.com/benblazak/ergodox-firmware'>the github page</a> as a
|
|
||||||
starting point for learning more about how this firmware implements
|
|
||||||
layers.</li>
|
|
||||||
<br>
|
|
||||||
<li>Shifted keys are labeled with an <code>sh</code> at the beginning. This
|
|
||||||
indicates that a 'Shift' is generated with that keypress, the same as if you
|
|
||||||
had held down 'Shift', and pressed that key.</li>
|
|
||||||
<br>
|
|
||||||
<li><code>(null)</code> indicates that no keypress or keyrelease will be
|
|
||||||
generated for that key, on that layer.</li>
|
|
||||||
<br>
|
|
||||||
<li>Blank keys indicate "transparency": if you press that key on that layer,
|
|
||||||
the key will act as if it was on whatever layer is active below the current
|
|
||||||
one.</li>
|
|
||||||
<br>
|
|
||||||
<li>Some keys may be labled with special functions (like
|
|
||||||
<code>[btldr]</code>, which tells the Teensy to wait for the host to send it
|
|
||||||
a new firmware).</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
""")[1:-1]
|
|
||||||
|
|
||||||
# suffix
|
|
||||||
doc.suffix = ("""
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
""")[1:-1]
|
|
||||||
|
|
||||||
# substitute into template
|
|
||||||
# -------
|
|
||||||
# note: this is not general enough to handle any possible layout well, at
|
|
||||||
# the moment. but it should handle more standard ones well. (hopefully
|
|
||||||
# minor) modifications may be necessary on a case by case basis
|
|
||||||
# -------
|
|
||||||
layer_number = -1
|
|
||||||
for (layout, layer) in zip( info.matrix_layout,
|
|
||||||
range(len(info.matrix_layout))):
|
|
||||||
layer_number += 1
|
|
||||||
svg = template.svg
|
|
||||||
for (name, (code, press, release)) \
|
|
||||||
in zip(info.matrix_positions, layout):
|
|
||||||
replace = ''
|
|
||||||
if press == 'kbfun_transparent':
|
|
||||||
replace = ''
|
|
||||||
elif press == 'kbfun_shift_press_release':
|
|
||||||
replace = 'sh ' + keycode_to_string.get(code, '[n/a]')
|
|
||||||
elif press == 'kbfun_jump_to_bootloader':
|
|
||||||
replace = '[btldr]'
|
|
||||||
elif press == 'NULL' and release == 'NULL':
|
|
||||||
replace = '(null)'
|
|
||||||
elif re.search(r'numpad', press+release):
|
|
||||||
replace = '[num]'
|
|
||||||
elif re.search(r'layer', press+release):
|
|
||||||
replace = 'la ' + re.findall(r'\d+', press+release)[0] + ' '
|
|
||||||
if re.search(r'push', press+release):
|
|
||||||
replace += '+'
|
|
||||||
if re.search(r'pop', press+release):
|
|
||||||
replace += '-'
|
|
||||||
replace += ' ' + str(code)
|
|
||||||
else:
|
|
||||||
replace = keycode_to_string.get(code, '[n/a]')
|
|
||||||
|
|
||||||
svg = re.sub(
|
|
||||||
'>'+name+'<', '>'+replace+'<', svg )
|
|
||||||
svg = re.sub(
|
|
||||||
r"\('(" + name + r".*)'\)",
|
|
||||||
r"('\1', " + str(layer) + r")",
|
|
||||||
svg )
|
|
||||||
|
|
||||||
doc.main += '<h2>Layer ' + str(layer_number) + '</h2>\n' + svg
|
|
||||||
|
|
||||||
# change the font size
|
|
||||||
doc.main = re.sub(r'22.5px', '15px', doc.main)
|
|
||||||
|
|
||||||
print(doc.prefix + doc.main + doc.suffix)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
keycode_to_string = {
|
|
||||||
0x01: "Error", # ErrorRollOver
|
|
||||||
0x02: "POSTFail",
|
|
||||||
0x03: "Error", # ErrorUndefined
|
|
||||||
0x04: "a A",
|
|
||||||
0x05: "b B",
|
|
||||||
0x06: "c C",
|
|
||||||
0x07: "d D",
|
|
||||||
0x08: "e E",
|
|
||||||
0x09: "f F",
|
|
||||||
0x0A: "g G",
|
|
||||||
0x0B: "h H",
|
|
||||||
0x0C: "i I",
|
|
||||||
0x0D: "j J",
|
|
||||||
0x0E: "k K",
|
|
||||||
0x0F: "l L",
|
|
||||||
0x10: "m M",
|
|
||||||
0x11: "n N",
|
|
||||||
0x12: "o O",
|
|
||||||
0x13: "p P",
|
|
||||||
0x14: "q Q",
|
|
||||||
0x15: "r R",
|
|
||||||
0x16: "s S",
|
|
||||||
0x17: "t T",
|
|
||||||
0x18: "u U",
|
|
||||||
0x19: "v V",
|
|
||||||
0x1A: "w W",
|
|
||||||
0x1B: "x X",
|
|
||||||
0x1C: "y Y",
|
|
||||||
0x1D: "z Z",
|
|
||||||
0x1E: "1 !",
|
|
||||||
0x1F: "2 @",
|
|
||||||
0x20: "3 #",
|
|
||||||
0x21: "4 $",
|
|
||||||
0x22: "5 %",
|
|
||||||
0x23: "6 ^",
|
|
||||||
0x24: "7 &",
|
|
||||||
0x25: "8 *",
|
|
||||||
0x26: "9 (",
|
|
||||||
0x27: "0 )",
|
|
||||||
0x28: "Return",
|
|
||||||
0x29: "Esc",
|
|
||||||
0x2A: "Backspace",
|
|
||||||
0x2B: "Tab",
|
|
||||||
0x2C: "Space",
|
|
||||||
0x2D: "- _",
|
|
||||||
0x2E: "= +",
|
|
||||||
0x2F: "[ {",
|
|
||||||
0x30: "] }",
|
|
||||||
0x31: "\ |",
|
|
||||||
0x32: "# ~",
|
|
||||||
0x33: "; :",
|
|
||||||
0x34: "\' \"",
|
|
||||||
0x35: "` ~",
|
|
||||||
0x36: ", <",
|
|
||||||
0x37: ". >",
|
|
||||||
0x38: "/ ?",
|
|
||||||
0x39: "Caps",
|
|
||||||
0x3A: "F1",
|
|
||||||
0x3B: "F2",
|
|
||||||
0x3C: "F3",
|
|
||||||
0x3D: "F4",
|
|
||||||
0x3E: "F5",
|
|
||||||
0x3F: "F6",
|
|
||||||
0x40: "F7",
|
|
||||||
0x41: "F8",
|
|
||||||
0x42: "F9",
|
|
||||||
0x43: "F10",
|
|
||||||
0x44: "F11",
|
|
||||||
0x45: "F12",
|
|
||||||
0x46: "PrintScreen",
|
|
||||||
0x47: "ScrollLock",
|
|
||||||
0x48: "Pause",
|
|
||||||
0x49: "Ins", # Insert
|
|
||||||
0x4A: "Hm", # Home
|
|
||||||
0x4B: "Pg\u2191", # up arrow
|
|
||||||
0x4C: "Delete",
|
|
||||||
0x4D: "End",
|
|
||||||
0x4E: "Pg\u2193", # down arrow
|
|
||||||
0x4F: "\u2192", # right arrow
|
|
||||||
0x50: "\u2190", # left arrow
|
|
||||||
0x51: "\u2193", # down arrow
|
|
||||||
0x52: "\u2191", # up arrow
|
|
||||||
|
|
||||||
0x53: "Num",
|
|
||||||
0x54: "/",
|
|
||||||
0x55: "*",
|
|
||||||
0x56: "-",
|
|
||||||
0x57: "+",
|
|
||||||
0x58: "Enter",
|
|
||||||
0x59: "1 End",
|
|
||||||
0x5A: "2 \u2193", # down arrow
|
|
||||||
0x5B: "3 Pg\u2193", # down arrow
|
|
||||||
0x5C: "4 \u2190", # left arrow
|
|
||||||
0x5D: "5",
|
|
||||||
0x5E: "6 \u2192", # right arrow
|
|
||||||
0x5F: "7 Hm", # Home
|
|
||||||
0x60: "8 \u2191", # up arrow
|
|
||||||
0x61: "9 Pg\u2191", # up arrow
|
|
||||||
0x62: "0 Ins", # Insert
|
|
||||||
0x63: ". Del",
|
|
||||||
|
|
||||||
0x64: "\ |",
|
|
||||||
0x65: "App",
|
|
||||||
0x66: "Power",
|
|
||||||
|
|
||||||
0x67: "=",
|
|
||||||
|
|
||||||
0x68: "F13",
|
|
||||||
0x69: "F14",
|
|
||||||
0x6A: "F15",
|
|
||||||
0x6B: "F16",
|
|
||||||
0x6C: "F17",
|
|
||||||
0x6D: "F18",
|
|
||||||
0x6E: "F19",
|
|
||||||
0x6F: "F20",
|
|
||||||
0x70: "F21",
|
|
||||||
0x71: "F22",
|
|
||||||
0x72: "F23",
|
|
||||||
0x73: "F24",
|
|
||||||
0x74: "Exec",
|
|
||||||
0x75: "Help",
|
|
||||||
0x76: "Menu",
|
|
||||||
0x77: "Select",
|
|
||||||
0x78: "Stop",
|
|
||||||
0x79: "Again",
|
|
||||||
0x7A: "Undo",
|
|
||||||
0x7B: "Cut",
|
|
||||||
0x7C: "Copy",
|
|
||||||
0x7D: "Paste",
|
|
||||||
0x7E: "Find",
|
|
||||||
0x7F: "Mute",
|
|
||||||
0x80: "VolUp",
|
|
||||||
0x81: "VolDown",
|
|
||||||
0x82: "LockingCapsLock",
|
|
||||||
0x83: "LockingNumLock",
|
|
||||||
0x84: "LockingScrollLock",
|
|
||||||
|
|
||||||
0x85: ",",
|
|
||||||
0x86: "=",
|
|
||||||
|
|
||||||
0x87: "Int1",
|
|
||||||
0x88: "Int2",
|
|
||||||
0x89: "Int3",
|
|
||||||
0x8A: "Int4",
|
|
||||||
0x8B: "Int5",
|
|
||||||
0x8C: "Int6",
|
|
||||||
0x8D: "Int7",
|
|
||||||
0x8E: "Int8",
|
|
||||||
0x8F: "Int9",
|
|
||||||
0x90: "LANG1",
|
|
||||||
0x91: "LANG2",
|
|
||||||
0x92: "LANG3",
|
|
||||||
0x93: "LANG4",
|
|
||||||
0x94: "LANG5",
|
|
||||||
0x95: "LANG6",
|
|
||||||
0x96: "LANG7",
|
|
||||||
0x97: "LANG8",
|
|
||||||
0x98: "LANG9",
|
|
||||||
0x99: "AlternateErase",
|
|
||||||
0x9A: "SysReq_Attention",
|
|
||||||
0x9B: "Cancel",
|
|
||||||
0x9C: "Clear",
|
|
||||||
0x9D: "Prior",
|
|
||||||
0x9E: "Return",
|
|
||||||
0x9F: "Separator",
|
|
||||||
0xA0: "Out",
|
|
||||||
0xA1: "Oper",
|
|
||||||
0xA2: "Clear_Again",
|
|
||||||
0xA3: "CrSel_Props",
|
|
||||||
0xA4: "ExSel",
|
|
||||||
|
|
||||||
0xB0: "00",
|
|
||||||
0xB1: "000",
|
|
||||||
|
|
||||||
0xB2: "Thousands_Sep",
|
|
||||||
0xB3: "Decimal_Sep",
|
|
||||||
0xB4: "$",
|
|
||||||
0xB5: "Currency_Subunit",
|
|
||||||
|
|
||||||
0xB6: "(",
|
|
||||||
0xB7: ")",
|
|
||||||
0xB8: "{",
|
|
||||||
0xB9: "}",
|
|
||||||
|
|
||||||
0xBA: "Tab",
|
|
||||||
0xBB: "Backspace",
|
|
||||||
0xBC: "A",
|
|
||||||
0xBD: "B",
|
|
||||||
0xBE: "C",
|
|
||||||
0xBF: "D",
|
|
||||||
0xC0: "E",
|
|
||||||
0xC1: "F",
|
|
||||||
0xC2: "XOR",
|
|
||||||
0xC3: "^",
|
|
||||||
0xC4: "%",
|
|
||||||
0xC5: "<",
|
|
||||||
0xC6: ">",
|
|
||||||
0xC7: "&",
|
|
||||||
0xC8: "&&",
|
|
||||||
0xC9: "|",
|
|
||||||
0xCA: "||",
|
|
||||||
0xCB: ":",
|
|
||||||
0xCC: "#",
|
|
||||||
0xCD: "Space",
|
|
||||||
0xCE: "@",
|
|
||||||
0xCF: "!",
|
|
||||||
0xD0: "Mem_Store",
|
|
||||||
0xD1: "Mem_Recall",
|
|
||||||
0xD2: "Mem_Clear",
|
|
||||||
0xD3: "Mem_+",
|
|
||||||
0xD4: "Mem_-",
|
|
||||||
0xD5: "Mem_*",
|
|
||||||
0xD6: "Mem_/",
|
|
||||||
0xD7: "+-",
|
|
||||||
0xD8: "Clear",
|
|
||||||
0xD9: "ClearEntry",
|
|
||||||
0xDA: "Binary",
|
|
||||||
0xDB: "Octal",
|
|
||||||
0xDC: ".",
|
|
||||||
0xDD: "Hexadecimal",
|
|
||||||
|
|
||||||
0xE0: "L-Ctrl",
|
|
||||||
0xE1: "L-Shift",
|
|
||||||
0xE2: "L-Alt",
|
|
||||||
0xE3: "L-GUI",
|
|
||||||
0xE4: "R-Ctrl",
|
|
||||||
0xE5: "R-Shift",
|
|
||||||
0xE6: "R-Alt",
|
|
||||||
0xE7: "R-GUI",
|
|
||||||
}
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,477 +0,0 @@
|
||||||
#! /usr/bin/env python3
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
|
|
||||||
# Released under The MIT License (MIT) (see "license.md")
|
|
||||||
# Project located at <https://github.com/benblazak/ergodox-firmware>
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
"""
|
|
||||||
Generate UI info file (in JSON)
|
|
||||||
|
|
||||||
Depends on:
|
|
||||||
- the project source code
|
|
||||||
- the project '.map' file (generated by the compiler)
|
|
||||||
"""
|
|
||||||
|
|
||||||
_FORMAT_DESCRIPTION = ("""
|
|
||||||
/* ----------------------------------------------------------------------------
|
|
||||||
* Version 0
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
* Hopefully the add-hoc conventions are clear enough... I didn't feel like
|
|
||||||
* investing the time in making it a real JSON Schema when there aren't many
|
|
||||||
* validators, and the most current completed draft at the moment (draft 3) is
|
|
||||||
* expired...
|
|
||||||
* ----------------------------------------------------------------------------
|
|
||||||
* Please note that in general, fields may be added without changing the
|
|
||||||
* version number, and that programs using this format are not required to fill
|
|
||||||
* (or read) any of the given fields.
|
|
||||||
* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
var ui_info = {
|
|
||||||
".meta-data": { // for the JSON file
|
|
||||||
"version": "<number>",
|
|
||||||
"date-generated": "<string>", // format: RFC 3339
|
|
||||||
"description": "<string>",
|
|
||||||
},
|
|
||||||
"keyboard-functions": {
|
|
||||||
"<(function name)>": {
|
|
||||||
"position": "<number>", // as given by the .map file
|
|
||||||
"length": "<number>", // as given by the .map file
|
|
||||||
"comments": {
|
|
||||||
"name": "<string>", // more user friendly name
|
|
||||||
"description": "<string>",
|
|
||||||
"notes": [
|
|
||||||
"<string>",
|
|
||||||
"..."
|
|
||||||
],
|
|
||||||
"..."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"..."
|
|
||||||
},
|
|
||||||
"layout-matrices": {
|
|
||||||
"<(matrix name)>": {
|
|
||||||
"position": "<number>", // as given by the .map file
|
|
||||||
"length": "<number>" // as given by the .map file
|
|
||||||
},
|
|
||||||
"..."
|
|
||||||
},
|
|
||||||
"mappings": {
|
|
||||||
/*
|
|
||||||
* The mappings prefixed with 'matrix' have their elements in the same
|
|
||||||
* order as the .hex file (whatever order that is). The mappings
|
|
||||||
* prefixed with 'physical' will have their elements in an order
|
|
||||||
* corresponding to thier physical position on the keyboard. You can
|
|
||||||
* convert between the two using the relative positions of the key-ids
|
|
||||||
* in 'physical-positions' and 'matrix-positions'.
|
|
||||||
*
|
|
||||||
* The current order of 'physical' mappings is:
|
|
||||||
* --------------------------------------------
|
|
||||||
* // left hand, spatial positions
|
|
||||||
* 00, 01, 02, 03, 04, 05, 06,
|
|
||||||
* 07, 08, 09, 10, 11, 12, 13,
|
|
||||||
* 14, 15, 16, 17, 18, 19,
|
|
||||||
* 20, 21, 22, 23, 24, 25, 26,
|
|
||||||
* 27, 28, 29, 30, 31,
|
|
||||||
* 32, 33,
|
|
||||||
* 34, 35, 36,
|
|
||||||
* 37, 38, 39,
|
|
||||||
|
|
||||||
* // right hand, spatial positions
|
|
||||||
* 40, 41, 42, 43, 44, 45, 46,
|
|
||||||
* 47, 48, 49, 50, 51, 52, 53,
|
|
||||||
* 54, 55, 56, 57, 58, 59,
|
|
||||||
* 60, 61, 62, 63, 64, 65, 66,
|
|
||||||
* 67, 68, 69, 70, 71,
|
|
||||||
* 72, 73,
|
|
||||||
* 74, 75, 76,
|
|
||||||
* 77, 78, 79,
|
|
||||||
* --------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
"physical-positions": [ // list of key-ids
|
|
||||||
"<string>", "..."
|
|
||||||
],
|
|
||||||
"matrix-positions": [ // list of key-ids
|
|
||||||
"<string>", "..."
|
|
||||||
],
|
|
||||||
"matrix-layout": [
|
|
||||||
[ // begin layer
|
|
||||||
[ // begin key
|
|
||||||
"<number>", // keycode
|
|
||||||
"<string>", // press function name (ex: 'kbfun_...')
|
|
||||||
"<string>" // release function name (ex: 'NULL')
|
|
||||||
],
|
|
||||||
"..." // more keys
|
|
||||||
],
|
|
||||||
"..." // more layers
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"miscellaneous": {
|
|
||||||
"git-commit-date": "<string>", // format: RFC 3339
|
|
||||||
"git-commit-id": "<string>",
|
|
||||||
"number-of-layers": "<number>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""")[1:-1]
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def gen_static(current_date=None, git_commit_date=None, git_commit_id=None):
|
|
||||||
"""Generate static information"""
|
|
||||||
|
|
||||||
return {
|
|
||||||
'.meta-data': {
|
|
||||||
'version': 0, # the format version number
|
|
||||||
'date-generated': current_date,
|
|
||||||
'description': _FORMAT_DESCRIPTION,
|
|
||||||
},
|
|
||||||
'miscellaneous': {
|
|
||||||
'git-commit-date': git_commit_date, # should be passed by makefile
|
|
||||||
'git-commit-id': git_commit_id, # should be passed by makefile
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def gen_derived(data):
|
|
||||||
return {} # don't really need this info anymore
|
|
||||||
# """
|
|
||||||
# Generate derived information
|
|
||||||
# Should be called last
|
|
||||||
# """
|
|
||||||
# return {
|
|
||||||
# 'miscellaneous': {
|
|
||||||
# 'number-of-layers':
|
|
||||||
# int( data['layout-matrices']['_kb_layout']['length']/(6*14) ),
|
|
||||||
# # because 6*14 is the number of bytes/layer for '_kb_layout'
|
|
||||||
# # (which is a uint8_t matrix)
|
|
||||||
# },
|
|
||||||
# }
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def parse_mapfile(map_file_path):
|
|
||||||
return {} # don't really need this info anymore
|
|
||||||
# """Parse the '.map' file"""
|
|
||||||
#
|
|
||||||
# def parse_keyboard_function(f, line):
|
|
||||||
# """Parse keyboard-functions in the '.map' file"""
|
|
||||||
#
|
|
||||||
# search = re.search(r'(0x\S+)\s+(0x\S+)', next(f))
|
|
||||||
# position = int( search.group(1), 16 )
|
|
||||||
# length = int( search.group(2), 16 )
|
|
||||||
#
|
|
||||||
# search = re.search(r'0x\S+\s+(\S+)', next(f))
|
|
||||||
# name = search.group(1)
|
|
||||||
#
|
|
||||||
# return {
|
|
||||||
# 'keyboard-functions': {
|
|
||||||
# name: {
|
|
||||||
# 'position': position,
|
|
||||||
# 'length': length,
|
|
||||||
# },
|
|
||||||
# },
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# def parse_layout_matrices(f, line):
|
|
||||||
# """Parse layout matrix information in the '.map' file"""
|
|
||||||
#
|
|
||||||
# name = re.search(r'.progmem.data.(_kb_layout\S*)', line).group(1)
|
|
||||||
#
|
|
||||||
# search = re.search(r'(0x\S+)\s+(0x\S+)', next(f))
|
|
||||||
# position = int( search.group(1), 16 )
|
|
||||||
# length = int( search.group(2), 16 )
|
|
||||||
#
|
|
||||||
# return {
|
|
||||||
# 'layout-matrices': {
|
|
||||||
# name: {
|
|
||||||
# 'position': position,
|
|
||||||
# 'length': length,
|
|
||||||
# },
|
|
||||||
# },
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# # --- parse_mapfile() ---
|
|
||||||
#
|
|
||||||
# # normalize paths
|
|
||||||
# map_file_path = os.path.abspath(map_file_path)
|
|
||||||
# # check paths
|
|
||||||
# if not os.path.exists(map_file_path):
|
|
||||||
# raise ValueError("invalid 'map_file_path' given")
|
|
||||||
#
|
|
||||||
# output = {}
|
|
||||||
#
|
|
||||||
# f = open(map_file_path)
|
|
||||||
#
|
|
||||||
# for line in f:
|
|
||||||
# if re.search(r'^\s*\.text\.kbfun_', line):
|
|
||||||
# dict_merge(output, parse_keyboard_function(f, line))
|
|
||||||
# elif re.search(r'^\s*\.progmem\.data.*layout', line):
|
|
||||||
# dict_merge(output, parse_layout_matrices(f, line))
|
|
||||||
#
|
|
||||||
# return output
|
|
||||||
|
|
||||||
|
|
||||||
def find_keyboard_functions(source_code_path):
|
|
||||||
"""Parse all files in the source directory"""
|
|
||||||
|
|
||||||
def read_comments(f, line):
|
|
||||||
"""
|
|
||||||
Read in properly formatted multi-line comments
|
|
||||||
- Comments must start with '/*' and end with '*/', each on their own
|
|
||||||
line
|
|
||||||
"""
|
|
||||||
comments = ''
|
|
||||||
while(line.strip() != r'*/'):
|
|
||||||
comments += line[2:].strip()+'\n'
|
|
||||||
line = next(f)
|
|
||||||
return comments
|
|
||||||
|
|
||||||
def parse_comments(comments):
|
|
||||||
"""
|
|
||||||
Parse an INI style comment string
|
|
||||||
- Fields begin with '[field-name]', and continue until the next field,
|
|
||||||
or the end of the comment
|
|
||||||
- Fields '[name]', '[description]', and '[note]' are treated specially
|
|
||||||
"""
|
|
||||||
|
|
||||||
def add_field(output, field, value):
|
|
||||||
"""Put a field+value pair in 'output', the way we want it, if the
|
|
||||||
pair is valid"""
|
|
||||||
|
|
||||||
value = value.strip()
|
|
||||||
|
|
||||||
if field is not None:
|
|
||||||
if field in ('name', 'description'):
|
|
||||||
if field not in output:
|
|
||||||
output[field] = value
|
|
||||||
else:
|
|
||||||
if field == 'note':
|
|
||||||
field = 'notes'
|
|
||||||
|
|
||||||
if field not in output:
|
|
||||||
output[field] = []
|
|
||||||
|
|
||||||
output[field] += [value]
|
|
||||||
|
|
||||||
# --- parse_comments() ---
|
|
||||||
|
|
||||||
output = {}
|
|
||||||
|
|
||||||
field = None
|
|
||||||
value = None
|
|
||||||
for line in comments.split('\n'):
|
|
||||||
line = line.strip()
|
|
||||||
|
|
||||||
if re.search(r'^\[.*\]$', line):
|
|
||||||
add_field(output, field, value)
|
|
||||||
field = line[1:-1]
|
|
||||||
value = None
|
|
||||||
|
|
||||||
else:
|
|
||||||
if value is None:
|
|
||||||
value = ''
|
|
||||||
if len(value) > 0 and value[-1] == '.':
|
|
||||||
line = ' '+line
|
|
||||||
value += ' '+line
|
|
||||||
|
|
||||||
add_field(output, field, value)
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
def parse_keyboard_function(f, line, comments):
|
|
||||||
"""Parse keyboard-functions in the source code"""
|
|
||||||
|
|
||||||
search = re.search(r'void\s+(kbfun_\S+)\s*\(void\)', line)
|
|
||||||
name = search.group(1)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'keyboard-functions': {
|
|
||||||
name: {
|
|
||||||
'comments': parse_comments(comments),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- find_keyboard_functions() ---
|
|
||||||
|
|
||||||
# normalize paths
|
|
||||||
source_code_path = os.path.abspath(source_code_path)
|
|
||||||
# check paths
|
|
||||||
if not os.path.exists(source_code_path):
|
|
||||||
raise ValueError("invalid 'source_code_path' given")
|
|
||||||
|
|
||||||
output = {}
|
|
||||||
|
|
||||||
for tup in os.walk(source_code_path):
|
|
||||||
for file_name in tup[2]:
|
|
||||||
# normalize paths
|
|
||||||
file_name = os.path.abspath( os.path.join( tup[0], file_name ) )
|
|
||||||
|
|
||||||
# ignore non '.c' files
|
|
||||||
if file_name[-2:] != '.c':
|
|
||||||
continue
|
|
||||||
|
|
||||||
f = open(file_name)
|
|
||||||
|
|
||||||
comments = ''
|
|
||||||
for line in f:
|
|
||||||
if line.strip() == r'/*':
|
|
||||||
comments = read_comments(f, line)
|
|
||||||
elif re.search(r'void\s+kbfun_\S+\s*\(void\)', line):
|
|
||||||
dict_merge(
|
|
||||||
output,
|
|
||||||
parse_keyboard_function(f, line, comments) )
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def gen_mappings(matrix_file_path, layout_file_path):
|
|
||||||
# normalize paths
|
|
||||||
matrix_file_path = os.path.abspath(matrix_file_path)
|
|
||||||
layout_file_path = os.path.abspath(layout_file_path)
|
|
||||||
|
|
||||||
def parse_matrix_file(matrix_file_path):
|
|
||||||
match = re.search( # find the whole 'KB_MATRIX_LAYER' macro
|
|
||||||
r'#define\s+KB_MATRIX_LAYER\s*\(([^)]+)\)[^{]*\{\{([^#]+)\}\}',
|
|
||||||
open(matrix_file_path).read() )
|
|
||||||
|
|
||||||
return {
|
|
||||||
"mappings": {
|
|
||||||
"physical-positions": re.findall(r'k..', match.group(1)),
|
|
||||||
"matrix-positions": re.findall(r'k..|na', match.group(2)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
def parse_layout_file(layout_file_path):
|
|
||||||
match = re.findall( # find each whole '_kb_layout*' matrix definition
|
|
||||||
r'(_kb_layout\w*)[^=]*=((?:[^{}]*\{){3}[^=]*(?:[^{}]*\}){3})',
|
|
||||||
subprocess.getoutput("gcc -E '"+layout_file_path+"'") )
|
|
||||||
|
|
||||||
layout = {}
|
|
||||||
# collect all the values
|
|
||||||
for (name, matrix) in match:
|
|
||||||
layout[name] = [
|
|
||||||
re.findall( # find all numbers and function pointers
|
|
||||||
r'[x0-9A-F]+|&\w+|NULL',
|
|
||||||
re.sub( # replace '((void *) 0)' with 'NULL'
|
|
||||||
r'\(\s*\(\s*void\s*\*\s*\)\s*0\s*\)',
|
|
||||||
'NULL',
|
|
||||||
el ) )
|
|
||||||
for el in
|
|
||||||
re.findall( # find each whole layer
|
|
||||||
r'(?:[^{}]*\{){2}((?:[^}]|\}\s*,)+)(?:[^{}]*\}){2}',
|
|
||||||
matrix ) ]
|
|
||||||
|
|
||||||
# make the numbers into actual numbers
|
|
||||||
layout['_kb_layout'] = \
|
|
||||||
[[eval(el) for el in layer] for layer in layout['_kb_layout']]
|
|
||||||
# remove the preceeding '&' from function pointers
|
|
||||||
for matrix in ('_kb_layout_press', '_kb_layout_release'):
|
|
||||||
layout[matrix] = \
|
|
||||||
[ [re.sub(r'&', '', el) for el in layer]
|
|
||||||
for layer in layout[matrix] ]
|
|
||||||
|
|
||||||
return {
|
|
||||||
"mappings": {
|
|
||||||
"matrix-layout":
|
|
||||||
# group them all properly
|
|
||||||
[ [[c, p, r] for (c, p, r) in zip(code, press, release)]
|
|
||||||
for (code, press, release) in
|
|
||||||
zip( layout['_kb_layout'],
|
|
||||||
layout['_kb_layout_press'],
|
|
||||||
layout['_kb_layout_release'] ) ]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return dict_merge(
|
|
||||||
parse_matrix_file(matrix_file_path),
|
|
||||||
parse_layout_file(layout_file_path) )
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def dict_merge(a, b):
|
|
||||||
"""
|
|
||||||
Recursively merge two dictionaries
|
|
||||||
- I was looking around for an easy way to do this, and found something
|
|
||||||
[here]
|
|
||||||
(http://www.xormedia.com/recursively-merge-dictionaries-in-python.html).
|
|
||||||
This is pretty close, but i didn't copy it exactly.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not isinstance(a, dict) or not isinstance(b, dict):
|
|
||||||
return b
|
|
||||||
|
|
||||||
for (key, value) in b.items():
|
|
||||||
if key in a:
|
|
||||||
a[key] = dict_merge(a[key], value)
|
|
||||||
else:
|
|
||||||
a[key] = value
|
|
||||||
|
|
||||||
return a
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def main():
|
|
||||||
arg_parser = argparse.ArgumentParser(
|
|
||||||
description = 'Generate project data for use with the UI' )
|
|
||||||
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--current-date',
|
|
||||||
help = ( "should be in the format rfc-3339 "
|
|
||||||
+ "(e.g. 2006-08-07 12:34:56-06:00)" ),
|
|
||||||
required = True )
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--git-commit-date',
|
|
||||||
help = ( "should be in the format rfc-3339 "
|
|
||||||
+ "(e.g. 2006-08-07 12:34:56-06:00)" ),
|
|
||||||
required = True )
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--git-commit-id',
|
|
||||||
help = "the git commit ID",
|
|
||||||
required = True )
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--map-file-path',
|
|
||||||
help = "the path to the '.map' file",
|
|
||||||
required = True )
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--source-code-path',
|
|
||||||
help = "the path to the source code directory",
|
|
||||||
required = True )
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--matrix-file-path',
|
|
||||||
help = "the path to the matrix file we're using",
|
|
||||||
required = True )
|
|
||||||
arg_parser.add_argument(
|
|
||||||
'--layout-file-path',
|
|
||||||
help = "the path to the layout file we're using",
|
|
||||||
required = True )
|
|
||||||
|
|
||||||
args = arg_parser.parse_args(sys.argv[1:])
|
|
||||||
|
|
||||||
output = {}
|
|
||||||
dict_merge( output, gen_static( args.current_date,
|
|
||||||
args.git_commit_date,
|
|
||||||
args.git_commit_id ) )
|
|
||||||
dict_merge(output, parse_mapfile(args.map_file_path))
|
|
||||||
dict_merge(output, find_keyboard_functions(args.source_code_path))
|
|
||||||
dict_merge(output, gen_mappings( args.matrix_file_path,
|
|
||||||
args.layout_file_path ))
|
|
||||||
dict_merge(output, gen_derived(output))
|
|
||||||
|
|
||||||
print(json.dumps(output, sort_keys=True, indent=4))
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
function keyclick(position, layer) {
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Before Width: | Height: | Size: 94 KiB |
|
@ -1,2 +0,0 @@
|
||||||
This directory is for projects closely related to the firmware.
|
|
||||||
|
|
Loading…
Reference in a new issue