08ce0142bad40f22d05d33fdef8a7c8907154e96
- Author
- Zach White <skullydazed@gmail.com>
- Committer
- GitHub <noreply@github.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/data/mappings/info_config.json b/data/mappings/info_config.json
2index 50694e38632dbd5b4a3a817ff7ce09d36965e5ec..3cb64dd3a2fbe968de16c44daa31366398d615e4 100644
3--- a/data/mappings/info_config.json
4+++ b/data/mappings/info_config.json
5@@ -76,6 +76,7 @@
6 "QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int"},
7 "QMK_LED": {"info_key": "qmk_lufa_bootloader.led"},
8 "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"},
9+ "SENDSTRING_BELL": {"info_key": "audio.macro_beep", "value_type": "bool"},
10 "SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"},
11 "SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"},
12 "SPLIT_USB_DETECT": {"info_key": "split.usb_detect.enabled", "value_type": "bool"},
13diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema
14index 65d44c94d2ce8d2ad279cc3dccafac3e3db3f179..37a0643ab2e89207964d2d10900764830114877b 100644
15--- a/data/schemas/keyboard.jsonschema
16+++ b/data/schemas/keyboard.jsonschema
17@@ -19,6 +19,7 @@
18 "type": "object",
19 "additionalProperties": false,
20 "properties": {
21+ "macro_beep": {"type": "boolean"},
22 "pins": {"$ref": "qmk.definitions.v1#/mcu_pin_array"},
23 "voices": {"type": "boolean"}
24 }
25diff --git a/data/schemas/keymap.jsonschema b/data/schemas/keymap.jsonschema
26index a4bdab966b6149f5b1c00188df8d5415762db2eb..faa250a9429ca420f014efaac6c299cd693465b0 100644
27--- a/data/schemas/keymap.jsonschema
28+++ b/data/schemas/keymap.jsonschema
29@@ -5,6 +5,7 @@
30 "type": "object",
31 "properties": {
32 "author": {"type": "string"},
33+ "host_language": {"$ref": "qmk.definitions.v1#/text_identifier"},
34 "keyboard": {"$ref": "qmk.definitions.v1#/text_identifier"},
35 "keymap": {"$ref": "qmk.definitions.v1#/text_identifier"},
36 "layout": {"$ref": "qmk.definitions.v1#/layout_macro"},
37@@ -15,10 +16,42 @@
38 "items": {"type": "string"}
39 }
40 },
41+ "macros": {
42+ "type": "array",
43+ "items": {
44+ "type": "array",
45+ "items": {
46+ "oneOf": [
47+ {
48+ "type": "string"
49+ },
50+ {
51+ "type": "object",
52+ "additionalProperties": false,
53+ "properties": {
54+ "action": {
55+ "type": "string",
56+ "enum": ['beep', 'delay', 'down', 'tap', 'up']
57+ },
58+ "keycodes": {
59+ "type": "array",
60+ "items": {
61+ "$ref": "qmk.definitions.v1#/text_identifier"
62+ }
63+ },
64+ "duration": {
65+ "$ref": "qmk.definitions.v1#/unsigned_int"
66+ }
67+ }
68+ }
69+ ]
70+ }
71+ }
72+ },
73 "config": {"$ref": "qmk.keyboard.v1"},
74 "notes": {
75 "type": "string",
76 "description": "asdf"
77 }
78 }
79-}
80+}
81diff --git a/docs/feature_macros.md b/docs/feature_macros.md
82index 6807111ca2d38d8896f5978036ce94e2a26118cd..81ade5859261a45c259905dd6df276b4a591eca6 100644
83--- a/docs/feature_macros.md
84+++ b/docs/feature_macros.md
85@@ -4,7 +4,107 @@ Macros allow you to send multiple keystrokes when pressing just one key. QMK has
86
87diff --git a/keyboards/handwired/pytest/macro/.noci b/keyboards/handwired/pytest/macro/.noci
88new file mode 100644
89index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
90--- /dev/null
91+++ b/keyboards/handwired/pytest/macro/.noci
92diff --git a/keyboards/handwired/pytest/macro/info.json b/keyboards/handwired/pytest/macro/info.json
93new file mode 100644
94index 0000000000000000000000000000000000000000..ed052a14a3ee8d0e1208202993781ca925cd8628
95--- /dev/null
96+++ b/keyboards/handwired/pytest/macro/info.json
97@@ -0,0 +1,10 @@
98+{
99+ "maintainer": "qmk",
100+ "layouts": {
101+ "LAYOUT_custom": {
102+ "layout": [
103+ { "label": "KC_Q", "matrix": [0, 0], "w": 1, "x": 0, "y": 0 }
104+ ]
105+ }
106+ }
107+}
108diff --git a/keyboards/handwired/pytest/macro/keymaps/default/keymap.json b/keyboards/handwired/pytest/macro/keymaps/default/keymap.json
109new file mode 100644
110index 0000000000000000000000000000000000000000..f319d862d83335f543d399b380a2abae19cda81f
111--- /dev/null
112+++ b/keyboards/handwired/pytest/macro/keymaps/default/keymap.json
113@@ -0,0 +1,15 @@
114+{
115+ "keyboard": "handwired/pytest/basic",
116+ "keymap": "default_json",
117+ "layout": "LAYOUT_ortho_1x1",
118+ "layers": [["MACRO_0"]],
119+ "macros": [
120+ [
121+ "Hello, World!",
122+ {"action":"tap", "keycodes":["ENTER"]}
123+ ]
124+ ],
125+ "author": "qmk",
126+ "notes": "This file is a keymap.json file for handwired/pytest/basic",
127+ "version": 1
128+}
129diff --git a/keyboards/handwired/pytest/macro/readme.md b/keyboards/handwired/pytest/macro/readme.md
130new file mode 100644
131index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
132--- /dev/null
133+++ b/keyboards/handwired/pytest/macro/readme.md
134diff --git a/keyboards/handwired/pytest/macro/rules.mk b/keyboards/handwired/pytest/macro/rules.mk
135new file mode 100644
136index 0000000000000000000000000000000000000000..6b42774dbffb0b5faeb45a5d39a2d40c1e26d1a3
137--- /dev/null
138+++ b/keyboards/handwired/pytest/macro/rules.mk
139@@ -0,0 +1 @@
140+MCU = atmega32u4
141diff --git a/lib/python/qmk/cli/json2c.py b/lib/python/qmk/cli/json2c.py
142index a90578c021d791eb77c8f310b4ca78c8c1e130bb..ae8248e6b72a04fcadcc183b88ddb2861acccd22 100755
143--- a/lib/python/qmk/cli/json2c.py
144+++ b/lib/python/qmk/cli/json2c.py
145@@ -33,7 +33,7 @@ def json2c(cli):
146 cli.args.output = None
147
148 # Generate the keymap
149- keymap_c = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
150+ keymap_c = qmk.keymap.generate_c(user_keymap)
151
152 if cli.args.output:
153 cli.args.output.parent.mkdir(parents=True, exist_ok=True)
154diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py
155index 2995a5fdabebd75d3131a35665d8ba537f3f257d..5a019437734c398bf0ba383a049c35ffaaca9ca0 100644
156--- a/lib/python/qmk/commands.py
157+++ b/lib/python/qmk/commands.py
158@@ -190,7 +190,7 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va
159 target = f'{keyboard_filesafe}_{user_keymap["keymap"]}'
160 keyboard_output = Path(f'{KEYBOARD_OUTPUT_PREFIX}{keyboard_filesafe}')
161 keymap_output = Path(f'{keyboard_output}_{user_keymap["keymap"]}')
162- c_text = qmk.keymap.generate_c(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
163+ c_text = qmk.keymap.generate_c(user_keymap)
164 keymap_dir = keymap_output / 'src'
165 keymap_c = keymap_dir / 'keymap.c'
166
167diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
168index 6eec49cfd1a7793529632d0ad24a1ff1434ab69d..00b5a78a5ac510bc0384c08c3936014261bb63f5 100644
169--- a/lib/python/qmk/keymap.py
170+++ b/lib/python/qmk/keymap.py
171@@ -17,6 +17,7 @@ from qmk.errors import CppError
172
173 # The `keymap.c` template to use when a keyboard doesn't have its own
174 DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
175+__INCLUDES__
176
177 /* THIS FILE WAS GENERATED!
178 *
179@@ -27,6 +28,7 @@ DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
180 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
181 __KEYMAP_GOES_HERE__
182 };
183+
184 """
185
186
187@@ -180,10 +182,11 @@ def generate_json(keymap, keyboard, layout, layers):
188 return new_keymap
189
190
191-def generate_c(keyboard, layout, layers):
192- """Returns a `keymap.c` or `keymap.json` for the specified keyboard, layout, and layers.
193+def generate_c(keymap_json):
194+ """Returns a `keymap.c`.
195+
196+ `keymap_json` is a dictionary with the following keys:
197
198- Args:
199 keyboard
200 The name of the keyboard
201
202@@ -192,19 +195,89 @@ def generate_c(keyboard, layout, layers):
203
204 layers
205 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
206+
207+ macros
208+ A sequence of strings containing macros to implement for this keyboard.
209 """
210- new_keymap = template_c(keyboard)
211+ new_keymap = template_c(keymap_json['keyboard'])
212 layer_txt = []
213- for layer_num, layer in enumerate(layers):
214+
215+ for layer_num, layer in enumerate(keymap_json['layers']):
216 if layer_num != 0:
217 layer_txt[-1] = layer_txt[-1] + ','
218 layer = map(_strip_any, layer)
219 layer_keys = ', '.join(layer)
220- layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
221+ layer_txt.append('\t[%s] = %s(%s)' % (layer_num, keymap_json['layout'], layer_keys))
222
223 keymap = '\n'.join(layer_txt)
224 new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap)
225
226+ if keymap_json.get('macros'):
227+ macro_txt = [
228+ 'bool process_record_user(uint16_t keycode, keyrecord_t *record) {',
229+ ' if (record->event.pressed) {',
230+ ' switch (keycode) {',
231+ ]
232+
233+ for i, macro_array in enumerate(keymap_json['macros']):
234+ macro = []
235+
236+ for macro_fragment in macro_array:
237+ if isinstance(macro_fragment, str):
238+ macro_fragment = macro_fragment.replace('\\', '\\\\')
239+ macro_fragment = macro_fragment.replace('\r\n', r'\n')
240+ macro_fragment = macro_fragment.replace('\n', r'\n')
241+ macro_fragment = macro_fragment.replace('\r', r'\n')
242+ macro_fragment = macro_fragment.replace('\t', r'\t')
243+ macro_fragment = macro_fragment.replace('"', r'\"')
244+
245+ macro.append(f'"{macro_fragment}"')
246+
247+ elif isinstance(macro_fragment, dict):
248+ newstring = []
249+
250+ if macro_fragment['action'] == 'delay':
251+ newstring.append(f"SS_DELAY({macro_fragment['duration']})")
252+
253+ elif macro_fragment['action'] == 'beep':
254+ newstring.append(r'"\a"')
255+
256+ elif macro_fragment['action'] == 'tap' and len(macro_fragment['keycodes']) > 1:
257+ last_keycode = macro_fragment['keycodes'].pop()
258+
259+ for keycode in macro_fragment['keycodes']:
260+ newstring.append(f'SS_DOWN(X_{keycode})')
261+
262+ newstring.append(f'SS_TAP(X_{last_keycode})')
263+
264+ for keycode in reversed(macro_fragment['keycodes']):
265+ newstring.append(f'SS_UP(X_{keycode})')
266+
267+ else:
268+ for keycode in macro_fragment['keycodes']:
269+ newstring.append(f"SS_{macro_fragment['action'].upper()}(X_{keycode})")
270+
271diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py
272index 1e3c64e7352656d80aadb3920c4e83390cdd0686..2973f817022e6a481f4d7faddc173040672d01d3 100644
273--- a/lib/python/qmk/tests/test_cli_commands.py
274+++ b/lib/python/qmk/tests/test_cli_commands.py
275@@ -142,6 +142,14 @@ def test_json2c():
276 assert result.stdout == '#include QMK_KEYBOARD_H\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\t[0] = LAYOUT_ortho_1x1(KC_A)};\n\n'
277
278
279+def test_json2c_macros():
280+ result = check_subcommand("json2c", 'keyboards/handwired/pytest/macro/keymaps/default/keymap.json')
281+ check_returncode(result)
282+ assert 'LAYOUT_ortho_1x1(MACRO_0)' in result.stdout
283+ assert 'case MACRO_0:' in result.stdout
284+ assert 'SEND_STRING("Hello, World!"SS_TAP(X_ENTER));' in result.stdout
285+
286+
287 def test_json2c_stdin():
288 result = check_subcommand_stdin('keyboards/handwired/pytest/has_template/keymaps/default_json/keymap.json', 'json2c', '-')
289 check_returncode(result)
290diff --git a/lib/python/qmk/tests/test_qmk_keymap.py b/lib/python/qmk/tests/test_qmk_keymap.py
291index b9e80df6724996b07dff0a031214054d9896cb7d..5e2efc12329b9948b494ee3021dbecacd089e8e6 100644
292--- a/lib/python/qmk/tests/test_qmk_keymap.py
293+++ b/lib/python/qmk/tests/test_qmk_keymap.py
294@@ -22,7 +22,13 @@ def test_template_json_pytest_has_template():
295
296
297 def test_generate_c_pytest_has_template():
298- templ = qmk.keymap.generate_c('handwired/pytest/has_template', 'LAYOUT', [['KC_A']])
299+ keymap_json = {
300+ 'keyboard': 'handwired/pytest/has_template',
301+ 'layout': 'LAYOUT',
302+ 'layers': [['KC_A']],
303+ 'macros': None,
304+ }
305+ templ = qmk.keymap.generate_c(keymap_json)
306 assert templ == '#include QMK_KEYBOARD_H\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\t[0] = LAYOUT(KC_A)};\n'
307
308
309diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
310index cde97074d398134837978918ec85a576ddca938c..d013a6a169d94a9947baec8cc39142c474f6a358 100644
311--- a/quantum/quantum_keycodes.h
312+++ b/quantum/quantum_keycodes.h
313@@ -558,6 +558,40 @@ enum quantum_keycodes {
314 PROGRAMMABLE_BUTTON_31,
315 PROGRAMMABLE_BUTTON_32,
316
317+ // Dedicated macro keys for Configurator and VIA
318+ MACRO_0,
319+ MACRO_1,
320+ MACRO_2,
321+ MACRO_3,
322+ MACRO_4,
323+ MACRO_5,
324+ MACRO_6,
325+ MACRO_7,
326+ MACRO_8,
327+ MACRO_9,
328+ MACRO_10,
329+ MACRO_11,
330+ MACRO_12,
331+ MACRO_13,
332+ MACRO_14,
333+ MACRO_15,
334+ MACRO_16,
335+ MACRO_17,
336+ MACRO_18,
337+ MACRO_19,
338+ MACRO_20,
339+ MACRO_21,
340+ MACRO_22,
341+ MACRO_23,
342+ MACRO_24,
343+ MACRO_25,
344+ MACRO_26,
345+ MACRO_27,
346+ MACRO_28,
347+ MACRO_29,
348+ MACRO_30,
349+ MACRO_31,
350+
351 // Start of custom keycode range for keyboards and keymaps - always leave at the end
352 SAFE_RANGE
353 };
354diff --git a/requirements-dev.txt b/requirements-dev.txt
355index 1db3b6d7331545d253b7cd8b27cd17feacef665d..72137073381c04b8f63a43141b1dfd93145a925c 100644
356--- a/requirements-dev.txt
357+++ b/requirements-dev.txt
358@@ -5,4 +5,5 @@
359 nose2
360 flake8
361 pep8-naming
362+pyflakes
363 yapf