keyboard stuff
1"""Used by the make system to generate keyboard.h from info.json.
2"""
3from pathlib import Path
4
5from milc import cli
6
7from qmk.path import normpath
8from qmk.info import info_json
9from qmk.commands import dump_lines
10from qmk.keyboard import keyboard_completer, keyboard_folder
11from qmk.constants import COL_LETTERS, ROW_LETTERS, GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
12
13
14def _generate_layouts(keyboard, kb_info_json):
15 """Generates the layouts macros.
16 """
17 if 'matrix_size' not in kb_info_json:
18 cli.log.error(f'{keyboard}: Invalid matrix config.')
19 return []
20
21 col_num = kb_info_json['matrix_size']['cols']
22 row_num = kb_info_json['matrix_size']['rows']
23
24 lines = []
25 lines.append('')
26 lines.append('// Layout content')
27 lines.append('')
28 lines.append('#define XXX KC_NO')
29
30 for layout_name, layout_data in kb_info_json['layouts'].items():
31 if layout_data['c_macro']:
32 continue
33
34 if not all('matrix' in key_data for key_data in layout_data['layout']):
35 cli.log.debug(f'{keyboard}/{layout_name}: No or incomplete matrix data!')
36 continue
37
38 layout_keys = []
39 layout_matrix = [['XXX'] * col_num for _ in range(row_num)]
40
41 for key_data in layout_data['layout']:
42 row, col = key_data['matrix']
43 identifier = f'k{ROW_LETTERS[row]}{COL_LETTERS[col]}'
44 if row >= row_num or col >= col_num:
45 cli.log.error(f'Skipping layouts due to {layout_name} containing invalid matrix values')
46 return []
47
48 layout_matrix[row][col] = identifier
49 layout_keys.append(identifier)
50
51 lines.append('')
52 lines.append(f'#define {layout_name}({", ".join(layout_keys)}) {{ \\')
53
54 rows = ', \\\n'.join([' { ' + ', '.join(row) + ' }' for row in layout_matrix])
55 rows += ' \\'
56 lines.append(rows)
57 lines.append('}')
58
59 for alias, target in kb_info_json.get('layout_aliases', {}).items():
60 lines.append('')
61 lines.append(f'#ifndef {alias}')
62 lines.append(f'# define {alias} {target}')
63 lines.append('#endif')
64
65 return lines
66
67
68def _generate_keycodes(kb_info_json):
69 """Generates keyboard level keycodes.
70 """
71 if 'keycodes' not in kb_info_json:
72 return []
73
74 lines = []
75 lines.append('')
76 lines.append('// Keycode content')
77 lines.append('')
78 lines.append('enum keyboard_keycodes {')
79
80 for index, item in enumerate(kb_info_json.get('keycodes')):
81 key = item["key"]
82 if index == 0:
83 lines.append(f' {key} = QK_KB_0,')
84 else:
85 lines.append(f' {key},')
86
87 lines.append('};')
88
89 for item in kb_info_json.get('keycodes', []):
90 key = item["key"]
91 for alias in item.get("aliases", []):
92 lines.append(f'#define {alias} {key}')
93
94 return lines
95
96
97@cli.argument('-i', '--include', nargs='?', arg_only=True, help='Optional file to include')
98@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
99@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
100@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate keyboard.h for.')
101@cli.subcommand('Used by the make system to generate keyboard.h from info.json', hidden=True)
102def generate_keyboard_h(cli):
103 """Generates the keyboard.h file.
104 """
105 # Build the info.json file
106 kb_info_json = info_json(cli.args.keyboard)
107
108 keyboard_h = cli.args.include
109 dd_layouts = _generate_layouts(cli.args.keyboard, kb_info_json)
110 dd_keycodes = _generate_keycodes(kb_info_json)
111 valid_config = dd_layouts or keyboard_h
112
113 # Build the layouts.h file.
114 keyboard_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '', '#include "quantum.h"']
115
116 if dd_layouts:
117 keyboard_h_lines.extend(dd_layouts)
118
119 if keyboard_h:
120 keyboard_h_lines.append(f'#include "{Path(keyboard_h).name}"')
121
122 if dd_keycodes:
123 keyboard_h_lines.extend(dd_keycodes)
124
125 # Protect against poorly configured keyboards
126 if not valid_config:
127 keyboard_h_lines.append('#error("<keyboard>.h is required unless your keyboard uses data-driven configuration. Please rename your keyboard\'s header file to <keyboard>.h")')
128
129 # Show the results
130 dump_lines(cli.args.output, keyboard_h_lines, cli.args.quiet)