From 938badc3b0a8b71647e2463241f2e3fe8578f32a Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Mon, 17 Jun 2024 21:51:53 +0100 Subject: [PATCH 1/2] Generate keymap dd keycodes to header (#20273) --- builddefs/build_keyboard.mk | 7 +++- lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/generate/keymap_h.py | 51 +++++++++++++++++++++++++ lib/python/qmk/keymap.py | 34 ++--------------- 4 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 lib/python/qmk/cli/generate/keymap_h.py diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index f0788e55c99..ebc52a93090 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -212,7 +212,12 @@ $(INTERMEDIATE_OUTPUT)/src/config.h: $(KEYMAP_JSON) $(eval CMD=$(QMK_BIN) generate-config-h --quiet --output $(KEYMAP_H) $(KEYMAP_JSON)) @$(BUILD_CMD) -generated-files: $(INTERMEDIATE_OUTPUT)/src/config.h $(INTERMEDIATE_OUTPUT)/src/keymap.c +$(INTERMEDIATE_OUTPUT)/src/keymap.h: $(KEYMAP_JSON) + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(eval CMD=$(QMK_BIN) generate-keymap-h --quiet --output $(INTERMEDIATE_OUTPUT)/src/keymap.h $(KEYMAP_JSON)) + @$(BUILD_CMD) + +generated-files: $(INTERMEDIATE_OUTPUT)/src/config.h $(INTERMEDIATE_OUTPUT)/src/keymap.c $(INTERMEDIATE_OUTPUT)/src/keymap.h endif diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 6d05a5fc21c..b656909f853 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -58,6 +58,7 @@ subcommands = [ 'qmk.cli.generate.keyboard_h', 'qmk.cli.generate.keycodes', 'qmk.cli.generate.keycodes_tests', + 'qmk.cli.generate.keymap_h', 'qmk.cli.generate.make_dependencies', 'qmk.cli.generate.rgb_breathe_table', 'qmk.cli.generate.rules_mk', diff --git a/lib/python/qmk/cli/generate/keymap_h.py b/lib/python/qmk/cli/generate/keymap_h.py new file mode 100644 index 00000000000..a3aaa405c0f --- /dev/null +++ b/lib/python/qmk/cli/generate/keymap_h.py @@ -0,0 +1,51 @@ +from argcomplete.completers import FilesCompleter + +from milc import cli + +import qmk.path +from qmk.commands import dump_lines +from qmk.commands import parse_configurator_json +from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE + + +def _generate_keycodes_function(keymap_json): + """Generates keymap level keycodes. + """ + lines = [] + lines.append('enum keymap_keycodes {') + + for index, item in enumerate(keymap_json.get('keycodes', [])): + key = item["key"] + if index == 0: + lines.append(f' {key} = QK_USER_0,') + else: + lines.append(f' {key},') + + lines.append('};') + + for item in keymap_json.get('keycodes', []): + key = item["key"] + for alias in item.get("aliases", []): + lines.append(f'#define {alias} {key}') + + return lines + + +@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') +@cli.subcommand('Creates a keymap.h from a QMK Configurator export.') +def generate_keymap_h(cli): + """Creates a keymap.h from a QMK Configurator export + """ + if cli.args.output and cli.args.output.name == '-': + cli.args.output = None + + keymap_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off'] + + keymap_json = parse_configurator_json(cli.args.filename) + + if 'keycodes' in keymap_json and keymap_json['keycodes'] is not None: + keymap_h_lines += _generate_keycodes_function(keymap_json) + + dump_lines(cli.args.output, keymap_h_lines, cli.args.quiet) diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py index b7bf897377c..ca76f1b2888 100644 --- a/lib/python/qmk/keymap.py +++ b/lib/python/qmk/keymap.py @@ -19,6 +19,9 @@ from qmk.info import info_json # The `keymap.c` template to use when a keyboard doesn't have its own DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H +#if __has_include("keymap.h") +# include "keymap.h" +#endif __INCLUDES__ /* THIS FILE WAS GENERATED! @@ -26,8 +29,6 @@ __INCLUDES__ * This file was generated by qmk json2c. You may or may not want to * edit it directly. */ -__KEYCODE_OUTPUT_GOES_HERE__ - const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { __KEYMAP_GOES_HERE__ }; @@ -125,29 +126,6 @@ def _generate_macros_function(keymap_json): return macro_txt -def _generate_keycodes_function(keymap_json): - """Generates keymap level keycodes. - """ - lines = [] - lines.append('enum keymap_keycodes {') - - for index, item in enumerate(keymap_json.get('keycodes', [])): - key = item["key"] - if index == 0: - lines.append(f' {key} = QK_USER_0,') - else: - lines.append(f' {key},') - - lines.append('};') - - for item in keymap_json.get('keycodes', []): - key = item["key"] - for alias in item.get("aliases", []): - lines.append(f'#define {alias} {key}') - - return lines - - def template_json(keyboard): """Returns a `keymap.json` template for a keyboard. @@ -350,12 +328,6 @@ def generate_c(keymap_json): hostlang = f'#include "keymap_{keymap_json["host_language"]}.h"\n#include "sendstring_{keymap_json["host_language"]}.h"\n' new_keymap = new_keymap.replace('__INCLUDES__', hostlang) - keycodes = '' - if 'keycodes' in keymap_json and keymap_json['keycodes'] is not None: - keycodes_txt = _generate_keycodes_function(keymap_json) - keycodes = '\n'.join(keycodes_txt) - new_keymap = new_keymap.replace('__KEYCODE_OUTPUT_GOES_HERE__', keycodes) - return new_keymap From 53a0cdc4467fb688faa2708fe75daa26686e918d Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Tue, 18 Jun 2024 03:44:22 +0100 Subject: [PATCH 2/2] Implement data driven joysticks (#22947) --- data/mappings/info_config.hjson | 5 +++ data/mappings/info_rules.hjson | 2 + data/schemas/keyboard.jsonschema | 30 +++++++++++++++ .../battleship_gamepad/battleship_gamepad.c | 6 --- .../handwired/battleship_gamepad/config.h | 21 ---------- .../battleship_gamepad/keyboard.json | 8 ++++ .../handwired/battleship_gamepad/rules.mk | 1 - lib/python/qmk/cli/generate/keyboard_c.py | 38 ++++++++++++++++++- lib/python/qmk/constants.py | 2 + lib/python/qmk/info.py | 15 +++++++- 10 files changed, 97 insertions(+), 31 deletions(-) delete mode 100644 keyboards/handwired/battleship_gamepad/config.h delete mode 100644 keyboards/handwired/battleship_gamepad/rules.mk diff --git a/data/mappings/info_config.hjson b/data/mappings/info_config.hjson index b61ca040714..4f731b60113 100644 --- a/data/mappings/info_config.hjson +++ b/data/mappings/info_config.hjson @@ -72,6 +72,11 @@ "LED_KANA_PIN": {"info_key": "indicators.kana"}, "LED_PIN_ON_STATE": {"info_key": "indicators.on_state", "value_type": "int"}, + // Joystick + "JOYSTICK_AXIS_COUNT": {"info_key": "joystick.axis_count", "value_type": "int"}, + "JOYSTICK_AXIS_RESOLUTION": {"info_key": "joystick.axis_resolution", "value_type": "int"}, + "JOYSTICK_BUTTON_COUNT": {"info_key": "joystick.button_count", "value_type": "int"}, + // Leader Key "LEADER_PER_KEY_TIMING": {"info_key": "leader_key.timing", "value_type": "flag"}, "LEADER_KEY_STRICT_KEY_PROCESSING": {"info_key": "leader_key.strict_processing", "value_type": "flag"}, diff --git a/data/mappings/info_rules.hjson b/data/mappings/info_rules.hjson index 384bc87b783..64972af63b0 100644 --- a/data/mappings/info_rules.hjson +++ b/data/mappings/info_rules.hjson @@ -25,6 +25,8 @@ "ENCODER_DRIVER": {"info_key": "encoder.driver"}, "FIRMWARE_FORMAT": {"info_key": "build.firmware_format"}, "HAPTIC_DRIVER": {"info_key": "haptic.driver"}, + "JOYSTICK_DRIVER": {"info_key": "joystick.driver"}, + "JOYSTICK_ENABLE": {"info_key": "joystick.enabled", "value_type": "bool"}, "KEYBOARD_SHARED_EP": {"info_key": "usb.shared_endpoint.keyboard", "value_type": "bool"}, "LAYOUTS": {"info_key": "community_layouts", "value_type": "list"}, "LED_MATRIX_DRIVER": {"info_key": "led_matrix.driver"}, diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 5b63acf8e80..e758c325c77 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -342,6 +342,36 @@ "on_state": {"$ref": "qmk.definitions.v1#/bit"} } }, + "joystick": { + "type": "object", + "properties": { + "enabled": {"type": "boolean"}, + "driver": {"type": "string"}, + "button_count": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "axis_resolution": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "axes": { + "type": "object", + "propertyNames": {"enum": ["x", "y", "z", "rx", "ry", "rz"]} + "additionalProperties": { + "oneOf": [ + { + "type": "object", + "properties": { + "input_pin": {"$ref": "qmk.definitions.v1#/mcu_pin"}, + "low": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "rest": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "high": {"$ref": "qmk.definitions.v1#/unsigned_int"} + } + }, + { + "type": "string", + "enum": ["virtual"] + } + ] + } + } + } + }, "keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"}, "layout_aliases": { "type": "object", diff --git a/keyboards/handwired/battleship_gamepad/battleship_gamepad.c b/keyboards/handwired/battleship_gamepad/battleship_gamepad.c index cccc03a2872..7b5e65a9907 100644 --- a/keyboards/handwired/battleship_gamepad/battleship_gamepad.c +++ b/keyboards/handwired/battleship_gamepad/battleship_gamepad.c @@ -16,12 +16,6 @@ #include "quantum.h" -/* joystick config */ -joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { - [0] = JOYSTICK_AXIS_IN(F5, 1023, 512, 0), - [1] = JOYSTICK_AXIS_IN(F4, 0, 512, 1023) -}; - /* joystick button code (thumbstick pressed) */ void keyboard_pre_init_kb(void) { gpio_set_pin_input_high(F6); diff --git a/keyboards/handwired/battleship_gamepad/config.h b/keyboards/handwired/battleship_gamepad/config.h deleted file mode 100644 index b785c80aadf..00000000000 --- a/keyboards/handwired/battleship_gamepad/config.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Copyright 2021 Andrew Braini - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -/* joystick configuration */ -#define JOYSTICK_BUTTON_COUNT 25 -#define JOYSTICK_AXIS_RESOLUTION 10 diff --git a/keyboards/handwired/battleship_gamepad/keyboard.json b/keyboards/handwired/battleship_gamepad/keyboard.json index 28327418758..8350f31fddb 100644 --- a/keyboards/handwired/battleship_gamepad/keyboard.json +++ b/keyboards/handwired/battleship_gamepad/keyboard.json @@ -13,6 +13,14 @@ "rows": ["B6", "B2", "B3", "B1", "F7"] }, "diode_direction": "COL2ROW", + "joystick": { + "button_count": 25, + "axis_resolution": 10, + "axes": { + "x": {"input_pin": "F5", "low": 1023, "rest": 512, "high": 0}, + "y": {"input_pin": "F4", "low": 0, "rest": 512, "high": 1023} + } + }, "processor": "atmega32u4", "bootloader": "caterina", "features": { diff --git a/keyboards/handwired/battleship_gamepad/rules.mk b/keyboards/handwired/battleship_gamepad/rules.mk deleted file mode 100644 index c5ab560bca9..00000000000 --- a/keyboards/handwired/battleship_gamepad/rules.mk +++ /dev/null @@ -1 +0,0 @@ -JOYSTICK_DRIVER = analog diff --git a/lib/python/qmk/cli/generate/keyboard_c.py b/lib/python/qmk/cli/generate/keyboard_c.py index 5a6c9674860..228b320942a 100755 --- a/lib/python/qmk/cli/generate/keyboard_c.py +++ b/lib/python/qmk/cli/generate/keyboard_c.py @@ -6,7 +6,7 @@ from qmk.info import info_json from qmk.commands import dump_lines from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.path import normpath -from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE +from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, JOYSTICK_AXES def _gen_led_configs(info_data): @@ -91,6 +91,41 @@ def _gen_matrix_mask(info_data): return lines +def _gen_joystick_axes(info_data): + """Convert info.json content to joystick_axes + """ + if 'axes' not in info_data.get('joystick', {}): + return [] + + axes = info_data['joystick']['axes'] + axes_keys = list(axes.keys()) + + lines = [] + lines.append('#ifdef JOYSTICK_ENABLE') + lines.append('joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {') + + # loop over all available axes - injecting virtual axis for those not specified + for index, cur in enumerate(JOYSTICK_AXES): + # bail out if we have generated all requested axis + if len(axes_keys) == 0: + break + + axis = 'virtual' + if cur in axes: + axis = axes[cur] + axes_keys.remove(cur) + + if axis == 'virtual': + lines.append(f" [{index}] = JOYSTICK_AXIS_VIRTUAL,") + else: + lines.append(f" [{index}] = JOYSTICK_AXIS_IN({axis['input_pin']}, {axis['low']}, {axis['rest']}, {axis['high']}),") + + 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.') @@ -105,6 +140,7 @@ def generate_keyboard_c(cli): keyboard_h_lines.extend(_gen_led_configs(kb_info_json)) keyboard_h_lines.extend(_gen_matrix_mask(kb_info_json)) + keyboard_h_lines.extend(_gen_joystick_axes(kb_info_json)) # Show the results dump_lines(cli.args.output, keyboard_h_lines, cli.args.quiet) diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 90e4452f2b9..b6f46180b29 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -320,3 +320,5 @@ LICENSE_TEXTS = [ you may not use this file except in compliance with the License. """]), ] + +JOYSTICK_AXES = ['x', 'y', 'z', 'rx', 'ry', 'rz'] diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 833271c09cc..091a11854c0 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -7,7 +7,7 @@ from dotty_dict import dotty from milc import cli -from qmk.constants import COL_LETTERS, ROW_LETTERS, CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS +from qmk.constants import COL_LETTERS, ROW_LETTERS, CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS, JOYSTICK_AXES from qmk.c_parse import find_layouts, parse_config_h_file, find_led_config from qmk.json_schema import deep_update, json_load, validate from qmk.keyboard import config_h, rules_mk @@ -249,8 +249,9 @@ def info_json(keyboard): info_data = _extract_rules_mk(info_data, rules_mk(str(keyboard))) info_data = _extract_config_h(info_data, config_h(str(keyboard))) - # Ensure that we have matrix row and column counts + # Ensure that we have various calculated values info_data = _matrix_size(info_data) + info_data = _joystick_axis_count(info_data) # Merge in data from info_data = _extract_led_config(info_data, str(keyboard)) @@ -800,6 +801,16 @@ def _matrix_size(info_data): return info_data +def _joystick_axis_count(info_data): + """Add info_data['joystick.axis_count'] if required + """ + if 'axes' in info_data.get('joystick', {}): + axes_keys = info_data['joystick']['axes'].keys() + info_data['joystick']['axis_count'] = max(JOYSTICK_AXES.index(a) for a in axes_keys) + 1 if axes_keys else 0 + + return info_data + + def _check_matrix(info_data): """Check the matrix to ensure that row/column count is consistent. """