From c6454b55852e1ef2dedc1f4134361ee693316066 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 24 Jan 2025 21:42:33 +1100 Subject: [PATCH] Add support for `features` object. --- data/schemas/community_module.jsonschema | 1 + data/schemas/keyboard.jsonschema | 11 +++--- lib/python/qmk/cli/generate/rules_mk.py | 48 +++++++++++++++++------- lib/python/qmk/json_encoders.py | 4 +- modules/hello_world/hello_world.c | 14 +++---- modules/hello_world/qmk_module.json | 3 ++ 6 files changed, 52 insertions(+), 29 deletions(-) diff --git a/data/schemas/community_module.jsonschema b/data/schemas/community_module.jsonschema index 34362a918b..a3474476df 100644 --- a/data/schemas/community_module.jsonschema +++ b/data/schemas/community_module.jsonschema @@ -12,5 +12,6 @@ "format": "uri" }, "keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"}, + "features": {"$ref": "qmk.keyboard.v1#/definitions/features_config"}, } } diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 21996f55be..c626765c3b 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -31,6 +31,11 @@ "pins": {"$ref": "qmk.definitions.v1#/mcu_pin_array"} } } + "features_config": { + "$ref": "qmk.definitions.v1#/boolean_array", + "propertyNames": {"$ref": "qmk.definitions.v1#/snake_case"}, + "not": {"required": ["lto"]} + }, }, "type": "object", "not": {"required": ["vendorId", "productId"]}, // reject via keys... @@ -328,11 +333,7 @@ "enabled": {"type": "boolean"} } }, - "features": { - "$ref": "qmk.definitions.v1#/boolean_array", - "propertyNames": {"$ref": "qmk.definitions.v1#/snake_case"}, - "not": {"required": ["lto"]} - }, + "features": { "$ref": "#/definitions/features_config" }, "indicators": { "type": "object", "properties": { diff --git a/lib/python/qmk/cli/generate/rules_mk.py b/lib/python/qmk/cli/generate/rules_mk.py index 84d7e11853..35e4f7e7c6 100755 --- a/lib/python/qmk/cli/generate/rules_mk.py +++ b/lib/python/qmk/cli/generate/rules_mk.py @@ -12,6 +12,7 @@ from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.commands import dump_lines, parse_configurator_json from qmk.path import normpath, FileType from qmk.constants import GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE +from qmk.community_modules import load_module_jsons def generate_rule(rules_key, rules_value): @@ -46,6 +47,37 @@ def process_mapping_rule(kb_info_json, rules_key, info_dict): return generate_rule(rules_key, rules_value) +def generate_features_rules(features_dict): + lines = [] + for feature, enabled in features_dict.items(): + feature = feature.upper() + enabled = 'yes' if enabled else 'no' + lines.append(generate_rule(f'{feature}_ENABLE', enabled)) + return lines + + +def generate_modules_rules(keyboard, filename): + lines = [] + modules = get_modules(keyboard, filename) + if len(modules) > 0: + lines.append('') + lines.append('OPT_DEFS += -DCOMMUNITY_MODULES_ENABLED=TRUE') + for module in modules: + lines.append('') + lines.append(f'COMMUNITY_MODULES += {module}') + lines.append(f'OPT_DEFS += -DCOMMUNITY_MODULE_{module.upper()}=TRUE') + lines.append(f'VPATH += modules/{module}') + lines.append(f'include modules/{module}/rules.mk') + + module_jsons = load_module_jsons(modules) + for module_json in module_jsons: + if 'features' in module_json: + lines.append('') + lines.append(f'# Module: {module_json["module_name"]}') + lines.extend(generate_features_rules(module_json['features'])) + return lines + + @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.') @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") @@ -80,10 +112,7 @@ def generate_rules_mk(cli): # Iterate through features to enable/disable them if 'features' in kb_info_json: - for feature, enabled in kb_info_json['features'].items(): - feature = feature.upper() - enabled = 'yes' if enabled else 'no' - rules_mk_lines.append(generate_rule(f'{feature}_ENABLE', enabled)) + rules_mk_lines.extend(generate_features_rules(kb_info_json['features'])) # Set SPLIT_TRANSPORT, if needed if kb_info_json.get('split', {}).get('transport', {}).get('protocol') == 'custom': @@ -99,16 +128,7 @@ def generate_rules_mk(cli): if converter: rules_mk_lines.append(generate_rule('CONVERT_TO', converter)) - modules = get_modules(cli.args.keyboard, cli.args.filename) - if len(modules) > 0: - rules_mk_lines.append('') - rules_mk_lines.append('OPT_DEFS += -DCOMMUNITY_MODULES_ENABLED=TRUE') - for module in modules: - rules_mk_lines.append('') - rules_mk_lines.append(f'COMMUNITY_MODULES += {module}') - rules_mk_lines.append(f'OPT_DEFS += -DCOMMUNITY_MODULE_{module.upper()}=TRUE') - rules_mk_lines.append(f'VPATH += modules/{module}') - rules_mk_lines.append(f'include modules/{module}/rules.mk') + rules_mk_lines.extend(generate_modules_rules(cli.args.keyboard, cli.args.filename)) # Show the results dump_lines(cli.args.output, rules_mk_lines) diff --git a/lib/python/qmk/json_encoders.py b/lib/python/qmk/json_encoders.py index 25a01e9e21..c6822c22b2 100755 --- a/lib/python/qmk/json_encoders.py +++ b/lib/python/qmk/json_encoders.py @@ -252,7 +252,9 @@ class CommunityModuleJSONEncoder(QMKJSONEncoder): return '01maintainer' if key == 'url': return '02url' + if key == 'features': + return '03features' if key == 'keycodes': - return '03keycodes' + return '04keycodes' return key diff --git a/modules/hello_world/hello_world.c b/modules/hello_world/hello_world.c index c3bbbc64b8..8e12ea636a 100644 --- a/modules/hello_world/hello_world.c +++ b/modules/hello_world/hello_world.c @@ -4,22 +4,18 @@ ASSERT_COMMUNITY_MODULES_MIN_API_VERSION(20250122); -static uint32_t init_timestamp = 0; +uint32_t delayed_hello_world(uint32_t trigger_time, void *cb_arg) { + print("Hello, world! I'm a QMK based keyboard!\n"); + return 0; +} void keyboard_post_init_hello_world(void) { keyboard_post_init_hello_world_kb(); - - init_timestamp = timer_read32(); + defer_exec(10000, delayed_hello_world, NULL); } void housekeeping_task_hello_world(void) { housekeeping_task_hello_world_kb(); - - static bool welcomed = false; - if(!welcomed && timer_elapsed32(init_timestamp) > 10000) { - welcomed = true; - print("Hello, world! I'm a QMK based keyboard!\n"); - } } bool process_record_hello_world(uint16_t keycode, keyrecord_t *record) { diff --git a/modules/hello_world/qmk_module.json b/modules/hello_world/qmk_module.json index 87410e08e6..1dbb1f6fbf 100644 --- a/modules/hello_world/qmk_module.json +++ b/modules/hello_world/qmk_module.json @@ -1,6 +1,9 @@ { "module_name": "Hello World", "maintainer": "QMK Maintainers", + "features": { + "deferred_exec": true + }, "keycodes": [ { "key": "CM_HELLO" } ]