at master 8.9 kB view raw
1"""Used by the make system to generate info_config.h from info.json. 2""" 3from pathlib import Path 4from dotty_dict import dotty 5 6from argcomplete.completers import FilesCompleter 7from milc import cli 8 9from qmk.info import info_json 10from qmk.json_schema import json_load 11from qmk.keyboard import keyboard_completer, keyboard_folder 12from qmk.commands import dump_lines, parse_configurator_json 13from qmk.path import normpath, FileType 14from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE 15 16 17def generate_define(define, value=None): 18 is_keymap = cli.args.filename 19 value = f' {value}' if value is not None else '' 20 if is_keymap: 21 return f""" 22#undef {define} 23#define {define}{value}""" 24 return f""" 25#ifndef {define} 26# define {define}{value} 27#endif // {define}""" 28 29 30def direct_pins(direct_pins, postfix): 31 """Return the config.h lines that set the direct pins. 32 """ 33 rows = [] 34 35 for row in direct_pins: 36 cols = ','.join(map(str, [col or 'NO_PIN' for col in row])) 37 rows.append('{' + cols + '}') 38 39 return generate_define(f'DIRECT_PINS{postfix}', f'{{ {", ".join(rows)} }}') 40 41 42def pin_array(define, pins, postfix): 43 """Return the config.h lines that set a pin array. 44 """ 45 pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins])) 46 47 return generate_define(f'{define}_PINS{postfix}', f'{{ {pin_array} }}') 48 49 50def matrix_pins(matrix_pins, postfix=''): 51 """Add the matrix config to the config.h. 52 """ 53 pins = [] 54 55 if 'direct' in matrix_pins: 56 pins.append(direct_pins(matrix_pins['direct'], postfix)) 57 58 if 'cols' in matrix_pins: 59 pins.append(pin_array('MATRIX_COL', matrix_pins['cols'], postfix)) 60 61 if 'rows' in matrix_pins: 62 pins.append(pin_array('MATRIX_ROW', matrix_pins['rows'], postfix)) 63 64 return '\n'.join(pins) 65 66 67def generate_matrix_size(kb_info_json, config_h_lines): 68 """Add the matrix size to the config.h. 69 """ 70 if 'matrix_size' in kb_info_json: 71 config_h_lines.append(generate_define('MATRIX_COLS', kb_info_json['matrix_size']['cols'])) 72 config_h_lines.append(generate_define('MATRIX_ROWS', kb_info_json['matrix_size']['rows'])) 73 74 75def generate_config_items(kb_info_json, config_h_lines): 76 """Iterate through the info_config map to generate basic config values. 77 """ 78 info_config_map = json_load(Path('data/mappings/info_config.hjson')) 79 80 for config_key, info_dict in info_config_map.items(): 81 info_key = info_dict['info_key'] 82 key_type = info_dict.get('value_type', 'raw') 83 to_c = info_dict.get('to_c', True) 84 85 if not to_c: 86 continue 87 88 try: 89 config_value = kb_info_json[info_key] 90 except KeyError: 91 continue 92 93 if key_type.startswith('array.array'): 94 config_h_lines.append(generate_define(config_key, f'{{ {", ".join(["{" + ",".join(list(map(str, x))) + "}" for x in config_value])} }}')) 95 elif key_type.startswith('array'): 96 config_h_lines.append(generate_define(config_key, f'{{ {", ".join(map(str, config_value))} }}')) 97 elif key_type == 'bool': 98 config_h_lines.append(generate_define(config_key, 'true' if config_value else 'false')) 99 elif key_type == 'flag': 100 if config_value: 101 config_h_lines.append(generate_define(config_key)) 102 elif key_type == 'mapping': 103 for key, value in config_value.items(): 104 config_h_lines.append(generate_define(key, value)) 105 elif key_type == 'str': 106 escaped_str = config_value.replace('\\', '\\\\').replace('"', '\\"') 107 config_h_lines.append(generate_define(config_key, f'"{escaped_str}"')) 108 elif key_type == 'bcd_version': 109 (major, minor, revision) = config_value.split('.') 110 config_h_lines.append(generate_define(config_key, f'0x{major.zfill(2)}{minor}{revision}')) 111 else: 112 config_h_lines.append(generate_define(config_key, config_value)) 113 114 115def generate_encoder_config(encoder_json, config_h_lines, postfix=''): 116 """Generate the config.h lines for encoders.""" 117 a_pads = [] 118 b_pads = [] 119 resolutions = [] 120 for encoder in encoder_json.get("rotary", []): 121 a_pads.append(encoder["pin_a"]) 122 b_pads.append(encoder["pin_b"]) 123 resolutions.append(encoder.get("resolution", None)) 124 125 config_h_lines.append(generate_define(f'ENCODER_A_PINS{postfix}', f'{{ {", ".join(a_pads)} }}')) 126 config_h_lines.append(generate_define(f'ENCODER_B_PINS{postfix}', f'{{ {", ".join(b_pads)} }}')) 127 128 if len(resolutions) == 0 or all(r is None for r in resolutions): 129 cli.log.debug(f"Skipping ENCODER_RESOLUTION{postfix} configuration") 130 return 131 132 resolutions = [4 if r is None else r for r in resolutions] 133 if len(set(resolutions)) == 1: 134 config_h_lines.append(generate_define(f'ENCODER_RESOLUTION{postfix}', resolutions[0])) 135 else: 136 config_h_lines.append(generate_define(f'ENCODER_RESOLUTIONS{postfix}', f'{{ {", ".join(map(str,resolutions))} }}')) 137 138 139def generate_split_config(kb_info_json, config_h_lines): 140 """Generate the config.h lines for split boards.""" 141 if 'handedness' in kb_info_json['split']: 142 # TODO: change SPLIT_HAND_MATRIX_GRID to require brackets 143 handedness = kb_info_json['split']['handedness'] 144 if 'matrix_grid' in handedness: 145 config_h_lines.append(generate_define('SPLIT_HAND_MATRIX_GRID', ', '.join(handedness['matrix_grid']))) 146 147 if 'protocol' in kb_info_json['split'].get('transport', {}): 148 if kb_info_json['split']['transport']['protocol'] == 'i2c': 149 config_h_lines.append(generate_define('USE_I2C')) 150 151 if 'right' in kb_info_json['split'].get('matrix_pins', {}): 152 config_h_lines.append(matrix_pins(kb_info_json['split']['matrix_pins']['right'], '_RIGHT')) 153 154 if 'right' in kb_info_json['split'].get('encoder', {}): 155 generate_encoder_config(kb_info_json['split']['encoder']['right'], config_h_lines, '_RIGHT') 156 157 158def generate_led_animations_config(feature, led_feature_json, config_h_lines, enable_prefix, animation_prefix): 159 if 'animation' in led_feature_json.get('default', {}): 160 config_h_lines.append(generate_define(f'{feature.upper()}_DEFAULT_MODE', f'{animation_prefix}{led_feature_json["default"]["animation"].upper()}')) 161 162 for animation in led_feature_json.get('animations', {}): 163 if led_feature_json['animations'][animation]: 164 config_h_lines.append(generate_define(f'{enable_prefix}{animation.upper()}')) 165 166 167@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.') 168@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to') 169@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") 170@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.') 171@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True) 172def generate_config_h(cli): 173 """Generates the info_config.h file. 174 """ 175 # Determine our keyboard/keymap 176 if cli.args.filename: 177 user_keymap = parse_configurator_json(cli.args.filename) 178 kb_info_json = dotty(user_keymap.get('config', {})) 179 elif cli.args.keyboard: 180 kb_info_json = dotty(info_json(cli.args.keyboard)) 181 else: 182 cli.log.error('You must supply a configurator export or `--keyboard`.') 183 cli.subcommands['generate-config-h'].print_help() 184 return False 185 186 # Build the info_config.h file. 187 config_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once'] 188 189 generate_config_items(kb_info_json, config_h_lines) 190 191 generate_matrix_size(kb_info_json, config_h_lines) 192 193 if 'matrix_pins' in kb_info_json: 194 config_h_lines.append(matrix_pins(kb_info_json['matrix_pins'])) 195 196 if 'encoder' in kb_info_json: 197 generate_encoder_config(kb_info_json['encoder'], config_h_lines) 198 199 if 'split' in kb_info_json: 200 generate_split_config(kb_info_json, config_h_lines) 201 202 if 'led_matrix' in kb_info_json: 203 generate_led_animations_config('led_matrix', kb_info_json['led_matrix'], config_h_lines, 'ENABLE_LED_MATRIX_', 'LED_MATRIX_') 204 205 if 'rgb_matrix' in kb_info_json: 206 generate_led_animations_config('rgb_matrix', kb_info_json['rgb_matrix'], config_h_lines, 'ENABLE_RGB_MATRIX_', 'RGB_MATRIX_') 207 208 if 'rgblight' in kb_info_json: 209 generate_led_animations_config('rgblight', kb_info_json['rgblight'], config_h_lines, 'RGBLIGHT_EFFECT_', 'RGBLIGHT_MODE_') 210 211 # Show the results 212 dump_lines(cli.args.output, config_h_lines, cli.args.quiet)