summaryrefslogtreecommitdiffstats
path: root/lib/python/qmk/keymap.py
diff options
context:
space:
mode:
authorlokher <lokher@gmail.com>2023-04-26 16:32:15 +0800
committerlokher <lokher@gmail.com>2023-04-26 16:32:15 +0800
commite4f4ceaf3f2e3d25fb282273a81f9b58790fc427 (patch)
treec0a257eab0ffe5238fdf2c04882e8ee1fe8fc46e /lib/python/qmk/keymap.py
parent103badc87cb50db1ff3851c84331e86ba78fb681 (diff)
merge upstream 713427c
Diffstat (limited to 'lib/python/qmk/keymap.py')
-rw-r--r--lib/python/qmk/keymap.py233
1 files changed, 133 insertions, 100 deletions
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index 315af35b73..dddf6449a7 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -12,8 +12,9 @@ from pygments.token import Token
from pygments import lex
import qmk.path
-from qmk.keyboard import find_keyboard_from_dir, rules_mk, keyboard_folder
+from qmk.keyboard import find_keyboard_from_dir, keyboard_folder
from qmk.errors import CppError
+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
@@ -29,9 +30,99 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
__KEYMAP_GOES_HERE__
};
+#if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)
+const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
+__ENCODER_MAP_GOES_HERE__
+};
+#endif // defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)
+
+__MACRO_OUTPUT_GOES_HERE__
+
"""
+def _generate_keymap_table(keymap_json):
+ lines = []
+ for layer_num, layer in enumerate(keymap_json['layers']):
+ if layer_num != 0:
+ lines[-1] = lines[-1] + ','
+ layer = map(_strip_any, layer)
+ layer_keys = ', '.join(layer)
+ lines.append('\t[%s] = %s(%s)' % (layer_num, keymap_json['layout'], layer_keys))
+ return lines
+
+
+def _generate_encodermap_table(keymap_json):
+ lines = []
+ for layer_num, layer in enumerate(keymap_json['encoders']):
+ if layer_num != 0:
+ lines[-1] = lines[-1] + ','
+ encoder_keycode_txt = ', '.join([f'ENCODER_CCW_CW({_strip_any(e["ccw"])}, {_strip_any(e["cw"])})' for e in layer])
+ lines.append('\t[%s] = {%s}' % (layer_num, encoder_keycode_txt))
+ return lines
+
+
+def _generate_macros_function(keymap_json):
+ macro_txt = [
+ 'bool process_record_user(uint16_t keycode, keyrecord_t *record) {',
+ ' if (record->event.pressed) {',
+ ' switch (keycode) {',
+ ]
+
+ for i, macro_array in enumerate(keymap_json['macros']):
+ macro = []
+
+ for macro_fragment in macro_array:
+ if isinstance(macro_fragment, str):
+ macro_fragment = macro_fragment.replace('\\', '\\\\')
+ macro_fragment = macro_fragment.replace('\r\n', r'\n')
+ macro_fragment = macro_fragment.replace('\n', r'\n')
+ macro_fragment = macro_fragment.replace('\r', r'\n')
+ macro_fragment = macro_fragment.replace('\t', r'\t')
+ macro_fragment = macro_fragment.replace('"', r'\"')
+
+ macro.append(f'"{macro_fragment}"')
+
+ elif isinstance(macro_fragment, dict):
+ newstring = []
+
+ if macro_fragment['action'] == 'delay':
+ newstring.append(f"SS_DELAY({macro_fragment['duration']})")
+
+ elif macro_fragment['action'] == 'beep':
+ newstring.append(r'"\a"')
+
+ elif macro_fragment['action'] == 'tap' and len(macro_fragment['keycodes']) > 1:
+ last_keycode = macro_fragment['keycodes'].pop()
+
+ for keycode in macro_fragment['keycodes']:
+ newstring.append(f'SS_DOWN(X_{keycode})')
+
+ newstring.append(f'SS_TAP(X_{last_keycode})')
+
+ for keycode in reversed(macro_fragment['keycodes']):
+ newstring.append(f'SS_UP(X_{keycode})')
+
+ else:
+ for keycode in macro_fragment['keycodes']:
+ newstring.append(f"SS_{macro_fragment['action'].upper()}(X_{keycode})")
+
+ macro.append(''.join(newstring))
+
+ new_macro = "".join(macro)
+ new_macro = new_macro.replace('""', '')
+ macro_txt.append(f' case QK_MACRO_{i}:')
+ macro_txt.append(f' SEND_STRING({new_macro});')
+ macro_txt.append(' return false;')
+
+ macro_txt.append(' }')
+ macro_txt.append(' }')
+ macro_txt.append('\n return true;')
+ macro_txt.append('};')
+ macro_txt.append('')
+ return macro_txt
+
+
def template_json(keyboard):
"""Returns a `keymap.json` template for a keyboard.
@@ -205,83 +296,26 @@ def generate_c(keymap_json):
A sequence of strings containing macros to implement for this keyboard.
"""
new_keymap = template_c(keymap_json['keyboard'])
- layer_txt = []
-
- for layer_num, layer in enumerate(keymap_json['layers']):
- if layer_num != 0:
- layer_txt[-1] = layer_txt[-1] + ','
- layer = map(_strip_any, layer)
- layer_keys = ', '.join(layer)
- layer_txt.append('\t[%s] = %s(%s)' % (layer_num, keymap_json['layout'], layer_keys))
-
+ layer_txt = _generate_keymap_table(keymap_json)
keymap = '\n'.join(layer_txt)
new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap)
- if keymap_json.get('macros'):
- macro_txt = [
- 'bool process_record_user(uint16_t keycode, keyrecord_t *record) {',
- ' if (record->event.pressed) {',
- ' switch (keycode) {',
- ]
-
- for i, macro_array in enumerate(keymap_json['macros']):
- macro = []
-
- for macro_fragment in macro_array:
- if isinstance(macro_fragment, str):
- macro_fragment = macro_fragment.replace('\\', '\\\\')
- macro_fragment = macro_fragment.replace('\r\n', r'\n')
- macro_fragment = macro_fragment.replace('\n', r'\n')
- macro_fragment = macro_fragment.replace('\r', r'\n')
- macro_fragment = macro_fragment.replace('\t', r'\t')
- macro_fragment = macro_fragment.replace('"', r'\"')
-
- macro.append(f'"{macro_fragment}"')
-
- elif isinstance(macro_fragment, dict):
- newstring = []
-
- if macro_fragment['action'] == 'delay':
- newstring.append(f"SS_DELAY({macro_fragment['duration']})")
-
- elif macro_fragment['action'] == 'beep':
- newstring.append(r'"\a"')
-
- elif macro_fragment['action'] == 'tap' and len(macro_fragment['keycodes']) > 1:
- last_keycode = macro_fragment['keycodes'].pop()
-
- for keycode in macro_fragment['keycodes']:
- newstring.append(f'SS_DOWN(X_{keycode})')
+ encodermap = ''
+ if 'encoders' in keymap_json and keymap_json['encoders'] is not None:
+ encoder_txt = _generate_encodermap_table(keymap_json)
+ encodermap = '\n'.join(encoder_txt)
+ new_keymap = new_keymap.replace('__ENCODER_MAP_GOES_HERE__', encodermap)
- newstring.append(f'SS_TAP(X_{last_keycode})')
+ macros = ''
+ if 'macros' in keymap_json and keymap_json['macros'] is not None:
+ macro_txt = _generate_macros_function(keymap_json)
+ macros = '\n'.join(macro_txt)
+ new_keymap = new_keymap.replace('__MACRO_OUTPUT_GOES_HERE__', macros)
- for keycode in reversed(macro_fragment['keycodes']):
- newstring.append(f'SS_UP(X_{keycode})')
-
- else:
- for keycode in macro_fragment['keycodes']:
- newstring.append(f"SS_{macro_fragment['action'].upper()}(X_{keycode})")
-
- macro.append(''.join(newstring))
-
- new_macro = "".join(macro)
- new_macro = new_macro.replace('""', '')
- macro_txt.append(f' case QK_MACRO_{i}:')
- macro_txt.append(f' SEND_STRING({new_macro});')
- macro_txt.append(' return false;')
-
- macro_txt.append(' }')
- macro_txt.append(' }')
- macro_txt.append('\n return true;')
- macro_txt.append('};')
- macro_txt.append('')
-
- new_keymap = '\n'.join((new_keymap, *macro_txt))
-
- if keymap_json.get('host_language'):
- new_keymap = new_keymap.replace('__INCLUDES__', f'#include "keymap_{keymap_json["host_language"]}.h"\n#include "sendstring_{keymap_json["host_language"]}.h"\n')
- else:
- new_keymap = new_keymap.replace('__INCLUDES__', '')
+ hostlang = ''
+ if 'host_language' in keymap_json and keymap_json['host_language'] is not None:
+ 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)
return new_keymap
@@ -374,11 +408,11 @@ def locate_keymap(keyboard, keymap):
return keymap_path
# Check community layouts as a fallback
- rules = rules_mk(keyboard)
+ info = info_json(keyboard)
- if "LAYOUTS" in rules:
- for layout in rules["LAYOUTS"].split():
- community_layout = Path('layouts/community') / layout / keymap
+ for community_parent in Path('layouts').glob('*/'):
+ for layout in info.get("community_layouts", []):
+ community_layout = community_parent / layout / keymap
if community_layout.exists():
if (community_layout / 'keymap.json').exists():
return community_layout / 'keymap.json'
@@ -408,37 +442,36 @@ def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=Fa
Returns:
a sorted list of valid keymap names.
"""
- # parse all the rules.mk files for the keyboard
- rules = rules_mk(keyboard)
names = set()
- if rules is not None:
- keyboards_dir = Path('keyboards')
- kb_path = keyboards_dir / keyboard
+ keyboards_dir = Path('keyboards')
+ kb_path = keyboards_dir / keyboard
+
+ # walk up the directory tree until keyboards_dir
+ # and collect all directories' name with keymap.c file in it
+ while kb_path != keyboards_dir:
+ keymaps_dir = kb_path / "keymaps"
+
+ if keymaps_dir.is_dir():
+ for keymap in keymaps_dir.iterdir():
+ if is_keymap_dir(keymap, c, json, additional_files):
+ keymap = keymap if fullpath else keymap.name
+ names.add(keymap)
- # walk up the directory tree until keyboards_dir
- # and collect all directories' name with keymap.c file in it
- while kb_path != keyboards_dir:
- keymaps_dir = kb_path / "keymaps"
+ kb_path = kb_path.parent
- if keymaps_dir.is_dir():
- for keymap in keymaps_dir.iterdir():
+ # Check community layouts as a fallback
+ info = info_json(keyboard)
+
+ for community_parent in Path('layouts').glob('*/'):
+ for layout in info.get("community_layouts", []):
+ cl_path = community_parent / layout
+ if cl_path.is_dir():
+ for keymap in cl_path.iterdir():
if is_keymap_dir(keymap, c, json, additional_files):
keymap = keymap if fullpath else keymap.name
names.add(keymap)
- kb_path = kb_path.parent
-
- # if community layouts are supported, get them
- if "LAYOUTS" in rules:
- for layout in rules["LAYOUTS"].split():
- cl_path = Path('layouts/community') / layout
- if cl_path.is_dir():
- for keymap in cl_path.iterdir():
- if is_keymap_dir(keymap, c, json, additional_files):
- keymap = keymap if fullpath else keymap.name
- names.add(keymap)
-
return sorted(names)