* gnu/packages/firmware.scm (make-ergodox-firmware): New procedure (ergodox-firmware-colemak-jc-mod): New variable. * gnu/packages/patches/ergodox-firmware-fix-json-target.patch: New file. * gnu/packages/patches/ergodox-firmware-fix-numpad.patch: Likewise. * gnu/local.mk (dist_patch_DATA): Register them.
		
			
				
	
	
		
			1405 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1405 lines
		
	
	
	
		
			36 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| Submitted upstream:
 | |
| <https://github.com/benblazak/ergodox-firmware/pull/99>
 | |
| <https://github.com/benblazak/ergodox-firmware/pull/98>
 | |
| 
 | |
| diff --git a/build-scripts/gen-layout.py b/build-scripts/gen-layout.py
 | |
| index fd5e54c..251a463 100755
 | |
| --- a/build-scripts/gen-layout.py
 | |
| +++ b/build-scripts/gen-layout.py
 | |
| @@ -22,8 +22,10 @@ import sys
 | |
|  
 | |
|  # -----------------------------------------------------------------------------
 | |
|  
 | |
| -class Namespace():
 | |
| -	pass
 | |
| +
 | |
| +class Namespace:
 | |
| +    pass
 | |
| +
 | |
|  
 | |
|  template = Namespace()
 | |
|  doc = Namespace()
 | |
| @@ -31,45 +33,45 @@ info = Namespace()
 | |
|  
 | |
|  # -----------------------------------------------------------------------------
 | |
|  
 | |
| +
 | |
|  def main():
 | |
| -	arg_parser = argparse.ArgumentParser(
 | |
| -			description = "Generate a picture of the firmware's "
 | |
| -			            + "keyboard layout" )
 | |
| +    arg_parser = argparse.ArgumentParser(
 | |
| +        description="Generate a picture of the firmware's " + "keyboard layout"
 | |
| +    )
 | |
|  
 | |
| -	arg_parser.add_argument(
 | |
| -			'--ui-info-file',
 | |
| -			required = True )
 | |
| +    arg_parser.add_argument("--ui-info-file", required=True)
 | |
|  
 | |
| -	args = arg_parser.parse_args(sys.argv[1:])
 | |
| +    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'
 | |
| +    # 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)
 | |
| +    # 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())
 | |
| +    # 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']
 | |
| +    info.matrix_positions = info.all["mappings"]["matrix-positions"]
 | |
| +    info.matrix_layout = info.all["mappings"]["matrix-layout"]
 | |
|  
 | |
| -	# prefix
 | |
| -	doc.prefix = ("""
 | |
| +    # prefix
 | |
| +    doc.prefix = (
 | |
| +        """
 | |
|  <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | |
|  <html>
 | |
|  
 | |
|  <head>
 | |
|    <script>
 | |
|  """
 | |
| -+ template.js +
 | |
| -""" </script>
 | |
| +        + template.js
 | |
| +        + """ </script>
 | |
|  </head>
 | |
|  
 | |
|  <body>
 | |
| @@ -78,9 +80,13 @@ def main():
 | |
|  
 | |
|  <ul>
 | |
|    <li>git commit date:
 | |
| -  <code>""" + info.all['miscellaneous']['git-commit-date'] + """</code></li>
 | |
| +  <code>"""
 | |
| +        + info.all["miscellaneous"]["git-commit-date"]
 | |
| +        + """</code></li>
 | |
|    <li>git commit id:
 | |
| -  <code>""" + info.all['miscellaneous']['git-commit-id'] + """</code></li>
 | |
| +  <code>"""
 | |
| +        + info.all["miscellaneous"]["git-commit-id"]
 | |
| +        + """</code></li>
 | |
|  </ul>
 | |
|  
 | |
|  <h2>Notes</h2>
 | |
| @@ -123,301 +129,293 @@ def main():
 | |
|  
 | |
|  <br>
 | |
|  
 | |
| -""")[1:-1]
 | |
| +"""
 | |
| +    )[1:-1]
 | |
|  
 | |
| -	# suffix
 | |
| -	doc.suffix = ("""
 | |
| +    # 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)
 | |
| +"""
 | |
| +    )[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",
 | |
| -		}
 | |
| +    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()
 | |
| -
 | |
| +if __name__ == "__main__":
 | |
| +    main()
 | |
| diff --git a/build-scripts/gen-ui-info.py b/build-scripts/gen-ui-info.py
 | |
| index 1c93d32..0fa52e3 100755
 | |
| --- a/build-scripts/gen-ui-info.py
 | |
| +++ b/build-scripts/gen-ui-info.py
 | |
| @@ -13,7 +13,16 @@ Depends on:
 | |
|  - the project '.map' file (generated by the compiler)
 | |
|  """
 | |
|  
 | |
