aboutsummaryrefslogtreecommitdiff
path: root/quantum/quantum.c
diff options
context:
space:
mode:
authorJack Humbert <jack.humb@gmail.com>2016-06-29 17:49:41 -0400
committerGitHub <noreply@github.com>2016-06-29 17:49:41 -0400
commit65faab3b89245f81c50b029ca178aed175d5f330 (patch)
tree49a7199b9e8804f4a212762b7e267c4d4228b900 /quantum/quantum.c
parent215c2119af5281072d5a6efb0308408793cadd08 (diff)
downloadqmk_firmware-65faab3b89245f81c50b029ca178aed175d5f330.tar.gz
qmk_firmware-65faab3b89245f81c50b029ca178aed175d5f330.zip
Moves features to their own files (process_*), adds tap dance feature (#460)
* non-working commit * working * subprojects implemented for planck * pass a subproject variable through to c * consolidates clueboard revisions * thanks for letting me know about conflicts.. * turn off audio for yang's * corrects starting paths for subprojects * messing around with travis * semicolon * travis script * travis script * script for travis * correct directory (probably), amend files to commit * remove origin before adding * git pull, correct syntax * git checkout * git pull origin branch * where are we? * where are we? * merging * force things to happen * adds commit message, adds add * rebase, no commit message * rebase branch * idk! * try just pull * fetch - merge * specify repo branch * checkout * goddammit * merge? idk * pls * after all * don't split up keyboards * syntax * adds quick for all-keyboards * trying out new script * script update * lowercase * all keyboards * stop replacing compiled.hex automatically * adds if statement * skip automated build branches * forces push to automated build branch * throw an add in there * upstream? * adds AUTOGEN * ignore all .hex files again * testing out new repo * global ident * generate script, keyboard_keymap.hex * skip generation for now, print pandoc info, submodule update * try trusty * and sudo * try generate * updates subprojects to keyboards * no idea * updates to keyboards * cleans up clueboard stuff * setup to use local readme * updates cluepad, planck experimental * remove extra led.c [ci skip] * audio and midi moved over to separate files * chording, leader, unicode separated * consolidate each [skip ci] * correct include * quantum: Add a tap dance feature (#451) * quantum: Add a tap dance feature With this feature one can specify keys that behave differently, based on the amount of times they have been tapped, and when interrupted, they get handled before the interrupter. To make it clear how this is different from `ACTION_FUNCTION_TAP`, lets explore a certain setup! We want one key to send `Space` on single tap, but `Enter` on double-tap. With `ACTION_FUNCTION_TAP`, it is quite a rain-dance to set this up, and has the problem that when the sequence is interrupted, the interrupting key will be send first. Thus, `SPC a` will result in `a SPC` being sent, if they are typed within `TAPPING_TERM`. With the tap dance feature, that'll come out as `SPC a`, correctly. The implementation hooks into two parts of the system, to achieve this: into `process_record_quantum()`, and the matrix scan. We need the latter to be able to time out a tap sequence even when a key is not being pressed, so `SPC` alone will time out and register after `TAPPING_TERM` time. But lets start with how to use it, first! First, you will need `TAP_DANCE_ENABLE=yes` in your `Makefile`, because the feature is disabled by default. This adds a little less than 1k to the firmware size. Next, you will want to define some tap-dance keys, which is easiest to do with the `TD()` macro, that - similar to `F()`, takes a number, which will later be used as an index into the `tap_dance_actions` array. This array specifies what actions shall be taken when a tap-dance key is in action. Currently, there are two possible options: * `ACTION_TAP_DANCE_DOUBLE(kc1, kc2)`: Sends the `kc1` keycode when tapped once, `kc2` otherwise. * `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the current state of the tap-dance action. The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise. And that's the bulk of it! Do note, however, that this implementation does have some consequences: keys do not register until either they reach the tapping ceiling, or they time out. This means that if you hold the key, nothing happens, no repeat, no nothing. It is possible to detect held state, and register an action then too, but that's not implemented yet. Keys also unregister immediately after being registered, so you can't even hold the second tap. This is intentional, to be consistent. And now, on to the explanation of how it works! The main entry point is `process_tap_dance()`, called from `process_record_quantum()`, which is run for every keypress, and our handler gets to run early. This function checks whether the key pressed is a tap-dance key. If it is not, and a tap-dance was in action, we handle that first, and enqueue the newly pressed key. If it is a tap-dance key, then we check if it is the same as the already active one (if there's one active, that is). If it is not, we fire off the old one first, then register the new one. If it was the same, we increment the counter and the timer. This means that you have `TAPPING_TERM` time to tap the key again, you do not have to input all the taps within that timeframe. This allows for longer tap counts, with minimal impact on responsiveness. Our next stop is `matrix_scan_tap_dance()`. This handles the timeout of tap-dance keys. For the sake of flexibility, tap-dance actions can be either a pair of keycodes, or a user function. The latter allows one to handle higher tap counts, or do extra things, like blink the LEDs, fiddle with the backlighting, and so on. This is accomplished by using an union, and some clever macros. In the end, lets see a full example! ```c enum { CT_SE = 0, CT_CLN, CT_EGG }; /* Have the above three on the keymap, TD(CT_SE), etc... */ void dance_cln (qk_tap_dance_state_t *state) { if (state->count == 1) { register_code (KC_RSFT); register_code (KC_SCLN); unregister_code (KC_SCLN); unregister_code (KC_RSFT); } else { register_code (KC_SCLN); unregister_code (KC_SCLN); reset_tap_dance (state); } } void dance_egg (qk_tap_dance_state_t *state) { if (state->count >= 100) { SEND_STRING ("Safety dance!"); reset_tap_dance (state); } } const qk_tap_dance_action_t tap_dance_actions[] = { [CT_SE] = ACTION_TAP_DANCE_DOUBLE (KC_SPC, KC_ENT) ,[CT_CLN] = ACTION_TAP_DANCE_FN (dance_cln) ,[CT_EGG] = ACTION_TAP_DANCE_FN (dance_egg) }; ``` This addresses #426. Signed-off-by: Gergely Nagy <algernon@madhouse-project.org> * hhkb: Fix the build with the new tap-dance feature Signed-off-by: Gergely Nagy <algernon@madhouse-project.org> * tap_dance: Move process_tap_dance further down Process the tap dance stuff after midi and audio, because those don't process keycodes, but row/col positions. Signed-off-by: Gergely Nagy <algernon@madhouse-project.org> * tap_dance: Use conditionals instead of dummy functions To be consistent with how the rest of the quantum features are implemented, use ifdefs instead of dummy functions. Signed-off-by: Gergely Nagy <algernon@madhouse-project.org> * Merge branch 'master' into quantum-keypress-process # Conflicts: # Makefile # keyboards/planck/rev3/config.h # keyboards/planck/rev4/config.h * update build script
Diffstat (limited to 'quantum/quantum.c')
-rw-r--r--quantum/quantum.c425
1 files changed, 22 insertions, 403 deletions
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 9c0f9691f..c0580e0aa 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -15,54 +15,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
15 return true; 15 return true;
16} 16}
17 17
18__attribute__ ((weak))
19void leader_start(void) {}
20
21__attribute__ ((weak))
22void leader_end(void) {}
23
24uint8_t starting_note = 0x0C;
25int offset = 7;
26
27
28#ifdef AUDIO_ENABLE
29 bool music_activated = false;
30
31 // music sequencer
32 static bool music_sequence_recording = false;
33 static bool music_sequence_playing = false;
34 static float music_sequence[16] = {0};
35 static uint8_t music_sequence_count = 0;
36 static uint8_t music_sequence_position = 0;
37
38 static uint16_t music_sequence_timer = 0;
39 static uint16_t music_sequence_interval = 100;
40
41#endif
42
43#ifdef MIDI_ENABLE
44 bool midi_activated = false;
45#endif
46
47// Leader key stuff
48bool leading = false;
49uint16_t leader_time = 0;
50
51uint16_t leader_sequence[5] = {0, 0, 0, 0, 0};
52uint8_t leader_sequence_size = 0;
53
54// Chording stuff
55#define CHORDING_MAX 4
56bool chording = false;
57
58uint8_t chord_keys[CHORDING_MAX] = {0};
59uint8_t chord_key_count = 0;
60uint8_t chord_key_down = 0;
61
62#ifdef UNICODE_ENABLE
63 static uint8_t input_mode;
64#endif
65
66// Shift / paren setup 18// Shift / paren setup
67 19
68#ifndef LSPO_KEY 20#ifndef LSPO_KEY
@@ -74,48 +26,6 @@ uint8_t chord_key_down = 0;
74 26
75static bool shift_interrupted[2] = {0, 0}; 27static bool shift_interrupted[2] = {0, 0};
76 28
77bool keys_chord(uint8_t keys[]) {
78 uint8_t keys_size = sizeof(keys)/sizeof(keys[0]);
79 bool pass = true;
80 uint8_t in = 0;
81 for (uint8_t i = 0; i < chord_key_count; i++) {
82 bool found = false;
83 for (uint8_t j = 0; j < keys_size; j++) {
84 if (chord_keys[i] == (keys[j] & 0xFF)) {
85 in++; // detects key in chord
86 found = true;
87 break;
88 }
89 }
90 if (found)
91 continue;
92 if (chord_keys[i] != 0) {
93 pass = false; // makes sure rest are blank
94 }
95 }
96 return (pass && (in == keys_size));
97}
98
99#ifdef UNICODE_ENABLE
100
101uint16_t hex_to_keycode(uint8_t hex)
102{
103 if (hex == 0x0) {
104 return KC_0;
105 } else if (hex < 0xA) {
106 return KC_1 + (hex - 0x1);
107 } else {
108 return KC_A + (hex - 0xA);
109 }
110}
111
112void set_unicode_mode(uint8_t os_target)
113{
114 input_mode = os_target;
115}
116
117#endif
118
119bool process_record_quantum(keyrecord_t *record) { 29bool process_record_quantum(keyrecord_t *record) {
120 30
121 /* This gets the keycode from the key pressed */ 31 /* This gets the keycode from the key pressed */
@@ -136,9 +46,6 @@ bool process_record_quantum(keyrecord_t *record) {
136 keycode = keymap_key_to_keycode(layer_switch_get_layer(key), key); 46 keycode = keymap_key_to_keycode(layer_switch_get_layer(key), key);
137 #endif 47 #endif
138 48
139 if (!process_record_kb(keycode, record))
140 return false;
141
142 // This is how you use actions here 49 // This is how you use actions here
143 // if (keycode == KC_LEAD) { 50 // if (keycode == KC_LEAD) {
144 // action_t action; 51 // action_t action;
@@ -147,278 +54,30 @@ bool process_record_quantum(keyrecord_t *record) {
147 // return false; 54 // return false;
148 // } 55 // }
149 56
57 if (!(
58 process_record_kb(keycode, record) &&
150 #ifdef MIDI_ENABLE 59 #ifdef MIDI_ENABLE
151 if (keycode == MI_ON && record->event.pressed) { 60 process_midi(keycode, record) &&
152 midi_activated = true;
153 music_scale_user();
154 return false;
155 }
156
157 if (keycode == MI_OFF && record->event.pressed) {
158 midi_activated = false;
159 midi_send_cc(&midi_device, 0, 0x7B, 0);
160 return false;
161 }
162
163 if (midi_activated) {
164 if (record->event.key.col == (MATRIX_COLS - 1) && record->event.key.row == (MATRIX_ROWS - 1)) {
165 if (record->event.pressed) {
166 starting_note++; // Change key
167 midi_send_cc(&midi_device, 0, 0x7B, 0);
168 }
169 return false;
170 }
171 if (record->event.key.col == (MATRIX_COLS - 2) && record->event.key.row == (MATRIX_ROWS - 1)) {
172 if (record->event.pressed) {
173 starting_note--; // Change key
174 midi_send_cc(&midi_device, 0, 0x7B, 0);
175 }
176 return false;
177 }
178 if (record->event.key.col == (MATRIX_COLS - 3) && record->event.key.row == (MATRIX_ROWS - 1) && record->event.pressed) {
179 offset++; // Change scale
180 midi_send_cc(&midi_device, 0, 0x7B, 0);
181 return false;
182 }
183 if (record->event.key.col == (MATRIX_COLS - 4) && record->event.key.row == (MATRIX_ROWS - 1) && record->event.pressed) {
184 offset--; // Change scale
185 midi_send_cc(&midi_device, 0, 0x7B, 0);
186 return false;
187 }
188 // basic
189 // uint8_t note = (starting_note + SCALE[record->event.key.col + offset])+12*(MATRIX_ROWS - record->event.key.row);
190 // advanced
191 // uint8_t note = (starting_note + record->event.key.col + offset)+12*(MATRIX_ROWS - record->event.key.row);
192 // guitar
193 uint8_t note = (starting_note + record->event.key.col + offset)+5*(MATRIX_ROWS - record->event.key.row);
194 // violin
195 // uint8_t note = (starting_note + record->event.key.col + offset)+7*(MATRIX_ROWS - record->event.key.row);
196
197 if (record->event.pressed) {
198 // midi_send_noteon(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127);
199 midi_send_noteon(&midi_device, 0, note, 127);
200 } else {
201 // midi_send_noteoff(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127);
202 midi_send_noteoff(&midi_device, 0, note, 127);
203 }
204
205 if (keycode < 0xFF) // ignores all normal keycodes, but lets RAISE, LOWER, etc through
206 return false;
207 }
208 #endif 61 #endif
209
210 #ifdef AUDIO_ENABLE 62 #ifdef AUDIO_ENABLE
211 if (keycode == AU_ON && record->event.pressed) { 63 process_music(keycode, record) &&
212 audio_on();
213 return false;
214 }
215
216 if (keycode == AU_OFF && record->event.pressed) {
217 audio_off();
218 return false;
219 }
220
221 if (keycode == AU_TOG && record->event.pressed) {
222 if (is_audio_on())
223 {
224 audio_off();
225 }
226 else
227 {
228 audio_on();
229 }
230 return false;
231 }
232
233 if (keycode == MU_ON && record->event.pressed) {
234 music_on();
235 return false;
236 }
237
238 if (keycode == MU_OFF && record->event.pressed) {
239 music_off();
240 return false;
241 }
242
243 if (keycode == MU_TOG && record->event.pressed) {
244 if (music_activated)
245 {
246 music_off();
247 }
248 else
249 {
250 music_on();
251 }
252 return false;
253 }
254
255 if (keycode == MUV_IN && record->event.pressed) {
256 voice_iterate();
257 music_scale_user();
258 return false;
259 }
260
261 if (keycode == MUV_DE && record->event.pressed) {
262 voice_deiterate();
263 music_scale_user();
264 return false;
265 }
266
267 if (music_activated) {
268
269 if (keycode == KC_LCTL && record->event.pressed) { // Start recording
270 stop_all_notes();
271 music_sequence_recording = true;
272 music_sequence_playing = false;
273 music_sequence_count = 0;
274 return false;
275 }
276
277 if (keycode == KC_LALT && record->event.pressed) { // Stop recording/playing
278 stop_all_notes();
279 music_sequence_recording = false;
280 music_sequence_playing = false;
281 return false;
282 }
283
284 if (keycode == KC_LGUI && record->event.pressed) { // Start playing
285 stop_all_notes();
286 music_sequence_recording = false;
287 music_sequence_playing = true;
288 music_sequence_position = 0;
289 music_sequence_timer = 0;
290 return false;
291 }
292
293 if (keycode == KC_UP) {
294 if (record->event.pressed)
295 music_sequence_interval-=10;
296 return false;
297 }
298
299 if (keycode == KC_DOWN) {
300 if (record->event.pressed)
301 music_sequence_interval+=10;
302 return false;
303 }
304
305 float freq = ((float)220.0)*pow(2.0, -5.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row));
306 if (record->event.pressed) {
307 play_note(freq, 0xF);
308 if (music_sequence_recording) {
309 music_sequence[music_sequence_count] = freq;
310 music_sequence_count++;
311 }
312 } else {
313 stop_note(freq);
314 }
315
316 if (keycode < 0xFF) // ignores all normal keycodes, but lets RAISE, LOWER, etc through
317 return false;
318 }
319 #endif 64 #endif
320 65 #ifdef TAP_DANCE_ENABLE
321#ifndef DISABLE_LEADER 66 process_tap_dance(keycode, record) &&
322 // Leader key set-up 67 #endif
323 if (record->event.pressed) { 68 #ifndef DISABLE_LEADER
324 if (!leading && keycode == KC_LEAD) { 69 process_leader(keycode, record) &&
325 leader_start(); 70 #endif
326 leading = true; 71 #ifndef DISABLE_CHORDING
327 leader_time = timer_read(); 72 process_chording(keycode, record) &&
328 leader_sequence_size = 0; 73 #endif
329 leader_sequence[0] = 0; 74 #ifdef UNICODE_ENABLE
330 leader_sequence[1] = 0; 75 process_unicode(keycode, record) &&
331 leader_sequence[2] = 0; 76 #endif
332 leader_sequence[3] = 0; 77 true)) {
333 leader_sequence[4] = 0; 78 return false;
334 return false;
335 }
336 if (leading && timer_elapsed(leader_time) < LEADER_TIMEOUT) {
337 leader_sequence[leader_sequence_size] = keycode;
338 leader_sequence_size++;
339 return false;
340 }
341 }
342#endif
343
344#define DISABLE_CHORDING
345#ifndef DISABLE_CHORDING
346
347 if (keycode >= QK_CHORDING && keycode <= QK_CHORDING_MAX) {
348 if (record->event.pressed) {
349 if (!chording) {
350 chording = true;
351 for (uint8_t i = 0; i < CHORDING_MAX; i++)
352 chord_keys[i] = 0;
353 chord_key_count = 0;
354 chord_key_down = 0;
355 }
356 chord_keys[chord_key_count] = (keycode & 0xFF);
357 chord_key_count++;
358 chord_key_down++;
359 return false;
360 } else {
361 if (chording) {
362 chord_key_down--;
363 if (chord_key_down == 0) {
364 chording = false;
365 // Chord Dictionary
366 if (keys_chord((uint8_t[]){KC_ENTER, KC_SPACE})) {
367 register_code(KC_A);
368 unregister_code(KC_A);
369 return false;
370 }
371 for (uint8_t i = 0; i < chord_key_count; i++) {
372 register_code(chord_keys[i]);
373 unregister_code(chord_keys[i]);
374 return false;
375 }
376 }
377 }
378 }
379 }
380
381#endif
382
383#ifdef UNICODE_ENABLE
384
385 if (keycode > QK_UNICODE && record->event.pressed) {
386 uint16_t unicode = keycode & 0x7FFF;
387 switch(input_mode) {
388 case UC_OSX:
389 register_code(KC_LALT);
390 break;
391 case UC_LNX:
392 register_code(KC_LCTL);
393 register_code(KC_LSFT);
394 register_code(KC_U);
395 unregister_code(KC_U);
396 break;
397 case UC_WIN:
398 register_code(KC_LALT);
399 register_code(KC_PPLS);
400 unregister_code(KC_PPLS);
401 break;
402 }
403 for(int i = 3; i >= 0; i--) {
404 uint8_t digit = ((unicode >> (i*4)) & 0xF);
405 register_code(hex_to_keycode(digit));
406 unregister_code(hex_to_keycode(digit));
407 }
408 switch(input_mode) {
409 case UC_OSX:
410 case UC_WIN:
411 unregister_code(KC_LALT);
412 break;
413 case UC_LNX:
414 unregister_code(KC_LCTL);
415 unregister_code(KC_LSFT);
416 break;
417 }
418 } 79 }
419 80
420#endif
421
422 // Shift / paren setup 81 // Shift / paren setup
423 82
424 switch(keycode) { 83 switch(keycode) {
@@ -657,46 +316,15 @@ void matrix_init_quantum() {
657 316
658void matrix_scan_quantum() { 317void matrix_scan_quantum() {
659 #ifdef AUDIO_ENABLE 318 #ifdef AUDIO_ENABLE
660 if (music_sequence_playing) { 319 matrix_scan_music();
661 if ((music_sequence_timer == 0) || (timer_elapsed(music_sequence_timer) > music_sequence_interval)) {
662 music_sequence_timer = timer_read();
663 stop_note(music_sequence[(music_sequence_position - 1 < 0)?(music_sequence_position - 1 + music_sequence_count):(music_sequence_position - 1)]);
664 play_note(music_sequence[music_sequence_position], 0xF);
665 music_sequence_position = (music_sequence_position + 1) % music_sequence_count;
666 }
667 }
668
669 #endif 320 #endif
670 321
322 #ifdef TAP_DANCE_ENABLE
323 matrix_scan_tap_dance();
324 #endif
671 matrix_scan_kb(); 325 matrix_scan_kb();
672} 326}
673 327
674#ifdef AUDIO_ENABLE
675 bool is_music_on(void) {
676 return (music_activated != 0);
677 }
678
679 void music_toggle(void) {
680 if (!music_activated) {
681 music_on();
682 } else {
683 music_off();
684 }
685 }
686
687 void music_on(void) {
688 music_activated = 1;
689 music_on_user();
690 }
691
692 void music_off(void) {
693 music_activated = 0;
694 stop_all_notes();
695 }
696
697#endif
698
699
700#if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN) 328#if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN)
701 329
702static const uint8_t backlight_pin = BACKLIGHT_PIN; 330static const uint8_t backlight_pin = BACKLIGHT_PIN;
@@ -1048,13 +676,4 @@ void startup_user() {}
1048__attribute__ ((weak)) 676__attribute__ ((weak))
1049void shutdown_user() {} 677void shutdown_user() {}
1050 678
1051__attribute__ ((weak))
1052void music_on_user() {}
1053
1054__attribute__ ((weak))
1055void audio_on_user() {}
1056
1057__attribute__ ((weak))
1058void music_scale_user() {}
1059
1060//------------------------------------------------------------------------------ 679//------------------------------------------------------------------------------