aboutsummaryrefslogtreecommitdiff
path: root/users/dennytom/chording_engine
diff options
context:
space:
mode:
Diffstat (limited to 'users/dennytom/chording_engine')
-rw-r--r--users/dennytom/chording_engine/README.md376
-rw-r--r--users/dennytom/chording_engine/chord.py466
-rw-r--r--users/dennytom/chording_engine/engine.part.1163
-rw-r--r--users/dennytom/chording_engine/engine.part.2323
-rw-r--r--users/dennytom/chording_engine/engine.part.3404
-rw-r--r--users/dennytom/chording_engine/keymap_def.schema.json337
-rw-r--r--users/dennytom/chording_engine/parser.py231
-rw-r--r--users/dennytom/chording_engine/state_machine.dot49
-rw-r--r--users/dennytom/chording_engine/state_machine.svg235
-rw-r--r--users/dennytom/chording_engine/tests/minunit.h288
-rw-r--r--users/dennytom/chording_engine/tests/test.c1259
-rw-r--r--users/dennytom/chording_engine/tests/test_full.sh11
-rw-r--r--users/dennytom/chording_engine/tests/test_keymap_def.json145
-rw-r--r--users/dennytom/chording_engine/tests/test_quick.sh6
14 files changed, 4293 insertions, 0 deletions
diff --git a/users/dennytom/chording_engine/README.md b/users/dennytom/chording_engine/README.md
new file mode 100644
index 000000000..3610b190b
--- /dev/null
+++ b/users/dennytom/chording_engine/README.md
@@ -0,0 +1,376 @@
1# README
2
3## About
4
5This is a custom combo engine. I call it chording engine mostly to differentiate it from QMK's combos. It is useful even if you are not using chording as a main input method to replace combos.
6
7Why does this exist? Typing on tiny keyboards can be challenging and you will end up relying on dances and / or combos. Pure QMK combos can be insufficient as they do not really support overlapping combos. For example, if you define 3 combos `(KC_Q, KC_W)`, `(KC_Z, KC_X)` and `(KC_Q, KC_W, KC_Z, KC_X)` and press Q, W, Z and X at the same time, all three combos will activate. Steno engines (and g Board Industries' custom steno inspired engine) solve this, however, they don't allow for comfortable typing in the traditional way. The steno chord activates only when *all* keys are lifted and makes it difficult to implement some advanced features. This engine treats each chord independently to allow for more comfortable typing experience.
8
9## TOC
10
11- [README](#readme)
12 - [About](#about)
13 - [TOC](#toc)
14 - [Start here](#start-here)
15 - [Steps](#steps)
16 - [Features](#features)
17 - [Chords](#chords)
18 - [Tap-Dance](#tap-dance)
19 - [Pseudolayers](#pseudolayers)
20 - [Control chords](#control-chords)
21 - [Settings up JSON definition](#settings-up-json-definition)
22 - [Keyboard and engine parameters](#keyboard-and-engine-parameters)
23 - [Pseudolayers](#pseudolayers-1)
24 - [Supported keycodes](#supported-keycodes)
25 - [Leader Key](#leader-key)
26 - [Extra code](#extra-code)
27 - [Further details](#further-details)
28 - [Implementation](#implementation)
29 - [Internal keycodes](#internal-keycodes)
30 - [Chords](#chords-1)
31 - [Caveats](#caveats)
32
33## Start here
34
35This engine therefore uses python parser that translates a JSON definition of keyboard specific information and keymap definition and produces `keymap.c`. Every function on this keymap is a chord (combo). The resulting keymap file is long and I do not encourage you to edit it. All you should have to edit is the JSON file. To produce the keymap file, run
36
37```sh
38./parser.py keymap_def.json keymap.c
39```
40
41To prepare the keymap JSON definition, you can use on of my keymaps as a starting point. I have on for butterstick and for georgi. There is also a JSON schema that has some examples and sane defaults. All details are explained in the next section. The parser tries to validate some of the things that the JSON schema can not. Finally there is a JSON in the tests folder that has at least one example of every feature.
42
43Watch out, you can not name your JSON file `keymap.json` if you place in the keymap folder. QMK creates `keymap.json` as a part of compilation process and if you already have one, it gets confused.
44
45## Steps
46
47When setting up a new keyboard, follow the steps:
48
491. Make a new directory for your keymap as QMK's documentation describes.
502. Write your JSON. Name it anything but `keymap.json`.
513. Depending on the keyboard / keymap, create `rules.mk` (follow QMK's documentation and note that if the keyboard's `rules.mk` include custom source files, this is the place you can remove them).
524. Use my python parser to generate the `keymap.c`. Run it from the `/users/dennytom/chording_engine` directory as it is using relative paths to some extra files.
535. Follow QMK's documentation to compile and flash your firmware.
54
55## Features
56
57### Chords
58
59Once again, *everything* on this keymap is a chord. Even sending `KC_Q` is done by pressing a single key chord. Chord gets activated after all it's keys get pressed. Only the longest chord gets activated. The order of the pressed keys *does not matter*, only the fact they have been pressed within the same time frame. An active chord gets deactivated if *any* of it's keys gets depressed. To activate the same single chord again, *all* it's keys have to be depressed and pressed again. With a few exceptions chords are independent of each other. No matter if some chords are currently active and some not, others can be activated or deactivated without affecting each other's state. *If you press keys to belonging to multiple different, non-overlapping chords, all get activated in the order they are defined in the keymap.*
60
61### Tap-Dance
62
63To make it even stranger, all chords are technically tap-dance chords. They are relatively simple state machines that execute a specific function every time they change state. For simplicity and optimization purposes, there are a few prewritten functions that implement common features like "send a single key" or "lock". Any number of chords can be "in dance" at any given moment without affecting each other's state. Custom dances can be easily added. Check out the `state_machine.png` to see all the states any chord can be in.
64
65### Pseudolayers
66
67Only one QMK layer is used. Following the butterstick's default keymap's example, the chording engine is using pseudolayers. The main difference to QMK's layers is that only one pseudolayer can be active at each time (meaning you can not use `KC_TRANS`, I actually don't know what will happen if you do). Chords can be activated only if they are on the currently active pseudolayer. Chords that are currently active do not get deactivated if the pseudolayer changes and will deactivate if any of their keys gets depressed even no matter the current pseudolayer. Locked chords (see below) and chords on the `ALWAYS_ON` pseudolayer can be activated anytime.
68
69### Control chords
70
71The engine implements a number of ways of changing how chords behave:
72
73* **Lock**: Similarly to QMK's lock, the next chord activated after the Lock chord will not deactivate on release of any of its keys, it will deactivate when all its keys get pressed again. Any number of chords can be locked at the same time. To make sure a locked chord can be unlocked, it can activate no matter the current pseudolayer. A chord can be locked mid dance.
74* **One shots**: Chords that send keycodes and chords that turn on pseudolayers can be one shots. If tapped, they will lock (stay active) until the next keycode gets sent, *not necessarily when the next chord gets activated*. If held, they will deactivate on release *even if no keycode got sent*.
75* **Tap-Hold**: Also called key-layer dance and key-key dance. Either sends a defined keycode on tap and temporarily switches pseudolayer on hold *or* sends two different keycodes on tap and hold.
76* **Command mode**: After getting activated for the first time, the keyboard switches to command mode. All *keycodes* that would get registered get buffered instead. After activating the Command mode chord for the second time, all buffered keycodes get released at the same time allowing for key combination that would be hard or impossible to press. The Command mode only affects keycodes. It is therefore possible to change pseudolayers or activate / deactivate other chords while in Command mode. While multiple Command mode chords can be defined, they would not be independent. The keyboard either is or is not in command mode and there is only one buffer.
77* **Leader key**: Just like pure QMK's Leader key, this allows you to add functions that get executed if the Leader key and a specific sequence of keycodes gets registered in a predefined order in a short timeframe. For example `:wq` can send `Ctrl+S` and `Ctrl+W` in a quick succession. While multiple Leader keys can be defined, they all would access the same list of sequences.
78* **Dynamic macro**: A sequence of keycodes can be recorded and stored in the RAM of the keyboard and replayed.
79
80## Settings up JSON definition
81
82The JSON definition has 3 main sections. The elements `keys`, `parameters` and `layers` teach the engine about the details of your keyboard and set its parameters. The elements `pseudolayers`, `leader_sequences` and `chord_sets` define your keymap. Finally, the elements `extra_code` and `extra_dependencies` allow you to include more code to extend the capabilities of the engine.
83
84### Keyboard and engine parameters
85
86I do not have experience with stenography, so the the steno keycodes are hard for me to remember. That is why the keymap is using new keycodes TOP1, TOP2, ... .
87
88```c
89 "keys": ["TOP1", "TOP2", "TOP3", ...]
90```
91
92You can name these however you like as long as they do not crash with QMK's keycodes.
93
94*The chording engine in it's current implementation can handle up to 64 keys. If you need to support more, contact me (email or u/DennyTom at Reddit).*
95
96All timings, maximum lengths for macros, command mode and leader function are defined in `keyboard_parameters` field. Almost all should be pretty self-explanatory.
97
98My keyboards are small, so I only use the engine, but you might want to use layers that combine chord-able keys and traditional QMK keys or layers with advanced keycodes, for example for stenography. The array `layers` defines all the parser needs to know:
99
100```json
101"layers": [
102 {
103 "type": "auto"
104 },
105 {
106 "type": "manual",
107 "keycodes": ["KC_1", "KC_2", "KC_3", "KC_4", "KC_5", "KC_6", "KC_7", "KC_8", "KC_9", "KC_0",
108 "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y", "KC_U", "KC_I", "KC_O", "KC_P"
109 ]
110 },
111 {
112 "type": "manual",
113 "keycodes": ["KC_1", "KC_2", "KC_3", "KC_4", "KC_5", "KC_6", "KC_7", "KC_8", "KC_9", "KC_0",
114 "BOT1", "BOT2", "BOT3", "BOT4", "BOT5", "BOT6", "BOT7", "BOT8", "BOT9", "BOT0"]
115 }
116 ]
117```
118
119This example defines 3 layers, one that is automatically populated with chording engine's internal keycodes, second that is populated with QMK's keycodes and third that uses both internal and QMK's keycodes. The layers do not have names, you have to access them with `TO(1)` and `TO(0)`.
120
121Some keyboards mangle the order of keycodes when registering them in the layers. For that fill up the `layout_function_name` with the name of function / macro. If your keyboard does not do it, leave that string empty.
122
123### Pseudolayers
124
125Array `pseudolayers` defines the keymap per pseudolayer. Each field has to contain the name for the layer and the list of chords.
126
127```JSON
128"pseudolayers": [
129 {
130 "name": "QWERTY",
131 "chords": [
132 {
133 "type": "simple",
134 "keycode": "SPACE",
135 "chord": ["BOT1", "BOT0"]
136 },
137 {
138 "type": "visual",
139 "keycode": "CLEAR_KB",
140 "chord": [
141 "X", "", "", "", "", "", "", "", "", "X",
142 "X", "", "", "", "", "", "", "", "", "X",
143 ]
144 },
145 {
146 "type": "visual_array",
147 "keys": ["TOP1", "TOP2", "TOP3"],
148 "dictionary": [
149 ["X", "X", " ", "ESC"],
150 [" ", "X", "X", "TAB"],
151 ["X", "X", "X", "ENTER"]
152 ]
153 },
154 {
155 "type": "chord_set",
156 "set": "rows",
157 "keycodes": [
158 "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
159 "A", "S", "D", "F", "G", "H", "J", "K", "L", ";",
160 "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/"
161 ]
162 }
163 ]
164 }
165]
166```
167
168The array `chord` defines chords. You can either use simple chord and list all the keys that have to pressed at the same time, or use the visual chord and place `"X"` over keys that will be part of the chord. You can also use `visual_array` to define a number of chords in a visual way on a subset of keys defined in the `keys` array. Finally, you can use `chord_set` to define a number of chords following a pattern that was set in the `chord_sets` array in the root object like this:
169
170```json
171"chord_sets": [
172 {
173 "name": "rows",
174 "chords": [
175 ["TOP1"], ["TOP2"], ["TOP3"], ["TOP4"], ["TOP5"], ["TOP6"], ["TOP7"], ["TOP8"], ["TOP9"], ["TOP0"]
176 ["TOP1", "BOT1"], ["TOP2", "BOT2"], ["TOP3", "BOT3"], ["TOP4", "BOT4"], ["TOP5", "BOT5"], ["TOP6", "BOT6"], ["TOP7", "BOT7"], ["TOP8", "BOT8"], ["TOP9", "BOT9"], ["TOP0", "BOT0"],
177 ["BOT1"], ["BOT2"], ["BOT3"], ["BOT4"], ["BOT5"], ["BOT6"], ["BOT7"], ["BOT8"], ["BOT9"], ["BOT0"]
178 ]
179 },
180 {
181 "name": "cols",
182 "chords": [
183 ["TOP1", "TOP2"], ["TOP2", "TOP3"], ["TOP3", "TOP4"], ["TOP4", "TOP5"], ["TOP5", "TOP6"], ["TOP6", "TOP7"], ["TOP7", "TOP8"], ["TOP8", "TOP9"], ["TOP9", "TOP0"],
184 ["TOP1", "TOP2", "BOT1", "BOT2"], ["TOP2", "TOP3", "BOT2", "BOT3"], ["TOP3", "TOP4", "BOT3", "BOT4"], ["TOP4", "TOP5", "BOT4", "BOT5"], ["TOP5", "TOP6", "BOT5", "BOT6"], ["TOP6", "TOP7", "BOT6", "BOT7"], ["TOP7", "TOP8", "BOT7", "BOT8"], ["TOP8", "TOP9", "BOT8", "BOT9"], ["TOP9", "TOP0", "BOT9", "BOT0"],
185 ["BOT1", "BOT2"], ["BOT2", "BOT3"], ["BOT3", "BOT4"], ["BOT4", "BOT5"], ["BOT5", "BOT6"], ["BOT6", "BOT7"], ["BOT7", "BOT8"], ["BOT8", "BOT9"], ["BOT9", "BOT00"],
186 ]
187 }
188]
189```
190
191
192
193You might notice that the code tries to do a few clever things when parsing keycodes:
194
195* If the keycode would be just a character basic keycode, it tries to allow the use of shortcuts. `Q` will get replaced with `KC_Q`, `,` becomes `KC_COMMA`. This *should* work for all KC_ keycodes unless I missed some.
196* `MO()` and `DF()` macros work the same way for pseudolayers as they would for layers in pure QMK.
197* `O()` is a shortcut for `OSK()` or `OSL()`.
198* `STR('...')` sends a string. Careful with quoting.
199* Special chords like Command mode have their own codes like `CMD`.
200* The empty strings get ignored.
201
202### Supported keycodes
203
204* **`X`** or **`KC_X`**: Send code `KC_X` just like a normal keyboard.
205
206* **`STR("X")`**: Send string "x" on each activation of the chord. Once again, watch out for quoting and escaping characters. If you want special characters (especially quotes) in your string, look up Python reference for string literals and experiment. Also, because of how the string gets parsed, it is not possible to use `(` in the string.
207
208* **`MO(X)`**: Temporary switch to pseudolayer `X`. Because only one pseudolayer can be active at any moment, this works by switching back to the pseudolayer the chord lives on on deactivation. If you chain `MO()`s on multiple pseudolayers and deactivate them in a random order, you might end up stranded on a pseudolayer. I recommend adding `CLEAR` somewhere on `ALWAYS_ON` pseudolayer just in case.
209
210* **`MO(X,Y)`**: Temporary switch to pseudolayer `Y`. Switches to pseudolayer `X` on deactivation. Especially useful when you want to put the `MO()` chord on `ALWAYS_ON`.
211
212* **`DF(X)`**: Permanent switch to pseudolayer `X`.
213
214* **`TO(X)`**: Switches the QMK layer to `X`.
215
216* **`O(X)`**: One-shot key `X` (if `X` starts with `"KC_"`) or one-shot layer `X` (otherwise) . Both have retro tapping enabled.
217
218* **Tap-holds**
219
220 * **`KK(X, Y)`**: Pulses code `X` on tap and code `Y` on hold.
221 * **`KL(X, Y)`**: Pulses code `X` on tap and switches to pseudolayer `Y` on hold. If during the hold no key gets registered, the code `X` will get sent instead (similar to QMK's retro tapping).
222 * **`KM(X, Y)`**: Same as `KK()` but meant for modifiers on hold. Instead of a timer to figure out tap-hold, uses retro tapping like behavior just like `KL()`. This has issues with GUI and ALT as they often have a meaning.
223 * The chording engine determines if you are holding a chord based on a *global* timer. If you start holding a tap-hold chord and very quickly start tapping other chords, the hold might not activate until a short moment *after the last* chord when the timer expires. If you are running into this, adjust timeouts or wait a brief moment after pressing the chord to make sure it switches into the hold state before pressing other chords.
224
225* **Autoshift**
226
227 * **`AS(X)`**: Pulses code `X` on tap and Pulses left shift + `X` on hold.
228 * **`AT`** : Toggles autoshift for all autoshift chords. If off, all `AS` chords act like `KC` chords.
229
230* **`LOCK`**: The lock key. Since tap-dances of chords are independent, it is possible to lock a chord *anywhere in it's dance if you time it right!*. If that happens, use the `CLEAR` chord or restart your keeb.
231
232* **`CMD`**: The command mode. The number of keycodes that can be buffered is defined in in `command_max_length`.
233
234* **`LEAD`**: The leader key. The maximum length of the sequences needs to be defined in `keyboard_params`. You can use `leader_sequences` array to add sequences:
235
236 ```json
237 "leader_sequences": [
238 {
239 "name": "fn_L1",
240 "function": "void fn_L1(void) { SEND(KC_LCTL); SEND(KC_LALT); SEND(KC_DEL); }",
241 "sequence": ["KC_Q", "KC_Z"]
242 }
243 ]
244 ```
245
246 When the engine notices the sequence, it will call the function defined in the field `name`. You can either define it in the `function` field, in the field `extra_code` or in an external file that you then have to insert manually or using the `extra_dependencies` array. The parser copy-pastes the contents `extra_code` of all files specified in the `extra_dependencies` array in the `keymap.c`.
247
248* **`M(X, VALUE1, VALUE2)`**: A custom macro. Adds a chord that will use function `X` and with `chord.value1 = VALUE1; chord.value2 = VALUE2;`. The function `X` can be arbitrary C function, go crazy. Just like with the leader sequences, you have to insert the code into the generated `keymap.c` manually or through `extra_code` or `extra_dependencies`. The following example defines a macro that acts exactly like `KC_MEH` (the chording engine *should* support `KC_MEH`, this is just an example):
249
250 ```c
251 void fn_M1(const struct Chord* self) {
252 switch (*self->state) {
253 case ACTIVATED:
254 key_in(KC_LCTL);
255 key_in(KC_LSFT);
256 key_in(KC_LALT);
257 break;
258 case DEACTIVATED:
259 key_out(KC_LCTL);
260 key_out(KC_LSFT);
261 key_out(KC_LALT);
262 break;
263 case FINISHED:
264 case FINISHED_FROM_ACTIVE:
265 break;
266 case RESTART:
267 key_out(KC_LCTL);
268 key_out(KC_LSFT);
269 key_out(KC_LALT);
270 break;
271 default:
272 break;
273 }
274 }
275 ```
276
277 Since this feels like it would be the most common way to use this feature, I wrote a macro for this:
278
279* **`MK(X1, X2, ...)`**: Acts like `KC()` except it registers / unregisters all `X1`, `X2`, ... codes at the same time.
280
281* **`D(X1, X2, ...)`**: A basic keycode dance. If tapped (or held), registers `X1`. If tapped and then tapped again (or held), registers `X2`, ... It *cannot* be combined with tap-hold, however holding will result in repeat. You can put in as many basic keycodes as you want, but the macro will break if you go beyond 256. It will try to expand shortened keycodes. Advanced keycodes are not supported.
282
283* **`DM_RECORD`, `DM_NEXT`, `DM_END`, `DM_PLAY`**: Start recording a dynamic macro. Once you start recording, basic keycodes will get stored. When replaying the macro, all keys you press before `DM_NEXT` or `DM_END` will get pressed at the same time. For example the sequence `DM_RECORD`, `KC_CTRL`, `KC_A`, `DM_NEXT`, `KC_BSPC`, `DM_END` will record a macro that when played will execute the sequence Ctrl+a, Backspace. `dynamic_macro_max_length` defines the maximum length of the macro to be recorded. You can increase it for the price of RAM. The example above requires 4 units of length to be saved (Ctrl, A, next, Backspace).
284
285* **`CLEAR_KB`**: clears keyboard, sets all chords to the default state and switches the pseudolayer to the default one. Basically the emergency stop button.
286
287* **`RESET`**: Go to the DFU flashing mode.
288
289**Caveat** of the current implementation is that the tap-hold, `MK` and `D` keycodes can not accept any of the keycodes that have some sort a function like dynamic macro specific chords, `CLEAR_KB`, `RESET`, `LOCK`, `AT`, ...
290
291### Leader Key
292
293The sequences are not defined by the *keys* you press but by the *keycodes* that get intercepted. The length of the sequence must be equal or shorter than the maximum (defined in `keyboard.inc`). Currently, the timeout for the leader sequence refreshes after each key pressed. If the sequence is not in the database, nothing will happen.
294
295### Extra code
296
297Extra C code needed to define custom chords can be added by quoting in in the `extra_code` element or by saving it in another header file and including it using the `extra_dependencies` element:
298
299```json
300{
301 "extra_code": "void double_dance(const struct Chord* self) { ... }\n",
302 "extra_dependencies": ["my_header.h"]
303}
304```
305
306
307
308## Further details
309
310### Implementation
311
312The source files are split into several files. `engine.part.1`, `engine.part.2` and `engine.part.3` contain C code that defines the Chord structure, implementations for all provided functions and the engine itself. `parser.py` generates keyboard and keymap dependent code. The file `chord.py` contains most of the logic required to properly translate chords from the JSON to the C code. I rarely write in python, if you have improvements, let me know, *please*.
313
314### Internal keycodes
315
316When `process_record_user()` gets one of the internal keycodes, it returns `true`, completely bypassing keyboard's and QMK's `process_record` functions. *All other* keycodes get passed down to QMK's standard processing.
317
318### Chords
319
320Each chord is defined by a constant structure, a function and two non-constant `int` variables keeping the track of the chord's state:
321
322```c
323struct Chord {
324 uint32_t keycodes_hash;
325 uint8_t pseudolayer;
326 uint8_t* state;
327 uint8_t* counter;
328 uint16_t value1;
329 uint8_t value2;
330 void (*function) (const struct Chord*);
331};
332
333uint8_t state_0 = IDLE;
334uint8_t counter_0 = 0;
335void function_0(struct Chord* self) {
336 switch (*self->state) {
337 case ACTIVATED:
338 register_code(self->value1);
339 break;
340 case DEACTIVATED:
341 unregister_code(self->value1);
342 break;
343 case FINISHED:
344 case PRESS_FROM_ACTIVE:
345 break;
346 case RESTART:
347 unregister_code(self->value1);
348 break;
349 default:
350 break;
351 }
352}
353const struct Chord chord_0 PROGMEM = {H_TOP1, QWERTY, &state_0, &counter_0, KC_Q, 0, function_0};
354```
355
356All chords have to be added to `list_of_chord` array that gets regularly scanned and processed. The function doesn't actually activate on all state changes, there are a few more like `IDLE` (nothing is currently happening to the chord) or `IN_ONE_SHOT` (the chord is one shot and is currently locked). Those are all necessary for internal use only. The ones you have to worry about are
357
358* `ACTIVATED`: Analogous to a key being pressed (this includes repeated presses for tap-dance)
359* `DEACTIVATED`: Analogous to a key being depressed (also can be repeated)
360* `FINISHED`: Happens if the chord got deactivated and then the dance timer expired.
361* `PRESS_FROM_ACTIVE`: Happens if the chord was active when the dance timer expired. Meaning you at least once activated the chord and then kept holding it down. Useful to recognize taps and holds.
362* `FINISHED_FROM_ACTIVE`: Happens *after* `PRESS_FROM_HOLD` if the chord is still active when the dance timer expires for the second time. Can be combined with the `counter` to recognize even longer presses. Useful if you want to recognize long presses, for example for autoshift functionality. In `keyboard.inc` you can set `LONG_PRESS_MULTIPLIER` to set how many times does dance timer have to expire for the autoshift to trigger.
363* `RESTART`: The dance is done. Happens immediately after `FINISHED` or on chord deactivation from `FINISHED_FROM_ACTIVE`. Anything you have to do to get the chord into `IDLE` mode happens here.
364
365The chords change states based on external and internal events. Anytime a chord's function is activated, it may change it's own state. Also, on certain events, the chording engine will trigger the functions of all chords in a specific state and *if the chords' state hasn't changed* it will then change it appropriately. The default behavior when a chord changes state is described by the following diagram:
366
367![state machine diagram](state_machine.png)
368The colors differentiate in which function the change happens, see `state_machine.dot` for a bit more detail. Black arrows happen in more than one function. Arrows without a label happen immediately.
369
370You can see that the diagram is not exhaustive. For example nothing leads into `IN_ONE_SHOT`. That is because the chord's function can change the chord's state. This is useful for some advanced chords that break the default behavir (one-shots) and for optimization (chords that just send `KC_X` do not need to ever go into dance).
371
372## Caveats
373
374Each chord stores as much as possible in `PROGMEM` and unless it needs it, doesn't allocate `counter`. However it still has to store it's `state` and sometimes the `counter` in RAM. If you keep adding more chords, at one point you will run out. If your firmware fits in the memory and your keyboard crashes, try optimizing your RAM usage.
375
376Also, the code is not perfect. I keep testing it, but can not guarantee that it is stable. Some functions take (very short but still) time and if you happen to create keypress event when the keyboard can not see it, a chord can get stuck in a funny state. That is especially fun if the pseudolayer changes and you can not immediately press it again. Just restart the keyboard or push the key a few times.
diff --git a/users/dennytom/chording_engine/chord.py b/users/dennytom/chording_engine/chord.py
new file mode 100644
index 000000000..707f36b82
--- /dev/null
+++ b/users/dennytom/chording_engine/chord.py
@@ -0,0 +1,466 @@
1from functools import reduce
2import re
3
4strings = []
5number_of_strings = -1
6
7def top_level_split(s):
8 """
9 Split `s` by top-level commas only. Commas within parentheses are ignored.
10 """
11
12 # Parse the string tracking whether the current character is within
13 # parentheses.
14 balance = 0
15 parts = []
16 part = ""
17
18 for i in range(len(s)):
19 c = s[i]
20 part += c
21 if c == '(':
22 balance += 1
23 elif c == ')':
24 balance -= 1
25 elif c == ',' and balance == 0 and not s[i+1] == ',':
26 part = part[:-1].strip()
27 parts.append(part)
28 part = ""
29
30 # Capture last part
31 if len(part):
32 parts.append(part.strip())
33
34 return parts
35
36def new_chord(on_pseudolayer, keycodes_hash, has_counter, value1, value2, function, output_buffer, index):
37 counter_link = "NULL"
38 output_buffer += "uint8_t state_" + str(index) + " = IDLE;\n"
39 if has_counter:
40 output_buffer += "uint8_t counter_" + str(index) + " = 0;\n"
41 counter_link = "&counter_" + str(index)
42 output_buffer += "const struct Chord chord_" + str(index) + " PROGMEM = {" + keycodes_hash + ", " + on_pseudolayer + ", &state_" + str(index) + ", " + counter_link + ", " + str(value1) + ", " + str(value2) + ", " + function + "};\n"
43 index += 1
44 return [output_buffer, index]
45
46def KC(on_pseudolayer, keycodes_hash, keycode, output_buffer, index):
47 return new_chord(on_pseudolayer, keycodes_hash, False, keycode, 0, "single_dance", output_buffer, index)
48
49def AS(on_pseudolayer, keycodes_hash, keycode, output_buffer, index):
50 return new_chord(on_pseudolayer, keycodes_hash, True, keycode, 0, "autoshift_dance", output_buffer, index)
51
52def AT(on_pseudolayer, keycodes_hash, output_buffer, index):
53 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "autoshift_toggle", output_buffer, index)
54
55def KL(on_pseudolayer, keycodes_hash, keycode, to_pseudolayer, output_buffer, index):
56 return new_chord(on_pseudolayer, keycodes_hash, True, keycode, to_pseudolayer, "key_layer_dance", output_buffer, index)
57
58def KK(on_pseudolayer, keycodes_hash, keycode1, keycode2, output_buffer, index):
59 return new_chord(on_pseudolayer, keycodes_hash, True, keycode1, keycode2, "key_key_dance", output_buffer, index)
60
61def KM(on_pseudolayer, keycodes_hash, keycode, to_pseudolayer, output_buffer, index):
62 return new_chord(on_pseudolayer, keycodes_hash, False, keycode, to_pseudolayer, "key_mod_dance", output_buffer, index)
63
64def MO(on_pseudolayer, keycodes_hash, to_pseudolayer, output_buffer, index):
65 return new_chord(on_pseudolayer, keycodes_hash, False, to_pseudolayer, 0, "temp_pseudolayer", output_buffer, index)
66
67def MO_alt(on_pseudolayer, keycodes_hash, from_pseudolayer, to_pseudolayer, output_buffer, index):
68 return new_chord(on_pseudolayer, keycodes_hash, False, to_pseudolayer, from_pseudolayer, "temp_pseudolayer_alt", output_buffer, index)
69
70def LOCK(on_pseudolayer, keycodes_hash, output_buffer, index):
71 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "lock", output_buffer, index)
72
73def DF(on_pseudolayer, keycodes_hash, to_pseudolayer, output_buffer, index):
74 return new_chord(on_pseudolayer, keycodes_hash, False, to_pseudolayer, 0, "perm_pseudolayer", output_buffer, index)
75
76def TO(on_pseudolayer, keycodes_hash, to_pseudolayer, output_buffer, index):
77 return new_chord(on_pseudolayer, keycodes_hash, False, to_pseudolayer, 0, "switch_layer", output_buffer, index)
78
79def OSK(on_pseudolayer, keycodes_hash, keycode, output_buffer, index):
80 return new_chord(on_pseudolayer, keycodes_hash, False, keycode, 0, "one_shot_key", output_buffer, index)
81
82def OSL(on_pseudolayer, keycodes_hash, to_pseudolayer, output_buffer, index):
83 return new_chord(on_pseudolayer, keycodes_hash, False, to_pseudolayer, 0, "one_shot_layer", output_buffer, index)
84
85def CMD(on_pseudolayer, keycodes_hash, output_buffer, index):
86 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "command", output_buffer, index)
87
88def DM_RECORD(on_pseudolayer, keycodes_hash, output_buffer, index):
89 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "dynamic_macro_record", output_buffer, index)
90
91def DM_NEXT(on_pseudolayer, keycodes_hash, output_buffer, index):
92 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "dynamic_macro_next", output_buffer, index)
93
94def DM_END(on_pseudolayer, keycodes_hash, output_buffer, index):
95 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "dynamic_macro_end", output_buffer, index)
96
97def DM_PLAY(on_pseudolayer, keycodes_hash, output_buffer, index):
98 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "dynamic_macro_play", output_buffer, index)
99
100def LEAD(on_pseudolayer, keycodes_hash, output_buffer, index):
101 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "leader", output_buffer, index)
102
103def CLEAR(on_pseudolayer, keycodes_hash, output_buffer, index):
104 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "clear", output_buffer, index)
105
106def RESET(on_pseudolayer, keycodes_hash, output_buffer, index):
107 return new_chord(on_pseudolayer, keycodes_hash, False, 0, 0, "reset", output_buffer, index)
108
109def STR(on_pseudolayer, keycodes_hash, string_input, output_buffer, index, number_of_strings, strings):
110 [a, b] = new_chord(on_pseudolayer, keycodes_hash, False, number_of_strings, 0, "string_in", output_buffer, index)
111 return [a, b, number_of_strings + 1, strings + [string_input]]
112
113def M(on_pseudolayer, keycodes_hash, value1, value2, fnc, output_buffer, index):
114 return new_chord(on_pseudolayer, keycodes_hash, True, value1, value2, fnc, output_buffer, index)
115
116def expand_keycode_fnc(DEFINITION):
117 if DEFINITION == "`":
118 DEFINITION = "GRAVE"
119 elif DEFINITION == "-":
120 DEFINITION = "MINUS"
121 elif DEFINITION == "=":
122 DEFINITION = "EQUAL"
123 elif DEFINITION == "[":
124 DEFINITION = "LBRACKET"
125 elif DEFINITION == "]":
126 DEFINITION = "RBRACKET"
127 elif DEFINITION == "\\":
128 DEFINITION = "BSLASH"
129 elif DEFINITION == ";":
130 DEFINITION = "SCOLON"
131 elif DEFINITION == "'":
132 DEFINITION = "QUOTE"
133 elif DEFINITION == ",":
134 DEFINITION = "COMMA"
135 elif DEFINITION == ".":
136 DEFINITION = "DOT"
137 elif DEFINITION == "/":
138 DEFINITION = "SLASH"
139 elif DEFINITION == "~":
140 DEFINITION = "TILDE"
141 elif DEFINITION == "*":
142 DEFINITION = "ASTERISK"
143 elif DEFINITION == "+":
144 DEFINITION = "PLUS"
145 elif DEFINITION == "(":
146 DEFINITION = "LEFT_PAREN"
147 elif DEFINITION == ")":
148 DEFINITION = "RIGHT_PAREN"
149 elif DEFINITION == "<":
150 DEFINITION = "LEFT_ANGLE_BRACKET"
151 elif DEFINITION == ">":
152 DEFINITION = "RIGHT_ANGLE_BRACKET"
153 elif DEFINITION == "{":
154 DEFINITION = "LEFT_CURLY_BRACE"
155 elif DEFINITION == "}":
156 DEFINITION = "RIGHT_CURLY_BRACE"
157 elif DEFINITION == "?":
158 DEFINITION = "QUESTION"
159 elif DEFINITION == "~":
160 DEFINITION = "TILDE"
161 elif DEFINITION == ":":
162 DEFINITION = "COLON"
163 elif DEFINITION == "_":
164 DEFINITION = "UNDERSCORE"
165 elif DEFINITION == '"':
166 DEFINITION = "DOUBLE_QUOTE"
167 elif DEFINITION == "@":
168 DEFINITION = "AT"
169 elif DEFINITION == "#":
170 DEFINITION = "HASH"
171 elif DEFINITION == "$":
172 DEFINITION = "DOLLAR"
173 elif DEFINITION == "!":
174 DEFINITION = "EXCLAIM"
175 elif DEFINITION == "%":
176 DEFINITION = "PERCENT"
177 elif DEFINITION == "^":
178 DEFINITION = "CIRCUMFLEX"
179 elif DEFINITION == "&":
180 DEFINITION = "AMPERSAND"
181 elif DEFINITION == "|":
182 DEFINITION = "PIPE"
183
184 if DEFINITION in [
185 "A", "a", "B", "b", "C", "c", "D", "d", "E", "e",
186 "F", "f", "G", "g", "H", "h", "I", "i", "J", "j",
187 "K", "k", "L", "l", "M", "m", "N", "n", "O", "o",
188 "P", "p", "Q", "q", "R", "r", "S", "s", "T", "t",
189 "U", "u", "V", "v", "W", "w", "X", "x", "Y", "y",
190 "Z", "z", "1", "2", "3", "4", "5", "6", "7", "8",
191 "9", "0", "F1", "F2", "F3", "F4", "F5", "F6", "F7",
192 "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15",
193 "F16", "F17", "F18", "F19", "F20", "F21", "F22",
194 "F23", "F24", "ENTER", "ENT", "ESCAPE", "ESC",
195 "BSPACE", "BSPC", "TAB", "SPACE", "SPC", "NONUS_HASH",
196 "NUHS", "NONUS_BSLASH", "NUBS", "COMMA", "COMM",
197 "DOT", "SLASH", "SLSH", "TILDE", "TILD", "EXCLAIM",
198 "EXLM", "AT", "HASH", "DOLLAR", "DLR", "PERCENT",
199 "PERC", "CIRCUMFLEX", "CIRC", "AMPERSAND", "AMPR",
200 "ASTERISK", "ASTR", "LEFT_PAREN", "LPRN", "RIGHT_PAREN",
201 "RPRN", "UNDERSCORE", "UNDS", "PLUS", "LEFT_CURLY_BRACE",
202 "LCBR", "RIGHT_CURLY_BRACE", "RCBR", "PIPE", "COLON",
203 "COLN", "DOUBLE_QUOTE", "DQUO", "DQT",
204 "LEFT_ANGLE_BRACKET", "LABK", "LT", "RIGHT_ANGLE_BRACKET",
205 "RABK", "GT", "QUESTION", "QUES", "SCOLON", "SCLN",
206 "QUOTE", "QUOT", "LBRACKET", "LBRC", "RBRACKET", "RBRC",
207 "BSLASH", "BSLS", "MINUS", "MINS", "EQUAL", "EQL",
208 "GRAVE", "GRV", "ZKHK", "CAPSLOCK", "CLCK", "CAPS",
209 "SCROLLOCK", "SLCK", "BRMD", "NUMLOCK", "NLCK",
210 "LOCKING_CAPS", "LCAP", "LOCKING_NUM", "LNUM",
211 "LOCKING_SCROLL", "LSCR", "LCTRL", "LCTL", "LSHIFT",
212 "LSFT", "LALT", "LGUI", "LCMD", "LWIN", "RCTRL",
213 "RCTL", "RSHIFT", "RSFT", "RALT", "RGUI", "RCMD",
214 "RWIN", "INT1", "RO", "INT2", "KANA", "INT3", "JYEN",
215 "INT4", "HENK", "INT5", "MHEN", "INT6", "INT7",
216 "INT8", "INT9", "LANG1", "HAEN", "LANG2", "HANJ",
217 "LANG3", "LANG4", "LANG5", "LANG6", "LANG7", "LANG8",
218 "LANG9", "PSCREEN", "PSCR", "PAUSE", "PAUS", "BRK",
219 "BRMU", "INSERT", "INS", "HOME", "PGUP", "DELETE",
220 "DEL", "END", "PGDOWN", "PGDN", "RIGHT", "RGHT",
221 "LEFT", "DOWN", "UP", "APPLICATION", "APP", "POWER",
222 "EXECUTE", "EXEC", "HELP", "MENU", "SELECT", "SLCT",
223 "STOP", "AGAIN", "AGIN", "UNDO", "CUT", "COPY",
224 "PASTE", "PSTE", "FIND", "MUTE", "VOLUP", "VOLDOWN",
225 "ALT_ERASE", "ERAS", "SYSREQ", "CANCEL", "CLEAR",
226 "CLR", "PRIOR", "RETURN", "SEPARATOR", "OUT", "OPER",
227 "CLEAR_AGAIN", "CRSEL", "EXSEL", "SYSTEM_POWER",
228 "PWR", "SYSTEM_SLEEP", "SLEP", "SYSTEM_WAKE", "WAKE",
229 "AUDIO_MUTE", "MUTE", "AUDIO_VOL_UP", "VOLU",
230 "AUDIO_VOL_DOWN", "VOLD", "MEDIA_NEXT_TRACK", "MNXT",
231 "MEDIA_PREV_TRACK", "MPRV", "CPRV", "MEDIA_STOP", "MSTP",
232 "MEDIA_PLAY_PAUSE", "MPLY", "MEDIA_SELECT", "MSEL",
233 "MEDIA_EJECT", "EJCT", "MAIL", "CALCULATOR", "CALC",
234 "MY_COMPUTER", "MYCM", "WWW_SEARCH", "WSCH", "WWW_HOME",
235 "WHOM", "WWW_BACK", "WBAK", "WWW_FORWARD", "WFWD",
236 "WWW_STOP", "WSTP", "WWW_REFRESH", "WREF",
237 "WWW_FAVORITES", "WFAV", "MEDIA_FAST_FORWARD", "MFFD",
238 "MEDIA_REWIND", "MRWD", "BRIGHTNESS_UP", "BRIU",
239 "BRIGHTNESS_DOWN", "BRID", "KP_SLASH", "PSLS",
240 "KP_ASTERISK", "PAST", "KP_MINUS", "PMNS", "KP_PLUS",
241 "PPLS", "KP_ENTER", "PENT", "KP_1", "P1", "KP_2", "P2",
242 "KP_3", "P3", "KP_4", "P4", "KP_5", "P5", "KP_6", "P6",
243 "KP_7", "P7", "KP_8", "P8", "KP_9", "P9", "KP_0", "P0",
244 "KP_DOT", "PDOT", "KP_EQUAL", "PEQL", "KP_COMMA", "PCMM",
245 "MS_BTN1", "BTN1", "MS_BTN2", "BTN2", "MS_BTN3", "BTN3",
246 "MS_BTN4", "BTN4", "MS_BTN5", "BTN5", "MS_BTN6", "BTN6",
247 "MS_LEFT", "MS_L", "MS_DOWN", "MS_D", "MS_UP", "MS_U",
248 "MS_RIGHT", "MS_R", "MS_WH_UP", "WH_U", "MS_WH_DOWN",
249 "WH_D", "MS_WH_LEFT", "MS_WH_L", "MS_WH_RIGHT", "MS_WH_R",
250 "KC_MS_ACCEL0", "ACL0", "KC_MS_ACCEL1", "ACL1",
251 "KC_MS_ACCEL2", "ACL2"
252 ]:
253 return "KC_" + DEFINITION
254 else:
255 return DEFINITION
256
257def MK(on_pseudolayer, keycodes_hash, definition, output_buffer, index):
258 l = len(definition.split(', '))
259 output_buffer += "void function_" + str(index) + "(const struct Chord* self) {\n"
260 output_buffer += " switch (*self->state) {\n"
261 output_buffer += " case ACTIVATED:\n"
262 for i in range(0, l):
263 val = definition.split(',')[i].strip()
264 code = expand_keycode_fnc(val)
265 output_buffer += " key_in(" + code + ");\n"
266 output_buffer += " break;\n"
267 output_buffer += " case DEACTIVATED:\n"
268 for i in range(0, l):
269 val = definition.split(',')[i].strip()
270 code = expand_keycode_fnc(val)
271 output_buffer += " key_out(" + code + ");\n"
272 output_buffer += " *self->state = IDLE;\n"
273 output_buffer += " break;\n"
274 output_buffer += " case RESTART:\n"
275 for i in range(0, l):
276 val = definition.split(',')[i].strip()
277 code = expand_keycode_fnc(val)
278 output_buffer += " key_out(" + code + ");\n"
279 output_buffer += " break;\n"
280 output_buffer += " default:\n"
281 output_buffer += " break;\n"
282 output_buffer += " };\n"
283 output_buffer += "}\n"
284 return new_chord(on_pseudolayer, keycodes_hash, True, 0, 0, "function_" + str(index), output_buffer, index)
285
286def D(on_pseudolayer, keycodes_hash, DEFINITION, output_buffer, index):
287 l = len(DEFINITION.split(','))
288 output_buffer += "void function_" + str(index) + "(const struct Chord* self) {\n"
289 output_buffer += " switch (*self->state) {\n"
290 output_buffer += " case ACTIVATED:\n"
291 output_buffer += " *self->counter = *self->counter + 1;\n"
292 output_buffer += " break;\n"
293 output_buffer += " case PRESS_FROM_ACTIVE:\n"
294 output_buffer += " switch (*self->counter) {\n"
295 for i in range(0, l):
296 val = DEFINITION.split(',')[i].strip()
297 code = expand_keycode_fnc(val)
298 output_buffer += " case " + str(i + 1) + ":\n"
299 output_buffer += " key_in( " + code + ");\n"
300 output_buffer += " break;\n"
301 output_buffer += " default:\n"
302 output_buffer += " break;\n"
303 output_buffer += " }\n"
304 output_buffer += " *self->state = FINISHED_FROM_ACTIVE;\n"
305 output_buffer += " break;\n"
306 output_buffer += " case FINISHED:\n"
307 output_buffer += " switch (*self->counter) {\n"
308 for i in range(0, l):
309 val = DEFINITION.split(',')[i].strip()
310 code = expand_keycode_fnc(val)
311 output_buffer += " case " + str(i + 1) + ":\n"
312 output_buffer += " tap_key( " + code + ");\n"
313 output_buffer += " break;\n"
314 output_buffer += " default:\n"
315 output_buffer += " break;\n"
316 output_buffer += " }\n"
317 output_buffer += " *self->counter = 0;\n"
318 output_buffer += " *self->state = IDLE;\n"
319 output_buffer += " break;\n"
320 output_buffer += " case RESTART:\n"
321 output_buffer += " switch (*self->counter) {\n"
322 for i in range(0, l):
323 val = DEFINITION.split(',')[i].strip()
324 code = expand_keycode_fnc(val)
325 output_buffer += " case " + str(i + 1) + ":\n"
326 output_buffer += " key_out( " + code + ");\n"
327 output_buffer += " break;\n"
328 output_buffer += " default:\n"
329 output_buffer += " break;\n"
330 output_buffer += " }\n"
331 output_buffer += " *self->counter = 0;\n"
332 output_buffer += " break;\n"
333 output_buffer += " default:\n"
334 output_buffer += " break;\n"
335 output_buffer += " }\n"
336 output_buffer += "}\n"
337 return new_chord(on_pseudolayer, keycodes_hash, True, 0, 0, "function_" + str(index), output_buffer, index)
338
339def O(on_pseudolayer, keycodes_hash, DEFINITION, output_buffer, index):
340 if DEFINITION[0:3] == "KC_":
341 return OSK(on_pseudolayer, keycodes_hash, DEFINITION, output_buffer, index)
342 else:
343 return OSL(on_pseudolayer, keycodes_hash, DEFINITION, output_buffer, index)
344
345def add_key(PSEUDOLAYER, KEYCODES_HASH, DEFINITION, output_buffer, index, number_of_strings, strings):
346 # if "= {" + KEYCODES_HASH + ", " + PSEUDOLAYER in output_buffer:
347 # KEYCODES_HASH = re.sub('H_', '', KEYCODES_HASH)
348 # raise Exception("You are trying to register a chord that you already registered (" + KEYCODES_HASH + ", " + PSEUDOLAYER + ")")
349
350 if DEFINITION == "":
351 return [output_buffer, index, number_of_strings, strings]
352 else:
353 split = DEFINITION.split("(")
354 type = split[0].strip()
355 if len(split) == 1:
356 if type == "LOCK":
357 [output_buffer, index] = LOCK(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
358 elif type == "AT":
359 [output_buffer, index] = AT(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
360 elif type == "CMD":
361 [output_buffer, index] = CMD(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
362 elif type == "LEAD":
363 [output_buffer, index] = LEAD(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
364 elif type == "DM_RECORD":
365 [output_buffer, index] = DM_RECORD(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
366 elif type == "DM_NEXT":
367 [output_buffer, index] = DM_NEXT(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
368 elif type == "DM_END":
369 [output_buffer, index] = DM_END(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
370 elif type == "DM_PLAY":
371 [output_buffer, index] = DM_PLAY(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
372 elif type == "CLEAR_KB":
373 [output_buffer, index] = CLEAR(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
374 elif type == "RESET":
375 [output_buffer, index] = RESET(PSEUDOLAYER, KEYCODES_HASH, output_buffer, index)
376 else:
377 code = expand_keycode_fnc(type)
378 [output_buffer, index] = KC(PSEUDOLAYER, KEYCODES_HASH, code, output_buffer, index)
379 else:
380 val = split[1][:-1].strip()
381 if type == "O":
382 code = expand_keycode_fnc(val)
383 [output_buffer, index] = O(PSEUDOLAYER, KEYCODES_HASH, code, output_buffer, index)
384 elif type == "D":
385 [output_buffer, index] = D(PSEUDOLAYER, KEYCODES_HASH, val, output_buffer, index)
386 elif type == "MK":
387 [output_buffer, index] = MK(PSEUDOLAYER, KEYCODES_HASH, val, output_buffer, index)
388 elif type == "M":
389 fnc = val.split(',')[0].strip()
390 val1 = val.split(',')[1].strip()
391 val2 = val.split(',')[2].strip()
392 [output_buffer, index] = M(PSEUDOLAYER, KEYCODES_HASH, val1, val2, fnc, output_buffer, index)
393 elif type == "KK":
394 val1 = val.split(',')[0].strip()
395 code1 = expand_keycode_fnc(val1)
396 val2 = val.split(',')[1].strip()
397 code2 = expand_keycode_fnc(val2)
398 [output_buffer, index] = KK(PSEUDOLAYER, KEYCODES_HASH, code1, code2, output_buffer, index)
399 elif type == "KL":
400 val1 = val.split(',')[0].strip()
401 code1 = expand_keycode_fnc(val1)
402 val2 = val.split(',')[1].strip()
403 [output_buffer, index] = KL(PSEUDOLAYER, KEYCODES_HASH, code1, val2, output_buffer, index)
404 elif type == "KM":
405 val1 = val.split(',')[0].strip()
406 code1 = expand_keycode_fnc(val1)
407 val2 = val.split(',')[1].strip()
408 code2 = expand_keycode_fnc(val2)
409 [output_buffer, index] = KM(PSEUDOLAYER, KEYCODES_HASH, code1, code2, output_buffer, index)
410 elif type == "AS":
411 code = expand_keycode_fnc(val)
412 [output_buffer, index] = AS(PSEUDOLAYER, KEYCODES_HASH, code, output_buffer, index)
413 elif type == "MO":
414 if not ',' in val:
415 [output_buffer, index] = MO(PSEUDOLAYER, KEYCODES_HASH, val, output_buffer, index)
416 else:
417 val1 = val.split(',')[0].strip()
418 val2 = val.split(',')[1].strip()
419 [output_buffer, index] = MO_alt(PSEUDOLAYER, KEYCODES_HASH, val1, val2, output_buffer, index)
420 elif type == "DF":
421 [output_buffer, index] = DF(PSEUDOLAYER, KEYCODES_HASH, val, output_buffer, index)
422 elif type == "TO":
423 [output_buffer, index] = TO(PSEUDOLAYER, KEYCODES_HASH, val, output_buffer, index)
424 elif type == "STR":
425 [output_buffer, index, number_of_strings, strings] = STR(PSEUDOLAYER, KEYCODES_HASH, val, output_buffer, index, number_of_strings, strings)
426 return [output_buffer, index, number_of_strings, strings]
427
428def add_leader_combo(DEFINITION, FUNCTION):
429 return list_of_leader_combos.append([DEFINITION, FUNCTION])
430
431def add_chord_set(PSEUDOLAYER, INPUT_STRING, TYPE, data, output_buffer, index, number_of_strings, strings):
432 chord_set = {}
433 for set in data["chord_sets"]:
434 if set["name"] == TYPE:
435 chord_set = set["chords"]
436 break
437
438 separated_string = top_level_split(INPUT_STRING)
439 for word, chord in zip(separated_string, chord_set):
440 chord_hash = reduce((lambda x, y: str(x) + " + " + str(y)), ["H_" + key for key in chord])
441 [output_buffer, index, number_of_strings, strings] = add_key(PSEUDOLAYER, chord_hash, word, output_buffer, index, number_of_strings, strings)
442
443 return [output_buffer, index, number_of_strings, strings]
444
445def add_dictionary(PSEUDOLAYER, keycodes, array, output_buffer, index, number_of_strings, strings):
446 for chord in array:
447 hash = ""
448 for word, key in zip(chord[:-1], keycodes):
449 if word == "X":
450 hash = hash + " + H_" + key
451 hash = hash[3:]
452 if hash != "":
453 [output_buffer, index, number_of_strings, strings] = add_key(PSEUDOLAYER, hash, chord[-1], output_buffer, index, number_of_strings, strings)
454
455 return [output_buffer, index, number_of_strings, strings]
456
457def secret_chord(PSEUDOLAYER, ACTION, INPUT_STRING, data, output_buffer, index, number_of_strings, strings):
458 separated_string = top_level_split(INPUT_STRING)
459 hash = ""
460 for word, key in zip(separated_string, data["keys"]):
461 if word == "X":
462 hash = hash + " + H_" + key
463
464 hash = hash[3:]
465 if hash != "":
466 return add_key(PSEUDOLAYER, hash, ACTION, output_buffer, index, number_of_strings, strings) \ No newline at end of file
diff --git a/users/dennytom/chording_engine/engine.part.1 b/users/dennytom/chording_engine/engine.part.1
new file mode 100644
index 000000000..73df4cdea
--- /dev/null
+++ b/users/dennytom/chording_engine/engine.part.1
@@ -0,0 +1,163 @@
1enum chord_states {
2 IDLE,
3 READY,
4 ACTIVATED,
5 DEACTIVATED,
6 PRESS_FROM_ACTIVE,
7 FINISHED_FROM_ACTIVE,
8 IDLE_IN_DANCE,
9 READY_IN_DANCE,
10 FINISHED,
11 LOCKED,
12 READY_LOCKED,
13 RESTART,
14 IN_ONE_SHOT
15};
16
17struct Chord {
18 uint32_t keycodes_hash;
19 uint8_t pseudolayer;
20 uint8_t* state;
21 uint8_t* counter;
22 uint16_t value1;
23 uint8_t value2;
24 void (*function) (const struct Chord*);
25};
26
27uint8_t current_pseudolayer = DEFAULT_PSEUDOLAYER;
28bool lock_next = false;
29uint16_t chord_timer = 0;
30uint16_t dance_timer = 0;
31bool autoshift_mode = true;
32uint8_t keycode_index = 0;
33uint8_t command_mode = 0;
34uint8_t command_ind = 0;
35bool in_leader_mode = false;
36uint8_t leader_ind = 0;
37uint16_t leader_timer = 0;
38uint8_t dynamic_macro_mode = false;
39uint8_t dynamic_macro_ind = 0;
40bool a_key_went_through = false;
41struct Chord* last_chord = NULL;
42
43bool handle_US_ANSI_shifted_keys(int16_t keycode, bool in) {
44 bool is_US_ANSI_shifted = true;
45
46 int16_t regular_keycode = KC_NO;
47 switch (keycode) {
48 case KC_TILDE:
49 regular_keycode = KC_GRAVE;
50 break;
51 case KC_EXCLAIM:
52 regular_keycode = KC_1;
53 break;
54 case KC_AT:
55 regular_keycode = KC_2;
56 break;
57 case KC_HASH:
58 regular_keycode = KC_3;
59 break;
60 case KC_DOLLAR:
61 regular_keycode = KC_4;
62 break;
63 case KC_PERCENT:
64 regular_keycode = KC_5;
65 break;
66 case KC_CIRCUMFLEX:
67 regular_keycode = KC_6;
68 break;
69 case KC_AMPERSAND:
70 regular_keycode = KC_7;
71 break;
72 case KC_ASTERISK:
73 regular_keycode = KC_8;
74 break;
75 case KC_LEFT_PAREN:
76 regular_keycode = KC_9;
77 break;
78 case KC_RIGHT_PAREN:
79 regular_keycode = KC_0;
80 break;
81 case KC_UNDERSCORE:
82 regular_keycode = KC_MINUS;
83 break;
84 case KC_PLUS:
85 regular_keycode = KC_EQUAL;
86 break;
87 case KC_LEFT_CURLY_BRACE:
88 regular_keycode = KC_LBRACKET;
89 break;
90 case KC_RIGHT_CURLY_BRACE:
91 regular_keycode = KC_RBRACKET;
92 break;
93 case KC_PIPE:
94 regular_keycode = KC_BSLASH;
95 break;
96 case KC_COLON:
97 regular_keycode = KC_SCOLON;
98 break;
99 case KC_DOUBLE_QUOTE:
100 regular_keycode = KC_QUOTE;
101 break;
102 case KC_LEFT_ANGLE_BRACKET:
103 regular_keycode = KC_COMMA;
104 break;
105 case KC_RIGHT_ANGLE_BRACKET:
106 regular_keycode = KC_DOT;
107 break;
108 case KC_QUESTION:
109 regular_keycode = KC_SLASH;
110 break;
111 default:
112 is_US_ANSI_shifted = false;
113 }
114 if (is_US_ANSI_shifted) {
115 if (in) {
116 register_code(KC_LSFT);
117 register_code(regular_keycode);
118 } else {
119 unregister_code(regular_keycode);
120 unregister_code(KC_LSFT);
121 }
122 }
123 return is_US_ANSI_shifted;
124}
125
126void key_in(int16_t keycode) {
127 if (command_mode == 1 && command_ind < COMMAND_MAX_LENGTH) {
128 command_buffer[command_ind] = keycode;
129 command_ind++;
130 a_key_went_through = true;
131 } else if (in_leader_mode && leader_ind < LEADER_MAX_LENGTH) {
132 leader_buffer[leader_ind] = keycode;
133 leader_ind++;
134 a_key_went_through = true;
135 } else if (dynamic_macro_mode && dynamic_macro_ind < DYNAMIC_MACRO_MAX_LENGTH) {
136 dynamic_macro_buffer[dynamic_macro_ind] = keycode;
137 dynamic_macro_ind++;
138 a_key_went_through = true;
139 } else {
140 if (!handle_US_ANSI_shifted_keys(keycode, true)) {
141 register_code(keycode);
142 }
143 send_keyboard_report();
144 a_key_went_through = true;
145 }
146}
147
148void key_out(int16_t keycode) {
149 if (command_mode == 0) {
150 if (!handle_US_ANSI_shifted_keys(keycode, false)) {
151 if (command_mode == 0 && in_leader_mode == false && dynamic_macro_mode == false) {
152 unregister_code(keycode);
153 }
154 }
155 send_keyboard_report();
156 }
157}
158
159void tap_key(int16_t keycode) {
160 key_in(keycode);
161 wait_ms(TAP_TIMEOUT);
162 key_out(keycode);
163} \ No newline at end of file
diff --git a/users/dennytom/chording_engine/engine.part.2 b/users/dennytom/chording_engine/engine.part.2
new file mode 100644
index 000000000..91dcbb750
--- /dev/null
+++ b/users/dennytom/chording_engine/engine.part.2
@@ -0,0 +1,323 @@
1void single_dance(const struct Chord* self) {
2 switch (*self->state) {
3 case ACTIVATED:
4 key_in(self->value1);
5 break;
6 case DEACTIVATED:
7 key_out(self->value1);
8 *self->state = IDLE;
9 break;
10 case RESTART:
11 key_out(self->value1);
12 break;
13 default:
14 break;
15 }
16}
17
18void key_layer_dance(const struct Chord* self) {
19 switch (*self->state) {
20 case ACTIVATED:
21 current_pseudolayer = self->value2;
22 a_key_went_through = false;
23 break;
24 case DEACTIVATED:
25 case RESTART:
26 if (!a_key_went_through) {
27 tap_key(self->value1);
28 }
29 current_pseudolayer = self->pseudolayer;
30 *self->state = IDLE; // does not have effect if the state was RESTART
31 break;
32 default:
33 break;
34 }
35}
36
37void key_mod_dance(const struct Chord* self) {
38 switch (*self->state) {
39 case ACTIVATED:
40 key_in(self->value2);
41 a_key_went_through = false;
42 break;
43 case DEACTIVATED:
44 case RESTART:
45 key_out(self->value2);
46 if (!a_key_went_through) {
47 tap_key(self->value1);
48 }
49 *self->state = IDLE; // does not have effect if the state was RESTART
50 break;
51 default:
52 break;
53 }
54}
55
56void key_key_dance(const struct Chord* self) {
57 switch (*self->state) {
58 case ACTIVATED:
59 break;
60 case DEACTIVATED:
61 tap_key(self->value1);
62 *self->state = IDLE;
63 break;
64 case FINISHED:
65 case PRESS_FROM_ACTIVE:
66 key_in(self->value2);
67 break;
68 case RESTART:
69 key_out(self->value2);
70 break;
71 default:
72 break;
73 }
74}
75
76void autoshift_dance_impl(const struct Chord* self) {
77 switch (*self->state) {
78 case ACTIVATED:
79 *self->counter = 0;
80 break;
81 case DEACTIVATED:
82 case RESTART:
83 tap_key(self->value1);
84 *self->state = IDLE;
85 break;
86 case FINISHED_FROM_ACTIVE:
87 if (*self->counter == (LONG_PRESS_MULTIPLIER - 2)) {
88 key_in(KC_LSFT);
89 tap_key(self->value1);
90 key_out(KC_LSFT);
91 *self->state = IDLE;
92 // the skip to IDLE is usually just a lag optimization,
93 // in this case it has a logic function, on a short
94 // press (still longer than a tap) the key does not get shifted
95 } else {
96 *self->counter += 1;
97 *self->state = PRESS_FROM_ACTIVE;
98 dance_timer = timer_read();
99 }
100 break;
101 default:
102 break;
103 }
104}
105
106void autoshift_dance(const struct Chord* self) {
107 if (autoshift_mode) {
108 autoshift_dance_impl(self);
109 } else {
110 single_dance(self);
111 }
112}
113
114void autoshift_toggle(const struct Chord* self){
115 if (*self->state == ACTIVATED) {
116 autoshift_mode = !autoshift_mode;
117 *self->state = IDLE;
118 }
119}
120
121void temp_pseudolayer(const struct Chord* self) {
122 switch (*self->state) {
123 case ACTIVATED:
124 current_pseudolayer = self->value1;
125 break;
126 case DEACTIVATED:
127 current_pseudolayer = self->pseudolayer;
128 *self->state = IDLE;
129 break;
130 case RESTART:
131 current_pseudolayer = self->pseudolayer;
132 break;
133 default:
134 break;
135 }
136}
137
138void temp_pseudolayer_alt(const struct Chord* self) {
139 switch (*self->state) {
140 case ACTIVATED:
141 current_pseudolayer = self->value1;
142 break;
143 case DEACTIVATED:
144 current_pseudolayer = self->value2;
145 *self->state = IDLE;
146 break;
147 case RESTART:
148 current_pseudolayer = self->value2;
149 break;
150 default:
151 break;
152 }
153}
154
155void perm_pseudolayer(const struct Chord* self) {
156 if (*self->state == ACTIVATED) {
157 current_pseudolayer = self->value1;
158 *self->state = IDLE;
159 }
160}
161
162void switch_layer(const struct Chord* self) {
163 if (*self->state == ACTIVATED) {
164 layer_move(self->value1);
165 *self->state = IDLE;
166 }
167}
168
169void lock(const struct Chord* self) {
170 if (*self->state == ACTIVATED) {
171 lock_next = true;
172 *self->state = IDLE;
173 }
174}
175
176void one_shot_key(const struct Chord* self) {
177 switch (*self->state) {
178 case ACTIVATED:
179 break;
180 case DEACTIVATED:
181 key_in(self->value1);
182 *self->state = IN_ONE_SHOT;
183 break;
184 case FINISHED:
185 case PRESS_FROM_ACTIVE:
186 key_in(self->value1);
187 a_key_went_through = false;
188 break;
189 case RESTART:
190 if (a_key_went_through) {
191 key_out(self->value1);
192 } else {
193 *self->state = IN_ONE_SHOT;
194 }
195 default:
196 break;
197 }
198}
199
200void one_shot_layer(const struct Chord* self) {
201 switch (*self->state) {
202 case ACTIVATED:
203 break;
204 case DEACTIVATED:
205 current_pseudolayer = self->value1;
206 *self->state = IN_ONE_SHOT;
207 break;
208 case FINISHED:
209 case PRESS_FROM_ACTIVE:
210 current_pseudolayer = self->value1;
211 a_key_went_through = false;
212 break;
213 case RESTART:
214 if (a_key_went_through) {
215 current_pseudolayer = self->pseudolayer;
216 } else {
217 *self->state = IN_ONE_SHOT;
218 }
219 default:
220 break;
221 }
222}
223
224void command(const struct Chord* self) {
225 if (*self->state == ACTIVATED) {
226 command_mode++;
227 *self->state = IDLE;
228 }
229}
230
231bool identical(uint16_t* buffer1, uint16_t* buffer2) {
232 bool same = true;
233 for (int i = 0; i < LEADER_MAX_LENGTH; i++) {
234 same = same && (buffer1[i] == buffer2[i]);
235 }
236 return same;
237}
238
239void leader(const struct Chord* self) {
240 if (*self->state == ACTIVATED) {
241 in_leader_mode = true;
242 *self->state = IDLE;
243 }
244}
245
246void dynamic_macro_record(const struct Chord* self) {
247 if (*self->state == ACTIVATED) {
248 for (int i = 0; i < DYNAMIC_MACRO_MAX_LENGTH; i++) {
249 dynamic_macro_buffer[i] = 0;
250 }
251 dynamic_macro_mode = true;
252 *self->state = IDLE;
253 }
254}
255
256void dynamic_macro_next(const struct Chord* self) {
257 if (*self->state == ACTIVATED) {
258 if (dynamic_macro_mode && dynamic_macro_ind < DYNAMIC_MACRO_MAX_LENGTH) {
259 dynamic_macro_buffer[dynamic_macro_ind] = 0;
260 dynamic_macro_ind++;
261 }
262 *self->state = IDLE;
263 }
264}
265
266void dynamic_macro_end(const struct Chord* self) {
267 if (*self->state == ACTIVATED) {
268 if (dynamic_macro_mode) {
269 dynamic_macro_mode = false;
270 }
271 *self->state = IDLE;
272 }
273}
274
275void dynamic_macro_play(const struct Chord* self) {
276 if (*self->state == ACTIVATED) {
277 int ind_start = 0;
278 while (ind_start < DYNAMIC_MACRO_MAX_LENGTH) {
279 for (int i = ind_start; i < DYNAMIC_MACRO_MAX_LENGTH; i++) {
280 if (dynamic_macro_buffer[i] == 0) {
281 break;
282 }
283 register_code(dynamic_macro_buffer[i]);
284 }
285 send_keyboard_report();
286 wait_ms(TAP_TIMEOUT);
287 for (int i = ind_start; i < DYNAMIC_MACRO_MAX_LENGTH; i++) {
288 if (dynamic_macro_buffer[i] == 0) {
289 ind_start = i + 1;
290 break;
291 }
292 unregister_code(dynamic_macro_buffer[i]);
293 }
294 send_keyboard_report();
295 }
296 *self->state = IDLE;
297 }
298}
299
300void string_in(const struct Chord* self) {
301 if (*self->state == ACTIVATED) {
302 char buffer[STRING_MAX_LENGTH];
303 strcpy_P(buffer, (char*)pgm_read_word(&(strings[self->value1])));
304 send_string(buffer);
305 }
306}
307
308void clear(const struct Chord* self);
309
310void reset_keyboard_kb(void){
311#ifdef WATCHDOG_ENABLE
312 MCUSR = 0;
313 wdt_disable();
314 wdt_reset();
315#endif
316 reset_keyboard();
317}
318
319void reset(const struct Chord* self) {
320 if (*self->state == ACTIVATED) {
321 reset_keyboard_kb();
322 }
323}
diff --git a/users/dennytom/chording_engine/engine.part.3 b/users/dennytom/chording_engine/engine.part.3
new file mode 100644
index 000000000..cf19008ab
--- /dev/null
+++ b/users/dennytom/chording_engine/engine.part.3
@@ -0,0 +1,404 @@
1bool are_hashed_keycodes_in_sound(HASH_TYPE keycodes_hash, HASH_TYPE sound) {
2 return (keycodes_hash & sound) == keycodes_hash;
3}
4
5uint8_t keycode_to_index(uint16_t keycode) {
6 return keycode - FIRST_INTERNAL_KEYCODE;
7}
8
9void sound_keycode_array(uint16_t keycode) {
10 uint8_t index = keycode_to_index(keycode);
11 keycode_index++;
12 keycodes_buffer_array[index] = keycode_index;
13}
14
15void silence_keycode_hash_array(HASH_TYPE keycode_hash) {
16 for (int i = 0; i < NUMBER_OF_KEYS; i++) {
17 bool index_in_hash = ((HASH_TYPE) 1 << i) & keycode_hash;
18 if (index_in_hash) {
19 uint8_t current_val = keycodes_buffer_array[i];
20 keycodes_buffer_array[i] = 0;
21 for (int j = 0; j < NUMBER_OF_KEYS; j++) {
22 if (keycodes_buffer_array[j] > current_val) {
23 keycodes_buffer_array[j]--;
24 }
25 }
26 keycode_index--;
27 }
28 }
29}
30
31bool are_hashed_keycodes_in_array(HASH_TYPE keycode_hash) {
32 for (int i = 0; i < NUMBER_OF_KEYS; i++) {
33 bool index_in_hash = ((HASH_TYPE) 1 << i) & keycode_hash;
34 bool index_in_array = (bool) keycodes_buffer_array[i];
35 if (index_in_hash && !index_in_array) {
36 return false;
37 }
38 }
39 return true;
40}
41
42void kill_one_shots(void) {
43 struct Chord chord_storage;
44 struct Chord* chord_ptr;
45 struct Chord* chord;
46
47 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
48 chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
49 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
50 chord = &chord_storage;
51
52 if (*chord->state == IN_ONE_SHOT) {
53 *chord->state = RESTART;
54 chord->function(chord);
55 if (*chord->state == RESTART) {
56 *chord->state = IDLE;
57 }
58 }
59 }
60}
61
62void process_finished_dances(void) {
63 struct Chord chord_storage;
64 struct Chord* chord_ptr;
65 struct Chord* chord;
66
67 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
68 chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
69 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
70 chord = &chord_storage;
71
72 if (*chord->state == ACTIVATED) {
73 *chord->state = PRESS_FROM_ACTIVE;
74 chord->function(chord);
75 if (a_key_went_through) {
76 kill_one_shots();
77 }
78 dance_timer = timer_read();
79 } else if (*chord->state == IDLE_IN_DANCE) {
80 *chord->state = FINISHED;
81 chord->function(chord);
82 if (*chord->state == FINISHED) {
83 *chord->state = RESTART;
84 if (*chord->state == RESTART) {
85 *chord->state = IDLE;
86 }
87 }
88 } else if (*chord->state == PRESS_FROM_ACTIVE) {
89 *chord->state = FINISHED_FROM_ACTIVE;
90 chord->function(chord);
91 if (a_key_went_through) {
92 kill_one_shots();
93 }
94 dance_timer = timer_read();
95 }
96 }
97}
98
99uint8_t keycodes_buffer_array_min(uint8_t* first_keycode_index) {
100 for (int i = 0; i < NUMBER_OF_KEYS; i++) {
101 if (keycodes_buffer_array[i] == 1) {
102 if (first_keycode_index != NULL) {
103 *first_keycode_index = (uint8_t) i;
104 }
105 return 1;
106 }
107 }
108 return 0;
109}
110
111void remove_subchords(void) {
112 struct Chord chord_storage;
113 struct Chord* chord_ptr;
114 struct Chord* chord;
115
116 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
117 chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
118 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
119 chord = &chord_storage;
120
121 if (!(*chord->state == READY || *chord->state == READY_IN_DANCE || *chord->state == READY_LOCKED)) {
122 continue;
123 }
124
125 struct Chord chord_storage_2;
126 struct Chord* chord_ptr_2;
127 struct Chord* chord_2;
128 for (int j = 0; j < NUMBER_OF_CHORDS; j++) {
129 if (i == j) {continue;}
130
131 chord_ptr_2 = (struct Chord*) pgm_read_word (&list_of_chords[j]);
132 memcpy_P(&chord_storage_2, chord_ptr_2, sizeof(struct Chord));
133 chord_2 = &chord_storage_2;
134
135 if (are_hashed_keycodes_in_sound(chord_2->keycodes_hash, chord->keycodes_hash)) {
136 if (*chord_2->state == READY) {
137 *chord_2->state = IDLE;
138 }
139 if (*chord_2->state == READY_IN_DANCE) {
140 *chord_2->state = IDLE_IN_DANCE;
141 }
142 if (*chord_2->state == READY_LOCKED) {
143 *chord_2->state = LOCKED;
144 }
145 }
146 }
147 }
148}
149
150void process_ready_chords(void) {
151 uint8_t first_keycode_index = 0;
152 while (keycodes_buffer_array_min(&first_keycode_index)) {
153 // find ready chords
154 struct Chord chord_storage;
155 struct Chord* chord_ptr;
156 struct Chord* chord;
157
158 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
159 chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
160 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
161 chord = &chord_storage;
162
163 // if the chord does not contain the first keycode
164 bool contains_first_keycode = ((uint32_t) 1 << first_keycode_index) & chord->keycodes_hash;
165 if (!contains_first_keycode) {
166 continue;
167 }
168
169 if (!are_hashed_keycodes_in_array(chord->keycodes_hash)){
170 continue;
171 }
172
173 if (*chord->state == LOCKED) {
174 *chord->state = READY_LOCKED;
175 continue;
176 }
177
178 if (!(chord->pseudolayer == current_pseudolayer || chord->pseudolayer == ALWAYS_ON)) {
179 continue;
180 }
181
182 if (*chord->state == IDLE) {
183 *chord->state = READY;
184 continue;
185 }
186
187 if (*chord->state == IDLE_IN_DANCE) {
188 *chord->state = READY_IN_DANCE;
189 }
190 }
191
192 // remove subchords
193 remove_subchords();
194
195 // execute logic
196 // this should be only one chord
197 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
198 chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
199 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
200 chord = &chord_storage;
201
202 if (*chord->state == READY_LOCKED) {
203 *chord->state = RESTART;
204 chord->function(chord);
205 if (*chord->state == RESTART) {
206 *chord->state = IDLE;
207 }
208 break;
209 }
210
211 if (*chord->state == READY || *chord->state == READY_IN_DANCE) {
212 if (last_chord && last_chord != chord) {
213 process_finished_dances();
214 }
215
216 bool lock_next_prev_state = lock_next;
217
218 *chord->state = ACTIVATED;
219 chord->function(chord);
220 dance_timer = timer_read();
221
222 if (lock_next && lock_next == lock_next_prev_state) {
223 lock_next = false;
224 *chord->state = PRESS_FROM_ACTIVE;
225 chord->function(chord);
226 if (*chord->state == PRESS_FROM_ACTIVE) {
227 *chord->state = LOCKED;
228 }
229 if (a_key_went_through) {
230 kill_one_shots();
231 }
232 }
233 break;
234 }
235 }
236
237 // silence notes
238 silence_keycode_hash_array(chord->keycodes_hash);
239 }
240}
241
242void deactivate_active_chords(uint16_t keycode) {
243 HASH_TYPE hash = (HASH_TYPE)1 << (keycode - SAFE_RANGE);
244 bool broken;
245 struct Chord chord_storage;
246 struct Chord* chord_ptr;
247 struct Chord* chord;
248
249 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
250 chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
251 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
252 chord = &chord_storage;
253
254 broken = are_hashed_keycodes_in_sound(hash, chord->keycodes_hash);
255 if (!broken) {
256 continue;
257 }
258
259 switch (*chord->state) {
260 case ACTIVATED:
261 *chord->state = DEACTIVATED;
262 chord->function(chord);
263
264 if (*chord->state == DEACTIVATED) {
265 dance_timer = timer_read();
266 *chord->state = IDLE_IN_DANCE;
267 }
268 if (*chord->state != IN_ONE_SHOT) {
269 kill_one_shots();
270 }
271 break;
272 case PRESS_FROM_ACTIVE:
273 case FINISHED_FROM_ACTIVE:
274 *chord->state = RESTART;
275 chord->function(chord);
276 if (*chord->state == RESTART) {
277 *chord->state = IDLE;
278 }
279 kill_one_shots();
280 break;
281 default:
282 break;
283 }
284 }
285
286}
287
288void process_command(void) {
289 command_mode = 0;
290 for (int i = 0; i < COMMAND_MAX_LENGTH; i++) {
291 if (command_buffer[i]) {
292 register_code(command_buffer[i]);
293 }
294 send_keyboard_report();
295 }
296 wait_ms(TAP_TIMEOUT);
297 for (int i = 0; i < COMMAND_MAX_LENGTH; i++) {
298 if (command_buffer[i]) {
299 unregister_code(command_buffer[i]);
300 }
301 send_keyboard_report();
302 }
303 for (int i = 0; i < COMMAND_MAX_LENGTH; i++) {
304 command_buffer[i] = 0;
305 }
306 command_ind = 0;
307}
308
309void process_leader(void) {
310 in_leader_mode = false;
311 for (int i = 0; i < NUMBER_OF_LEADER_COMBOS; i++) {
312 uint16_t trigger[LEADER_MAX_LENGTH];
313 memcpy_P(trigger, leader_triggers[i], LEADER_MAX_LENGTH * sizeof(uint16_t));
314
315 if (identical(leader_buffer, trigger)) {
316 (*leader_functions[i])();
317 break;
318 }
319 }
320 for (int i = 0; i < LEADER_MAX_LENGTH; i++) {
321 leader_buffer[i] = 0;
322 }
323}
324
325bool process_record_user(uint16_t keycode, keyrecord_t *record) {
326 if (keycode < FIRST_INTERNAL_KEYCODE || keycode > LAST_INTERNAL_KEYCODE) {
327 return true;
328 }
329
330 if (record->event.pressed) {
331 sound_keycode_array(keycode);
332 } else {
333 process_ready_chords();
334 deactivate_active_chords(keycode);
335 }
336 chord_timer = timer_read();
337 leader_timer = timer_read();
338
339 return false;
340}
341
342void matrix_scan_user(void) {
343 bool chord_timer_expired = timer_elapsed(chord_timer) > CHORD_TIMEOUT;
344 if (chord_timer_expired && keycodes_buffer_array_min(NULL)) {
345 process_ready_chords();
346 }
347
348 bool dance_timer_expired = timer_elapsed(dance_timer) > DANCE_TIMEOUT;
349 if (dance_timer_expired) { // would love to have && in_dance but not sure how
350 process_finished_dances();
351 }
352
353 bool in_command_mode = command_mode == 2;
354 if (in_command_mode) {
355 process_command();
356 }
357
358 bool leader_timer_expired = timer_elapsed(leader_timer) > LEADER_TIMEOUT;
359 if (leader_timer_expired && in_leader_mode) {
360 process_leader();
361 }
362
363}
364
365void clear(const struct Chord* self) {
366 if (*self->state == ACTIVATED) {
367 // kill all chords
368 struct Chord chord_storage;
369 struct Chord* chord_ptr;
370 struct Chord* chord;
371
372 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
373 chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
374 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
375 chord = &chord_storage;
376
377 *chord->state = IDLE;
378
379 if (chord->counter) {
380 *chord->counter = 0;
381 }
382 }
383
384 // clear keyboard
385 clear_keyboard();
386 send_keyboard_report();
387
388 // switch to default pseudolayer
389 current_pseudolayer = DEFAULT_PSEUDOLAYER;
390
391 // clear all keyboard states
392 lock_next = false;
393 autoshift_mode = true;
394 command_mode = 0;
395 in_leader_mode = false;
396 leader_ind = 0;
397 dynamic_macro_mode = false;
398 a_key_went_through = false;
399
400 for (int i = 0; i < DYNAMIC_MACRO_MAX_LENGTH; i++) {
401 dynamic_macro_buffer[i] = 0;
402 }
403 }
404} \ No newline at end of file
diff --git a/users/dennytom/chording_engine/keymap_def.schema.json b/users/dennytom/chording_engine/keymap_def.schema.json
new file mode 100644
index 000000000..9f9a8c5cb
--- /dev/null
+++ b/users/dennytom/chording_engine/keymap_def.schema.json
@@ -0,0 +1,337 @@
1{
2 "definitions": {},
3 "$schema": "http://json-schema.org/draft-07/schema#",
4 "type": "object",
5 "title": "The Keymap definition",
6 "required": [
7 "keys",
8 "parameters",
9 "layers",
10 "chord_sets",
11 "pseudolayers",
12 "leader_sequences",
13 "extra_code",
14 "extra_dependencies"
15 ],
16 "properties": {
17 "keys": {
18 "type": "array",
19 "title": "The Internal Keycodes",
20 "description": "Name Keycodes for the Chording Engine. These can be any string except already valid QMK keycodes.",
21 "items": {
22 "type": "string",
23 "title": "Individual Keycode",
24 "examples": [
25 "L1",
26 "R1",
27 "TOP1",
28 "THUMB_3",
29 "Bottom_6"
30 ]
31 }
32 },
33 "parameters": {
34 "type": "object",
35 "title": "Keyboard Parameters",
36 "description": "Keyboard, user and layout specific parameters like timers, max length for buffers and default pseudolayer.",
37 "required": [
38 "layout_function_name",
39 "chord_timeout",
40 "dance_timeout",
41 "leader_timeout",
42 "tap_timeout",
43 "command_max_length",
44 "leader_max_length",
45 "dynamic_macro_max_length",
46 "string_max_length",
47 "long_press_multiplier",
48 "default_pseudolayer"
49 ],
50 "properties": {
51 "layout_function_name": {
52 "type": "string",
53 "examples": [
54 "LAYOUT_ginny",
55 ""
56 ]
57 },
58 "chord_timeout": {
59 "type": "integer",
60 "title": "The Chord Timeout",
61 "description": "The time in ms you have to press additional keys before the engine assumes you finished pressing keys that are part of a chord.",
62 "default": 100
63 },
64 "dance_timeout": {
65 "type": "integer",
66 "title": "The Dance Timeout",
67 "description": "The time in ms you have to repeatedly activate a chord before the engine assumes you finished a dance.",
68 "default": 200
69 },
70 "leader_timeout": {
71 "type": "integer",
72 "title": "The Leader Timeout",
73 "description": "The time in ms you have to activate additional chords before the engine assumes you finished adding chords to a leader sequence.",
74 "default": 750
75 },
76 "tap_timeout": {
77 "type": "integer",
78 "title": "The Tap Timeout",
79 "description": "The time in ms you have to finish pressing a chord before the engine assumes that you didn't just tap it but are holding it down.",
80 "default": 50
81 },
82 "command_max_length": {
83 "type": "integer",
84 "title": "Command Max Length",
85 "description": "The maximum length for chords buffered in command mode.",
86 "default": 5
87 },
88 "leader_max_length": {
89 "type": "integer",
90 "title": "Leader Max Length",
91 "description": "The maximum length of leader sequences you can define in your keymap",
92 "default": 5
93 },
94 "dynamic_macro_max_length": {
95 "type": "integer",
96 "title": "Dynamic Macro Max Length",
97 "description": "The maximum number of chords (including Dynamic Macro Next) you can record to a Dynamic Macro",
98 "default": 20
99 },
100 "string_max_length": {
101 "type": "integer",
102 "title": "String Max Length",
103 "description": "The maximum length of a string that the STR() chord can accept.",
104 "default": 16
105 },
106 "long_press_multiplier": {
107 "type": "integer",
108 "title": "Long Press Multiplier",
109 "description": "How many times does the chord timer have expire before a chord is registered as not only held but in a long press (for example for Autoshift). Has to be integer.",
110 "default": 3
111 },
112 "default_pseudolayer": {
113 "type": "string",
114 "title": "Default Pseudolayer",
115 "description": "Which pseudolayer should be active when the keyboard starts / restarts.",
116 "examples": [
117 "BASE",
118 "QWERTY"
119 ]
120 }
121 }
122 },
123 "layers": {
124 "type": "array",
125 "title": "QMK Layers",
126 "description": "The layers QMK needs to know about. Can contain chording engine's internal keycodes or QMK's keycodes. Do not define chords here, those belong in pseudolayers.",
127 "minItems": 1,
128 "uniqueItems": false,
129 "items": {
130 "type": "object",
131 "title": "Individual Layers",
132 "required": [
133 "type"
134 ],
135 "properties": {
136 "type": {
137 "type": "string",
138 "title": "Type of Individual Layers",
139 "description": "Auto layer fills all keycodes to be chording engine's internal keycodes, manual let's you place internal and QMK's keycodes however you wish.",
140 "examples": [
141 "auto",
142 "manual"
143 ]
144 },
145 "keycodes": {
146 "type": "array",
147 "title": "Individual Chord",
148 "description": "A list of of keys that need to be pressed to activate this chord",
149 "items": {
150 "type": "string",
151 "title": "Individual Keycodes",
152 "description": "A keycode that is a part of the individual chord. Has to be an internal keycode."
153 }
154 }
155 }
156 }
157 },
158 "chord_sets": {
159 "type": "array",
160 "title": "Chord Sets Definitions",
161 "description": "Describes predefined sets of chords to ease defining a number of chords in a pseudolayer.",
162 "items": {
163 "type": "object",
164 "required": [
165 "name",
166 "chords"
167 ],
168 "properties": {
169 "name": {
170 "type": "string",
171 "title": "Name of the Set",
172 "examples": [
173 "rows",
174 "asetniop"
175 ]
176 },
177 "chords": {
178 "type": "array",
179 "title": "Chords",
180 "description": "List of all chords in this set",
181 "minItems": 1,
182 "uniqueItems": true,
183 "items": {
184 "type": "array",
185 "title": "Individual Chord",
186 "description": "A list of of keys that need to be pressed to activate this chord",
187 "items": {
188 "type": "string",
189 "title": "Individual Keycodes",
190 "description": "A keycode that is a part of the individual chord. Has to be an internal keycode."
191 }
192 }
193 }
194 }
195 }
196 },
197 "pseudolayers": {
198 "type": "array",
199 "title": "Pseudolayers",
200 "description": "The pseudolayers holding the chords to be processed by the chording engine.",
201 "minItems": 1,
202 "uniqueItems": true,
203 "items": {
204 "type": "object",
205 "required": [
206 "name"
207 ],
208 "properties": {
209 "name": {
210 "type": "string",
211 "title": "Name of the Pseudolayer",
212 "default": null,
213 "examples": [
214 "ALWAYS_ON",
215 "QWERTY"
216 ]
217 },
218 "chords": {
219 "type": "array",
220 "title": "Chords",
221 "description": "List of chords belonging on the pseudolayer.",
222 "items": {
223 "type": "object",
224 "required": [
225 "type"
226 ],
227 "properties": {
228 "type": {
229 "type": "string",
230 "title": "Type of the chord array",
231 "description": "Defines how this objects describes one or more chords.",
232 "examples": [
233 "visual_array",
234 "visual",
235 "simple",
236 "chord_set"
237 ]
238 },
239 "keys": {
240 "type": "array",
241 "title": "Subset of keys",
242 "description": "Subset of internal keycodes that will be used when defining the chords. For visual_array type only.",
243 "examples": [
244 "[\"L1\", \"L2\", \"L3\", \"L4\", \"R1\", \"R2\", \"R3\", \"R4\"]"
245 ],
246 "items": {
247 "type": "string"
248 }
249 },
250 "dictionary": {
251 "type": "array",
252 "title": "Dictionary",
253 "description": "A table. Each row defines in a visual way which keys have to be pressed and what is the desired outcome. For visual_array type only.",
254 "items": {
255 "type": "array",
256 "items": {
257 "type": "string",
258 "examples": [
259 "[\"X"\, \" "\, \" "\, \"X"\, \"X"\, \" "\, \" "\, \"X"\, \"MO(BASE, NUM)\"]
260 ]
261 }
262 }
263 },
264 "keycode": {
265 "type": "string",
266 "title": "Keycode",
267 "description": "A keycode to be assigned to the chord when it is registered. For simple and visual types only."
268 },
269 "set": {
270 "type": "string",
271 "title": "Chord set",
272 "description": "Name of the chord set to be used. Has to be one already defined in the chord_sets array. For chord_set type only."
273 },
274 "keycodes": {
275 "type": "array",
276 "title": "Keycodes",
277 "description": "List of keycodes to be assigned to each chord when it is registered. For set type only.",
278 "items": {
279 "type": "string"
280 }
281 },
282 "chord": {
283 "type": "array",
284 "title": "Chord",
285 "description": "Array of \"X\"'s and \" \"'s that shows which keys have to be pressed to activate the chord. For visual type only ",
286 "items": {
287 "type": "string"
288 }
289 }
290 }
291 }
292 }
293 }
294 }
295 },
296 "leader_sequences": {
297 "type": "array",
298 "items": {
299 "type": "object",
300 "properties": {
301 "name": {
302 "type": "string",
303 "examples": ["fn_L1"]
304 },
305 "function": {
306 "type": "string",
307 "description": "C code the sequence should run. Instead of here, can be defined in extra_dependencies or extra_code",
308 "examples": ["void fn_L1(void) { SEND(KC_LCTL); SEND(KC_LALT); SEND(KC_DEL); }"]
309 },
310 "sequence": {
311 "type": "array",
312 "items": {
313 "type": "string"
314 },
315 "examples": [["KC_Q", "KC_Z"]]
316 }
317 }
318 }
319 },
320 "extra_code": {
321 "type": "string",
322 "label": "Extra Code",
323 "description": "C code to be inserted into the generated keymap",
324 "examples": ["void fn_L1(void) {\n SEND(KC_LCTL);\n SEND(KC_LALT);\n SEND(KC_DEL);\n}\n"],
325 "default": ""
326 },
327 "extra_dependencies": {
328 "type": "array",
329 "label": "Extra Dependencies",
330 "description": "List of files to be #include'd in the generated keymap",
331 "examples": [
332 "[\"user_functions.c\"]"
333 ],
334 "default": ""
335 }
336 }
337} \ No newline at end of file
diff --git a/users/dennytom/chording_engine/parser.py b/users/dennytom/chording_engine/parser.py
new file mode 100644
index 000000000..b62cf007e
--- /dev/null
+++ b/users/dennytom/chording_engine/parser.py
@@ -0,0 +1,231 @@
1#!/usr/bin/env python3
2
3import json
4from functools import reduce
5from chord import *
6import sys
7
8comma_separator = (lambda x, y: str(x) + ", " + str(y))
9string_sum = (lambda x, y: str(x) + " + " + str(y))
10newline_separator = (lambda x, y: str(x) + "\n" + str(y))
11
12def add_includes(data):
13 output_buffer = ""
14 if not ("do_not_include_QMK" in data["parameters"] and data["parameters"]["do_not_include_QMK"] == True):
15 output_buffer += "#include QMK_KEYBOARD_H\n"
16 if len(data["extra_dependencies"]) > 0:
17 for dependecy in data["extra_dependencies"]:
18 output_buffer += '#include "' + dependecy + '"\n'
19
20 return output_buffer + "\n"
21
22def add_parameters(data):
23 output_buffer = ""
24
25 number_of_keys = len(data["keys"])
26 if number_of_keys <= 8:
27 hash_type = "uint8_t"
28 elif number_of_keys <= 16:
29 hash_type = "uint16_t"
30 elif number_of_keys <= 32:
31 hash_type = "uint32_t"
32 elif number_of_keys <= 64:
33 hash_type = "uint64_t"
34 else:
35 raise Exception("The engine currently supports only up to 64 keys.")
36
37 output_buffer += "#define CHORD_TIMEOUT " + str(data["parameters"]["chord_timeout"]) + "\n"
38 output_buffer += "#define DANCE_TIMEOUT " + str(data["parameters"]["dance_timeout"]) + "\n"
39 output_buffer += "#define LEADER_TIMEOUT " + str(data["parameters"]["leader_timeout"]) + "\n"
40 output_buffer += "#define TAP_TIMEOUT " + str(data["parameters"]["tap_timeout"]) + "\n"
41 output_buffer += "#define LONG_PRESS_MULTIPLIER " + str(data["parameters"]["long_press_multiplier"]) + "\n"
42 output_buffer += "#define DYNAMIC_MACRO_MAX_LENGTH " + str(data["parameters"]["dynamic_macro_max_length"]) + "\n"
43 output_buffer += "#define COMMAND_MAX_LENGTH " + str(data["parameters"]["command_max_length"]) + "\n"
44 output_buffer += "#define STRING_MAX_LENGTH " + str(data["parameters"]["string_max_length"]) + "\n"
45 output_buffer += "#define LEADER_MAX_LENGTH " + str(data["parameters"]["leader_max_length"]) + "\n"
46 output_buffer += "#define HASH_TYPE " + hash_type + "\n"
47 output_buffer += "#define NUMBER_OF_KEYS " + str(len(data["keys"])) + "\n"
48 output_buffer += "#define DEFAULT_PSEUDOLAYER " + data["parameters"]["default_pseudolayer"] + "\n"
49
50 return output_buffer + "\n"
51
52def add_keycodes(data):
53 output_buffer = ""
54
55 if not len(data["keys"]) == len(set(data["keys"])):
56 raise Exception("The keys must have unique names")
57
58 for key, counter in zip(data["keys"], range(0, len(data["keys"]))):
59 output_buffer += "#define H_" + key + " ((HASH_TYPE) 1 << " + str(counter) + ")\n"
60 output_buffer += "\n"
61
62 output_buffer += "enum internal_keycodes {\n"
63 output_buffer += " " + data["keys"][0] + " = SAFE_RANGE,\n"
64 output_buffer += " " + reduce(comma_separator, [key for key in data["keys"][1:]]) + ",\n"
65 output_buffer += " FIRST_INTERNAL_KEYCODE = " + data["keys"][0] + ",\n"
66 output_buffer += " LAST_INTERNAL_KEYCODE = " + data["keys"][-1] + "\n"
67 output_buffer += "};\n"
68
69 return output_buffer + "\n"
70
71def add_pseudolayers(data):
72 output_buffer = ""
73
74 if len(data["pseudolayers"]) == 0:
75 raise Exception("You didn't define any pseudolayers")
76
77 if not len([pseudolayer["name"] for pseudolayer in data["pseudolayers"]]) == len(set([pseudolayer["name"] for pseudolayer in data["pseudolayers"]])):
78 raise Exception("The pseudolayers must have unique names")
79
80 pseudolayers = data["pseudolayers"]
81 if not "ALWAYS_ON" in [layer["name"] for layer in pseudolayers]:
82 pseudolayers += [{"name": "ALWAYS_ON", "chords": []}] # the engine expects ALWAYS_ON to exist
83
84 output_buffer += "enum pseudolayers {\n"
85 output_buffer += " " + reduce(comma_separator, [layer["name"] for layer in pseudolayers]) + "\n"
86 output_buffer += "};\n"
87
88 return output_buffer + "\n"
89
90def add_layers(data):
91 output_buffer = ""
92
93 output_buffer += "const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n"
94 for layer, counter in zip(data["layers"], range(0,len(data["layers"]))):
95 if layer["type"] == "auto":
96 output_buffer += " [" + str(counter) + "] = " + data["parameters"]["layout_function_name"] + "(" + reduce(comma_separator, [key for key in data["keys"]]) + "),\n"
97 else:
98 output_buffer += " [" + str(counter) + "] = " + data["parameters"]["layout_function_name"] + "(" + reduce(comma_separator, [key for key in layer["keycodes"]]) + "),\n"
99 output_buffer += "};\n"
100 output_buffer += "size_t keymapsCount = " + str(len(data["layers"])) + ";\n"
101
102 return output_buffer + "\n"
103
104def prep_buffers(data):
105 output_buffer = ""
106
107 output_buffer += "uint8_t keycodes_buffer_array[] = {\n"
108 output_buffer += " " + reduce(comma_separator, ["0"] * len(data["keys"])) + "\n"
109 output_buffer += "};\n"
110 output_buffer += "\n"
111
112 output_buffer += "uint8_t command_buffer[] = {\n"
113 output_buffer += " " + reduce(comma_separator, ["0"] * data["parameters"]["command_max_length"]) + "\n"
114 output_buffer += "};\n"
115 output_buffer += "\n"
116
117 output_buffer += "uint16_t leader_buffer[] = {\n"
118 output_buffer += " " + reduce(comma_separator, ["0"] * data["parameters"]["leader_max_length"]) + "\n"
119 output_buffer += "};\n"
120 output_buffer += "\n"
121
122 output_buffer += "uint8_t dynamic_macro_buffer[] = {\n"
123 output_buffer += " " + reduce(comma_separator, ["0"] * data["parameters"]["dynamic_macro_max_length"]) + "\n"
124 output_buffer += "};"
125
126 return output_buffer + "\n"
127
128def parse_keyboard_specifics(data):
129 keyboard_part_0 = add_includes(data)
130 keyboard_part_0 += add_keycodes(data)
131 keyboard_part_0 += add_pseudolayers(data)
132 keyboard_part_0 += add_parameters(data)
133 keyboard_part_0 += add_layers(data)
134 keyboard_part_0 += prep_buffers(data)
135
136 return keyboard_part_0 + '\n'
137
138def parse_chords(data):
139 keyboard_part_2 = ""
140 strings = []
141 number_of_strings = 0
142 number_of_chords = 0
143
144 for pseudolayer in data["pseudolayers"]:
145 name = pseudolayer["name"]
146 for chord in pseudolayer["chords"]:
147 if chord["type"] == "chord_set":
148 keycodes = reduce(comma_separator, [word for word in chord["keycodes"]])
149 [keyboard_part_2, number_of_chords, number_of_strings, strings] = add_chord_set(name, keycodes, chord["set"], data, keyboard_part_2, number_of_chords, number_of_strings, strings)
150 if chord["type"] == "visual_array":
151 [keyboard_part_2, number_of_chords, number_of_strings, strings] = add_dictionary(name, chord["keys"], chord["dictionary"], keyboard_part_2, number_of_chords, number_of_strings, strings)
152 if chord["type"] == "visual":
153 keycodes = reduce(comma_separator, [word for word in chord["chord"]])
154 [keyboard_part_2, number_of_chords, number_of_strings, strings] = secret_chord(name, chord["keycode"], keycodes, data, keyboard_part_2, number_of_chords, number_of_strings, strings)
155 elif chord["type"] == "simple":
156 keycodes = reduce(string_sum, ["H_" + word for word in chord["chord"]])
157 [keyboard_part_2, number_of_chords, number_of_strings, strings] = add_key(name, keycodes, chord["keycode"], keyboard_part_2, number_of_chords, number_of_strings, strings)
158 keyboard_part_2 += "\n"
159
160 keyboard_part_2 += "const struct Chord* const list_of_chords[] PROGMEM = {\n"
161 keyboard_part_2 += " " + reduce(comma_separator, ["&chord_" + str(i) for i in range(0, number_of_chords)]) + "\n"
162 keyboard_part_2 += "};\n"
163 keyboard_part_2 += "\n"
164
165 if len(data["leader_sequences"]) > 0:
166 keyboard_part_2 += reduce(newline_separator, [sequence["function"] for sequence in data["leader_sequences"]]) + "\n\n"
167 keyboard_part_2 += "const uint16_t leader_triggers[][LEADER_MAX_LENGTH] PROGMEM = {\n"
168 for sequence in data["leader_sequences"]:
169 keyboard_part_2 += " {" + reduce(comma_separator, sequence["sequence"] + ["0"] * (data["parameters"]["leader_max_length"] - len(sequence["sequence"]))) + "},\n"
170 keyboard_part_2 += "};\n\n"
171 keyboard_part_2 += "void (*leader_functions[]) (void) = {\n"
172 keyboard_part_2 += " " + reduce(comma_separator, ["&" + sequence["name"] for sequence in data["leader_sequences"]]) + "\n"
173 keyboard_part_2 += "};\n"
174 else:
175 keyboard_part_2 += "const uint16_t** const leader_triggers PROGMEM = NULL;\n"
176 keyboard_part_2 += "void (*leader_functions[]) (void) = {};\n"
177 keyboard_part_2 += "\n"
178
179 keyboard_part_2 += "#define NUMBER_OF_CHORDS " + str(number_of_chords) + "\n"
180 keyboard_part_2 += "#define NUMBER_OF_LEADER_COMBOS " + str(len(data["leader_sequences"]))
181
182 return keyboard_part_2 + "\n\n"
183
184def parse_strings_for_chords(data):
185 keyboard_part_1 = ""
186
187 for string, i in zip(strings, range(0, len(strings))):
188 keyboard_part_1 += "const char string_" + str(i) + " [] PROGMEM = \"" + string + "\";\n"
189
190 keyboard_part_1 += "\n"
191 keyboard_part_1 += "const char * const strings[] PROGMEM = {\n"
192 if len(strings) > 0:
193 keyboard_part_1 += " " + reduce(comma_separator, ["string_" + str(i) for i in range(0, len(strings))])
194 keyboard_part_1 += "\n};\n"
195
196 return keyboard_part_1
197
198def main():
199 if len(sys.argv) != 3:
200 raise Exception("Wrong number of arguments.\n\nUsage: python parser.py keymap.json keymap.c")
201
202 input_filepath = sys.argv[1]
203 output_filepath = sys.argv[2]
204
205 with open(input_filepath, "r") as read_file:
206 data = json.load(read_file)
207
208 keyboard_part_0 = parse_keyboard_specifics(data)
209 keyboard_part_1 = parse_strings_for_chords(data)
210 keyboard_part_2 = parse_chords(data)
211
212 engine_part_1 = open("engine.part.1", "r").read()
213 engine_part_2 = open("engine.part.2", "r").read() + "\n"
214 engine_part_3 = open("engine.part.3", "r").read()
215
216 output_buffer = keyboard_part_0
217 output_buffer += engine_part_1
218
219 if len(data["extra_code"]) > 0:
220 output_buffer += data["extra_code"] + "\n"
221
222 output_buffer += keyboard_part_1
223 output_buffer += engine_part_2
224 output_buffer += keyboard_part_2
225 output_buffer += engine_part_3
226
227 with open(output_filepath, "w") as write_file:
228 write_file.write(output_buffer)
229
230if __name__ == "__main__":
231 main() \ No newline at end of file
diff --git a/users/dennytom/chording_engine/state_machine.dot b/users/dennytom/chording_engine/state_machine.dot
new file mode 100644
index 000000000..431e6f69b
--- /dev/null
+++ b/users/dennytom/chording_engine/state_machine.dot
@@ -0,0 +1,49 @@
1digraph {
2 IDLE
3 READY
4 ACTIVATED
5 DEACTIVATED
6 PRESS_FROM_ACTIVE
7 FINISHED_FROM_ACTIVE
8 IDLE_IN_DANCE
9 READY_IN_DANCE
10 FINISHED
11 LOCKED
12 READY_LOCKED
13 RESTART
14 IN_ONE_SHOT
15
16 // common
17 FINISHED -> RESTART;
18 RESTART -> IDLE;
19 DEACTIVATED -> IDLE_IN_DANCE;
20
21 // kill_one_shots()
22 IN_ONE_SHOT -> RESTART [label="non-one-shot key went through", color="blue"];
23
24 // process_finished_dances()
25 ACTIVATED -> PRESS_FROM_ACTIVE [label="dance timer", color="green"];
26 IDLE_IN_DANCE -> FINISHED [label="dance timer", color="green"];
27 PRESS_FROM_ACTIVE -> FINISHED_FROM_ACTIVE [label="dance timer", color="green"];
28
29 // remove_subchords()
30 READY -> IDLE [label="superchord active", color="red"];
31 READY_IN_DANCE -> IDLE_IN_DANCE [label="superchord active", color="red"];
32 READY_LOCKED -> LOCKED [label="superchord active", color="red"];
33
34 // process_ready_chords()
35 LOCKED -> READY_LOCKED [label="all keys pressed", color="orange"];
36 IDLE -> READY [label="all keys pressed", color="orange"];
37 IDLE_IN_DANCE -> READY_IN_DANCE [label="all keys pressed", color="orange"];
38
39 READY_LOCKED -> RESTART [label="chord timer", color="orange"];
40 READY -> ACTIVATED [label="chord timer", color="orange"];
41 READY_IN_DANCE -> ACTIVATED [label="chord timer", color="orange"];
42 ACTIVATED -> PRESS_FROM_ACTIVE [label="lock next", color="orange"];
43 PRESS_FROM_ACTIVE -> LOCKED [label="lock next", color="orange"];
44
45 // deactivate_active_chords()
46 ACTIVATED -> DEACTIVATED [label="a key lifted", color="purple"];
47 PRESS_FROM_ACTIVE -> RESTART [label="a key lifted", color="orange"];
48 FINISHED_FROM_ACTIVE -> DEACTIVATED [label="a key lifted", color="orange"];
49} \ No newline at end of file
diff --git a/users/dennytom/chording_engine/state_machine.svg b/users/dennytom/chording_engine/state_machine.svg
new file mode 100644
index 000000000..773168988
--- /dev/null
+++ b/users/dennytom/chording_engine/state_machine.svg
@@ -0,0 +1,235 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4<!-- Generated by graphviz version 2.40.1 (20161225.0304)
5 -->
6<!-- Title: %0 Pages: 1 -->
7<svg width="829pt" height="754pt"
8 viewBox="0.00 0.00 829.35 754.40" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 750.4)">
10<title>%0</title>
11<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-750.4 825.3486,-750.4 825.3486,4 -4,4"/>
12<!-- IDLE -->
13<g id="node1" class="node">
14<title>IDLE</title>
15<ellipse fill="none" stroke="#000000" cx="492" cy="-728.4" rx="33.043" ry="18"/>
16<text text-anchor="middle" x="492" y="-724.2" font-family="Times,serif" font-size="14.00" fill="#000000">IDLE</text>
17</g>
18<!-- READY -->
19<g id="node2" class="node">
20<title>READY</title>
21<ellipse fill="none" stroke="#000000" cx="404" cy="-639.6" rx="44.0814" ry="18"/>
22<text text-anchor="middle" x="404" y="-635.4" font-family="Times,serif" font-size="14.00" fill="#000000">READY</text>
23</g>
24<!-- IDLE&#45;&gt;READY -->
25<g id="edge12" class="edge">
26<title>IDLE&#45;&gt;READY</title>
27<path fill="none" stroke="#ffa500" d="M487.6897,-710.0931C484.347,-699.0742 478.772,-685.3407 470,-675.6 463.1872,-668.0349 454.3989,-661.7377 445.4714,-656.6382"/>
28<polygon fill="#ffa500" stroke="#ffa500" points="447.0426,-653.5099 436.5654,-651.9352 443.7738,-659.6998 447.0426,-653.5099"/>
29<text text-anchor="middle" x="524.317" y="-679.8" font-family="Times,serif" font-size="14.00" fill="#000000">all keys pressed</text>
30</g>
31<!-- READY&#45;&gt;IDLE -->
32<g id="edge8" class="edge">
33<title>READY&#45;&gt;IDLE</title>
34<path fill="none" stroke="#ff0000" d="M383.1899,-655.7528C371.9777,-666.4401 362.0803,-680.6044 370.88,-692.4 380.5143,-705.3144 418.9714,-715.2612 450.0017,-721.3939"/>
35<polygon fill="#ff0000" stroke="#ff0000" points="449.7109,-724.9011 460.188,-723.3263 451.0156,-718.0238 449.7109,-724.9011"/>
36<text text-anchor="middle" x="420.56" y="-679.8" font-family="Times,serif" font-size="14.00" fill="#000000">superchord active</text>
37</g>
38<!-- ACTIVATED -->
39<g id="node3" class="node">
40<title>ACTIVATED</title>
41<ellipse fill="none" stroke="#000000" cx="404" cy="-550.8" rx="66.0512" ry="18"/>
42<text text-anchor="middle" x="404" y="-546.6" font-family="Times,serif" font-size="14.00" fill="#000000">ACTIVATED</text>
43</g>
44<!-- READY&#45;&gt;ACTIVATED -->
45<g id="edge15" class="edge">
46<title>READY&#45;&gt;ACTIVATED</title>
47<path fill="none" stroke="#ffa500" d="M404,-621.2006C404,-609.0949 404,-593.0076 404,-579.2674"/>
48<polygon fill="#ffa500" stroke="#ffa500" points="407.5001,-578.872 404,-568.872 400.5001,-578.8721 407.5001,-578.872"/>
49<text text-anchor="middle" x="436.4611" y="-591" font-family="Times,serif" font-size="14.00" fill="#000000">chord timer</text>
50</g>
51<!-- DEACTIVATED -->
52<g id="node4" class="node">
53<title>DEACTIVATED</title>
54<ellipse fill="none" stroke="#000000" cx="301" cy="-284.4" rx="79.2922" ry="18"/>
55<text text-anchor="middle" x="301" y="-280.2" font-family="Times,serif" font-size="14.00" fill="#000000">DEACTIVATED</text>
56</g>
57<!-- ACTIVATED&#45;&gt;DEACTIVATED -->
58<g id="edge19" class="edge">
59<title>ACTIVATED&#45;&gt;DEACTIVATED</title>
60<path fill="none" stroke="#a020f0" d="M414.457,-532.6765C417.1337,-527.1214 419.6389,-520.8699 421,-514.8 426.1765,-491.7151 456.5634,-425.2497 416,-355.2 402.3607,-331.646 377.4429,-314.8951 354.2805,-303.6207"/>
61<polygon fill="#a020f0" stroke="#a020f0" points="355.7259,-300.433 345.1798,-299.4173 352.7907,-306.7879 355.7259,-300.433"/>
62<text text-anchor="middle" x="468.4881" y="-413.4" font-family="Times,serif" font-size="14.00" fill="#000000">a key lifted</text>
63</g>
64<!-- PRESS_FROM_ACTIVE -->
65<g id="node5" class="node">
66<title>PRESS_FROM_ACTIVE</title>
67<ellipse fill="none" stroke="#000000" cx="279" cy="-462" rx="111.797" ry="18"/>
68<text text-anchor="middle" x="279" y="-457.8" font-family="Times,serif" font-size="14.00" fill="#000000">PRESS_FROM_ACTIVE</text>
69</g>
70<!-- ACTIVATED&#45;&gt;PRESS_FROM_ACTIVE -->
71<g id="edge5" class="edge">
72<title>ACTIVATED&#45;&gt;PRESS_FROM_ACTIVE</title>
73<path fill="none" stroke="#00ff00" d="M381.0189,-533.8085C363.6663,-521.0049 341.3076,-504.5706 332,-498 326.1999,-493.9054 319.9973,-489.6147 313.9535,-485.4802"/>
74<polygon fill="#00ff00" stroke="#00ff00" points="315.5139,-482.3085 305.2776,-479.5755 311.5753,-488.0954 315.5139,-482.3085"/>
75<text text-anchor="middle" x="387.8454" y="-502.2" font-family="Times,serif" font-size="14.00" fill="#000000">dance timer</text>
76</g>
77<!-- ACTIVATED&#45;&gt;PRESS_FROM_ACTIVE -->
78<g id="edge17" class="edge">
79<title>ACTIVATED&#45;&gt;PRESS_FROM_ACTIVE</title>
80<path fill="none" stroke="#ffa500" d="M346.8191,-541.6158C317.1162,-535.5544 285.1886,-526.5544 276.2896,-514.8 271.0903,-507.9325 270.0671,-499.0047 270.857,-490.45"/>
81<polygon fill="#ffa500" stroke="#ffa500" points="274.3462,-490.8071 272.5608,-480.3638 267.444,-489.6411 274.3462,-490.8071"/>
82<text text-anchor="middle" x="302.8552" y="-502.2" font-family="Times,serif" font-size="14.00" fill="#000000">lock next</text>
83</g>
84<!-- IDLE_IN_DANCE -->
85<g id="node7" class="node">
86<title>IDLE_IN_DANCE</title>
87<ellipse fill="none" stroke="#000000" cx="293" cy="-195.6" rx="86.2367" ry="18"/>
88<text text-anchor="middle" x="293" y="-191.4" font-family="Times,serif" font-size="14.00" fill="#000000">IDLE_IN_DANCE</text>
89</g>
90<!-- DEACTIVATED&#45;&gt;IDLE_IN_DANCE -->
91<g id="edge3" class="edge">
92<title>DEACTIVATED&#45;&gt;IDLE_IN_DANCE</title>
93<path fill="none" stroke="#000000" d="M299.3424,-266.0006C298.241,-253.775 296.7738,-237.4887 295.5279,-223.6599"/>
94<polygon fill="#000000" stroke="#000000" points="299.0114,-223.3176 294.6281,-213.672 292.0396,-223.9458 299.0114,-223.3176"/>
95</g>
96<!-- FINISHED_FROM_ACTIVE -->
97<g id="node6" class="node">
98<title>FINISHED_FROM_ACTIVE</title>
99<ellipse fill="none" stroke="#000000" cx="280" cy="-373.2" rx="127.3672" ry="18"/>
100<text text-anchor="middle" x="280" y="-369" font-family="Times,serif" font-size="14.00" fill="#000000">FINISHED_FROM_ACTIVE</text>
101</g>
102<!-- PRESS_FROM_ACTIVE&#45;&gt;FINISHED_FROM_ACTIVE -->
103<g id="edge7" class="edge">
104<title>PRESS_FROM_ACTIVE&#45;&gt;FINISHED_FROM_ACTIVE</title>
105<path fill="none" stroke="#00ff00" d="M279.2072,-443.6006C279.3435,-431.4949 279.5247,-415.4076 279.6794,-401.6674"/>
106<polygon fill="#00ff00" stroke="#00ff00" points="283.1836,-401.3108 279.7965,-391.272 276.184,-401.2319 283.1836,-401.3108"/>
107<text text-anchor="middle" x="312.8454" y="-413.4" font-family="Times,serif" font-size="14.00" fill="#000000">dance timer</text>
108</g>
109<!-- LOCKED -->
110<g id="node10" class="node">
111<title>LOCKED</title>
112<ellipse fill="none" stroke="#000000" cx="84" cy="-373.2" rx="50.38" ry="18"/>
113<text text-anchor="middle" x="84" y="-369" font-family="Times,serif" font-size="14.00" fill="#000000">LOCKED</text>
114</g>
115<!-- PRESS_FROM_ACTIVE&#45;&gt;LOCKED -->
116<g id="edge18" class="edge">
117<title>PRESS_FROM_ACTIVE&#45;&gt;LOCKED</title>
118<path fill="none" stroke="#ffa500" d="M241.402,-444.8785C207.8161,-429.584 158.7472,-407.2387 124.3482,-391.574"/>
119<polygon fill="#ffa500" stroke="#ffa500" points="125.6113,-388.3034 115.06,-387.3442 122.7102,-394.6739 125.6113,-388.3034"/>
120<text text-anchor="middle" x="224.8552" y="-413.4" font-family="Times,serif" font-size="14.00" fill="#000000">lock next</text>
121</g>
122<!-- RESTART -->
123<g id="node12" class="node">
124<title>RESTART</title>
125<ellipse fill="none" stroke="#000000" cx="201" cy="-18" rx="53.9098" ry="18"/>
126<text text-anchor="middle" x="201" y="-13.8" font-family="Times,serif" font-size="14.00" fill="#000000">RESTART</text>
127</g>
128<!-- PRESS_FROM_ACTIVE&#45;&gt;RESTART -->
129<g id="edge20" class="edge">
130<title>PRESS_FROM_ACTIVE&#45;&gt;RESTART</title>
131<path fill="none" stroke="#ffa500" d="M190.3393,-450.9993C108.6441,-438.3099 0,-413.8018 0,-373.2 0,-373.2 0,-373.2 0,-106.8 0,-74.7037 86.8374,-46.4364 146.3579,-30.8311"/>
132<polygon fill="#ffa500" stroke="#ffa500" points="147.578,-34.1313 156.3916,-28.2515 145.835,-27.3517 147.578,-34.1313"/>
133<text text-anchor="middle" x="31.4881" y="-235.8" font-family="Times,serif" font-size="14.00" fill="#000000">a key lifted</text>
134</g>
135<!-- FINISHED_FROM_ACTIVE&#45;&gt;DEACTIVATED -->
136<g id="edge21" class="edge">
137<title>FINISHED_FROM_ACTIVE&#45;&gt;DEACTIVATED</title>
138<path fill="none" stroke="#ffa500" d="M284.3512,-354.8006C287.2424,-342.575 291.0939,-326.2887 294.3642,-312.4599"/>
139<polygon fill="#ffa500" stroke="#ffa500" points="297.8308,-313.0091 296.7262,-302.472 291.0187,-311.3981 297.8308,-313.0091"/>
140<text text-anchor="middle" x="324.4881" y="-324.6" font-family="Times,serif" font-size="14.00" fill="#000000">a key lifted</text>
141</g>
142<!-- READY_IN_DANCE -->
143<g id="node8" class="node">
144<title>READY_IN_DANCE</title>
145<ellipse fill="none" stroke="#000000" cx="402" cy="-106.8" rx="97.2741" ry="18"/>
146<text text-anchor="middle" x="402" y="-102.6" font-family="Times,serif" font-size="14.00" fill="#000000">READY_IN_DANCE</text>
147</g>
148<!-- IDLE_IN_DANCE&#45;&gt;READY_IN_DANCE -->
149<g id="edge13" class="edge">
150<title>IDLE_IN_DANCE&#45;&gt;READY_IN_DANCE</title>
151<path fill="none" stroke="#ffa500" d="M285.2465,-177.3414C281.8879,-166.3406 280.1649,-152.6093 287.366,-142.8 292.9374,-135.2107 308.8319,-128.4976 327.077,-122.9864"/>
152<polygon fill="#ffa500" stroke="#ffa500" points="328.1338,-126.3246 336.79,-120.2157 326.2135,-119.5932 328.1338,-126.3246"/>
153<text text-anchor="middle" x="332.317" y="-147" font-family="Times,serif" font-size="14.00" fill="#000000">all keys pressed</text>
154</g>
155<!-- FINISHED -->
156<g id="node9" class="node">
157<title>FINISHED</title>
158<ellipse fill="none" stroke="#000000" cx="201" cy="-106.8" rx="55.0323" ry="18"/>
159<text text-anchor="middle" x="201" y="-102.6" font-family="Times,serif" font-size="14.00" fill="#000000">FINISHED</text>
160</g>
161<!-- IDLE_IN_DANCE&#45;&gt;FINISHED -->
162<g id="edge6" class="edge">
163<title>IDLE_IN_DANCE&#45;&gt;FINISHED</title>
164<path fill="none" stroke="#00ff00" d="M231.9976,-182.6889C220.3684,-177.4272 209.5756,-170.022 202.3092,-159.6 197.3878,-152.5413 195.8986,-143.5625 195.9866,-135.0199"/>
165<polygon fill="#00ff00" stroke="#00ff00" points="199.4821,-135.2114 196.7515,-124.9745 192.5023,-134.6799 199.4821,-135.2114"/>
166<text text-anchor="middle" x="235.8454" y="-147" font-family="Times,serif" font-size="14.00" fill="#000000">dance timer</text>
167</g>
168<!-- READY_IN_DANCE&#45;&gt;ACTIVATED -->
169<g id="edge16" class="edge">
170<title>READY_IN_DANCE&#45;&gt;ACTIVATED</title>
171<path fill="none" stroke="#ffa500" d="M460.9306,-121.2262C473.3432,-126.4214 485.5301,-133.4169 495,-142.8 512.9398,-160.5753 516,-170.3454 516,-195.6 516,-462 516,-462 516,-462 516,-495.4749 484.8997,-518.3284 455.476,-532.4591"/>
172<polygon fill="#ffa500" stroke="#ffa500" points="453.6869,-529.4272 446.0233,-536.7429 456.5764,-535.803 453.6869,-529.4272"/>
173<text text-anchor="middle" x="548.4611" y="-324.6" font-family="Times,serif" font-size="14.00" fill="#000000">chord timer</text>
174</g>
175<!-- READY_IN_DANCE&#45;&gt;IDLE_IN_DANCE -->
176<g id="edge9" class="edge">
177<title>READY_IN_DANCE&#45;&gt;IDLE_IN_DANCE</title>
178<path fill="none" stroke="#ff0000" d="M398.1917,-124.942C395.0071,-136.1761 389.421,-150.2021 380,-159.6 372.9696,-166.6132 364.3766,-172.3251 355.3586,-176.9585"/>
179<polygon fill="#ff0000" stroke="#ff0000" points="353.8046,-173.8215 346.241,-181.2406 356.7803,-180.1575 353.8046,-173.8215"/>
180<text text-anchor="middle" x="441.56" y="-147" font-family="Times,serif" font-size="14.00" fill="#000000">superchord active</text>
181</g>
182<!-- FINISHED&#45;&gt;RESTART -->
183<g id="edge1" class="edge">
184<title>FINISHED&#45;&gt;RESTART</title>
185<path fill="none" stroke="#000000" d="M201,-88.4006C201,-76.2949 201,-60.2076 201,-46.4674"/>
186<polygon fill="#000000" stroke="#000000" points="204.5001,-46.072 201,-36.072 197.5001,-46.0721 204.5001,-46.072"/>
187</g>
188<!-- READY_LOCKED -->
189<g id="node11" class="node">
190<title>READY_LOCKED</title>
191<ellipse fill="none" stroke="#000000" cx="116" cy="-284.4" rx="88.0442" ry="18"/>
192<text text-anchor="middle" x="116" y="-280.2" font-family="Times,serif" font-size="14.00" fill="#000000">READY_LOCKED</text>
193</g>
194<!-- LOCKED&#45;&gt;READY_LOCKED -->
195<g id="edge11" class="edge">
196<title>LOCKED&#45;&gt;READY_LOCKED</title>
197<path fill="none" stroke="#ffa500" d="M62.8266,-356.7262C52.2277,-346.3972 43.0571,-332.745 50.366,-320.4 53.6683,-314.8223 58.175,-310.0516 63.2916,-305.985"/>
198<polygon fill="#ffa500" stroke="#ffa500" points="65.4987,-308.7148 71.7485,-300.1596 61.5278,-302.9501 65.4987,-308.7148"/>
199<text text-anchor="middle" x="95.317" y="-324.6" font-family="Times,serif" font-size="14.00" fill="#000000">all keys pressed</text>
200</g>
201<!-- READY_LOCKED&#45;&gt;LOCKED -->
202<g id="edge10" class="edge">
203<title>READY_LOCKED&#45;&gt;LOCKED</title>
204<path fill="none" stroke="#ff0000" d="M134.3377,-302.181C142.7003,-312.508 149.4503,-325.708 143,-337.2 139.1182,-344.1159 133.3081,-349.8603 126.8241,-354.5772"/>
205<polygon fill="#ff0000" stroke="#ff0000" points="124.8745,-351.6695 118.3451,-360.0132 128.6526,-357.5624 124.8745,-351.6695"/>
206<text text-anchor="middle" x="195.56" y="-324.6" font-family="Times,serif" font-size="14.00" fill="#000000">superchord active</text>
207</g>
208<!-- READY_LOCKED&#45;&gt;RESTART -->
209<g id="edge14" class="edge">
210<title>READY_LOCKED&#45;&gt;RESTART</title>
211<path fill="none" stroke="#ffa500" d="M114.0574,-266.3175C111.0132,-230.457 108.1435,-148.6335 137,-88.8 145.6041,-70.9596 160.1275,-54.343 173.1661,-41.6972"/>
212<polygon fill="#ffa500" stroke="#ffa500" points="175.702,-44.1178 180.6155,-34.7312 170.9209,-39.0049 175.702,-44.1178"/>
213<text text-anchor="middle" x="152.4611" y="-147" font-family="Times,serif" font-size="14.00" fill="#000000">chord timer</text>
214</g>
215<!-- RESTART&#45;&gt;IDLE -->
216<g id="edge2" class="edge">
217<title>RESTART&#45;&gt;IDLE</title>
218<path fill="none" stroke="#000000" d="M251.8307,-24.1482C358.5139,-37.7418 596,-71.9196 596,-106.8 596,-639.6 596,-639.6 596,-639.6 596,-665.1965 591.0323,-674.2337 573,-692.4 561.7252,-703.7586 546.243,-711.8298 531.8327,-717.4099"/>
219<polygon fill="#000000" stroke="#000000" points="530.3349,-714.2281 522.0925,-720.8849 532.6871,-720.8211 530.3349,-714.2281"/>
220</g>
221<!-- IN_ONE_SHOT -->
222<g id="node13" class="node">
223<title>IN_ONE_SHOT</title>
224<ellipse fill="none" stroke="#000000" cx="700" cy="-106.8" rx="75.8643" ry="18"/>
225<text text-anchor="middle" x="700" y="-102.6" font-family="Times,serif" font-size="14.00" fill="#000000">IN_ONE_SHOT</text>
226</g>
227<!-- IN_ONE_SHOT&#45;&gt;RESTART -->
228<g id="edge4" class="edge">
229<title>IN_ONE_SHOT&#45;&gt;RESTART</title>
230<path fill="none" stroke="#0000ff" d="M676.9312,-89.3704C659.5151,-77.2164 634.5042,-61.8271 610,-54 547.5343,-34.0473 363.5933,-24.1795 265.053,-20.2067"/>
231<polygon fill="#0000ff" stroke="#0000ff" points="265.0515,-16.704 254.9213,-19.8069 264.7755,-23.6986 265.0515,-16.704"/>
232<text text-anchor="middle" x="733.6743" y="-58.2" font-family="Times,serif" font-size="14.00" fill="#000000">non&#45;one&#45;shot key went through</text>
233</g>
234</g>
235</svg>
diff --git a/users/dennytom/chording_engine/tests/minunit.h b/users/dennytom/chording_engine/tests/minunit.h
new file mode 100644
index 000000000..ed71b6253
--- /dev/null
+++ b/users/dennytom/chording_engine/tests/minunit.h
@@ -0,0 +1,288 @@
1#define mu_assert(message, test) \
2 do { \
3 if (!(test)) { \
4 return message; \
5 } \
6 } while (0)
7
8#define RED "\033[0;31m"
9#define GREEN "\033[0;32m"
10#define NC "\033[0m"
11
12enum ASSERT_TYPES {
13 UINT,
14 INT
15};
16
17#define BUFF_SIZE 1024
18char buffer[BUFF_SIZE];
19
20#define ASSERT_EQ(type, actual, expected) \
21 do { \
22 if (actual != expected) { \
23 switch (type) { \
24 case UINT: \
25 snprintf(buffer, BUFF_SIZE, "\nline %d\nvar %s\nactual = %u\nexpected = %u\n", __LINE__, #actual, actual, expected); \
26 break; \
27 case INT: \
28 snprintf(buffer, BUFF_SIZE, "\nline %d\nvar %s\nactual = %d\nexpected = %d\n", __LINE__, #actual, actual, expected); \
29 break; \
30 default: \
31 snprintf(buffer, BUFF_SIZE, "\nline %d\nunsupported ASSERT_EQ type\n", __LINE__); \
32 break; \
33 } \
34 printf("%s\n", buffer); \
35 passed = false; \
36 all_passed = false; \
37 } \
38 } while (0)
39
40#include <stdio.h>
41#include <stdint.h>
42#include <stddef.h>
43#include <stdbool.h>
44#include <string.h>
45
46#define MATRIX_ROWS 2
47#define MATRIX_COLS 10
48#define LAYOUT_test( \
49 k09, k08, k07, k06, k05, k04, k03, k02, k01, k00, \
50 k19, k18, k17, k16, k15, k14, k13, k12, k11, k10 \
51) { \
52 { k00, k01, k02, k03, k04, k05, k06, k07, k08, k09}, \
53 { k10, k11, k12, k13, k14, k15, k16, k17, k18, k19}, \
54}
55
56#define PROGMEM
57#define memcpy_P memcpy
58const struct Chord* pgm_read_word(const struct Chord* const* chord) {return *chord;}
59
60typedef struct {
61 uint8_t col;
62 uint8_t row;
63} keypos_t;
64
65typedef struct {
66 keypos_t key;
67 bool pressed;
68 uint16_t time;
69} keyevent_t;
70
71typedef struct {
72 bool interrupted :1;
73 bool reserved2 :1;
74 bool reserved1 :1;
75 bool reserved0 :1;
76 uint8_t count :4;
77} tap_t;
78
79typedef struct {
80 keyevent_t event;
81 tap_t tap;
82} keyrecord_t;
83
84keyrecord_t pressed = {{{0,0},true,0}, {0,0,0,0,0}};
85keyrecord_t depressed = {{{0,0},false,0}, {0,0,0,0,0}};
86
87enum keycodes {
88 KC_NO,
89 KC_TILDE,
90 KC_GRAVE,
91 KC_EXCLAIM,
92 KC_1,
93 KC_AT,
94 KC_2,
95 KC_HASH,
96 KC_3,
97 KC_DOLLAR,
98 KC_4,
99 KC_PERCENT,
100 KC_5,
101 KC_CIRCUMFLEX,
102 KC_6,
103 KC_AMPERSAND,
104 KC_7,
105 KC_ASTERISK,
106 KC_8,
107 KC_LEFT_PAREN,
108 KC_9,
109 KC_RIGHT_PAREN,
110 KC_0,
111 KC_UNDERSCORE,
112 KC_MINUS,
113 KC_PLUS,
114 KC_EQUAL,
115 KC_LEFT_CURLY_BRACE,
116 KC_LBRACKET,
117 KC_RIGHT_CURLY_BRACE,
118 KC_RBRACKET,
119 KC_PIPE,
120 KC_BSLASH,
121 KC_COLON,
122 KC_SCOLON,
123 KC_DOUBLE_QUOTE,
124 KC_QUOTE,
125 KC_LEFT_ANGLE_BRACKET,
126 KC_COMMA,
127 KC_RIGHT_ANGLE_BRACKET,
128 KC_DOT,
129 KC_QUESTION,
130 KC_SLASH,
131 KC_Q,
132 KC_W,
133 KC_E,
134 KC_R,
135 KC_T,
136 KC_Y,
137 KC_U,
138 KC_I,
139 KC_O,
140 KC_P,
141 KC_A,
142 KC_S,
143 KC_D,
144 KC_F,
145 KC_G,
146 KC_H,
147 KC_J,
148 KC_K,
149 KC_L,
150 KC_Z,
151 KC_X,
152 KC_C,
153 KC_V,
154 KC_B,
155 KC_N,
156 KC_M,
157 KC_ESC,
158 KC_LSFT,
159 KC_LCTL,
160 KC_LGUI,
161 KC_LALT,
162 KC_RALT,
163 KC_RCTL,
164 KC_RGUI,
165 KC_RSFT,
166 KC_TAB,
167 KC_DEL,
168 KC_INS,
169 KC_BSPC,
170 KC_ENTER,
171 KC_SPACE,
172 KC_F1,
173 KC_F2,
174 KC_F3,
175 KC_F4,
176 KC_F5,
177 KC_F6,
178 KC_F7,
179 KC_F8,
180 KC_F9,
181 KC_F10,
182 KC_F11,
183 KC_F12,
184 KC_LEFT,
185 KC_DOWN,
186 KC_UP,
187 KC_RIGHT,
188
189 SAFE_RANGE
190};
191
192#define HISTORY 20
193
194int16_t current_time;
195uint8_t keyboard_history[HISTORY][SAFE_RANGE-1];
196int16_t time_history[HISTORY];
197uint8_t history_index;
198
199void register_code(int16_t keycode) {
200 history_index++;
201 for (int j = 0; j < SAFE_RANGE-1; j++) {
202 keyboard_history[history_index][j] = keyboard_history[history_index-1][j];
203 }
204 keyboard_history[history_index][keycode] = 1;
205 time_history[history_index] = current_time;
206};
207void unregister_code(int16_t keycode) {
208 history_index++;
209 for (int j = 0; j < SAFE_RANGE-1; j++) {
210 keyboard_history[history_index][j] = keyboard_history[history_index-1][j];
211 }
212 keyboard_history[history_index][keycode] = 0;
213 time_history[history_index] = current_time;
214};
215void send_keyboard_report(void) { /*still don't know what this does*/ };
216void matrix_scan_user (void);
217void wait_ms(uint16_t ms) {
218 current_time += ms;
219};
220uint16_t timer_read(void) {
221 uint16_t result = current_time;
222 return result;
223};
224uint16_t timer_elapsed(uint16_t timer) {
225 uint16_t result = current_time - timer;
226 return result;
227};
228void layer_move(int16_t layer) { /*ignoring for now*/ };
229void clear_keyboard(void) {
230 history_index++;
231 for (int j = 0; j < SAFE_RANGE-1; j++) {
232 keyboard_history[history_index][j] = 0;
233 }
234 time_history[history_index] = current_time;
235};
236void reset_keyboard(void) { /*ignoring for now*/ };
237
238void pause_ms(uint16_t ms) {
239 for (int i = 0; i < ms; i++) {
240 current_time++;
241 matrix_scan_user();
242 }
243};
244
245#define TEST(name) \
246 do { \
247 printf("%s\n", name); \
248 passed = true; \
249 do { \
250 uint8_t clear_state = ACTIVATED; \
251 struct Chord clear_chord PROGMEM = {0, QWERTY, &clear_state, NULL, 0, 0, clear}; \
252 clear_chord.function(&clear_chord); \
253 } while (0); \
254 current_time = 0; \
255 history_index = 0; \
256 for (int j = 0; j < SAFE_RANGE-1; j++) { \
257 keyboard_history[0][j] = 0; \
258 } \
259 time_history[0] = 0; \
260 for (int i = 1; i < HISTORY; i++) { \
261 for (int j = 0; j < SAFE_RANGE-1; j++) { \
262 keyboard_history[i][j] = -1; \
263 } \
264 time_history[i] = -1; \
265 }
266
267#define END_TEST \
268 if (passed) { \
269 printf(GREEN"PASSED"NC"\n"); \
270 } else { \
271 printf(RED"FAILED"NC"\n"); \
272 } \
273 } while(0);
274
275#define MAIN \
276int main(int argc, char **argv) { \
277 bool passed = true; \
278 bool all_passed = true;
279
280#define END \
281 printf("\n"); \
282 if (all_passed) { \
283 printf(GREEN"ALL TESTS PASSED"NC"\n"); \
284 } else { \
285 printf(RED"TESTS FAILED"NC"\n"); \
286 } \
287 return 1 - all_passed; \
288} \ No newline at end of file
diff --git a/users/dennytom/chording_engine/tests/test.c b/users/dennytom/chording_engine/tests/test.c
new file mode 100644
index 000000000..0cc172f0a
--- /dev/null
+++ b/users/dennytom/chording_engine/tests/test.c
@@ -0,0 +1,1259 @@
1#include "minunit.h"
2#include "test_keymap.c"
3
4MAIN
5
6// CLEAR_KB
7TEST("clear")
8 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
9 struct Chord* chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
10 struct Chord chord_storage;
11 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
12 struct Chord* chord = &chord_storage;
13
14 *chord->state = READY;
15
16 if (chord->counter) {
17 *chord->counter = 1;
18 }
19 }
20
21 history_index++;
22 for (int j = 0; j < SAFE_RANGE-1; j++) {
23 keyboard_history[history_index][j] = 1;
24 }
25
26 current_pseudolayer = 5;
27 lock_next = true;
28 autoshift_mode = false;
29 command_mode = 1;
30 in_leader_mode = true;
31 dynamic_macro_mode = true;
32 a_key_went_through = true;
33
34 for (int i = 0; i < DYNAMIC_MACRO_MAX_LENGTH; i++) {
35 dynamic_macro_buffer[i] = 1;
36 }
37
38 uint8_t clear_state = ACTIVATED;
39 struct Chord clear_chord PROGMEM = {0, QWERTY, &clear_state, NULL, 0, 0, clear};
40 clear_chord.function(&clear_chord);
41
42 for (int i = 0; i < NUMBER_OF_CHORDS; i++) {
43 struct Chord* chord_ptr = (struct Chord*) pgm_read_word (&list_of_chords[i]);
44 struct Chord chord_storage;
45 memcpy_P(&chord_storage, chord_ptr, sizeof(struct Chord));
46 struct Chord* chord = &chord_storage;
47
48 ASSERT_EQ(UINT, *chord->state, IDLE);
49
50 if (chord->counter) {
51 ASSERT_EQ(UINT, *chord->counter, 0);
52 }
53 }
54
55 for (int j = 0; j < SAFE_RANGE-1; j++) {
56 ASSERT_EQ(UINT, keyboard_history[history_index][j], 0);
57 }
58
59 ASSERT_EQ(UINT, current_pseudolayer, 1);
60 ASSERT_EQ(UINT, lock_next, false);
61 ASSERT_EQ(UINT, autoshift_mode, true);
62 ASSERT_EQ(UINT, command_mode, 0);
63 ASSERT_EQ(UINT, in_leader_mode, false);
64 ASSERT_EQ(UINT, leader_ind, 0);
65 ASSERT_EQ(UINT, dynamic_macro_mode, false);
66 ASSERT_EQ(UINT, a_key_went_through, false);
67
68 for (int i = 0; i < DYNAMIC_MACRO_MAX_LENGTH; i++) {
69 ASSERT_EQ(UINT, dynamic_macro_buffer[i], 0);
70 }
71END_TEST
72
73TEST("pause_ms")
74 pause_ms(500);
75 ASSERT_EQ(UINT, current_time, 500);
76END_TEST
77
78// KC
79TEST("single_dance_held_states")
80 ASSERT_EQ(UINT, state_1, IDLE);
81 process_record_user(TOP1, &pressed);
82 pause_ms(CHORD_TIMEOUT);
83 ASSERT_EQ(UINT, state_1, IDLE);
84 pause_ms(1);
85 ASSERT_EQ(UINT, state_1, ACTIVATED);
86 pause_ms(DANCE_TIMEOUT);
87 ASSERT_EQ(UINT, state_1, ACTIVATED);
88 pause_ms(1);
89 ASSERT_EQ(UINT, state_1, PRESS_FROM_ACTIVE);
90 pause_ms(DANCE_TIMEOUT);
91 ASSERT_EQ(UINT, state_1, PRESS_FROM_ACTIVE);
92 pause_ms(1);
93 ASSERT_EQ(UINT, state_1, FINISHED_FROM_ACTIVE);
94 process_record_user(TOP1, &depressed);
95 ASSERT_EQ(UINT, state_1, IDLE);
96END_TEST
97
98TEST("single_dance_held_codes")
99 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
100 process_record_user(TOP1, &pressed);
101 pause_ms(CHORD_TIMEOUT);
102 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
103 pause_ms(1);
104 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 1);
105 pause_ms(DANCE_TIMEOUT);
106 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 1);
107 pause_ms(1);
108 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 1);
109 pause_ms(DANCE_TIMEOUT);
110 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 1);
111 pause_ms(1);
112 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 1);
113 process_record_user(TOP1, &depressed);
114 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
115END_TEST
116
117TEST("single_dance_tapped_states")
118 ASSERT_EQ(UINT, state_1, IDLE);
119 process_record_user(TOP1, &pressed);
120 pause_ms(CHORD_TIMEOUT);
121 ASSERT_EQ(UINT, state_1, IDLE);
122 pause_ms(1);
123 ASSERT_EQ(UINT, state_1, ACTIVATED);
124 process_record_user(TOP1, &depressed);
125 ASSERT_EQ(UINT, state_1, IDLE);
126END_TEST
127
128TEST("single_dance_tapped_codes")
129 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
130 process_record_user(TOP1, &pressed);
131 pause_ms(CHORD_TIMEOUT);
132 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
133 pause_ms(1);
134 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 1);
135 process_record_user(TOP1, &depressed);
136 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
137END_TEST
138
139// I can not actually track the states if the tap is faster than chord timeout
140
141TEST("single_dance_tapped_fast_codes")
142 ASSERT_EQ(UINT, state_0, IDLE);
143 process_record_user(TOP1, &pressed);
144 pause_ms(1);
145 process_record_user(TOP1, &depressed);
146 ASSERT_EQ(UINT, keyboard_history[0][KC_Q], 0);
147 ASSERT_EQ(UINT, keyboard_history[1][KC_Q], 1);
148 ASSERT_EQ(UINT, keyboard_history[2][KC_Q], 0);
149END_TEST
150
151TEST("subchords_are_ignored")
152 ASSERT_EQ(UINT, state_0, IDLE);
153 process_record_user(TOP1, &pressed);
154 pause_ms(1);
155 process_record_user(TOP2, &pressed);
156 pause_ms(CHORD_TIMEOUT + 1);
157 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
158 ASSERT_EQ(UINT, keyboard_history[history_index][KC_W], 0);
159 ASSERT_EQ(UINT, keyboard_history[history_index][KC_ESC], 1);
160END_TEST
161
162TEST("multiple_chords_at_once")
163 ASSERT_EQ(UINT, state_0, IDLE);
164 process_record_user(TOP1, &pressed);
165 pause_ms(1);
166 process_record_user(TOP3, &pressed);
167 pause_ms(CHORD_TIMEOUT + 1);
168 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 1);
169 ASSERT_EQ(UINT, keyboard_history[history_index][KC_E], 1);
170END_TEST
171
172// MO
173TEST("momentary_layer")
174 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
175 process_record_user(BOT7, &pressed);
176 pause_ms(1);
177 process_record_user(BOT8, &pressed);
178 pause_ms(CHORD_TIMEOUT + 1);
179 ASSERT_EQ(UINT, current_pseudolayer, NUM);
180 process_record_user(BOT7, &depressed);
181 pause_ms(1);
182 process_record_user(BOT8, &depressed);
183 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
184END_TEST
185
186TEST("momentary_layer_reset")
187 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
188 process_record_user(BOT7, &pressed);
189 pause_ms(1);
190 process_record_user(BOT8, &pressed);
191 pause_ms(CHORD_TIMEOUT + 1);
192 ASSERT_EQ(UINT, current_pseudolayer, NUM);
193 pause_ms(DANCE_TIMEOUT + 1);
194 ASSERT_EQ(UINT, current_pseudolayer, NUM);
195 process_record_user(BOT7, &depressed);
196 pause_ms(1);
197 process_record_user(BOT8, &depressed);
198 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
199END_TEST
200
201TEST("momentary_layer_alt")
202 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
203
204 process_record_user(TOP8, &pressed);
205 pause_ms(1);
206 process_record_user(TOP9, &pressed);
207 pause_ms(1);
208 process_record_user(TOP0, &pressed);
209 pause_ms(1);
210 process_record_user(BOT8, &pressed);
211 pause_ms(1);
212 process_record_user(BOT9, &pressed);
213 pause_ms(1);
214 process_record_user(BOT0, &pressed);
215 pause_ms(CHORD_TIMEOUT + 1);
216
217 ASSERT_EQ(UINT, current_pseudolayer, NUM);
218
219 process_record_user(TOP8, &depressed);
220 pause_ms(1);
221 process_record_user(TOP9, &depressed);
222 pause_ms(1);
223 process_record_user(TOP0, &depressed);
224 pause_ms(1);
225 process_record_user(BOT8, &depressed);
226 pause_ms(1);
227 process_record_user(BOT9, &depressed);
228 pause_ms(1);
229 process_record_user(BOT0, &depressed);
230
231 ASSERT_EQ(UINT, current_pseudolayer, FNC);
232END_TEST
233
234// DF
235TEST("permanent_layer")
236 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
237 process_record_user(BOT9, &pressed);
238 pause_ms(1);
239 process_record_user(BOT0, &pressed);
240 pause_ms(CHORD_TIMEOUT + 1);
241 ASSERT_EQ(UINT, current_pseudolayer, NUM);
242 process_record_user(BOT9, &depressed);
243 pause_ms(1);
244 process_record_user(BOT0, &depressed);
245 ASSERT_EQ(UINT, current_pseudolayer, NUM);
246 pause_ms(1000);
247 ASSERT_EQ(UINT, current_pseudolayer, NUM);
248END_TEST
249
250// AT
251TEST("autoshift_toggle")
252 ASSERT_EQ(UINT, autoshift_mode, 1);
253 uint8_t state = ACTIVATED;
254 struct Chord chord PROGMEM = {0, QWERTY, &state, NULL, 0, 0, autoshift_toggle};
255 chord.function(&chord);
256 ASSERT_EQ(UINT, autoshift_mode, 0);
257 state = ACTIVATED;
258 chord.function(&chord);
259 ASSERT_EQ(UINT, autoshift_mode, 1);
260END_TEST
261
262// AS
263TEST("autoshift_tap")
264 process_record_user(BOT1, &pressed);
265 pause_ms(CHORD_TIMEOUT + 1);
266 process_record_user(BOT1, &depressed);
267
268 ASSERT_EQ(UINT, keyboard_history[0][KC_Z], 0);
269 ASSERT_EQ(UINT, keyboard_history[0][KC_LSFT], 0);
270
271 ASSERT_EQ(UINT, keyboard_history[1][KC_Z], 1);
272 ASSERT_EQ(UINT, keyboard_history[1][KC_LSFT], 0);
273
274 ASSERT_EQ(UINT, keyboard_history[2][KC_Z], 0);
275 ASSERT_EQ(UINT, keyboard_history[2][KC_LSFT], 0);
276END_TEST
277
278TEST("autoshift_hold")
279 process_record_user(BOT1, &pressed);
280 pause_ms(CHORD_TIMEOUT + 1);
281 pause_ms(LONG_PRESS_MULTIPLIER * (DANCE_TIMEOUT + 1));
282 process_record_user(BOT1, &depressed);
283
284 ASSERT_EQ(UINT, keyboard_history[0][KC_Z], 0);
285 ASSERT_EQ(UINT, keyboard_history[0][KC_LSFT], 0);
286
287 ASSERT_EQ(UINT, keyboard_history[1][KC_Z], 0);
288 ASSERT_EQ(UINT, keyboard_history[1][KC_LSFT], 1);
289
290 ASSERT_EQ(UINT, keyboard_history[2][KC_Z], 1);
291 ASSERT_EQ(UINT, keyboard_history[2][KC_LSFT], 1);
292
293 ASSERT_EQ(UINT, keyboard_history[3][KC_Z], 0);
294 ASSERT_EQ(UINT, keyboard_history[3][KC_LSFT], 1);
295
296 ASSERT_EQ(UINT, keyboard_history[4][KC_Z], 0);
297 ASSERT_EQ(UINT, keyboard_history[4][KC_LSFT], 0);
298END_TEST
299
300TEST("autoshift_hold_off")
301 autoshift_mode = 0;
302 process_record_user(BOT1, &pressed);
303 pause_ms(CHORD_TIMEOUT + 1);
304 pause_ms(LONG_PRESS_MULTIPLIER * (DANCE_TIMEOUT + 1));
305 process_record_user(BOT1, &depressed);
306
307 ASSERT_EQ(UINT, keyboard_history[0][KC_Z], 0);
308 ASSERT_EQ(UINT, keyboard_history[0][KC_LSFT], 0);
309
310 ASSERT_EQ(UINT, keyboard_history[1][KC_Z], 1);
311 ASSERT_EQ(UINT, keyboard_history[1][KC_LSFT], 0);
312
313 ASSERT_EQ(UINT, keyboard_history[2][KC_Z], 0);
314 ASSERT_EQ(UINT, keyboard_history[2][KC_LSFT], 0);
315END_TEST
316
317// LOCK
318TEST("lock")
319 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
320 process_record_user(BOT1, &pressed);
321 process_record_user(BOT2, &pressed);
322 pause_ms(CHORD_TIMEOUT + 1);
323 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
324 process_record_user(BOT1, &depressed);
325 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
326 pause_ms(1);
327 process_record_user(BOT2, &depressed);
328 pause_ms(1);
329 process_record_user(TOP1, &pressed);
330 process_record_user(TOP2, &pressed);
331 process_record_user(BOT1, &pressed);
332 process_record_user(BOT2, &pressed);
333 pause_ms(CHORD_TIMEOUT + 1);
334 process_record_user(TOP1, &depressed);
335 pause_ms(1);
336 process_record_user(TOP2, &depressed);
337 process_record_user(BOT1, &depressed);
338 process_record_user(BOT2, &depressed);
339 pause_ms(1);
340 process_record_user(BOT1, &pressed);
341 process_record_user(BOT2, &pressed);
342 pause_ms(CHORD_TIMEOUT + 1);
343 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
344 process_record_user(BOT1, &depressed);
345 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
346 pause_ms(1);
347 process_record_user(BOT2, &depressed);
348 pause_ms(1000);
349 process_record_user(BOT1, &pressed);
350 process_record_user(BOT2, &pressed);
351 pause_ms(CHORD_TIMEOUT + 1);
352 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
353 process_record_user(BOT1, &depressed);
354 pause_ms(1);
355 process_record_user(BOT2, &depressed);
356 pause_ms(1000);
357 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
358END_TEST
359
360// OSK
361TEST("one_shot_key_tap")
362 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
363 process_record_user(BOT2, &pressed);
364 process_record_user(BOT3, &pressed);
365 pause_ms(CHORD_TIMEOUT + 1);
366 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
367 process_record_user(BOT2, &depressed);
368 pause_ms(1);
369 process_record_user(BOT3, &depressed);
370 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
371 pause_ms(1000);
372 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
373
374 process_record_user(TOP1, &pressed);
375 pause_ms(CHORD_TIMEOUT + 1);
376 process_record_user(TOP1, &depressed);
377 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
378END_TEST
379
380TEST("one_shot_key_hold")
381 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
382 process_record_user(BOT2, &pressed);
383 process_record_user(BOT3, &pressed);
384 pause_ms(CHORD_TIMEOUT + 1);
385 pause_ms(DANCE_TIMEOUT + 1);
386 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
387
388 process_record_user(TOP1, &pressed);
389 pause_ms(CHORD_TIMEOUT + 1);
390 process_record_user(TOP1, &depressed);
391 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
392
393 process_record_user(BOT2, &depressed);
394 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
395END_TEST
396
397TEST("one_shot_key_retrotapping")
398 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
399 process_record_user(BOT2, &pressed);
400 process_record_user(BOT3, &pressed);
401 pause_ms(CHORD_TIMEOUT + 1);
402 pause_ms(DANCE_TIMEOUT + 1);
403 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
404
405 pause_ms(1000);
406
407 process_record_user(BOT2, &depressed);
408 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 1);
409END_TEST
410
411// OSL
412TEST("one_shot_layer_tap")
413 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
414 process_record_user(BOT6, &pressed);
415 process_record_user(BOT7, &pressed);
416 pause_ms(CHORD_TIMEOUT + 1);
417 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
418 process_record_user(BOT6, &depressed);
419 pause_ms(1);
420 process_record_user(BOT7, &depressed);
421 ASSERT_EQ(UINT, current_pseudolayer, NUM);
422 pause_ms(1000);
423 ASSERT_EQ(UINT, current_pseudolayer, NUM);
424
425 process_record_user(TOP1, &pressed);
426 pause_ms(CHORD_TIMEOUT + 1);
427 process_record_user(TOP1, &depressed);
428 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
429END_TEST
430
431TEST("one_shot_layer_hold")
432 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
433 process_record_user(BOT6, &pressed);
434 process_record_user(BOT7, &pressed);
435 pause_ms(CHORD_TIMEOUT + 1);
436 pause_ms(DANCE_TIMEOUT + 1);
437 ASSERT_EQ(UINT, current_pseudolayer, NUM);
438
439 process_record_user(TOP1, &pressed);
440 pause_ms(CHORD_TIMEOUT + 1);
441 process_record_user(TOP1, &depressed);
442 ASSERT_EQ(UINT, current_pseudolayer, NUM);
443
444 process_record_user(BOT6, &depressed);
445 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
446END_TEST
447
448TEST("one_shot_layer_retrotapping")
449 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
450 process_record_user(BOT6, &pressed);
451 process_record_user(BOT7, &pressed);
452 pause_ms(CHORD_TIMEOUT + 1);
453 pause_ms(DANCE_TIMEOUT + 1);
454 ASSERT_EQ(UINT, current_pseudolayer, NUM);
455
456 pause_ms(1000);
457
458 process_record_user(BOT6, &depressed);
459 ASSERT_EQ(UINT, current_pseudolayer, NUM);
460END_TEST
461
462// CMD
463TEST("command_mode")
464 // start recording
465 process_record_user(TOP5, &pressed);
466 process_record_user(TOP6, &pressed);
467 process_record_user(BOT5, &pressed);
468 process_record_user(BOT6, &pressed);
469 pause_ms(1);
470 process_record_user(TOP5, &depressed);
471 process_record_user(TOP6, &depressed);
472 process_record_user(BOT5, &depressed);
473 process_record_user(BOT6, &depressed);
474
475 ASSERT_EQ(UINT, command_mode, 1);
476
477 // record shift+q
478 process_record_user(BOT1, &pressed);
479 process_record_user(BOT2, &pressed);
480 pause_ms(CHORD_TIMEOUT + 1);
481 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
482 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
483 process_record_user(BOT1, &depressed);
484 process_record_user(BOT2, &depressed);
485 pause_ms(1000);
486
487 process_record_user(TOP1, &pressed);
488 pause_ms(CHORD_TIMEOUT + 1);
489 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
490 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
491 process_record_user(TOP1, &depressed);
492 pause_ms(1000);
493
494
495 ASSERT_EQ(UINT, keyboard_history[history_index][KC_Q], 0);
496 ASSERT_EQ(UINT, keyboard_history[history_index][KC_LSFT], 0);
497 // execute
498 process_record_user(TOP5, &pressed);
499 process_record_user(TOP6, &pressed);
500 process_record_user(BOT5, &pressed);
501 process_record_user(BOT6, &pressed);
502 pause_ms(CHORD_TIMEOUT + 1);
503
504 ASSERT_EQ(UINT, command_mode, 0);
505
506 // test history
507 ASSERT_EQ(UINT, keyboard_history[0][KC_Q], 0);
508 ASSERT_EQ(UINT, keyboard_history[0][KC_LSFT], 0);
509
510 ASSERT_EQ(UINT, keyboard_history[1][KC_Q], 0);
511 ASSERT_EQ(UINT, keyboard_history[1][KC_LSFT], 1);
512
513 ASSERT_EQ(UINT, keyboard_history[2][KC_Q], 1);
514 ASSERT_EQ(UINT, keyboard_history[2][KC_LSFT], 1);
515
516 ASSERT_EQ(UINT, keyboard_history[3][KC_Q], 1);
517 ASSERT_EQ(UINT, keyboard_history[3][KC_LSFT], 0);
518
519 ASSERT_EQ(UINT, keyboard_history[4][KC_Q], 0);
520 ASSERT_EQ(UINT, keyboard_history[4][KC_LSFT], 0);
521
522 ASSERT_EQ(UINT, keyboard_history[5][KC_Q], 255);
523 ASSERT_EQ(UINT, keyboard_history[5][KC_LSFT], 255);
524END_TEST
525
526// KK
527TEST("key_key_dance_tap")
528 process_record_user(BOT2, &pressed);
529 pause_ms(CHORD_TIMEOUT + 1);
530 process_record_user(BOT2, &depressed);
531
532 ASSERT_EQ(UINT, keyboard_history[0][KC_X], 0);
533 ASSERT_EQ(UINT, keyboard_history[0][KC_LCTL], 0);
534
535 ASSERT_EQ(UINT, keyboard_history[1][KC_X], 1);
536 ASSERT_EQ(UINT, keyboard_history[1][KC_LCTL], 0);
537
538 ASSERT_EQ(UINT, keyboard_history[2][KC_X], 0);
539 ASSERT_EQ(UINT, keyboard_history[2][KC_LCTL], 0);
540END_TEST
541
542TEST("key_key_dance_hold")
543 process_record_user(BOT2, &pressed);
544 pause_ms(CHORD_TIMEOUT + 1);
545 pause_ms(DANCE_TIMEOUT + 1);
546 process_record_user(BOT2, &depressed);
547
548 ASSERT_EQ(UINT, keyboard_history[0][KC_X], 0);
549 ASSERT_EQ(UINT, keyboard_history[0][KC_LCTL], 0);
550
551 ASSERT_EQ(UINT, keyboard_history[1][KC_X], 0);
552 ASSERT_EQ(UINT, keyboard_history[1][KC_LCTL], 1);
553
554 ASSERT_EQ(UINT, keyboard_history[2][KC_X], 0);
555 ASSERT_EQ(UINT, keyboard_history[2][KC_LCTL], 0);
556END_TEST
557
558// KL
559TEST("key_layer_tap")
560 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
561 process_record_user(BOT3, &pressed);
562 pause_ms(CHORD_TIMEOUT + 1);
563 ASSERT_EQ(UINT, current_pseudolayer, NUM);
564 process_record_user(BOT3, &depressed);
565 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
566 pause_ms(1000);
567
568 ASSERT_EQ(UINT, keyboard_history[0][KC_C], 0);
569 ASSERT_EQ(UINT, keyboard_history[1][KC_C], 1);
570 ASSERT_EQ(UINT, keyboard_history[2][KC_C], 0);
571 ASSERT_EQ(UINT, keyboard_history[3][KC_C], 255);
572END_TEST
573
574TEST("key_layer_retrotapping")
575 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
576 process_record_user(BOT3, &pressed);
577 pause_ms(1000);
578 ASSERT_EQ(UINT, current_pseudolayer, NUM);
579 process_record_user(BOT3, &depressed);
580 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
581 pause_ms(1000);
582
583 ASSERT_EQ(UINT, keyboard_history[0][KC_C], 0);
584 ASSERT_EQ(UINT, keyboard_history[1][KC_C], 1);
585 ASSERT_EQ(UINT, keyboard_history[2][KC_C], 0);
586 ASSERT_EQ(UINT, keyboard_history[3][KC_C], 255);
587END_TEST
588
589TEST("key_layer_hold_quick_typist")
590 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
591 process_record_user(BOT3, &pressed);
592 pause_ms(CHORD_TIMEOUT + 1);
593 ASSERT_EQ(UINT, current_pseudolayer, NUM);
594
595 pause_ms(1);
596 process_record_user(TOP1, &pressed);
597 pause_ms(1);
598 process_record_user(TOP1, &depressed);
599 pause_ms(1);
600 process_record_user(TOP1, &pressed);
601 pause_ms(1);
602 process_record_user(TOP1, &depressed);
603 pause_ms(1);
604 process_record_user(TOP1, &pressed);
605 pause_ms(1);
606 process_record_user(TOP1, &depressed);
607 ASSERT_EQ(UINT, current_pseudolayer, NUM);
608 pause_ms(1);
609
610 process_record_user(BOT3, &depressed);
611 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
612
613 ASSERT_EQ(UINT, keyboard_history[0][KC_1], 0);
614 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 1);
615 ASSERT_EQ(UINT, keyboard_history[2][KC_1], 0);
616 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 1);
617 ASSERT_EQ(UINT, keyboard_history[4][KC_1], 0);
618 ASSERT_EQ(UINT, keyboard_history[5][KC_1], 1);
619 ASSERT_EQ(UINT, keyboard_history[6][KC_1], 0);
620 ASSERT_EQ(UINT, keyboard_history[7][KC_1], 255);
621END_TEST
622
623TEST("key_layer_hold_slow_typist")
624 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
625 process_record_user(BOT3, &pressed);
626 pause_ms(CHORD_TIMEOUT + 1);
627 ASSERT_EQ(UINT, current_pseudolayer, NUM);
628
629 pause_ms(1000);
630 process_record_user(TOP1, &pressed);
631 pause_ms(1000);
632 process_record_user(TOP1, &depressed);
633 pause_ms(1000);
634 process_record_user(TOP1, &pressed);
635 pause_ms(1000);
636 process_record_user(TOP1, &depressed);
637 pause_ms(1000);
638 process_record_user(TOP1, &pressed);
639 pause_ms(1000);
640 process_record_user(TOP1, &depressed);
641 ASSERT_EQ(UINT, current_pseudolayer, NUM);
642 pause_ms(1);
643
644 process_record_user(BOT3, &depressed);
645 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
646
647 ASSERT_EQ(UINT, keyboard_history[0][KC_1], 0);
648 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 1);
649 ASSERT_EQ(UINT, keyboard_history[2][KC_1], 0);
650 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 1);
651 ASSERT_EQ(UINT, keyboard_history[4][KC_1], 0);
652 ASSERT_EQ(UINT, keyboard_history[5][KC_1], 1);
653 ASSERT_EQ(UINT, keyboard_history[6][KC_1], 0);
654 ASSERT_EQ(UINT, keyboard_history[7][KC_1], 255);
655END_TEST
656
657// KM
658TEST("key_mod_tap")
659 ASSERT_EQ(UINT, keyboard_history[0][KC_LALT], 0);
660 ASSERT_EQ(UINT, keyboard_history[0][KC_V], 0);
661 process_record_user(BOT4, &pressed);
662 pause_ms(CHORD_TIMEOUT + 1);
663 ASSERT_EQ(UINT, keyboard_history[1][KC_LALT], 1);
664 ASSERT_EQ(UINT, keyboard_history[1][KC_V], 0);
665 process_record_user(BOT4, &depressed);
666 ASSERT_EQ(UINT, keyboard_history[2][KC_LALT], 0);
667 ASSERT_EQ(UINT, keyboard_history[2][KC_V], 0);
668 pause_ms(1000);
669
670 ASSERT_EQ(UINT, keyboard_history[3][KC_LALT], 0);
671 ASSERT_EQ(UINT, keyboard_history[3][KC_V], 1);
672 ASSERT_EQ(UINT, keyboard_history[4][KC_LALT], 0);
673 ASSERT_EQ(UINT, keyboard_history[4][KC_V], 0);
674 ASSERT_EQ(UINT, keyboard_history[5][KC_LALT], 255);
675 ASSERT_EQ(UINT, keyboard_history[5][KC_V], 255);
676END_TEST
677
678TEST("key_mod_retrotapping")
679 ASSERT_EQ(UINT, keyboard_history[0][KC_LALT], 0);
680 ASSERT_EQ(UINT, keyboard_history[0][KC_V], 0);
681 process_record_user(BOT4, &pressed);
682 pause_ms(1000);
683 ASSERT_EQ(UINT, keyboard_history[1][KC_LALT], 1);
684 ASSERT_EQ(UINT, keyboard_history[1][KC_V], 0);
685 process_record_user(BOT4, &depressed);
686 ASSERT_EQ(UINT, keyboard_history[2][KC_LALT], 0);
687 ASSERT_EQ(UINT, keyboard_history[2][KC_V], 0);
688 pause_ms(1000);
689
690 ASSERT_EQ(UINT, keyboard_history[3][KC_LALT], 0);
691 ASSERT_EQ(UINT, keyboard_history[3][KC_V], 1);
692 ASSERT_EQ(UINT, keyboard_history[4][KC_LALT], 0);
693 ASSERT_EQ(UINT, keyboard_history[4][KC_V], 0);
694 ASSERT_EQ(UINT, keyboard_history[5][KC_LALT], 255);
695 ASSERT_EQ(UINT, keyboard_history[5][KC_V], 255);
696END_TEST
697
698TEST("key_mod_hold_quick_typist")
699 process_record_user(BOT4, &pressed);
700 pause_ms(CHORD_TIMEOUT + 1);
701
702 pause_ms(1);
703 process_record_user(TOP1, &pressed);
704 pause_ms(1);
705 process_record_user(TOP1, &depressed);
706 pause_ms(1);
707 process_record_user(TOP1, &pressed);
708 pause_ms(1);
709 process_record_user(TOP1, &depressed);
710 pause_ms(1);
711 process_record_user(TOP1, &pressed);
712 pause_ms(1);
713 process_record_user(TOP1, &depressed);
714 pause_ms(1);
715
716 process_record_user(BOT4, &depressed);
717 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
718
719 ASSERT_EQ(UINT, keyboard_history[0][KC_LALT], 0);
720 ASSERT_EQ(UINT, keyboard_history[0][KC_V], 0);
721 ASSERT_EQ(UINT, keyboard_history[1][KC_LALT], 1);
722 ASSERT_EQ(UINT, keyboard_history[1][KC_V], 0);
723 ASSERT_EQ(UINT, keyboard_history[2][KC_LALT], 1);
724 ASSERT_EQ(UINT, keyboard_history[2][KC_V], 0);
725 ASSERT_EQ(UINT, keyboard_history[3][KC_LALT], 1);
726 ASSERT_EQ(UINT, keyboard_history[3][KC_V], 0);
727 ASSERT_EQ(UINT, keyboard_history[4][KC_LALT], 1);
728 ASSERT_EQ(UINT, keyboard_history[4][KC_V], 0);
729 ASSERT_EQ(UINT, keyboard_history[5][KC_LALT], 1);
730 ASSERT_EQ(UINT, keyboard_history[5][KC_V], 0);
731 ASSERT_EQ(UINT, keyboard_history[6][KC_LALT], 1);
732 ASSERT_EQ(UINT, keyboard_history[6][KC_V], 0);
733 ASSERT_EQ(UINT, keyboard_history[7][KC_LALT], 1);
734 ASSERT_EQ(UINT, keyboard_history[7][KC_V], 0);
735 ASSERT_EQ(UINT, keyboard_history[8][KC_LALT], 0);
736 ASSERT_EQ(UINT, keyboard_history[8][KC_V], 0);
737 ASSERT_EQ(UINT, keyboard_history[9][KC_LALT], 255);
738 ASSERT_EQ(UINT, keyboard_history[9][KC_V], 255);
739END_TEST
740
741TEST("key_mod_hold_slow_typist")
742 process_record_user(BOT4, &pressed);
743 pause_ms(CHORD_TIMEOUT + 1);
744
745 pause_ms(1000);
746 process_record_user(TOP1, &pressed);
747 pause_ms(1000);
748 process_record_user(TOP1, &depressed);
749 pause_ms(1000);
750 process_record_user(TOP1, &pressed);
751 pause_ms(1000);
752 process_record_user(TOP1, &depressed);
753 pause_ms(1000);
754 process_record_user(TOP1, &pressed);
755 pause_ms(1000);
756 process_record_user(TOP1, &depressed);
757 pause_ms(1000);
758
759 process_record_user(BOT4, &depressed);
760 ASSERT_EQ(UINT, current_pseudolayer, QWERTY);
761
762 ASSERT_EQ(UINT, keyboard_history[0][KC_LALT], 0);
763 ASSERT_EQ(UINT, keyboard_history[0][KC_V], 0);
764 ASSERT_EQ(UINT, keyboard_history[0][KC_Q], 0);
765 ASSERT_EQ(UINT, keyboard_history[1][KC_LALT], 1);
766 ASSERT_EQ(UINT, keyboard_history[1][KC_V], 0);
767 ASSERT_EQ(UINT, keyboard_history[1][KC_Q], 0);
768 ASSERT_EQ(UINT, keyboard_history[2][KC_LALT], 1);
769 ASSERT_EQ(UINT, keyboard_history[2][KC_V], 0);
770 ASSERT_EQ(UINT, keyboard_history[2][KC_Q], 1);
771 ASSERT_EQ(UINT, keyboard_history[3][KC_LALT], 1);
772 ASSERT_EQ(UINT, keyboard_history[3][KC_V], 0);
773 ASSERT_EQ(UINT, keyboard_history[3][KC_Q], 0);
774 ASSERT_EQ(UINT, keyboard_history[4][KC_LALT], 1);
775 ASSERT_EQ(UINT, keyboard_history[4][KC_V], 0);
776 ASSERT_EQ(UINT, keyboard_history[4][KC_Q], 1);
777 ASSERT_EQ(UINT, keyboard_history[5][KC_LALT], 1);
778 ASSERT_EQ(UINT, keyboard_history[5][KC_V], 0);
779 ASSERT_EQ(UINT, keyboard_history[5][KC_Q], 0);
780 ASSERT_EQ(UINT, keyboard_history[6][KC_LALT], 1);
781 ASSERT_EQ(UINT, keyboard_history[6][KC_V], 0);
782 ASSERT_EQ(UINT, keyboard_history[6][KC_Q], 1);
783 ASSERT_EQ(UINT, keyboard_history[7][KC_LALT], 1);
784 ASSERT_EQ(UINT, keyboard_history[7][KC_V], 0);
785 ASSERT_EQ(UINT, keyboard_history[7][KC_Q], 0);
786 ASSERT_EQ(UINT, keyboard_history[8][KC_LALT], 0);
787 ASSERT_EQ(UINT, keyboard_history[8][KC_V], 0);
788 ASSERT_EQ(UINT, keyboard_history[8][KC_Q], 0);
789 ASSERT_EQ(UINT, keyboard_history[9][KC_LALT], 255);
790 ASSERT_EQ(UINT, keyboard_history[9][KC_V], 255);
791 ASSERT_EQ(UINT, keyboard_history[9][KC_Q], 255);
792END_TEST
793
794// LEADER
795TEST("leader_triggers_global")
796 uint8_t state = ACTIVATED;
797 struct Chord chord PROGMEM = {0, QWERTY, &state, NULL, 0, 0, leader};
798 chord.function(&chord);
799
800 ASSERT_EQ(UINT, in_leader_mode, 1);
801END_TEST
802
803TEST("leader_no_follow")
804 uint8_t state = ACTIVATED;
805 struct Chord chord PROGMEM = {0, QWERTY, &state, NULL, 0, 0, leader};
806 chord.function(&chord);
807
808 ASSERT_EQ(UINT, in_leader_mode, 1);
809
810 pause_ms(1000);
811
812 ASSERT_EQ(UINT, in_leader_mode, 0);
813 ASSERT_EQ(UINT, keyboard_history[1][KC_O], 255);
814END_TEST
815
816TEST("leader_wrong_follow")
817 process_record_user(TOP2, &pressed);
818 process_record_user(TOP3, &pressed);
819 process_record_user(BOT2, &pressed);
820 process_record_user(BOT3, &pressed);
821 pause_ms(1);
822 process_record_user(TOP2, &depressed);
823 process_record_user(TOP3, &depressed);
824 process_record_user(BOT2, &depressed);
825 process_record_user(BOT3, &depressed);
826
827 ASSERT_EQ(UINT, in_leader_mode, 1);
828
829 pause_ms(1);
830 process_record_user(TOP1, &pressed);
831 pause_ms(1);
832 process_record_user(TOP1, &depressed);
833 pause_ms(1);
834 process_record_user(TOP2, &pressed);
835 pause_ms(1);
836 process_record_user(TOP2, &depressed);
837
838 pause_ms(LEADER_TIMEOUT);
839 pause_ms(1);
840
841 ASSERT_EQ(UINT, in_leader_mode, 0);
842 ASSERT_EQ(UINT, keyboard_history[1][KC_Q], 255);
843END_TEST
844
845TEST("leader_correct_follow")
846 process_record_user(TOP2, &pressed);
847 process_record_user(TOP3, &pressed);
848 process_record_user(BOT2, &pressed);
849 process_record_user(BOT3, &pressed);
850 pause_ms(1);
851 process_record_user(TOP2, &depressed);
852 process_record_user(TOP3, &depressed);
853 process_record_user(BOT2, &depressed);
854 process_record_user(BOT3, &depressed);
855
856 ASSERT_EQ(UINT, in_leader_mode, 1);
857
858 pause_ms(1);
859 process_record_user(TOP0, &pressed);
860 pause_ms(1);
861 process_record_user(TOP0, &depressed);
862 pause_ms(1);
863 process_record_user(TOP9, &pressed);
864 pause_ms(1);
865 process_record_user(TOP9, &depressed);
866
867 pause_ms(LEADER_TIMEOUT);
868 ASSERT_EQ(UINT, in_leader_mode, 1);
869
870 pause_ms(1);
871 ASSERT_EQ(UINT, in_leader_mode, 0);
872
873 ASSERT_EQ(UINT, keyboard_history[1][KC_O], 0);
874 ASSERT_EQ(UINT, keyboard_history[1][KC_P], 0);
875 ASSERT_EQ(UINT, keyboard_history[1][KC_A], 0);
876 ASSERT_EQ(UINT, keyboard_history[1][KC_S], 1);
877
878 ASSERT_EQ(UINT, keyboard_history[2][KC_O], 0);
879 ASSERT_EQ(UINT, keyboard_history[2][KC_P], 0);
880 ASSERT_EQ(UINT, keyboard_history[2][KC_A], 0);
881 ASSERT_EQ(UINT, keyboard_history[2][KC_S], 0);
882
883 ASSERT_EQ(UINT, keyboard_history[3][KC_O], 255);
884 ASSERT_EQ(UINT, keyboard_history[3][KC_P], 255);
885 ASSERT_EQ(UINT, keyboard_history[3][KC_A], 255);
886 ASSERT_EQ(UINT, keyboard_history[3][KC_S], 255);
887
888 ASSERT_EQ(UINT, keyboard_history[4][KC_O], 255);
889 ASSERT_EQ(UINT, keyboard_history[4][KC_P], 255);
890 ASSERT_EQ(UINT, keyboard_history[4][KC_A], 255);
891 ASSERT_EQ(UINT, keyboard_history[4][KC_S], 255);
892
893 ASSERT_EQ(UINT, keyboard_history[5][KC_Q], 255);
894END_TEST
895
896// DYNAMIC MACRO
897TEST("dynamic_macro_record_mode")
898 current_pseudolayer = NUM;
899
900 // record
901 ASSERT_EQ(UINT, dynamic_macro_mode, 0);
902 process_record_user(BOT7, &pressed);
903 process_record_user(BOT7, &depressed);
904 ASSERT_EQ(UINT, dynamic_macro_mode, 1);
905 pause_ms(1000);
906 ASSERT_EQ(UINT, dynamic_macro_mode, 1);
907END_TEST
908
909TEST("dynamic_macro_record_mode_off")
910 current_pseudolayer = NUM;
911
912 process_record_user(BOT7, &pressed);
913 process_record_user(BOT7, &depressed);
914 ASSERT_EQ(UINT, dynamic_macro_mode, 1);
915
916 process_record_user(BOT9, &pressed);
917 process_record_user(BOT9, &depressed);
918 ASSERT_EQ(UINT, dynamic_macro_mode, 0);
919END_TEST
920
921TEST("dynamic_macro_record_one")
922 current_pseudolayer = NUM;
923
924 process_record_user(BOT7, &pressed);
925 process_record_user(BOT7, &depressed);
926 ASSERT_EQ(UINT, dynamic_macro_mode, 1);
927
928 process_record_user(TOP1, &pressed);
929 process_record_user(TOP1, &depressed);
930
931 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 255);
932
933 process_record_user(BOT9, &pressed);
934 process_record_user(BOT9, &depressed);
935
936 pause_ms(1000);
937
938 process_record_user(BOT0, &pressed);
939 process_record_user(BOT0, &depressed);
940
941 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 1);
942 ASSERT_EQ(UINT, keyboard_history[2][KC_1], 0);
943 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 255);
944
945 pause_ms(1000);
946
947 process_record_user(BOT0, &pressed);
948 process_record_user(BOT0, &depressed);
949
950 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 1);
951 ASSERT_EQ(UINT, keyboard_history[4][KC_1], 0);
952 ASSERT_EQ(UINT, keyboard_history[5][KC_1], 255);
953END_TEST
954
955TEST("dynamic_macro_record_two")
956 current_pseudolayer = NUM;
957
958 process_record_user(BOT7, &pressed);
959 process_record_user(BOT7, &depressed);
960 ASSERT_EQ(UINT, dynamic_macro_mode, 1);
961
962 process_record_user(TOP1, &pressed);
963 process_record_user(TOP1, &depressed);
964
965 process_record_user(TOP2, &pressed);
966 process_record_user(TOP2, &depressed);
967
968 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 255);
969
970 process_record_user(BOT9, &pressed);
971 process_record_user(BOT9, &depressed);
972
973 pause_ms(1000);
974
975 process_record_user(BOT0, &pressed);
976 process_record_user(BOT0, &depressed);
977
978 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 1);
979 ASSERT_EQ(UINT, keyboard_history[2][KC_2], 1);
980 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 0);
981 ASSERT_EQ(UINT, keyboard_history[4][KC_2], 0);
982 ASSERT_EQ(UINT, keyboard_history[5][KC_1], 255);
983END_TEST
984
985TEST("dynamic_macro_record_two_parts")
986 current_pseudolayer = NUM;
987
988 process_record_user(BOT7, &pressed);
989 process_record_user(BOT7, &depressed);
990 ASSERT_EQ(UINT, dynamic_macro_mode, 1);
991
992 process_record_user(TOP1, &pressed);
993 process_record_user(TOP1, &depressed);
994
995 process_record_user(TOP2, &pressed);
996 process_record_user(TOP2, &depressed);
997
998 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 255);
999
1000 process_record_user(BOT8, &pressed);
1001 process_record_user(BOT8, &depressed);
1002
1003 process_record_user(TOP3, &pressed);
1004 process_record_user(TOP3, &depressed);
1005
1006 process_record_user(BOT9, &pressed);
1007 process_record_user(BOT9, &depressed);
1008
1009 pause_ms(1000);
1010
1011 process_record_user(BOT0, &pressed);
1012 process_record_user(BOT0, &depressed);
1013
1014 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 1);
1015 ASSERT_EQ(UINT, keyboard_history[1][KC_2], 0);
1016 ASSERT_EQ(UINT, keyboard_history[1][KC_3], 0);
1017
1018 ASSERT_EQ(UINT, keyboard_history[2][KC_1], 1);
1019 ASSERT_EQ(UINT, keyboard_history[2][KC_2], 1);
1020 ASSERT_EQ(UINT, keyboard_history[2][KC_3], 0);
1021
1022 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 0);
1023 ASSERT_EQ(UINT, keyboard_history[3][KC_2], 1);
1024 ASSERT_EQ(UINT, keyboard_history[3][KC_3], 0);
1025
1026 ASSERT_EQ(UINT, keyboard_history[4][KC_1], 0);
1027 ASSERT_EQ(UINT, keyboard_history[4][KC_2], 0);
1028 ASSERT_EQ(UINT, keyboard_history[4][KC_3], 0);
1029
1030 ASSERT_EQ(UINT, keyboard_history[5][KC_1], 0);
1031 ASSERT_EQ(UINT, keyboard_history[5][KC_2], 0);
1032 ASSERT_EQ(UINT, keyboard_history[5][KC_3], 1);
1033
1034 ASSERT_EQ(UINT, keyboard_history[6][KC_1], 0);
1035 ASSERT_EQ(UINT, keyboard_history[6][KC_2], 0);
1036 ASSERT_EQ(UINT, keyboard_history[6][KC_3], 0);
1037
1038 ASSERT_EQ(UINT, keyboard_history[7][KC_1], 255);
1039END_TEST
1040
1041// dance + M()
1042
1043TEST("dance_tap")
1044 process_record_user(BOT0, &pressed);
1045 process_record_user(BOT0, &depressed);
1046
1047 pause_ms(1000);
1048
1049 ASSERT_EQ(UINT, keyboard_history[1][KC_9], 1);
1050 ASSERT_EQ(UINT, keyboard_history[1][KC_0], 0);
1051 ASSERT_EQ(UINT, keyboard_history[2][KC_9], 0);
1052 ASSERT_EQ(UINT, keyboard_history[2][KC_0], 0);
1053 ASSERT_EQ(UINT, keyboard_history[3][KC_9], 255);
1054END_TEST
1055
1056TEST("dance_hold")
1057 process_record_user(BOT0, &pressed);
1058 pause_ms(1000);
1059
1060 ASSERT_EQ(UINT, keyboard_history[1][KC_9], 1);
1061
1062 process_record_user(BOT0, &depressed);
1063 ASSERT_EQ(UINT, keyboard_history[2][KC_9], 0);
1064END_TEST
1065
1066TEST("dance_tap_tap")
1067 process_record_user(BOT0, &pressed);
1068 process_record_user(BOT0, &depressed);
1069 process_record_user(BOT0, &pressed);
1070 process_record_user(BOT0, &depressed);
1071
1072 pause_ms(1000);
1073
1074 ASSERT_EQ(UINT, keyboard_history[1][KC_9], 0);
1075 ASSERT_EQ(UINT, keyboard_history[1][KC_0], 1);
1076 ASSERT_EQ(UINT, keyboard_history[2][KC_9], 0);
1077 ASSERT_EQ(UINT, keyboard_history[2][KC_0], 0);
1078 ASSERT_EQ(UINT, keyboard_history[3][KC_9], 255);
1079END_TEST
1080
1081TEST("dance_tap_hold")
1082 process_record_user(BOT0, &pressed);
1083 process_record_user(BOT0, &depressed);
1084 pause_ms(1);
1085 process_record_user(BOT0, &pressed);
1086 pause_ms(1000);
1087
1088 ASSERT_EQ(UINT, keyboard_history[1][KC_0], 1);
1089
1090 process_record_user(BOT0, &depressed);
1091 ASSERT_EQ(UINT, keyboard_history[2][KC_0], 0);
1092END_TEST
1093
1094// MK
1095TEST("multiple_keys")
1096 current_pseudolayer = NUM;
1097
1098 process_record_user(BOT1, &pressed);
1099 process_record_user(BOT1, &depressed);
1100
1101 ASSERT_EQ(UINT, keyboard_history[1][KC_LCTL], 1);
1102 ASSERT_EQ(UINT, keyboard_history[1][KC_LSFT], 0);
1103
1104 ASSERT_EQ(UINT, keyboard_history[2][KC_LCTL], 1);
1105 ASSERT_EQ(UINT, keyboard_history[2][KC_LSFT], 1);
1106
1107 ASSERT_EQ(UINT, keyboard_history[3][KC_LCTL], 0);
1108 ASSERT_EQ(UINT, keyboard_history[3][KC_LSFT], 1);
1109
1110 ASSERT_EQ(UINT, keyboard_history[4][KC_LCTL], 0);
1111 ASSERT_EQ(UINT, keyboard_history[4][KC_LSFT], 0);
1112
1113 ASSERT_EQ(UINT, keyboard_history[5][KC_LCTL], 255);
1114END_TEST
1115
1116TEST("multiple_keys_interleaved")
1117 current_pseudolayer = NUM;
1118
1119 process_record_user(BOT1, &pressed);
1120 pause_ms(CHORD_TIMEOUT+1);
1121
1122 process_record_user(TOP1, &pressed);
1123 process_record_user(TOP1, &depressed);
1124 process_record_user(TOP1, &pressed);
1125 process_record_user(TOP1, &depressed);
1126 process_record_user(TOP1, &pressed);
1127 process_record_user(TOP1, &depressed);
1128
1129 process_record_user(BOT1, &depressed);
1130
1131 ASSERT_EQ(UINT, keyboard_history[1][KC_LCTL], 1);
1132 ASSERT_EQ(UINT, keyboard_history[1][KC_LSFT], 0);
1133
1134 ASSERT_EQ(UINT, keyboard_history[2][KC_LCTL], 1);
1135 ASSERT_EQ(UINT, keyboard_history[2][KC_LSFT], 1);
1136
1137 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 1);
1138 ASSERT_EQ(UINT, keyboard_history[4][KC_1], 0);
1139 ASSERT_EQ(UINT, keyboard_history[5][KC_1], 1);
1140 ASSERT_EQ(UINT, keyboard_history[6][KC_1], 0);
1141 ASSERT_EQ(UINT, keyboard_history[7][KC_1], 1);
1142 ASSERT_EQ(UINT, keyboard_history[8][KC_1], 0);
1143
1144 ASSERT_EQ(UINT, keyboard_history[9][KC_LCTL], 0);
1145 ASSERT_EQ(UINT, keyboard_history[9][KC_LSFT], 1);
1146
1147 ASSERT_EQ(UINT, keyboard_history[10][KC_LCTL], 0);
1148 ASSERT_EQ(UINT, keyboard_history[10][KC_LSFT], 0);
1149
1150 ASSERT_EQ(UINT, keyboard_history[11][KC_LCTL], 255);
1151END_TEST
1152
1153// D
1154TEST("dance_one")
1155 current_pseudolayer = NUM;
1156
1157 process_record_user(BOT3, &pressed);
1158 process_record_user(BOT3, &depressed);
1159
1160 pause_ms(CHORD_TIMEOUT+DANCE_TIMEOUT+2);
1161
1162 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 1);
1163 ASSERT_EQ(UINT, keyboard_history[2][KC_1], 0);
1164 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 255);
1165
1166 process_record_user(BOT3, &pressed);
1167 process_record_user(BOT3, &depressed);
1168
1169 pause_ms(CHORD_TIMEOUT+DANCE_TIMEOUT+2);
1170
1171 ASSERT_EQ(UINT, keyboard_history[1][KC_1], 1);
1172 ASSERT_EQ(UINT, keyboard_history[2][KC_1], 0);
1173 ASSERT_EQ(UINT, keyboard_history[3][KC_1], 1);
1174 ASSERT_EQ(UINT, keyboard_history[4][KC_1], 0);
1175 ASSERT_EQ(UINT, keyboard_history[5][KC_1], 255);
1176END_TEST
1177
1178TEST("dance_two")
1179 current_pseudolayer = NUM;
1180
1181 process_record_user(BOT3, &pressed);
1182 process_record_user(BOT3, &depressed);
1183 process_record_user(BOT3, &pressed);
1184 process_record_user(BOT3, &depressed);
1185
1186 pause_ms(CHORD_TIMEOUT+DANCE_TIMEOUT+2);
1187
1188 ASSERT_EQ(UINT, keyboard_history[1][KC_2], 1);
1189 ASSERT_EQ(UINT, keyboard_history[2][KC_2], 0);
1190 ASSERT_EQ(UINT, keyboard_history[3][KC_2], 255);
1191
1192 process_record_user(BOT3, &pressed);
1193 process_record_user(BOT3, &depressed);
1194 process_record_user(BOT3, &pressed);
1195 process_record_user(BOT3, &depressed);
1196
1197 pause_ms(CHORD_TIMEOUT+DANCE_TIMEOUT+2);
1198
1199 ASSERT_EQ(UINT, keyboard_history[1][KC_2], 1);
1200 ASSERT_EQ(UINT, keyboard_history[2][KC_2], 0);
1201 ASSERT_EQ(UINT, keyboard_history[3][KC_2], 1);
1202 ASSERT_EQ(UINT, keyboard_history[4][KC_2], 0);
1203 ASSERT_EQ(UINT, keyboard_history[5][KC_2], 255);
1204END_TEST
1205
1206TEST("dance_three")
1207 current_pseudolayer = NUM;
1208
1209 process_record_user(BOT3, &pressed);
1210 process_record_user(BOT3, &depressed);
1211 process_record_user(BOT3, &pressed);
1212 process_record_user(BOT3, &depressed);
1213 process_record_user(BOT3, &pressed);
1214 process_record_user(BOT3, &depressed);
1215
1216 pause_ms(CHORD_TIMEOUT+DANCE_TIMEOUT+2);
1217
1218 ASSERT_EQ(UINT, keyboard_history[1][KC_3], 1);
1219 ASSERT_EQ(UINT, keyboard_history[2][KC_3], 0);
1220 ASSERT_EQ(UINT, keyboard_history[3][KC_3], 255);
1221
1222 process_record_user(BOT3, &pressed);
1223 process_record_user(BOT3, &depressed);
1224 process_record_user(BOT3, &pressed);
1225 process_record_user(BOT3, &depressed);
1226 process_record_user(BOT3, &pressed);
1227 process_record_user(BOT3, &depressed);
1228
1229 pause_ms(CHORD_TIMEOUT+DANCE_TIMEOUT+2);
1230
1231 ASSERT_EQ(UINT, keyboard_history[1][KC_3], 1);
1232 ASSERT_EQ(UINT, keyboard_history[2][KC_3], 0);
1233 ASSERT_EQ(UINT, keyboard_history[3][KC_3], 1);
1234 ASSERT_EQ(UINT, keyboard_history[4][KC_3], 0);
1235 ASSERT_EQ(UINT, keyboard_history[5][KC_3], 255);
1236END_TEST
1237
1238TEST("dance_two_held")
1239 current_pseudolayer = NUM;
1240
1241 process_record_user(BOT3, &pressed);
1242 process_record_user(BOT3, &depressed);
1243 process_record_user(BOT3, &pressed);
1244
1245 pause_ms(CHORD_TIMEOUT+DANCE_TIMEOUT+2);
1246
1247 ASSERT_EQ(UINT, keyboard_history[1][KC_2], 1);
1248 ASSERT_EQ(UINT, keyboard_history[2][KC_2], 255);
1249
1250 process_record_user(BOT3, &depressed);
1251 ASSERT_EQ(UINT, keyboard_history[2][KC_2], 0);
1252 ASSERT_EQ(UINT, keyboard_history[3][KC_2], 255);
1253END_TEST
1254
1255// These two are leaving the chording engine, they kinda have to be tested manually
1256// TO
1257// RESET
1258
1259END
diff --git a/users/dennytom/chording_engine/tests/test_full.sh b/users/dennytom/chording_engine/tests/test_full.sh
new file mode 100644
index 000000000..ea93aec8f
--- /dev/null
+++ b/users/dennytom/chording_engine/tests/test_full.sh
@@ -0,0 +1,11 @@
1#!/bin/sh
2cd ..
3python3 parser.py tests/test_keymap_def.json tests/test_keymap.c && \
4gcc -g tests/test.c -o tests/test && \
5tests/test && \
6python3 parser.py ../../../keyboards/butterstick/keymaps/tomas/keymap_def.json ../../../keyboards/butterstick/keymaps/tomas/keymap.c && \
7python3 parser.py ../../../keyboards/georgi/keymaps/tomas/keymap_def.json ../../../keyboards/georgi/keymaps/tomas/keymap.c && \
8python3 parser.py ../../../keyboards/georgi/keymaps/buttery/keymap_def.json ../../../keyboards/georgi/keymaps/buttery/keymap.c && \
9cd ../../../ && \
10make butterstick && \
11make georgi \ No newline at end of file
diff --git a/users/dennytom/chording_engine/tests/test_keymap_def.json b/users/dennytom/chording_engine/tests/test_keymap_def.json
new file mode 100644
index 000000000..eae2ed37c
--- /dev/null
+++ b/users/dennytom/chording_engine/tests/test_keymap_def.json
@@ -0,0 +1,145 @@
1{
2 "keys": [
3 "TOP1", "TOP2", "TOP3", "TOP4", "TOP5", "TOP6", "TOP7", "TOP8", "TOP9", "TOP0",
4 "BOT1", "BOT2", "BOT3", "BOT4", "BOT5", "BOT6", "BOT7", "BOT8", "BOT9", "BOT0"
5 ],
6 "parameters": {
7 "do_not_include_QMK": true,
8 "layout_function_name": "LAYOUT_test",
9 "chord_timeout": 100,
10 "dance_timeout": 200,
11 "leader_timeout": 750,
12 "tap_timeout": 50,
13 "command_max_length": 5,
14 "leader_max_length": 5,
15 "dynamic_macro_max_length": 20,
16 "string_max_length": 16,
17 "long_press_multiplier": 3,
18 "default_pseudolayer": "QWERTY"
19 },
20 "layers": [
21 {
22 "type": "auto"
23 },
24 {
25 "type": "manual",
26 "keycodes": ["KC_1", "KC_2", "KC_3", "KC_4", "KC_5", "KC_6", "KC_7", "KC_8", "KC_9", "KC_0",
27 "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T", "KC_Y", "KC_U", "KC_I", "KC_O", "KC_P"
28 ]
29 }
30 ],
31 "chord_sets": [
32 {
33 "name": "rows",
34 "chords": [
35 ["TOP1"], ["TOP2"], ["TOP3"], ["TOP4"], ["TOP5"], ["TOP6"], ["TOP7"], ["TOP8"], ["TOP9"], ["TOP0"],
36 ["TOP1", "BOT1"], ["TOP2", "BOT2"], ["TOP3", "BOT3"], ["TOP4", "BOT4"], ["TOP5", "BOT5"], ["TOP6", "BOT6"], ["TOP7", "BOT7"], ["TOP8", "BOT8"], ["TOP9", "BOT9"], ["TOP0", "BOT0"],
37 ["BOT1"], ["BOT2"], ["BOT3"], ["BOT4"], ["BOT5"], ["BOT6"], ["BOT7"], ["BOT8"], ["BOT9"], ["BOT0"]
38 ]
39 },
40 {
41 "name": "cols",
42 "chords": [
43 ["TOP1", "TOP2"], ["TOP2", "TOP3"], ["TOP3", "TOP4"], ["TOP4", "TOP5"], ["TOP5", "TOP6"], ["TOP6", "TOP7"], ["TOP7", "TOP8"], ["TOP8", "TOP9"], ["TOP9", "TOP0"],
44 ["TOP1", "TOP2", "BOT1", "BOT2"], ["TOP2", "TOP3", "BOT2", "BOT3"], ["TOP3", "TOP4", "BOT3", "BOT4"], ["TOP4", "TOP5", "BOT4", "BOT5"], ["TOP5", "TOP6", "BOT5", "BOT6"], ["TOP6", "TOP7", "BOT6", "BOT7"], ["TOP7", "TOP8", "BOT7", "BOT8"], ["TOP8", "TOP9", "BOT8", "BOT9"], ["TOP9", "TOP0", "BOT9", "BOT0"],
45 ["BOT1", "BOT2"], ["BOT2", "BOT3"], ["BOT3", "BOT4"], ["BOT4", "BOT5"], ["BOT5", "BOT6"], ["BOT6", "BOT7"], ["BOT7", "BOT8"], ["BOT8", "BOT9"], ["BOT9", "BOT0"]
46 ]
47 },
48 {
49 "name": "asetniop",
50 "chords": [
51 ["TOP1"], ["TOP2"], ["TOP3"], ["TOP4"], ["TOP7"], ["TOP8"], ["TOP9"], ["TOP0"],
52 ["TOP1", "TOP2"], ["TOP2", "TOP3"], ["TOP3", "TOP4"], ["TOP4", "TOP7"], ["TOP7", "TOP8"], ["TOP8", "TOP9"], ["TOP9", "TOP0"],
53 ["TOP1", "TOP3"], ["TOP2", "TOP4"], ["TOP3", "TOP7"], ["TOP4", "TOP8"], ["TOP7", "TOP9"], ["TOP8", "TOP0"],
54 ["TOP1", "TOP4"], ["TOP2", "TOP7"], ["TOP3", "TOP8"], ["TOP4", "TOP9"], ["TOP7", "TOP0"],
55 ["TOP1", "TOP7"], ["TOP2", "TOP8"], ["TOP3", "TOP9"], ["TOP4", "TOP0"],
56 ["TOP1", "TOP8"], ["TOP2", "TOP9"], ["TOP3", "TOP0"],
57 ["TOP1", "TOP9"], ["TOP2", "TOP0"],
58 ["TOP1", "TOP0"]
59 ]
60 }
61 ],
62 "pseudolayers": [
63 {
64 "name": "ALWAYS_ON",
65 "chords": [
66 {
67 "type": "visual",
68 "chord": [
69 " ", " ", " ", " ", " ", " ", " ", "X", "X", "X",
70 " ", " ", " ", " ", " ", " ", " ", "X", "X", "X"
71 ],
72 "keycode": "MO(FNC, NUM)"
73 }
74 ]
75 },
76 {
77 "name": "QWERTY",
78 "chords": [
79 {
80 "type": "chord_set",
81 "set": "rows",
82 "keycodes": [
83 "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P",
84 "A", "S", "D", "F", "G", "H", "J", "K", "L", ";",
85 "AS(Z)", "KK(X, LCTL)", "KL(C, NUM)", "KM(V, LALT)", "B", "N", "M", "COMMA", ".", "M(double_dance, KC_9, KC_0)"
86 ]
87 },
88 {
89 "type": "chord_set",
90 "set": "cols",
91 "keycodes": [
92 "ESC", "", "", "", "", "", "", "", "",
93 "", "LEAD", "", "", "CMD", "", "", "", "",
94 "LSFT", "O(LSFT)", "", "", "", "O(NUM)", "MO(NUM)", "", "DF(NUM)"
95 ]
96 },
97 {
98 "type": "visual",
99 "chord": [
100 "X", "X", " ", " ", " ", " ", " ", " ", " ", " ",
101 "X", "X", " ", " ", " ", " ", " ", " ", " ", " "
102 ],
103 "keycode": "LOCK"
104 }
105 ]
106 },
107 {
108 "name": "NUM",
109 "chords": [
110 {
111 "type": "chord_set",
112 "set": "rows",
113 "keycodes": [
114 "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
115 "", "", "", "", "", "", "", "", "", "",
116 "", "KC_LEFT", "D(KC_1, KC_2, KC_3)", "", "", "", "DM_RECORD", "DM_NEXT", "DM_END", "DM_PLAY"
117 ]
118 },
119 {
120 "type": "simple",
121 "chord": ["BOT1"],
122 "keycode": "MK(KC_LCTL, KC_LSFT)"
123 }
124 ]
125 },
126 {
127 "name": "FNC",
128 "chords": []
129 }
130 ],
131 "leader_sequences": [
132 {
133 "name": "fnc_L1",
134 "function": "void fnc_L1(void) { key_in(KC_A); clear_keyboard(); }",
135 "sequence": ["KC_O", "KC_P"]
136 },
137 {
138 "name": "fnc_L2",
139 "function": "void fnc_L2(void) { key_in(KC_S); clear_keyboard(); }",
140 "sequence": ["KC_P", "KC_O"]
141 }
142 ],
143 "extra_code": "void double_dance(const struct Chord* self) {\n switch (*self->state) {\n case ACTIVATED:\n *self->counter = (*self->counter + 1) % 2;\n break;\n case PRESS_FROM_ACTIVE:\n if (*self->counter == 1) {\n key_in(self->value1);\n } else {\n key_in(self->value2);\n }\n *self->state = FINISHED_FROM_ACTIVE;\n break;\n case FINISHED:\n if (*self->counter == 1) {\n tap_key(self->value1);\n } else {\n tap_key(self->value2);\n }\n *self->counter = 0;\n *self->state = IDLE;\n break;\n case RESTART:\n if (*self->counter == 1) {\n key_out(self->value1);\n } else {\n key_out(self->value2);\n }\n *self->counter = 0;\n break;\n default:\n break;\n }\n}\n",
144 "extra_dependencies": []
145} \ No newline at end of file
diff --git a/users/dennytom/chording_engine/tests/test_quick.sh b/users/dennytom/chording_engine/tests/test_quick.sh
new file mode 100644
index 000000000..3740122e2
--- /dev/null
+++ b/users/dennytom/chording_engine/tests/test_quick.sh
@@ -0,0 +1,6 @@
1#!/bin/sh
2
3cd ..
4python3 parser.py tests/test_keymap_def.json tests/test_keymap.c && \
5gcc -g tests/test.c -o tests/test && \
6tests/test \ No newline at end of file