| -_FORMAT_DESCRIPTION = ("""
 | |
| +import argparse
 | |
| +import json
 | |
| +import os
 | |
| +import pathlib
 | |
| +import re
 | |
| +import subprocess
 | |
| +import sys
 | |
| +
 | |
| +_FORMAT_DESCRIPTION = (
 | |
| +    """
 | |
|  /* ----------------------------------------------------------------------------
 | |
|   * Version 0
 | |
|   * ----------------------------------------------------------------------------
 | |
| @@ -31,7 +40,7 @@ var ui_info = {
 | |
|      ".meta-data": {                    // for the JSON file
 | |
|          "version": "<number>",
 | |
|          "date-generated": "<string>",  // format: RFC 3339
 | |
| -		"description": "<string>",
 | |
| +                "description": "<string>",
 | |
|      },
 | |
|      "keyboard-functions": {
 | |
|          "<(function name)>": {
 | |
| @@ -57,7 +66,7 @@ var ui_info = {
 | |
|          "..."
 | |
|      },
 | |
|      "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
 | |
| @@ -113,365 +122,304 @@ var ui_info = {
 | |
|          "number-of-layers": "<number>"
 | |
|      }
 | |
|  }
 | |
| -""")[1:-1]
 | |
| +"""
 | |
| +)[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)
 | |
| -# 		},
 | |
| -# 	}
 | |
| -
 | |
| -# -----------------------------------------------------------------------------
 | |
| +    """Generate static information"""
 | |
|  
 | |
| -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
 | |
| +    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 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
 | |
| +    """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) )
 | |
| +    # normalize paths
 | |
| +    matrix_file_path = os.path.abspath(matrix_file_path)
 | |
| +    layout_file_path = os.path.abspath(layout_file_path)
 | |
| +    layout_name = pathlib.Path(layout_file_path).with_suffix('').name
 | |
| +
 | |
| +    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):
 | |
| +        output = subprocess.check_output(
 | |
| +            ['avr-gcc', f'-DMAKEFILE_KEYBOARD_LAYOUT={layout_name}',
 | |
| +             '-E', layout_file_path], encoding='UTF-8')
 | |
| +        match = re.findall(  # find each whole '_kb_layout*' matrix definition
 | |
| +            r"(_kb_layout\w*)[^=]*=((?:[^{}]*\{){3}[^=]*(?:[^{}]*\}){3})",
 | |
| +            output,
 | |
| +        )
 | |
| +
 | |
| +        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.
 | |
| -	"""
 | |
| +    """
 | |
| +    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
 | |
|  
 | |
| -	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
 | |
|  
 | |
| -	for (key, value) in b.items():
 | |
| -		if key in a:
 | |
| -			a[key] = dict_merge(a[key], value)
 | |
| -		else:
 | |
| -			a[key] = value
 | |
| +    return a
 | |
|  
 | |
| -	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))
 | |
| +    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, find_keyboard_functions(args.source_code_path))
 | |
| +    dict_merge(
 | |
| +        output, gen_mappings(args.matrix_file_path, args.layout_file_path)
 | |
| +    )
 | |
| +
 | |
| +    print(json.dumps(output, sort_keys=True, indent=4))
 | |
|  
 | |
| -# -----------------------------------------------------------------------------
 | |
|  
 | |
| -if __name__ == '__main__':
 | |
| -	main()
 | |
| +# -----------------------------------------------------------------------------
 | |
|  
 | |
| +if __name__ == "__main__":
 | |
| +    main()
 | |
| diff --git a/makefile b/makefile
 | |
| index d9fe10c..971ee0e 100644
 | |
| --- a/makefile
 | |
| +++ b/makefile
 | |
| @@ -58,24 +58,27 @@ SCRIPTS := build-scripts
 | |
|  all: dist
 | |
|  
 | |
|  clean:
 | |
| -	git clean -dX  # remove ignored files and directories
 | |
| -	-rm -r '$(BUILD)'
 | |
| +	git clean -fdX  # remove ignored files and directories
 | |
| +	rm -rf '$(BUILD)'
 | |
|  
 | |
|  checkin:
 | |
|  	-git commit -a
 | |
|  
 | |
|  build-dir:
 | |
| -	-rm -r '$(BUILD)/$(TARGET)'*
 | |
| -	-mkdir -p '$(BUILD)/$(TARGET)'
 | |
| +	rm -rf '$(BUILD)/$(TARGET)'*
 | |
| +	mkdir -p '$(BUILD)/$(TARGET)'
 | |
|  
 | |
|  firmware:
 | |
|  	cd src; $(MAKE) LAYOUT=$(LAYOUT) all
 | |
|  
 | |
| -$(ROOT)/firmware.%: firmware
 | |
| +$(ROOT):
 | |
| +	mkdir -p '$@'
 | |
| +
 | |
| +$(ROOT)/firmware.%: firmware $(ROOT)
 | |
|  	cp 'src/firmware.$*' '$@'
 | |
|  
 | |
|  
 | |
| -$(ROOT)/firmware--ui-info.json: $(SCRIPTS)/gen-ui-info.py checkin
 | |
| +$(ROOT)/firmware--ui-info.json: $(SCRIPTS)/gen-ui-info.py checkin firmware
 | |
|  	( ./'$<' \
 | |
|  		--current-date '$(shell $(DATE_PROG) --rfc-3339 s)' \
 | |
|  		--git-commit-date '$(GIT_COMMIT_DATE)' \
 |