Browse Source

Custom keycodes in JSON

zvecr 3 years ago
parent
commit
2731667ff4

+ 32 - 0
data/schemas/definitions.jsonschema

@@ -71,6 +71,38 @@
         "type": "string",
         "pattern": "^[0-9a-z][0-9a-z_/]*$"
     },
+    "keycode": {
+        "type": "string",
+        "minLength": 2,
+        "maxLength": 50,
+        "pattern": "^[A-Z][A-Zs_0-9]*$"
+    },
+    "keycode_short": {
+        "type": "string",
+        "minLength": 2,
+        "maxLength": 7,
+        "pattern": "^[A-Z][A-Zs_0-9]*$"
+    },
+    "keycode_decl": {
+        "type": "object",
+        "required": [
+            "key"
+        ],
+        "properties": {
+            "key": {"$ref": "#/keycode"},
+            "label": {"$ref": "#/text_identifier"},
+            "aliases": {
+                "type": "array",
+                "minItems": 1,
+                "items": {"$ref": "#/keycode_short"}
+            }
+        }
+    },
+    "keycode_decl_array": {
+        "type": "array",
+        "minItems": 1
+        "items": {"$ref": "#/keycode_decl"}
+    },
     "mcu_pin_array": {
         "type": "array",
         "items": {"$ref": "#/mcu_pin"}

+ 1 - 0
data/schemas/keyboard.jsonschema

@@ -246,6 +246,7 @@
                 "on_state": {"$ref": "qmk.definitions.v1#/bit"}
             }
         },
+        "keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"},
         "layout_aliases": {
             "type": "object",
             "additionalProperties": {"$ref": "qmk.definitions.v1#/layout_macro"}

+ 3 - 7
data/schemas/keycodes.jsonschema

@@ -8,11 +8,7 @@
             "type": "string",
             "minLength": 2,
             "maxLength": 50,
-            "pattern": "^[A-Zs_0-9]*$"
-        },
-        "hex_number_4d": {
-            "type": "string",
-            "pattern": "^0x[0-9A-F]{4}$"
+            "pattern": "^[A-Z][A-Zs_0-9]*$"
         }
     },
     "properties": {
@@ -34,10 +30,10 @@
         "keycodes": {
             "type": "object",
             "propertyNames": {
-                "$ref": "#/definitions/hex_number_4d"
+                "$ref": "qmk.definitions.v1#/hex_number_4d"
             },
             "additionalProperties": {
-                "type": "object",
+                "type": "object", // use 'qmk.definitions.v1#/keycode_decl' when problem keycodes are removed
                 "required": [
                     "key"
                 ],

+ 1 - 0
data/schemas/keymap.jsonschema

@@ -67,6 +67,7 @@
                 }
             }
         },
+        "keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"},
         "config": {"$ref": "qmk.keyboard.v1"},
         "notes": {
             "type": "string"

+ 1 - 2
keyboards/annepro2/annepro2.c

@@ -13,9 +13,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-
+#include QMK_KEYBOARD_H
 #include "hal.h"
-#include "annepro2.h"
 #include "annepro2_ble.h"
 #include "spi_master.h"
 #include "ap2_led.h"

+ 0 - 20
keyboards/annepro2/annepro2.h

@@ -57,23 +57,3 @@ extern ble_capslock_t ble_capslock;
     /* ROW5  */ { K40,   KC_NO, K42,   K43,   KC_NO, KC_NO, K46,   KC_NO, KC_NO, K49,   K4A,   K4B,   K4C,   KC_NO}, \
 }
 // clang-format on
-
-enum AP2KeyCodes {
-    KC_AP2_BT1 = QK_KB_0,
-    KC_AP2_BT2,
-    KC_AP2_BT3,
-    KC_AP2_BT4,
-    KC_AP2_BT_UNPAIR,
-    KC_AP2_USB,
-    KC_AP_LED_ON,
-    KC_AP_LED_OFF,
-    KC_AP_LED_TOG,
-    KC_AP_LED_NEXT_PROFILE,
-    KC_AP_LED_PREV_PROFILE,
-    KC_AP_LED_NEXT_INTENSITY,
-    KC_AP_LED_SPEED,
-    KC_AP_RGB_VAI,
-    KC_AP_RGB_VAD,
-    KC_AP_RGB_TOG,
-    KC_AP_RGB_MOD,
-};

+ 19 - 0
keyboards/annepro2/info.json

@@ -6,6 +6,25 @@
         "vid": "0xFEED",
         "device_version": "13.3.7"
     },
