at master 6.6 kB view raw
1"""Used by the make system to generate keycodes.h from keycodes_{version}.json 2""" 3from milc import cli 4 5from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE 6from qmk.commands import dump_lines 7from qmk.path import normpath 8from qmk.keycodes import load_spec 9 10 11def _translate_group(group): 12 """Fix up any issues with badly chosen values 13 """ 14 if group == 'modifiers': 15 return 'modifier' 16 if group == 'media': 17 return 'consumer' 18 return group 19 20 21def _render_key(key): 22 width = 7 23 if 'S(' in key: 24 width += len('S()') 25 if 'A(' in key: 26 width += len('A()') 27 if 'RCTL(' in key: 28 width += len('RCTL()') 29 if 'ALGR(' in key: 30 width += len('ALGR()') 31 return key.ljust(width) 32 33 34def _render_label(label): 35 label = label.replace("\\", "(backslash)") 36 return label 37 38 39def _generate_ranges(lines, keycodes): 40 lines.append('') 41 lines.append('enum qk_keycode_ranges {') 42 lines.append('// Ranges') 43 for key, value in keycodes["ranges"].items(): 44 lo, mask = map(lambda x: int(x, 16), key.split("/")) 45 hi = lo + mask 46 define = value.get("define") 47 lines.append(f' {define.ljust(30)} = 0x{lo:04X},') 48 lines.append(f' {(define + "_MAX").ljust(30)} = 0x{hi:04X},') 49 lines.append('};') 50 51 52def _generate_defines(lines, keycodes): 53 lines.append('') 54 lines.append('enum qk_keycode_defines {') 55 lines.append('// Keycodes') 56 for key, value in keycodes["keycodes"].items(): 57 lines.append(f' {value.get("key")} = {key},') 58 59 lines.append('') 60 lines.append('// Alias') 61 for key, value in keycodes["keycodes"].items(): 62 temp = value.get("key") 63 for alias in value.get("aliases", []): 64 lines.append(f' {alias.ljust(10)} = {temp},') 65 66 lines.append('};') 67 68 69def _generate_helpers(lines, keycodes): 70 lines.append('') 71 lines.append('// Range Helpers') 72 for value in keycodes["ranges"].values(): 73 define = value.get("define") 74 lines.append(f'#define IS_{define}(code) ((code) >= {define} && (code) <= {define + "_MAX"})') 75 76 # extract min/max 77 temp = {} 78 for key, value in keycodes["keycodes"].items(): 79 group = value.get('group', None) 80 if not group: 81 continue 82 if group not in temp: 83 temp[group] = [0xFFFF, 0] 84 key = int(key, 16) 85 if key < temp[group][0]: 86 temp[group][0] = key 87 if key > temp[group][1]: 88 temp[group][1] = key 89 90 lines.append('') 91 lines.append('// Group Helpers') 92 for group, codes in temp.items(): 93 lo = keycodes["keycodes"][f'0x{codes[0]:04X}']['key'] 94 hi = keycodes["keycodes"][f'0x{codes[1]:04X}']['key'] 95 lines.append(f'#define IS_{_translate_group(group).upper()}_KEYCODE(code) ((code) >= {lo} && (code) <= {hi})') 96 97 lines.append('') 98 lines.append('// Switch statement Helpers') 99 for group, codes in temp.items(): 100 lo = keycodes["keycodes"][f'0x{codes[0]:04X}']['key'] 101 hi = keycodes["keycodes"][f'0x{codes[1]:04X}']['key'] 102 name = f'{_translate_group(group).upper()}_KEYCODE_RANGE' 103 lines.append(f'#define {name.ljust(35)} {lo} ... {hi}') 104 105 106def _generate_aliases(lines, keycodes): 107 # Work around ChibiOS ch.h include guard 108 if 'CH_H' in [value['key'] for value in keycodes['aliases'].values()]: 109 lines.append('') 110 lines.append('#undef CH_H') 111 112 lines.append('') 113 lines.append('// Aliases') 114 for key, value in keycodes["aliases"].items(): 115 define = _render_key(value.get("key")) 116 val = _render_key(key) 117 if 'label' in value: 118 lines.append(f'#define {define} {val} // {_render_label(value.get("label"))}') 119 else: 120 lines.append(f'#define {define} {val}') 121 122 lines.append('') 123 for key, value in keycodes["aliases"].items(): 124 for alias in value.get("aliases", []): 125 lines.append(f'#define {alias} {value.get("key")}') 126 127 128def _generate_version(lines, keycodes, prefix=''): 129 version = keycodes['version'] 130 major, minor, patch = map(int, version.split('.')) 131 132 bcd = f'0x{major:02d}{minor:02d}{patch:04d}' 133 134 lines.append('') 135 lines.append(f'#define QMK_{prefix}KEYCODES_VERSION "{version}"') 136 lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_BCD {bcd}') 137 lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_MAJOR {major}') 138 lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_MINOR {minor}') 139 lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_PATCH {patch}') 140 141 142@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.') 143@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') 144@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") 145@cli.subcommand('Used by the make system to generate keycodes.h from keycodes_{version}.json', hidden=True) 146def generate_keycodes(cli): 147 """Generates the keycodes.h file. 148 """ 149 150 # Build the keycodes.h file. 151 keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off'] 152 153 keycodes = load_spec(cli.args.version) 154 155 _generate_version(keycodes_h_lines, keycodes) 156 _generate_ranges(keycodes_h_lines, keycodes) 157 _generate_defines(keycodes_h_lines, keycodes) 158 _generate_helpers(keycodes_h_lines, keycodes) 159 160 # Show the results 161 dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet) 162 163 164@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.') 165@cli.argument('-l', '--lang', arg_only=True, required=True, help='Language of keycodes to generate.') 166@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') 167@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") 168@cli.subcommand('Used by the make system to generate keymap_{lang}.h from keycodes_{lang}_{version}.json', hidden=True) 169def generate_keycode_extras(cli): 170 """Generates the header file. 171 """ 172 173 # Build the header file. 174 keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '#include "keycodes.h"', '// clang-format off'] 175 176 keycodes = load_spec(cli.args.version, cli.args.lang) 177 178 _generate_version(keycodes_h_lines, keycodes, f'{cli.args.lang.upper()}_') 179 _generate_aliases(keycodes_h_lines, keycodes) 180 181 # Show the results 182 dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)