diff options
-rw-r--r-- | gnu/local.mk | 2 | ||||
-rw-r--r-- | gnu/packages/firmware.scm | 77 | ||||
-rw-r--r-- | gnu/packages/patches/ergodox-firmware-fix-json-target.patch | 1405 | ||||
-rw-r--r-- | gnu/packages/patches/ergodox-firmware-fix-numpad.patch | 18 |
4 files changed, 1501 insertions, 1 deletions
diff --git a/gnu/local.mk b/gnu/local.mk index 8a6e23cacf..9fa52833cb 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -1125,6 +1125,8 @@ dist_patch_DATA = \ %D%/packages/patches/enblend-enfuse-reproducible.patch \ %D%/packages/patches/enjarify-setup-py.patch \ %D%/packages/patches/enlightenment-fix-setuid-path.patch \ + %D%/packages/patches/ergodox-firmware-fix-json-target.patch \ + %D%/packages/patches/ergodox-firmware-fix-numpad.patch \ %D%/packages/patches/erlang-man-path.patch \ %D%/packages/patches/esmini-no-clutter-log.patch \ %D%/packages/patches/esmini-use-pkgconfig.patch \ diff --git a/gnu/packages/firmware.scm b/gnu/packages/firmware.scm index dd982f6ac3..cac67829ed 100644 --- a/gnu/packages/firmware.scm +++ b/gnu/packages/firmware.scm @@ -85,7 +85,8 @@ #:use-module (ice-9 format) #:use-module (ice-9 match) - #:export (make-qmk-firmware)) + #:export (make-ergodox-firmware + make-qmk-firmware)) (define-public ath9k-htc-firmware (package @@ -1224,6 +1225,80 @@ AR100.") ;;; +;;; ErgoDox firmware. +;;; + +(define* (make-ergodox-firmware/implementation layout #:key override.c + override.h) + "Return an ergodox-firmware package for LAYOUT, optionally using OVERRIDE.C, +a C source file-like object to override LAYOUT which may be accompanied by +OVERRIDE.H, to also override the corresponding layout include file." + (let ((revision "0") + (commit "89b7e2bfdafb2a87e0248846d5c95cc5e9a27858")) + (package + (name (string-append "ergodox-firmware-" layout)) + (version (git-version "1" revision commit)) + (source (origin + (method git-fetch) + (uri (git-reference + (url "https://github.com/benblazak/ergodox-firmware") + (commit commit))) + (file-name (git-file-name name version)) + (sha256 + (base32 + "1z28frxyb21nz90frycrpsbxjp09374wawayvjphnwc8njlvkkpy")) + (patches + (search-patches "ergodox-firmware-fix-json-target.patch" + "ergodox-firmware-fix-numpad.patch")))) + (build-system gnu-build-system) + (arguments + (list + #:tests? #f ;no test suite + #:make-flags + #~(list (string-append "LAYOUT=" #$layout) + ;; Simplify the output directory name. + "ROOT=output") + #:phases + #~(modify-phases %standard-phases + (add-after 'unpack 'copy-override-files + (lambda _ + (when #$override.c + (copy-file #$override.c + (format #f "src/keyboard/ergodox/layout/~a.c" + #$layout))) + (when #$override.h + (copy-file #$override.h + (format #f "src/keyboard/ergodox/layout/~a.h" + #$layout))))) + ;; The Makefile-based build system lacks configure + ;; and install targets. + (delete 'configure) + (replace 'install + (lambda _ + (with-directory-excursion "output" + (install-file "firmware.hex" #$output) + (install-file "firmware.eep" #$output) + (install-file "firmware--layout.html" #$output))))))) + (native-inputs (list (make-avr-toolchain) python)) + (home-page "https://www.ergodox.io") + (synopsis "Firmware for the ErgoDox keyboard") + (description (format #f "This package contains the original firmware for +the ErgoDox keyboard, built using the ~a layout (as defined in the +@file{src/keyboard/ergodox/layout/~@*~a.c} source file). It contains the +@file{firmware.hex} and the @file{firmware.eep} files, which can be loaded to +a target using the @code{teensy-loader-cli} package as well as a +@file{firmware--layout.html} file, useful to easily visualize the +corresponding layout." layout)) + (license license:expat)))) + +(define make-ergodox-firmware + (memoize make-ergodox-firmware/implementation)) + +(define-public ergodox-firmware-colemak-jc-mod + (make-ergodox-firmware "colemak-jc-mod")) + + +;;; ;;; QMK Firmware. ;;; diff --git a/gnu/packages/patches/ergodox-firmware-fix-json-target.patch b/gnu/packages/patches/ergodox-firmware-fix-json-target.patch new file mode 100644 index 0000000000..52da4e2497 --- /dev/null +++ b/gnu/packages/patches/ergodox-firmware-fix-json-target.patch @@ -0,0 +1,1405 @@ +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)' \ diff --git a/gnu/packages/patches/ergodox-firmware-fix-numpad.patch b/gnu/packages/patches/ergodox-firmware-fix-numpad.patch new file mode 100644 index 0000000000..47af9f8398 --- /dev/null +++ b/gnu/packages/patches/ergodox-firmware-fix-numpad.patch @@ -0,0 +1,18 @@ +Submitted upstream: https://github.com/benblazak/ergodox-firmware/pull/100 + +diff --git a/src/lib/key-functions/public/special.c b/src/lib/key-functions/public/special.c +index 42aba45..6488137 100644 +--- a/src/lib/key-functions/public/special.c ++++ b/src/lib/key-functions/public/special.c +@@ -102,9 +102,9 @@ void kbfun_2_keys_capslock_press_release(void) { + static uint8_t numpad_layer_id; + + static inline void numpad_toggle_numlock(void) { +- _kbfun_press_release(true, KEY_LockingNumLock); ++ _kbfun_press_release(true, KEYPAD_NumLock_Clear); + usb_keyboard_send(); +- _kbfun_press_release(false, KEY_LockingNumLock); ++ _kbfun_press_release(false, KEYPAD_NumLock_Clear); + usb_keyboard_send(); + } + |