+    "keycodes": [
+        { "key": "KC_AP2_BT1", "aliases": ["AP2_BT1"] },
+        { "key": "KC_AP2_BT2", "aliases": ["AP2_BT2"] },
+        { "key": "KC_AP2_BT3", "aliases": ["AP2_BT3"] },
+        { "key": "KC_AP2_BT4", "aliases": ["AP2_BT4"] },
+        { "key": "KC_AP2_BT_UNPAIR" },
+        { "key": "KC_AP2_USB" },
+        { "key": "KC_AP_LED_ON" },
+        { "key": "KC_AP_LED_OFF" },
+        { "key": "KC_AP_LED_TOG" },
+        { "key": "KC_AP_LED_NEXT_PROFILE" },
+        { "key": "KC_AP_LED_PREV_PROFILE" },
+        { "key": "KC_AP_LED_NEXT_INTENSITY" },
+        { "key": "KC_AP_LED_SPEED" },
+        { "key": "KC_AP_RGB_VAI" },
+        { "key": "KC_AP_RGB_VAD" },
+        { "key": "KC_AP_RGB_TOG" },
+        { "key": "KC_AP_RGB_MOD" }
+    ],
     "layouts": {
         "LAYOUT_60_ansi": {
             "layout": [

+ 12 - 0
keyboards/handwired/pytest/basic/keymaps/dd_keycodes/keymap.json

@@ -0,0 +1,12 @@
+{
+    "keyboard": "handwired/pytest/basic",
+    "keymap": "default_json",
+    "layout": "LAYOUT_ortho_1x1",
+    "layers": [["EXAMPLE_1"]],
+    "keycodes": [
+        { "key": "EXAMPLE_1" }
+    ],
+    "author": "qmk",
+    "notes": "This file is a keymap.json file for handwired/pytest/basic",
+    "version": 1
+}

+ 6 - 0
lib/python/qmk/cli/generate/api.py

@@ -67,6 +67,12 @@ def _filtered_copy(src, dst):
         dst.write_text(json.dumps(data), encoding='utf-8')
         return dst
 
+    if dst.suffix == '.jsonschema':
+        data = json_load(src)
+
+        dst.write_text(json.dumps(data), encoding='utf-8')
+        return dst
+
     return shutil.copy2(src, dst)
 
 

+ 38 - 6
lib/python/qmk/cli/generate/keyboard_h.py

@@ -11,12 +11,9 @@ from qmk.keyboard import keyboard_completer, keyboard_folder
 from qmk.constants import COL_LETTERS, ROW_LETTERS, GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
 
 
-def _generate_layouts(keyboard):
-    """Generates the layouts.h file.
+def _generate_layouts(keyboard, kb_info_json):
+    """Generates the layouts macros.
     """
-    # Build the info.json file
-    kb_info_json = info_json(keyboard)
-
     if 'matrix_size' not in kb_info_json:
         cli.log.error(f'{keyboard}: Invalid matrix config.')
         return []
@@ -65,6 +62,32 @@ def _generate_layouts(keyboard):
     return lines
 
 
+def _generate_keycodes(kb_info_json):
+    """Generates keyboard level keycodes.
+    """
+    if 'keycodes' not in kb_info_json:
+        return []
+
+    lines = []
+    lines.append('enum keyboard_keycodes {')
+
+    for index, item in enumerate(kb_info_json.get('keycodes')):
+        key = item["key"]
+        if index == 0:
+            lines.append(f'  {key} = QK_KB_0,')
+        else:
+            lines.append(f'  {key},')
+
+    lines.append('};')
+
+    for item in kb_info_json.get('keycodes', []):
+        key = item["key"]
+        for alias in item.get("aliases", []):
+            lines.append(f'#define {alias} {key}')
+
+    return lines
+
+
 @cli.argument('-i', '--include', nargs='?', arg_only=True, help='Optional file to include')
 @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")
@@ -73,8 +96,12 @@ def _generate_layouts(keyboard):
 def generate_keyboard_h(cli):
     """Generates the keyboard.h file.
     """
+    # Build the info.json file
+    kb_info_json = info_json(cli.args.keyboard)
+
     keyboard_h = cli.args.include
-    dd_layouts = _generate_layouts(cli.args.keyboard)
+    dd_layouts = _generate_layouts(cli.args.keyboard, kb_info_json)
+    dd_keycodes = _generate_keycodes(kb_info_json)
     valid_config = dd_layouts or keyboard_h
 
     # Build the layouts.h file.
@@ -87,6 +114,11 @@ def generate_keyboard_h(cli):
     if keyboard_h:
         keyboard_h_lines.append(f'#include "{Path(keyboard_h).name}"')
 
+    keyboard_h_lines.append('')
+    keyboard_h_lines.append('// Keycode content')
+    if dd_keycodes:
+        keyboard_h_lines.extend(dd_keycodes)
+
     # Protect against poorly configured keyboards
     if not valid_config:
         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")')

+ 6 - 0
lib/python/qmk/info.py

@@ -93,6 +93,12 @@ def _validate(keyboard, info_data):
         if layout_name not in layouts and layout_name not in layout_aliases:
             _log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name))
 
+    # keycodes with length > 7 must have short forms for visualisation purposes
+    for decl in info_data.get('keycodes', []):
+        if len(decl["key"]) > 7:
+            if not decl.get("aliases", []):
+                _log_error(info_data, f'Keycode {decl["key"]} has no short form alias')
+
 
 def info_json(keyboard):
     """Generate the info.json data for a specific keyboard.

+ 30 - 0
lib/python/qmk/keymap.py

@@ -25,6 +25,7 @@ __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__
@@ -123,6 +124,29 @@ 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.
 
@@ -317,6 +341,12 @@ 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