From 9bc7e9afbd32e3ed34580f1be9a79e6040158de5 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Fri, 9 Dec 2022 00:54:52 +0000 Subject: Initial uk+us DD keymap_extras migration (#19031) --- lib/python/qmk/keycodes.py | 73 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 18 deletions(-) (limited to 'lib/python/qmk/keycodes.py') diff --git a/lib/python/qmk/keycodes.py b/lib/python/qmk/keycodes.py index cf1ee0767a..600163bab9 100644 --- a/lib/python/qmk/keycodes.py +++ b/lib/python/qmk/keycodes.py @@ -2,7 +2,42 @@ from pathlib import Path from qmk.json_schema import deep_update, json_load, validate -CONSTANTS_PATH = Path('data/constants/keycodes/') +CONSTANTS_PATH = Path('data/constants/') +KEYCODES_PATH = CONSTANTS_PATH / 'keycodes' +EXTRAS_PATH = KEYCODES_PATH / 'extras' + + +def _find_versions(path, prefix): + ret = [] + for file in path.glob(f'{prefix}_[0-9].[0-9].[0-9].hjson'): + ret.append(file.stem.split('_')[-1]) + + ret.sort(reverse=True) + return ret + + +def _load_fragments(path, prefix, version): + file = path / f'{prefix}_{version}.hjson' + if not file.exists(): + raise ValueError(f'Requested keycode spec ({prefix}:{version}) is invalid!') + + # Load base + spec = json_load(file) + + # Merge in fragments + fragments = path.glob(f'{prefix}_{version}_*.hjson') + for file in fragments: + deep_update(spec, json_load(file)) + + return spec + + +def _search_path(lang=None): + return EXTRAS_PATH if lang else KEYCODES_PATH + + +def _search_prefix(lang=None): + return f'keycodes_{lang}' if lang else 'keycodes' def _validate(spec): @@ -19,26 +54,20 @@ def _validate(spec): raise ValueError(f'Keycode spec contains duplicate keycodes! ({",".join(duplicates)})') -def load_spec(version): +def load_spec(version, lang=None): """Build keycode data from the requested spec file """ if version == 'latest': - version = list_versions()[0] + version = list_versions(lang)[0] - file = CONSTANTS_PATH / f'keycodes_{version}.hjson' - if not file.exists(): - raise ValueError(f'Requested keycode spec ({version}) is invalid!') + path = _search_path(lang) + prefix = _search_prefix(lang) # Load base - spec = json_load(file) - - # Merge in fragments - fragments = CONSTANTS_PATH.glob(f'keycodes_{version}_*.hjson') - for file in fragments: - deep_update(spec, json_load(file)) + spec = _load_fragments(path, prefix, version) # Sort? - spec['keycodes'] = dict(sorted(spec['keycodes'].items())) + spec['keycodes'] = dict(sorted(spec.get('keycodes', {}).items())) # Validate? _validate(spec) @@ -46,12 +75,20 @@ def load_spec(version): return spec -def list_versions(): +def list_versions(lang=None): """Return available versions - sorted newest first """ - ret = [] - for file in CONSTANTS_PATH.glob('keycodes_[0-9].[0-9].[0-9].hjson'): - ret.append(file.stem.split('_')[1]) + path = _search_path(lang) + prefix = _search_prefix(lang) + + return _find_versions(path, prefix) + + +def list_languages(): + """Return available languages + """ + ret = set() + for file in EXTRAS_PATH.glob('keycodes_*_[0-9].[0-9].[0-9].hjson'): + ret.add(file.stem.split('_')[1]) - ret.sort(reverse=True) return ret -- cgit v1.2.3 From 24adecd9227931ebaec16115e05055e0b580d351 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sun, 1 Jan 2023 19:16:38 +0000 Subject: Implement XAP style merge semantics for DD keycodes (#19397) --- lib/python/qmk/json_schema.py | 36 ++++++++++++++++++++++++++++-- lib/python/qmk/keycodes.py | 52 ++++++++++++++++++++++++++++++------------- 2 files changed, 71 insertions(+), 17 deletions(-) (limited to 'lib/python/qmk/keycodes.py') diff --git a/lib/python/qmk/json_schema.py b/lib/python/qmk/json_schema.py index 934e2f841f..c886a0d868 100644 --- a/lib/python/qmk/json_schema.py +++ b/lib/python/qmk/json_schema.py @@ -1,12 +1,13 @@ """Functions that help us generate and use info.json files. """ import json +import hjson +import jsonschema from collections.abc import Mapping from functools import lru_cache +from typing import OrderedDict from pathlib import Path -import hjson -import jsonschema from milc import cli @@ -101,3 +102,34 @@ def deep_update(origdict, newdict): origdict[key] = value return origdict + + +def merge_ordered_dicts(dicts): + """Merges nested OrderedDict objects resulting from reading a hjson file. + Later input dicts overrides earlier dicts for plain values. + Arrays will be appended. If the first entry of an array is "!reset!", the contents of the array will be cleared and replaced with RHS. + Dictionaries will be recursively merged. If any entry is "!reset!", the contents of the dictionary will be cleared and replaced with RHS. + """ + result = OrderedDict() + + def add_entry(target, k, v): + if k in target and isinstance(v, (OrderedDict, dict)): + if "!reset!" in v: + target[k] = v + else: + target[k] = merge_ordered_dicts([target[k], v]) + if "!reset!" in target[k]: + del target[k]["!reset!"] + elif k in target and isinstance(v, list): + if v[0] == '!reset!': + target[k] = v[1:] + else: + target[k] = target[k] + v + else: + target[k] = v + + for d in dicts: + for (k, v) in d.items(): + add_entry(result, k, v) + + return result diff --git a/lib/python/qmk/keycodes.py b/lib/python/qmk/keycodes.py index 600163bab9..d2f2492829 100644 --- a/lib/python/qmk/keycodes.py +++ b/lib/python/qmk/keycodes.py @@ -1,6 +1,6 @@ from pathlib import Path -from qmk.json_schema import deep_update, json_load, validate +from qmk.json_schema import merge_ordered_dicts, deep_update, json_load, validate CONSTANTS_PATH = Path('data/constants/') KEYCODES_PATH = CONSTANTS_PATH / 'keycodes' @@ -16,20 +16,13 @@ def _find_versions(path, prefix): return ret -def _load_fragments(path, prefix, version): - file = path / f'{prefix}_{version}.hjson' - if not file.exists(): - raise ValueError(f'Requested keycode spec ({prefix}:{version}) is invalid!') +def _potential_search_versions(version, lang=None): + versions = list_versions(lang) + versions.reverse() - # Load base - spec = json_load(file) + loc = versions.index(version) + 1 - # Merge in fragments - fragments = path.glob(f'{prefix}_{version}_*.hjson') - for file in fragments: - deep_update(spec, json_load(file)) - - return spec + return versions[:loc] def _search_path(lang=None): @@ -40,6 +33,34 @@ def _search_prefix(lang=None): return f'keycodes_{lang}' if lang else 'keycodes' +def _locate_files(path, prefix, versions): + # collate files by fragment "type" + files = {'_': []} + for version in versions: + files['_'].append(path / f'{prefix}_{version}.hjson') + + for file in path.glob(f'{prefix}_{version}_*.hjson'): + fragment = file.stem.replace(f'{prefix}_{version}_', '') + if fragment not in files: + files[fragment] = [] + files[fragment].append(file) + + return files + + +def _process_files(files): + # allow override within types of fragments - but not globally + spec = {} + for category in files.values(): + specs = [] + for file in category: + specs.append(json_load(file)) + + deep_update(spec, merge_ordered_dicts(specs)) + + return spec + + def _validate(spec): # first throw it to the jsonschema validate(spec, 'qmk.keycodes.v1') @@ -62,9 +83,10 @@ def load_spec(version, lang=None): path = _search_path(lang) prefix = _search_prefix(lang) + versions = _potential_search_versions(version, lang) - # Load base - spec = _load_fragments(path, prefix, version) + # Load bases + any fragments + spec = _process_files(_locate_files(path, prefix, versions)) # Sort? spec['keycodes'] = dict(sorted(spec.get('keycodes', {}).items())) -- cgit v1.2.3 From 9f2cd9119f18deb824ef7840c69f97c635b485cd Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Wed, 22 Feb 2023 22:50:09 +0000 Subject: Reallocate user/kb keycode ranges (#19907) --- data/constants/keycodes/keycodes_0.0.2.hjson | 9 +++ data/constants/keycodes/keycodes_0.0.2_user.hjson | 64 ++++++++++---------- lib/python/qmk/json_schema.py | 3 + lib/python/qmk/keycodes.py | 1 + quantum/keycodes.h | 74 +++++++++++------------ 5 files changed, 82 insertions(+), 69 deletions(-) (limited to 'lib/python/qmk/keycodes.py') diff --git a/data/constants/keycodes/keycodes_0.0.2.hjson b/data/constants/keycodes/keycodes_0.0.2.hjson index 650235e647..dc789eca50 100644 --- a/data/constants/keycodes/keycodes_0.0.2.hjson +++ b/data/constants/keycodes/keycodes_0.0.2.hjson @@ -1,5 +1,14 @@ { "ranges": { + "0x7E00/0x00FF": "!delete!", + "0x7F00/0x00FF": "!delete!", + + "0x7E00/0x003F": { + "define": "QK_KB" + }, + "0x7E40/0x01BF": { + "define": "QK_USER" + }, "0x8000/0X3FFF": { "define": "QK_UNICODEMAP" }, diff --git a/data/constants/keycodes/keycodes_0.0.2_user.hjson b/data/constants/keycodes/keycodes_0.0.2_user.hjson index b41731b1be..42392dc649 100644 --- a/data/constants/keycodes/keycodes_0.0.2_user.hjson +++ b/data/constants/keycodes/keycodes_0.0.2_user.hjson @@ -1,130 +1,130 @@ { "keycodes": { - "0x7F00": { + "0x7E40": { "group": "user", "key": "QK_USER_0", }, - "0x7F01": { + "0x7E41": { "group": "user", "key": "QK_USER_1", }, - "0x7F02": { + "0x7E42": { "group": "user", "key": "QK_USER_2", }, - "0x7F03": { + "0x7E43": { "group": "user", "key": "QK_USER_3", }, - "0x7F04": { + "0x7E44": { "group": "user", "key": "QK_USER_4", }, - "0x7F05": { + "0x7E45": { "group": "user", "key": "QK_USER_5", }, - "0x7F06": { + "0x7E46": { "group": "user", "key": "QK_USER_6", }, - "0x7F07": { + "0x7E47": { "group": "user", "key": "QK_USER_7", }, - "0x7F08": { + "0x7E48": { "group": "user", "key": "QK_USER_8", }, - "0x7F09": { + "0x7E49": { "group": "user", "key": "QK_USER_9", }, - "0x7F0A": { + "0x7E4A": { "group": "user", "key": "QK_USER_10", }, - "0x7F0B": { + "0x7E4B": { "group": "user", "key": "QK_USER_11", }, - "0x7F0C": { + "0x7E4C": { "group": "user", "key": "QK_USER_12", }, - "0x7F0D": { + "0x7E4D": { "group": "user", "key": "QK_USER_13", }, - "0x7F0E": { + "0x7E4E": { "group": "user", "key": "QK_USER_14", }, - "0x7F0F": { + "0x7E4F": { "group": "user", "key": "QK_USER_15", }, - "0x7F10": { + "0x7E50": { "group": "user", "key": "QK_USER_16", }, - "0x7F11": { + "0x7E51": { "group": "user", "key": "QK_USER_17", }, - "0x7F12": { + "0x7E52": { "group": "user", "key": "QK_USER_18", }, - "0x7F13": { + "0x7E53": { "group": "user", "key": "QK_USER_19", }, - "0x7F14": { + "0x7E54": { "group": "user", "key": "QK_USER_20", }, - "0x7F15": { + "0x7E55": { "group": "user", "key": "QK_USER_21", }, - "0x7F16": { + "0x7E56": { "group": "user", "key": "QK_USER_22", }, - "0x7F17": { + "0x7E57": { "group": "user", "key": "QK_USER_23", }, - "0x7F18": { + "0x7E58": { "group": "user", "key": "QK_USER_24", }, - "0x7F19": { + "0x7E59": { "group": "user", "key": "QK_USER_25", }, - "0x7F1A": { + "0x7E5A": { "group": "user", "key": "QK_USER_26", }, - "0x7F1B": { + "0x7E5B": { "group": "user", "key": "QK_USER_27", }, - "0x7F1C": { + "0x7E5C": { "group": "user", "key": "QK_USER_28", }, - "0x7F1D": { + "0x7E5D": { "group": "user", "key": "QK_USER_29", }, - "0x7F1E": { + "0x7E5E": { "group": "user", "key": "QK_USER_30", }, - "0x7F1F": { + "0x7E5F": { "group": "user", "key": "QK_USER_31", }, diff --git a/lib/python/qmk/json_schema.py b/lib/python/qmk/json_schema.py index c886a0d868..b00df749cc 100644 --- a/lib/python/qmk/json_schema.py +++ b/lib/python/qmk/json_schema.py @@ -107,6 +107,7 @@ def deep_update(origdict, newdict): def merge_ordered_dicts(dicts): """Merges nested OrderedDict objects resulting from reading a hjson file. Later input dicts overrides earlier dicts for plain values. + If any value is "!delete!", the existing value will be removed from its parent. Arrays will be appended. If the first entry of an array is "!reset!", the contents of the array will be cleared and replaced with RHS. Dictionaries will be recursively merged. If any entry is "!reset!", the contents of the dictionary will be cleared and replaced with RHS. """ @@ -125,6 +126,8 @@ def merge_ordered_dicts(dicts): target[k] = v[1:] else: target[k] = target[k] + v + elif v == "!delete!" and isinstance(target, (OrderedDict, dict)): + del target[k] else: target[k] = v diff --git a/lib/python/qmk/keycodes.py b/lib/python/qmk/keycodes.py index d2f2492829..966930547c 100644 --- a/lib/python/qmk/keycodes.py +++ b/lib/python/qmk/keycodes.py @@ -90,6 +90,7 @@ def load_spec(version, lang=None): # Sort? spec['keycodes'] = dict(sorted(spec.get('keycodes', {}).items())) + spec['ranges'] = dict(sorted(spec.get('ranges', {}).items())) # Validate? _validate(spec) diff --git a/quantum/keycodes.h b/quantum/keycodes.h index dfebd22044..34b13c29af 100644 --- a/quantum/keycodes.h +++ b/quantum/keycodes.h @@ -77,13 +77,13 @@ enum qk_keycode_ranges { QK_QUANTUM = 0x7C00, QK_QUANTUM_MAX = 0x7DFF, QK_KB = 0x7E00, - QK_KB_MAX = 0x7EFF, - QK_USER = 0x7F00, + QK_KB_MAX = 0x7E3F, + QK_USER = 0x7E40, QK_USER_MAX = 0x7FFF, - QK_UNICODE = 0x8000, - QK_UNICODE_MAX = 0xFFFF, QK_UNICODEMAP = 0x8000, QK_UNICODEMAP_MAX = 0xBFFF, + QK_UNICODE = 0x8000, + QK_UNICODE_MAX = 0xFFFF, QK_UNICODEMAP_PAIR = 0xC000, QK_UNICODEMAP_PAIR_MAX = 0xFFFF, }; @@ -753,38 +753,38 @@ enum qk_keycode_defines { QK_KB_29 = 0x7E1D, QK_KB_30 = 0x7E1E, QK_KB_31 = 0x7E1F, - QK_USER_0 = 0x7F00, - QK_USER_1 = 0x7F01, - QK_USER_2 = 0x7F02, - QK_USER_3 = 0x7F03, - QK_USER_4 = 0x7F04, - QK_USER_5 = 0x7F05, - QK_USER_6 = 0x7F06, - QK_USER_7 = 0x7F07, - QK_USER_8 = 0x7F08, - QK_USER_9 = 0x7F09, - QK_USER_10 = 0x7F0A, - QK_USER_11 = 0x7F0B, - QK_USER_12 = 0x7F0C, - QK_USER_13 = 0x7F0D, - QK_USER_14 = 0x7F0E, - QK_USER_15 = 0x7F0F, - QK_USER_16 = 0x7F10, - QK_USER_17 = 0x7F11, - QK_USER_18 = 0x7F12, - QK_USER_19 = 0x7F13, - QK_USER_20 = 0x7F14, - QK_USER_21 = 0x7F15, - QK_USER_22 = 0x7F16, - QK_USER_23 = 0x7F17, - QK_USER_24 = 0x7F18, - QK_USER_25 = 0x7F19, - QK_USER_26 = 0x7F1A, - QK_USER_27 = 0x7F1B, - QK_USER_28 = 0x7F1C, - QK_USER_29 = 0x7F1D, - QK_USER_30 = 0x7F1E, - QK_USER_31 = 0x7F1F, + QK_USER_0 = 0x7E40, + QK_USER_1 = 0x7E41, + QK_USER_2 = 0x7E42, + QK_USER_3 = 0x7E43, + QK_USER_4 = 0x7E44, + QK_USER_5 = 0x7E45, + QK_USER_6 = 0x7E46, + QK_USER_7 = 0x7E47, + QK_USER_8 = 0x7E48, + QK_USER_9 = 0x7E49, + QK_USER_10 = 0x7E4A, + QK_USER_11 = 0x7E4B, + QK_USER_12 = 0x7E4C, + QK_USER_13 = 0x7E4D, + QK_USER_14 = 0x7E4E, + QK_USER_15 = 0x7E4F, + QK_USER_16 = 0x7E50, + QK_USER_17 = 0x7E51, + QK_USER_18 = 0x7E52, + QK_USER_19 = 0x7E53, + QK_USER_20 = 0x7E54, + QK_USER_21 = 0x7E55, + QK_USER_22 = 0x7E56, + QK_USER_23 = 0x7E57, + QK_USER_24 = 0x7E58, + QK_USER_25 = 0x7E59, + QK_USER_26 = 0x7E5A, + QK_USER_27 = 0x7E5B, + QK_USER_28 = 0x7E5C, + QK_USER_29 = 0x7E5D, + QK_USER_30 = 0x7E5E, + QK_USER_31 = 0x7E5F, // Alias XXXXXXX = KC_NO, @@ -1391,8 +1391,8 @@ enum qk_keycode_defines { #define IS_QK_QUANTUM(code) ((code) >= QK_QUANTUM && (code) <= QK_QUANTUM_MAX) #define IS_QK_KB(code) ((code) >= QK_KB && (code) <= QK_KB_MAX) #define IS_QK_USER(code) ((code) >= QK_USER && (code) <= QK_USER_MAX) -#define IS_QK_UNICODE(code) ((code) >= QK_UNICODE && (code) <= QK_UNICODE_MAX) #define IS_QK_UNICODEMAP(code) ((code) >= QK_UNICODEMAP && (code) <= QK_UNICODEMAP_MAX) +#define IS_QK_UNICODE(code) ((code) >= QK_UNICODE && (code) <= QK_UNICODE_MAX) #define IS_QK_UNICODEMAP_PAIR(code) ((code) >= QK_UNICODEMAP_PAIR && (code) <= QK_UNICODEMAP_PAIR_MAX) // Group Helpers -- cgit v1.2.3