From fe3eac28ba3170908833ef2b9ad0aac6261fb58c Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 22 Jan 2025 17:00:06 +1100 Subject: [PATCH] Preprocessor guards, OS detection hooks. --- .../qmk/cli/generate/community_modules.py | 87 +++++++++++++------ modules/hello_world/hello_world.c | 2 +- quantum/os_detection.c | 7 ++ 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/lib/python/qmk/cli/generate/community_modules.py b/lib/python/qmk/cli/generate/community_modules.py index 7ef041a728..5c72cd2455 100644 --- a/lib/python/qmk/cli/generate/community_modules.py +++ b/lib/python/qmk/cli/generate/community_modules.py @@ -1,3 +1,4 @@ +import contextlib from argcomplete.completers import FilesCompleter from milc import cli @@ -16,20 +17,30 @@ class ModuleAPI(AttrDict): MODULE_API_LIST = [ - ModuleAPI(ret_type='void', name='keyboard_pre_init', args='void', call_params='', ret_val=None), - ModuleAPI(ret_type='void', name='keyboard_post_init', args='void', call_params='', ret_val=None), - ModuleAPI(ret_type='bool', name='pre_process_record', args='uint16_t keycode, keyrecord_t *record', call_params='keycode, record', ret_val='true'), - ModuleAPI(ret_type='bool', name='process_record', args='uint16_t keycode, keyrecord_t *record', call_params='keycode, record', ret_val='true'), - ModuleAPI(ret_type='void', name='post_process_record', args='uint16_t keycode, keyrecord_t *record', call_params='keycode, record', ret_val=None), - ModuleAPI(ret_type='void', name='housekeeping_task', args='void', call_params='', ret_val=None), - ModuleAPI(ret_type='void', name='suspend_power_down', args='void', call_params='', ret_val=None), - ModuleAPI(ret_type='void', name='suspend_wakeup_init', args='void', call_params='', ret_val=None), - ModuleAPI(ret_type='bool', name='shutdown', args='bool jump_to_bootloader', call_params='jump_to_bootloader', ret_val='true'), + ModuleAPI(ret_type='void', name='keyboard_pre_init', args='void', call_params='', ret_val=None, guard=None), + ModuleAPI(ret_type='void', name='keyboard_post_init', args='void', call_params='', ret_val=None, guard=None), + ModuleAPI(ret_type='bool', name='pre_process_record', args='uint16_t keycode, keyrecord_t *record', call_params='keycode, record', ret_val='true', guard=None), + ModuleAPI(ret_type='bool', name='process_record', args='uint16_t keycode, keyrecord_t *record', call_params='keycode, record', ret_val='true', guard=None), + ModuleAPI(ret_type='void', name='post_process_record', args='uint16_t keycode, keyrecord_t *record', call_params='keycode, record', ret_val=None, guard=None), + ModuleAPI(ret_type='void', name='housekeeping_task', args='void', call_params='', ret_val=None, guard=None), + ModuleAPI(ret_type='void', name='suspend_power_down', args='void', call_params='', ret_val=None, guard=None), + ModuleAPI(ret_type='void', name='suspend_wakeup_init', args='void', call_params='', ret_val=None, guard=None), + ModuleAPI(ret_type='bool', name='shutdown', args='bool jump_to_bootloader', call_params='jump_to_bootloader', ret_val='true', guard=None), + ModuleAPI(ret_type='bool', name='process_detected_host_os', args='os_variant_t os', call_params='os', ret_val='true', guard="defined(OS_DETECTION_ENABLE)"), ] MODULE_API_VERSION = '20250122' +@contextlib.contextmanager +def api_guard(lines, api): + if api.guard: + lines.append(f'#if {api.guard}') + yield + if api.guard: + lines.append(f'#endif // {api.guard}') + + @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('filename', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file') @@ -48,22 +59,31 @@ def generate_community_modules_h(cli): '#include ', '', f'#define COMMUNITY_MODULES_API_VERSION {MODULE_API_VERSION}', - f'#define ASSERT_COMMUNITY_MODULES_API_VERSION(x) _Static_assert((x) <= COMMUNITY_MODULES_API_VERSION, "Modules API mismatch, module requires higher version of QMK with API >= {MODULE_API_VERSION}.")', + f'#define ASSERT_COMMUNITY_MODULES_MIN_API_VERSION(x) _Static_assert((x) <= COMMUNITY_MODULES_API_VERSION, "Community module requires higher version of QMK modules API -- needs: " #x ", current: {MODULE_API_VERSION}.")', + '', + '#ifdef OS_DETECTION_ENABLE', + '#include "os_detection.h"', + '#endif // OS_DETECTION_ENABLE', '', 'typedef struct keyrecord_t keyrecord_t;', ] + keymap_json = parse_configurator_json(cli.args.filename) + if keymap_json and 'modules' in keymap_json: for module in keymap_json['modules']: lines.append('') lines.append(f'// From module: {module}') for api in MODULE_API_LIST: - lines.append(f'{api.ret_type} {api.name}_{module}({api.args});') + with api_guard(lines, api): + lines.append(f'{api.ret_type} {api.name}_{module}({api.args});') lines.append('') - lines.append(f'// Parent implementation') + lines.append('// Core wrapper') for api in MODULE_API_LIST: - lines.append(f'{api.ret_type} {api.name}_modules({api.args});') + with api_guard(lines, api): + lines.append(f'{api.ret_type} {api.name}_modules({api.args});') + dump_lines(cli.args.output, lines, cli.args.quiet, remove_repeated_newlines=True) @@ -77,29 +97,40 @@ def generate_community_modules_c(cli): if cli.args.output and cli.args.output.name == '-': cli.args.output = None - lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '', '#include "community_modules.h"'] + lines = [ + GPL2_HEADER_C_LIKE, + GENERATED_HEADER_C_LIKE, + '', + '#include "community_modules.h"', + ] + keymap_json = parse_configurator_json(cli.args.filename) + if keymap_json and 'modules' in keymap_json: + for module in keymap_json['modules']: lines.append('') for api in MODULE_API_LIST: lines.append('') - lines.append(f'__attribute__((weak)) {api.ret_type} {api.name}_{module}({api.args}) {{') - if api.ret_val: - lines.append(f' return {api.ret_val};') - lines.append('}') + with api_guard(lines, api): + lines.append(f'__attribute__((weak)) {api.ret_type} {api.name}_{module}({api.args}) {{') + if api.ret_val: + lines.append(f' return {api.ret_val};') + lines.append('}') for api in MODULE_API_LIST: lines.append('') - lines.append(f'{api.ret_type} {api.name}_modules({api.args}) {{') - if api.ret_type == 'bool': - lines.append(' return true') - for module in keymap_json['modules']: + with api_guard(lines, api): + lines.append(f'{api.ret_type} {api.name}_modules({api.args}) {{') if api.ret_type == 'bool': - lines.append(f' && {api.name}_{module}({api.call_params})') - else: - lines.append(f' {api.name}_{module}({api.call_params});') - if api.ret_type == 'bool': - lines.append(' ;') - lines.append('}') + lines.append(' return true') + for module in keymap_json['modules']: + if api.ret_type == 'bool': + lines.append(f' && {api.name}_{module}({api.call_params})') + else: + lines.append(f' {api.name}_{module}({api.call_params});') + if api.ret_type == 'bool': + lines.append(' ;') + lines.append('}') + dump_lines(cli.args.output, lines, cli.args.quiet, remove_repeated_newlines=True) diff --git a/modules/hello_world/hello_world.c b/modules/hello_world/hello_world.c index 4fe9bd052e..e46023cebf 100644 --- a/modules/hello_world/hello_world.c +++ b/modules/hello_world/hello_world.c @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include QMK_KEYBOARD_H -ASSERT_COMMUNITY_MODULES_API_VERSION(20250122); +ASSERT_COMMUNITY_MODULES_MIN_API_VERSION(20250122); static uint32_t init_timestamp = 0; diff --git a/quantum/os_detection.c b/quantum/os_detection.c index 0dd048a8c4..f3ce2d4cdd 100644 --- a/quantum/os_detection.c +++ b/quantum/os_detection.c @@ -72,6 +72,8 @@ static volatile struct usb_device_state maxprev_usb_device_state = {.configure_s static volatile bool debouncing = false; static volatile fast_timer_t last_time = 0; +bool process_detected_host_os_modules(os_variant_t os); + void os_detection_task(void) { #ifdef OS_DETECTION_KEYBOARD_RESET // resetting the keyboard on the USB device state change callback results in instability, so delegate that to this task @@ -96,12 +98,17 @@ void os_detection_task(void) { if (detected_os != reported_os || first_report) { first_report = false; reported_os = detected_os; + process_detected_host_os_modules(detected_os); process_detected_host_os_kb(detected_os); } } } } +__attribute__((weak)) bool process_detected_host_os_modules(os_variant_t os) { + return true; +} + __attribute__((weak)) bool process_detected_host_os_kb(os_variant_t detected_os) { return process_detected_host_os_user(detected_os); }