]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/common/action.c
Add one-hand support.
[qmk_firmware.git] / tmk_core / common / action.c
1 /*
2 Copyright 2012,2013 Jun Wako <wakojun@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 #include "host.h"
18 #include "keycode.h"
19 #include "keyboard.h"
20 #include "mousekey.h"
21 #include "command.h"
22 #include "led.h"
23 #include "backlight.h"
24 #include "action_layer.h"
25 #include "action_tapping.h"
26 #include "action_macro.h"
27 #include "action_util.h"
28 #include "action.h"
29
30 #ifdef DEBUG_ACTION
31 #include "debug.h"
32 #else
33 #include "nodebug.h"
34 #endif
35
36
37 void action_exec(keyevent_t event)
38 {
39     if (!IS_NOEVENT(event)) {
40         dprint("\n---- action_exec: start -----\n");
41         dprint("EVENT: "); debug_event(event); dprintln();
42     }
43
44 #ifdef ONEHAND_ENABLE
45     if (!IS_NOEVENT(event)) {
46         process_hand_swap(&event);
47     }
48 #endif
49
50     keyrecord_t record = { .event = event };
51
52 #ifndef NO_ACTION_TAPPING
53     action_tapping_process(record);
54 #else
55     process_record(&record);
56     if (!IS_NOEVENT(record.event)) {
57         dprint("processed: "); debug_record(record); dprintln();
58     }
59 #endif
60 }
61
62 #ifdef ONEHAND_ENABLE
63 bool swap_hands = false;
64
65 void process_hand_swap(keyevent_t *event) {
66     static swap_state_row_t swap_state[MATRIX_ROWS];
67
68     keypos_t pos = event->key;
69     swap_state_row_t col_bit = (swap_state_row_t)1<<pos.col;
70     bool do_swap = event->pressed ? swap_hands :
71                                     swap_state[pos.row] & (col_bit);
72
73     if (do_swap) {
74         event->key = hand_swap_config[pos.row][pos.col];
75         swap_state[pos.row] |= col_bit;
76     } else {
77         swap_state[pos.row] &= ~(col_bit);
78     }
79 }
80 #endif
81
82 #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS)
83 bool disable_action_cache = false;
84
85 void process_record_nocache(keyrecord_t *record)
86 {
87     disable_action_cache = true;
88     process_record(record);
89     disable_action_cache = false;
90 }
91 #else
92 void process_record_nocache(keyrecord_t *record)
93 {
94     process_record(record);
95 }
96 #endif
97
98 __attribute__ ((weak))
99 bool process_record_quantum(keyrecord_t *record) {
100     return true;
101 }
102
103 void process_record(keyrecord_t *record) 
104 {
105     if (IS_NOEVENT(record->event)) { return; }
106
107     if(!process_record_quantum(record))
108         return;
109
110     action_t action = store_or_get_action(record->event.pressed, record->event.key);
111     dprint("ACTION: "); debug_action(action);
112 #ifndef NO_ACTION_LAYER
113     dprint(" layer_state: "); layer_debug();
114     dprint(" default_layer_state: "); default_layer_debug();
115 #endif
116     dprintln();
117
118     process_action(record, action);
119 }
120
121 void process_action(keyrecord_t *record, action_t action)
122 {
123     bool do_release_oneshot = false;
124     keyevent_t event = record->event;
125 #ifndef NO_ACTION_TAPPING
126     uint8_t tap_count = record->tap.count;
127 #endif
128
129 #if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
130     if (has_oneshot_layer_timed_out()) {
131         dprintf("Oneshot layer: timeout\n");
132         clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
133     }
134 #endif
135
136     if (event.pressed) {
137         // clear the potential weak mods left by previously pressed keys
138         clear_weak_mods();
139     }
140
141 #ifndef NO_ACTION_ONESHOT
142     // notice we only clear the one shot layer if the pressed key is not a modifier.
143     if (is_oneshot_layer_active() && event.pressed && !IS_MOD(action.key.code)) {
144         clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
145         do_release_oneshot = !is_oneshot_layer_active();
146     }
147 #endif
148
149     switch (action.kind.id) {
150         /* Key and Mods */
151         case ACT_LMODS:
152         case ACT_RMODS:
153             {
154                 uint8_t mods = (action.kind.id == ACT_LMODS) ?  action.key.mods :
155                                                                 action.key.mods<<4;
156                 if (event.pressed) {
157                     if (mods) {
158                         if (IS_MOD(action.key.code)) {
159                             // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
160                             // this also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT)
161                             add_mods(mods);
162                         } else {
163                             add_weak_mods(mods);
164                         }
165                         send_keyboard_report();
166                     }
167                     register_code(action.key.code);
168                 } else {
169                     unregister_code(action.key.code);
170                     if (mods) {
171                         if (IS_MOD(action.key.code)) {
172                             del_mods(mods);
173                         } else {
174                             del_weak_mods(mods);
175                         }
176                         send_keyboard_report();
177                     }
178                 }
179             }
180             break;
181 #ifndef NO_ACTION_TAPPING
182         case ACT_LMODS_TAP:
183         case ACT_RMODS_TAP:
184             {
185                 uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ?  action.key.mods :
186                                                                     action.key.mods<<4;
187                 switch (action.layer_tap.code) {
188     #ifndef NO_ACTION_ONESHOT
189                     case MODS_ONESHOT:
190                         // Oneshot modifier
191                         if (event.pressed) {
192                             if (tap_count == 0) {
193                                 dprint("MODS_TAP: Oneshot: 0\n");
194                                 register_mods(mods);
195                             } else if (tap_count == 1) {
196                                 dprint("MODS_TAP: Oneshot: start\n");
197                                 set_oneshot_mods(mods);
198                     #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
199                             } else if (tap_count == ONESHOT_TAP_TOGGLE) {
200                                 dprint("MODS_TAP: Toggling oneshot");
201                                 clear_oneshot_mods();
202                                 set_oneshot_locked_mods(mods);
203                                 register_mods(mods);
204                     #endif
205                             } else {
206                                 register_mods(mods);
207                             }
208                         } else {
209                             if (tap_count == 0) {
210                                 clear_oneshot_mods();
211                                 unregister_mods(mods);
212                             } else if (tap_count == 1) {
213                                 // Retain Oneshot mods
214                     #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
215                                 if (mods & get_mods()) {
216                                     clear_oneshot_locked_mods();
217                                     clear_oneshot_mods();
218                                     unregister_mods(mods);
219                                 }
220                             } else if (tap_count == ONESHOT_TAP_TOGGLE) {
221                                 // Toggle Oneshot Layer
222                     #endif
223                             } else {
224                                 clear_oneshot_mods();
225                                 unregister_mods(mods);
226                             }
227                         }
228                         break;
229     #endif
230                     case MODS_TAP_TOGGLE:
231                         if (event.pressed) {
232                             if (tap_count <= TAPPING_TOGGLE) {
233                                 register_mods(mods);
234                             }
235                         } else {
236                             if (tap_count < TAPPING_TOGGLE) {
237                                 unregister_mods(mods);
238                             }
239                         }
240                         break;
241                     default:
242                         if (event.pressed) {
243                             if (tap_count > 0) {
244 #ifndef IGNORE_MOD_TAP_INTERRUPT
245                                 if (record->tap.interrupted) {
246                                     dprint("mods_tap: tap: cancel: add_mods\n");
247                                     // ad hoc: set 0 to cancel tap
248                                     record->tap.count = 0;
249                                     register_mods(mods);
250                                 } else
251 #endif
252                                 {
253                                     dprint("MODS_TAP: Tap: register_code\n");
254                                     register_code(action.key.code);
255                                 }
256                             } else {
257                                 dprint("MODS_TAP: No tap: add_mods\n");
258                                 register_mods(mods);
259                             }
260                         } else {
261                             if (tap_count > 0) {
262                                 dprint("MODS_TAP: Tap: unregister_code\n");
263                                 unregister_code(action.key.code);
264                             } else {
265                                 dprint("MODS_TAP: No tap: add_mods\n");
266                                 unregister_mods(mods);
267                             }
268                         }
269                         break;
270                 }
271             }
272             break;
273 #endif
274 #ifdef EXTRAKEY_ENABLE
275         /* other HID usage */
276         case ACT_USAGE:
277             switch (action.usage.page) {
278                 case PAGE_SYSTEM:
279                     if (event.pressed) {
280                         host_system_send(action.usage.code);
281                     } else {
282                         host_system_send(0);
283                     }
284                     break;
285                 case PAGE_CONSUMER:
286                     if (event.pressed) {
287                         host_consumer_send(action.usage.code);
288                     } else {
289                         host_consumer_send(0);
290                     }
291                     break;
292             }
293             break;
294 #endif
295 #ifdef MOUSEKEY_ENABLE
296         /* Mouse key */
297         case ACT_MOUSEKEY:
298             if (event.pressed) {
299                 mousekey_on(action.key.code);
300                 mousekey_send();
301             } else {
302                 mousekey_off(action.key.code);
303                 mousekey_send();
304             }
305             break;
306 #endif
307 #ifndef NO_ACTION_LAYER
308         case ACT_LAYER:
309             if (action.layer_bitop.on == 0) {
310                 /* Default Layer Bitwise Operation */
311                 if (!event.pressed) {
312                     uint8_t shift = action.layer_bitop.part*4;
313                     uint32_t bits = ((uint32_t)action.layer_bitop.bits)<<shift;
314                     uint32_t mask = (action.layer_bitop.xbit) ? ~(((uint32_t)0xf)<<shift) : 0;
315                     switch (action.layer_bitop.op) {
316                         case OP_BIT_AND: default_layer_and(bits | mask); break;
317                         case OP_BIT_OR:  default_layer_or(bits | mask);  break;
318                         case OP_BIT_XOR: default_layer_xor(bits | mask); break;
319                         case OP_BIT_SET: default_layer_and(mask); default_layer_or(bits); break;
320                     }
321                 }
322             } else {
323                 /* Layer Bitwise Operation */
324                 if (event.pressed ? (action.layer_bitop.on & ON_PRESS) :
325                                     (action.layer_bitop.on & ON_RELEASE)) {
326                     uint8_t shift = action.layer_bitop.part*4;
327                     uint32_t bits = ((uint32_t)action.layer_bitop.bits)<<shift;
328                     uint32_t mask = (action.layer_bitop.xbit) ? ~(((uint32_t)0xf)<<shift) : 0;
329                     switch (action.layer_bitop.op) {
330                         case OP_BIT_AND: layer_and(bits | mask); break;
331                         case OP_BIT_OR:  layer_or(bits | mask);  break;
332                         case OP_BIT_XOR: layer_xor(bits | mask); break;
333                         case OP_BIT_SET: layer_and(mask); layer_or(bits); break;
334                     }
335                 }
336             }
337             break;
338     #ifndef NO_ACTION_TAPPING
339         case ACT_LAYER_TAP:
340         case ACT_LAYER_TAP_EXT:
341             switch (action.layer_tap.code) {
342                 case 0xe0 ... 0xef:
343                     /* layer On/Off with modifiers(left only) */
344                     if (event.pressed) {
345                         layer_on(action.layer_tap.val);
346                         register_mods(action.layer_tap.code & 0x0f);
347                     } else {
348                         layer_off(action.layer_tap.val);
349                         unregister_mods(action.layer_tap.code & 0x0f);
350                     }
351                     break;
352                 case OP_TAP_TOGGLE:
353                     /* tap toggle */
354                     if (event.pressed) {
355                         if (tap_count < TAPPING_TOGGLE) {
356                             layer_invert(action.layer_tap.val);
357                         }
358                     } else {
359                         if (tap_count <= TAPPING_TOGGLE) {
360                             layer_invert(action.layer_tap.val);
361                         }
362                     }
363                     break;
364                 case OP_ON_OFF:
365                     event.pressed ? layer_on(action.layer_tap.val) :
366                                     layer_off(action.layer_tap.val);
367                     break;
368                 case OP_OFF_ON:
369                     event.pressed ? layer_off(action.layer_tap.val) :
370                                     layer_on(action.layer_tap.val);
371                     break;
372                 case OP_SET_CLEAR:
373                     event.pressed ? layer_move(action.layer_tap.val) :
374                                     layer_clear();
375                     break;
376             #ifndef NO_ACTION_ONESHOT
377                 case OP_ONESHOT:
378                     // Oneshot modifier
379                 #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
380                     do_release_oneshot = false;
381                     if (event.pressed) {
382                         del_mods(get_oneshot_locked_mods());
383                         if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
384                             reset_oneshot_layer();
385                             layer_off(action.layer_tap.val);
386                             break;
387                         } else if (tap_count < ONESHOT_TAP_TOGGLE) {
388                             layer_on(action.layer_tap.val);
389                             set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
390                         }
391                     } else {
392                         add_mods(get_oneshot_locked_mods());
393                         if (tap_count >= ONESHOT_TAP_TOGGLE) {
394                             reset_oneshot_layer();
395                             clear_oneshot_locked_mods();
396                             set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
397                         } else {
398                             clear_oneshot_layer_state(ONESHOT_PRESSED);
399                         }
400                     }
401                 #else
402                     if (event.pressed) {
403                         layer_on(action.layer_tap.val);
404                         set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
405                     } else {
406                         clear_oneshot_layer_state(ONESHOT_PRESSED);
407                         if (tap_count > 1) {
408                             clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
409                         }
410                     }
411                 #endif
412                     break;
413             #endif
414                 default:
415                     /* tap key */
416                     if (event.pressed) {
417                         if (tap_count > 0) {
418                             dprint("KEYMAP_TAP_KEY: Tap: register_code\n");
419                             register_code(action.layer_tap.code);
420                         } else {
421                             dprint("KEYMAP_TAP_KEY: No tap: On on press\n");
422                             layer_on(action.layer_tap.val);
423                         }
424                     } else {
425                         if (tap_count > 0) {
426                             dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
427                             unregister_code(action.layer_tap.code);
428                         } else {
429                             dprint("KEYMAP_TAP_KEY: No tap: Off on release\n");
430                             layer_off(action.layer_tap.val);
431                         }
432                     }
433                     break;
434             }
435             break;
436     #endif
437 #endif
438         /* Extentions */
439 #ifndef NO_ACTION_MACRO
440         case ACT_MACRO:
441             action_macro_play(action_get_macro(record, action.func.id, action.func.opt));
442             break;
443 #endif
444 #ifdef BACKLIGHT_ENABLE
445         case ACT_BACKLIGHT:
446             if (!event.pressed) {
447                 switch (action.backlight.opt) {
448                     case BACKLIGHT_INCREASE:
449                         backlight_increase();
450                         break;
451                     case BACKLIGHT_DECREASE:
452                         backlight_decrease();
453                         break;
454                     case BACKLIGHT_TOGGLE:
455                         backlight_toggle();
456                         break;
457                     case BACKLIGHT_STEP:
458                         backlight_step();
459                         break;
460                     case BACKLIGHT_LEVEL:
461                         backlight_level(action.backlight.level);
462                         break;
463                 }
464             }
465             break;
466 #endif
467         case ACT_COMMAND:
468             switch (action.command.id) {
469 #ifdef ONEHAND_ENABLE
470                 case CMD_SWAP_HANDS:
471                     swap_hands = event.pressed;
472                     break;
473 #endif
474             }
475             break;
476 #ifndef NO_ACTION_FUNCTION
477         case ACT_FUNCTION:
478             action_function(record, action.func.id, action.func.opt);
479             break;
480 #endif
481         default:
482             break;
483     }
484
485 #ifndef NO_ACTION_ONESHOT
486     /* Because we switch layers after a oneshot event, we need to release the
487      * key before we leave the layer or no key up event will be generated.
488      */
489     if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED )   ) {
490         record->event.pressed = false;
491         layer_on(get_oneshot_layer());
492         process_record(record);
493         layer_off(get_oneshot_layer());
494     }
495 #endif
496 }
497
498
499
500
501 /*
502  * Utilities for actions.
503  */
504 void register_code(uint8_t code)
505 {
506     if (code == KC_NO) {
507         return;
508     }
509
510 #ifdef LOCKING_SUPPORT_ENABLE
511     else if (KC_LOCKING_CAPS == code) {
512 #ifdef LOCKING_RESYNC_ENABLE
513         // Resync: ignore if caps lock already is on
514         if (host_keyboard_leds() & (1<<USB_LED_CAPS_LOCK)) return;
515 #endif
516         add_key(KC_CAPSLOCK);
517         send_keyboard_report();
518         del_key(KC_CAPSLOCK);
519         send_keyboard_report();
520     }
521
522     else if (KC_LOCKING_NUM == code) {
523 #ifdef LOCKING_RESYNC_ENABLE
524         if (host_keyboard_leds() & (1<<USB_LED_NUM_LOCK)) return;
525 #endif
526         add_key(KC_NUMLOCK);
527         send_keyboard_report();
528         del_key(KC_NUMLOCK);
529         send_keyboard_report();
530     }
531
532     else if (KC_LOCKING_SCROLL == code) {
533 #ifdef LOCKING_RESYNC_ENABLE
534         if (host_keyboard_leds() & (1<<USB_LED_SCROLL_LOCK)) return;
535 #endif
536         add_key(KC_SCROLLLOCK);
537         send_keyboard_report();
538         del_key(KC_SCROLLLOCK);
539         send_keyboard_report();
540     }
541 #endif
542
543     else if IS_KEY(code) {
544         // TODO: should push command_proc out of this block?
545         if (command_proc(code)) return;
546
547 #ifndef NO_ACTION_ONESHOT
548 /* TODO: remove
549         if (oneshot_state.mods && !oneshot_state.disabled) {
550             uint8_t tmp_mods = get_mods();
551             add_mods(oneshot_state.mods);
552
553             add_key(code);
554             send_keyboard_report();
555
556             set_mods(tmp_mods);
557             send_keyboard_report();
558             oneshot_cancel();
559         } else
560 */
561 #endif
562         {
563             add_key(code);
564             send_keyboard_report();
565         }
566     }
567     else if IS_MOD(code) {
568         add_mods(MOD_BIT(code));
569         send_keyboard_report();
570     }
571     else if IS_SYSTEM(code) {
572         host_system_send(KEYCODE2SYSTEM(code));
573     }
574     else if IS_CONSUMER(code) {
575         host_consumer_send(KEYCODE2CONSUMER(code));
576     }
577 }
578
579 void unregister_code(uint8_t code)
580 {
581     if (code == KC_NO) {
582         return;
583     }
584
585 #ifdef LOCKING_SUPPORT_ENABLE
586     else if (KC_LOCKING_CAPS == code) {
587 #ifdef LOCKING_RESYNC_ENABLE
588         // Resync: ignore if caps lock already is off
589         if (!(host_keyboard_leds() & (1<<USB_LED_CAPS_LOCK))) return;
590 #endif
591         add_key(KC_CAPSLOCK);
592         send_keyboard_report();
593         del_key(KC_CAPSLOCK);
594         send_keyboard_report();
595     }
596
597     else if (KC_LOCKING_NUM == code) {
598 #ifdef LOCKING_RESYNC_ENABLE
599         if (!(host_keyboard_leds() & (1<<USB_LED_NUM_LOCK))) return;
600 #endif
601         add_key(KC_NUMLOCK);
602         send_keyboard_report();
603         del_key(KC_NUMLOCK);
604         send_keyboard_report();
605     }
606
607     else if (KC_LOCKING_SCROLL == code) {
608 #ifdef LOCKING_RESYNC_ENABLE
609         if (!(host_keyboard_leds() & (1<<USB_LED_SCROLL_LOCK))) return;
610 #endif
611         add_key(KC_SCROLLLOCK);
612         send_keyboard_report();
613         del_key(KC_SCROLLLOCK);
614         send_keyboard_report();
615     }
616 #endif
617
618     else if IS_KEY(code) {
619         del_key(code);
620         send_keyboard_report();
621     }
622     else if IS_MOD(code) {
623         del_mods(MOD_BIT(code));
624         send_keyboard_report();
625     }
626     else if IS_SYSTEM(code) {
627         host_system_send(0);
628     }
629     else if IS_CONSUMER(code) {
630         host_consumer_send(0);
631     }
632 }
633
634 void register_mods(uint8_t mods)
635 {
636     if (mods) {
637         add_mods(mods);
638         send_keyboard_report();
639     }
640 }
641
642 void unregister_mods(uint8_t mods)
643 {
644     if (mods) {
645         del_mods(mods);
646         send_keyboard_report();
647     }
648 }
649
650 void clear_keyboard(void)
651 {
652     clear_mods();
653     clear_keyboard_but_mods();
654 }
655
656 void clear_keyboard_but_mods(void)
657 {
658     clear_weak_mods();
659     clear_macro_mods();
660     clear_keys();
661     send_keyboard_report();
662 #ifdef MOUSEKEY_ENABLE
663     mousekey_clear();
664     mousekey_send();
665 #endif
666 #ifdef EXTRAKEY_ENABLE
667     host_system_send(0);
668     host_consumer_send(0);
669 #endif
670 }
671
672 bool is_tap_key(keypos_t key)
673 {
674     action_t action = layer_switch_get_action(key);
675
676     switch (action.kind.id) {
677         case ACT_LMODS_TAP:
678         case ACT_RMODS_TAP:
679         case ACT_LAYER_TAP:
680         case ACT_LAYER_TAP_EXT:
681             switch (action.layer_tap.code) {
682                 case 0x00 ... 0xdf:
683                 case OP_TAP_TOGGLE:
684                 case OP_ONESHOT:
685                     return true;
686             }
687             return false;
688         case ACT_MACRO:
689         case ACT_FUNCTION:
690             if (action.func.opt & FUNC_TAP) { return true; }
691             return false;
692     }
693     return false;
694 }
695
696
697 /*
698  * debug print
699  */
700 void debug_event(keyevent_t event)
701 {
702     dprintf("%04X%c(%u)", (event.key.row<<8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time);
703 }
704
705 void debug_record(keyrecord_t record)
706 {
707     debug_event(record.event);
708 #ifndef NO_ACTION_TAPPING
709     dprintf(":%u%c", record.tap.count, (record.tap.interrupted ? '-' : ' '));
710 #endif
711 }
712
713 void debug_action(action_t action)
714 {
715     switch (action.kind.id) {
716         case ACT_LMODS:             dprint("ACT_LMODS");             break;
717         case ACT_RMODS:             dprint("ACT_RMODS");             break;
718         case ACT_LMODS_TAP:         dprint("ACT_LMODS_TAP");         break;
719         case ACT_RMODS_TAP:         dprint("ACT_RMODS_TAP");         break;
720         case ACT_USAGE:             dprint("ACT_USAGE");             break;
721         case ACT_MOUSEKEY:          dprint("ACT_MOUSEKEY");          break;
722         case ACT_LAYER:             dprint("ACT_LAYER");             break;
723         case ACT_LAYER_TAP:         dprint("ACT_LAYER_TAP");         break;
724         case ACT_LAYER_TAP_EXT:     dprint("ACT_LAYER_TAP_EXT");     break;
725         case ACT_MACRO:             dprint("ACT_MACRO");             break;
726         case ACT_COMMAND:           dprint("ACT_COMMAND");           break;
727         case ACT_FUNCTION:          dprint("ACT_FUNCTION");          break;
728         default:                    dprint("UNKNOWN");               break;
729     }
730     dprintf("[%X:%02X]", action.kind.param>>8, action.kind.param&0xff);
731 }