diff options
Diffstat (limited to 'lib/python/qmk/cli')
-rw-r--r-- | lib/python/qmk/cli/c2json.py | 59 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/config_h.py | 59 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/keyboard_c.py | 27 | ||||
-rw-r--r-- | lib/python/qmk/cli/generate/keycodes.py | 8 | ||||
-rwxr-xr-x | lib/python/qmk/cli/generate/rules_mk.py | 26 |
5 files changed, 119 insertions, 60 deletions
diff --git a/lib/python/qmk/cli/c2json.py b/lib/python/qmk/cli/c2json.py index 7f6aca070a..f7f1f2ffba 100644 --- a/lib/python/qmk/cli/c2json.py +++ b/lib/python/qmk/cli/c2json.py @@ -1,45 +1,57 @@ """Generate a keymap.json from a keymap.c file. """ +import re import json from argcomplete.completers import FilesCompleter from milc import cli -import qmk.keymap import qmk.path from qmk.json_encoders import InfoJSONEncoder +from qmk.decorators import automagic_keyboard, automagic_keymap from qmk.keyboard import keyboard_completer, keyboard_folder +from qmk.keymap import locate_keymap, find_keymap_from_dir, generate_json, c2json as c2json_impl from qmk.errors import CppError +from qmk.commands import dump_lines @cli.argument('--no-cpp', arg_only=True, action='store_false', help='Do not use \'cpp\' on keymap.c') @cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to') @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") -@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='The keyboard\'s name') -@cli.argument('-km', '--keymap', arg_only=True, required=True, help='The keymap\'s name') -@cli.argument('filename', arg_only=True, completer=FilesCompleter('.c'), help='keymap.c file') +@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard\'s name') +@cli.argument('-km', '--keymap', help='The keymap\'s name') +@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.c'), help='keymap.c file') @cli.subcommand('Creates a keymap.json from a keymap.c file.') +@automagic_keyboard +@automagic_keymap def c2json(cli): """Generate a keymap.json from a keymap.c file. This command uses the `qmk.keymap` module to generate a keymap.json from a keymap.c file. The generated keymap is written to stdout, or to a file if -o is provided. """ - if cli.args.filename != '-': - cli.args.filename = qmk.path.normpath(cli.args.filename) + filename = cli.args.filename + keyboard = cli.config.c2json.keyboard + keymap = cli.config.c2json.keymap - # Error checking - if not cli.args.filename.exists(): - cli.log.error('C file does not exist!') - cli.print_usage() - return False + if filename: + if not keyboard and not keymap: + # fallback to inferring keyboard/keymap from path + (keymap, found_type) = find_keymap_from_dir(filename) + if found_type == 'keymap_directory': + keyboard = re.search(fr"keyboards/(.+)/keymaps/{keymap}/.*", filename.as_posix()).group(1) - # Environment processing - if cli.args.output == ('-'): - cli.args.output = None + elif keyboard and keymap: + if not filename: + # fallback to inferring keyboard/keymap from path + filename = locate_keymap(keyboard, keymap) + + if not all((filename, keyboard, keymap)): + cli.log.error('You must supply keyboard and keymap, a path to a keymap.c within qmk_firmware, or absolute filename and keyboard and keymap') + cli.print_help() + return False - # Parse the keymap.c try: - keymap_json = qmk.keymap.c2json(cli.args.keyboard, cli.args.keymap, cli.args.filename, use_cpp=cli.args.no_cpp) + keymap_json = c2json_impl(keyboard, keymap, filename, use_cpp=cli.args.no_cpp) except CppError as e: if cli.config.general.verbose: cli.log.debug('The C pre-processor ran into a fatal error: %s', e) @@ -48,19 +60,14 @@ def c2json(cli): # Generate the keymap.json try: - keymap_json = qmk.keymap.generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers']) + keymap_json = generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers']) except KeyError: cli.log.error('Something went wrong. Try to use --no-cpp.') return False if cli.args.output: - cli.args.output.parent.mkdir(parents=True, exist_ok=True) - if cli.args.output.exists(): - cli.args.output.replace(cli.args.output.parent / (cli.args.output.name + '.bak')) - cli.args.output.write_text(json.dumps(keymap_json, cls=InfoJSONEncoder, sort_keys=True)) - - if not cli.args.quiet: - cli.log.info('Wrote keymap to %s.', cli.args.output) - + keymap_lines = [json.dumps(keymap_json, cls=InfoJSONEncoder, sort_keys=True)] else: - print(json.dumps(keymap_json)) + keymap_lines = [json.dumps(keymap_json)] + + dump_lines(cli.args.output, keymap_lines, cli.args.quiet) diff --git a/lib/python/qmk/cli/generate/config_h.py b/lib/python/qmk/cli/generate/config_h.py index 64d4db6ffe..00fb1d9585 100755 --- a/lib/python/qmk/cli/generate/config_h.py +++ b/lib/python/qmk/cli/generate/config_h.py @@ -15,7 +15,12 @@ from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE def generate_define(define, value=None): + is_keymap = cli.args.filename value = f' {value}' if value is not None else '' + if is_keymap: + return f""" +#undef {define} +#define {define}{value}""" return f""" #ifndef {define} # define {define}{value} @@ -67,6 +72,19 @@ def generate_matrix_size(kb_info_json, config_h_lines): config_h_lines.append(generate_define('MATRIX_ROWS', kb_info_json['matrix_size']['rows'])) +def generate_matrix_masked(kb_info_json, config_h_lines): + """"Enable matrix mask if required""" + mask_required = False + + if 'matrix_grid' in kb_info_json.get('dip_switch', {}): + mask_required = True + if 'matrix_grid' in kb_info_json.get('split', {}).get('handedness', {}): + mask_required = True + + if mask_required: + config_h_lines.append(generate_define('MATRIX_MASKED')) + + def generate_config_items(kb_info_json, config_h_lines): """Iterate through the info_config map to generate basic config values. """ @@ -75,9 +93,9 @@ def generate_config_items(kb_info_json, config_h_lines): for config_key, info_dict in info_config_map.items(): info_key = info_dict['info_key'] key_type = info_dict.get('value_type', 'raw') - to_config = info_dict.get('to_config', True) + to_c = info_dict.get('to_c', True) - if not to_config: + if not to_c: continue try: @@ -130,23 +148,11 @@ def generate_encoder_config(encoder_json, config_h_lines, postfix=''): def generate_split_config(kb_info_json, config_h_lines): """Generate the config.h lines for split boards.""" - if 'primary' in kb_info_json['split']: - if kb_info_json['split']['primary'] in ('left', 'right'): - config_h_lines.append('') - config_h_lines.append('#ifndef MASTER_LEFT') - config_h_lines.append('# ifndef MASTER_RIGHT') - if kb_info_json['split']['primary'] == 'left': - config_h_lines.append('# define MASTER_LEFT') - elif kb_info_json['split']['primary'] == 'right': - config_h_lines.append('# define MASTER_RIGHT') - config_h_lines.append('# endif // MASTER_RIGHT') - config_h_lines.append('#endif // MASTER_LEFT') - elif kb_info_json['split']['primary'] == 'pin': - config_h_lines.append(generate_define('SPLIT_HAND_PIN')) - elif kb_info_json['split']['primary'] == 'matrix_grid': - config_h_lines.append(generate_define('SPLIT_HAND_MATRIX_GRID', f'{{ {",".join(kb_info_json["split"]["matrix_grid"])} }}')) - elif kb_info_json['split']['primary'] == 'eeprom': - config_h_lines.append(generate_define('EE_HANDS')) + if 'handedness' in kb_info_json['split']: + # TODO: change SPLIT_HAND_MATRIX_GRID to require brackets + handedness = kb_info_json['split']['handedness'] + if 'matrix_grid' in handedness: + config_h_lines.append(generate_define('SPLIT_HAND_MATRIX_GRID', ', '.join(handedness['matrix_grid']))) if 'protocol' in kb_info_json['split'].get('transport', {}): if kb_info_json['split']['transport']['protocol'] == 'i2c': @@ -159,10 +165,13 @@ def generate_split_config(kb_info_json, config_h_lines): generate_encoder_config(kb_info_json['split']['encoder']['right'], config_h_lines, '_RIGHT') -def generate_led_animations_config(led_feature_json, config_h_lines, prefix): +def generate_led_animations_config(feature, led_feature_json, config_h_lines, enable_prefix, animation_prefix): + if 'animation' in led_feature_json.get('default', {}): + config_h_lines.append(generate_define(f'{feature.upper()}_DEFAULT_MODE', f'{animation_prefix}{led_feature_json["default"]["animation"].upper()}')) + for animation in led_feature_json.get('animations', {}): if led_feature_json['animations'][animation]: - config_h_lines.append(generate_define(f'{prefix}{animation.upper()}')) + config_h_lines.append(generate_define(f'{enable_prefix}{animation.upper()}')) @cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.') @@ -191,6 +200,8 @@ def generate_config_h(cli): generate_matrix_size(kb_info_json, config_h_lines) + generate_matrix_masked(kb_info_json, config_h_lines) + if 'matrix_pins' in kb_info_json: config_h_lines.append(matrix_pins(kb_info_json['matrix_pins'])) @@ -201,13 +212,13 @@ def generate_config_h(cli): generate_split_config(kb_info_json, config_h_lines) if 'led_matrix' in kb_info_json: - generate_led_animations_config(kb_info_json['led_matrix'], config_h_lines, 'ENABLE_LED_MATRIX_') + generate_led_animations_config('led_matrix', kb_info_json['led_matrix'], config_h_lines, 'ENABLE_LED_MATRIX_', 'LED_MATRIX_') if 'rgb_matrix' in kb_info_json: - generate_led_animations_config(kb_info_json['rgb_matrix'], config_h_lines, 'ENABLE_RGB_MATRIX_') + generate_led_animations_config('rgb_matrix', kb_info_json['rgb_matrix'], config_h_lines, 'ENABLE_RGB_MATRIX_', 'RGB_MATRIX_') if 'rgblight' in kb_info_json: - generate_led_animations_config(kb_info_json['rgblight'], config_h_lines, 'RGBLIGHT_EFFECT_') + generate_led_animations_config('rgblight', kb_info_json['rgblight'], config_h_lines, 'RGBLIGHT_EFFECT_', 'RGBLIGHT_MODE_') # Show the results dump_lines(cli.args.output, config_h_lines, cli.args.quiet) diff --git a/lib/python/qmk/cli/generate/keyboard_c.py b/lib/python/qmk/cli/generate/keyboard_c.py index 9004b41abb..f8a2372cf3 100755 --- a/lib/python/qmk/cli/generate/keyboard_c.py +++ b/lib/python/qmk/cli/generate/keyboard_c.py @@ -57,6 +57,32 @@ def _gen_led_config(info_data): return lines +def _gen_matrix_mask(info_data): + """Convert info.json content to matrix_mask + """ + cols = info_data['matrix_size']['cols'] + rows = info_data['matrix_size']['rows'] + + # Default mask to everything disabled + mask = [['0'] * cols for i in range(rows)] + + # Mirror layout macros squashed on top of each other + for layout_data in info_data['layouts'].values(): + for key_data in layout_data['layout']: + row, col = key_data['matrix'] + mask[row][col] = '1' + + lines = [] + lines.append('#ifdef MATRIX_MASKED') + lines.append('__attribute__((weak)) const matrix_row_t matrix_mask[] = {') + for i in range(rows): + lines.append(f' 0b{"".join(reversed(mask[i]))},') + lines.append('};') + lines.append('#endif') + + return lines + + @cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") @cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate keyboard.c for.') @@ -70,6 +96,7 @@ def generate_keyboard_c(cli): keyboard_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#include QMK_KEYBOARD_H', ''] keyboard_h_lines.extend(_gen_led_config(kb_info_json)) + keyboard_h_lines.extend(_gen_matrix_mask(kb_info_json)) # Show the results dump_lines(cli.args.output, keyboard_h_lines, cli.args.quiet) diff --git a/lib/python/qmk/cli/generate/keycodes.py b/lib/python/qmk/cli/generate/keycodes.py index ed8b6827bd..719fced5d5 100644 --- a/lib/python/qmk/cli/generate/keycodes.py +++ b/lib/python/qmk/cli/generate/keycodes.py @@ -94,6 +94,14 @@ def _generate_helpers(lines, keycodes): hi = keycodes["keycodes"][f'0x{codes[1]:04X}']['key'] lines.append(f'#define IS_{ _translate_group(group).upper() }_KEYCODE(code) ((code) >= {lo} && (code) <= {hi})') + lines.append('') + lines.append('// Switch statement Helpers') + for group, codes in temp.items(): + lo = keycodes["keycodes"][f'0x{codes[0]:04X}']['key'] + hi = keycodes["keycodes"][f'0x{codes[1]:04X}']['key'] + name = f'{ _translate_group(group).upper() }_KEYCODE_RANGE' + lines.append(f'#define { name.ljust(35) } {lo} ... {hi}') + def _generate_aliases(lines, keycodes): # Work around ChibiOS ch.h include guard diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py index fc272da6c6..5291556109 100755 --- a/lib/python/qmk/cli/generate/rules_mk.py +++ b/lib/python/qmk/cli/generate/rules_mk.py @@ -14,6 +14,12 @@ from qmk.path import normpath, FileType from qmk.constants import GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE +def generate_rule(rules_key, rules_value): + is_keymap = cli.args.filename + rule_assignment_operator = '=' if is_keymap else '?=' + return f'{rules_key} {rule_assignment_operator} {rules_value}' + + def process_mapping_rule(kb_info_json, rules_key, info_dict): """Return the rules.mk line(s) for a mapping rule. """ @@ -29,15 +35,15 @@ def process_mapping_rule(kb_info_json, rules_key, info_dict): return None if key_type in ['array', 'list']: - return f'{rules_key} ?= {" ".join(rules_value)}' + return generate_rule(rules_key, " ".join(rules_value)) elif key_type == 'bool': - return f'{rules_key} ?= {"yes" if rules_value else "no"}' + return generate_rule(rules_key, "yes" if rules_value else "no") elif key_type == 'mapping': - return '\n'.join([f'{key} ?= {value}' for key, value in rules_value.items()]) + return '\n'.join([generate_rule(key, value) for key, value in rules_value.items()]) elif key_type == 'str': - return f'{rules_key} ?= "{rules_value}"' + return generate_rule(rules_key, f'"{rules_value}"') - return f'{rules_key} ?= {rules_value}' + return generate_rule(rules_key, rules_value) @cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.') @@ -77,21 +83,21 @@ def generate_rules_mk(cli): for feature, enabled in kb_info_json['features'].items(): feature = feature.upper() enabled = 'yes' if enabled else 'no' - rules_mk_lines.append(f'{feature}_ENABLE ?= {enabled}') + rules_mk_lines.append(generate_rule(f'{feature}_ENABLE', enabled)) # Set SPLIT_TRANSPORT, if needed if kb_info_json.get('split', {}).get('transport', {}).get('protocol') == 'custom': - rules_mk_lines.append('SPLIT_TRANSPORT ?= custom') + rules_mk_lines.append(generate_rule('SPLIT_TRANSPORT', 'custom')) # Set CUSTOM_MATRIX, if needed if kb_info_json.get('matrix_pins', {}).get('custom'): if kb_info_json.get('matrix_pins', {}).get('custom_lite'): - rules_mk_lines.append('CUSTOM_MATRIX ?= lite') + rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'lite')) else: - rules_mk_lines.append('CUSTOM_MATRIX ?= yes') + rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'yes')) if converter: - rules_mk_lines.append(f'CONVERT_TO ?= {converter}') + rules_mk_lines.append(generate_rule('CONVERT_TO', converter)) # Show the results dump_lines(cli.args.output, rules_mk_lines) |