]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/process_keycode/process_combo.c
Added support for timing out combos if a key as been pressed for longer than COMBO_TERM
[qmk_firmware.git] / quantum / process_keycode / process_combo.c
1 #include "process_combo.h"
2 #include "print.h"
3
4 #define SEND_KEY(key) \
5 do { \
6     register_code16(key); \
7     send_keyboard_report(); \
8     unregister_code16(key); \
9 } while(0)
10
11 #define COMBO_TIMER_ELAPSED -1
12
13 #if COMBO_TERM
14 #define IS_COMBO_KEY_HELD(combo)            (COMBO_TIMER_ELAPSED == combo->timer ? false : true)
15 #define RESET_COMBO_TIMER_AND_KEY(combo)    combo->timer = 0; combo->key = 0
16 #else
17 #define IS_COMBO_KEY_HELD(combo)            (true)
18 #define RESET_COMBO_TIMER_AND_KEY(combo)    do {} while (0)
19 #endif
20
21
22 __attribute__ ((weak))
23 combo_t key_combos[COMBO_COUNT] = {
24
25 };
26
27 static inline void reset_combo(combo_t *combo)
28 {
29     combo->state = 0;
30     RESET_COMBO_TIMER_AND_KEY(combo);
31 }
32
33 #define ALL_COMBO_KEYS_ARE_DOWN (((1<<count)-1) == combo->state)
34 #define NO_COMBO_KEYS_ARE_DOWN  (0 == combo->state)
35 #define KEY_STATE_DOWN(key)     do{ combo->state |= (1<<key); } while(0)
36 #define KEY_STATE_UP(key)       do{ combo->state &= ~(1<<key); } while(0)
37 static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) 
38 {
39     uint8_t count = 0;
40     uint8_t index = -1;
41     /* Find index of keycode and number of combo keys */
42     for (const uint16_t *keys = combo->keys; ;++count) {
43         uint16_t key = pgm_read_word(&keys[count]);
44         if (keycode == key) index = count;
45         if (COMBO_END == key) break;
46     }
47
48     /* Return if not a combo key */
49     if (-1 == index) return false;
50
51     bool is_combo_active = IS_COMBO_KEY_HELD(combo);
52
53     if (record->event.pressed) {
54         KEY_STATE_DOWN(index);
55         
56 #if COMBO_TERM
57         if (is_combo_active) {
58             combo->timer = timer_read();
59             combo->key = keycode;
60         }
61 #endif
62
63     } else {
64         if (is_combo_active && combo->state) { /* Combo key was tapped */
65             RESET_COMBO_TIMER_AND_KEY(combo);            
66             SEND_KEY(keycode);
67         }
68
69 #if COMBO_TERM
70         if (!is_combo_active && keycode == combo->key) { /* Held combo key was released */
71             unregister_code16(combo->key);
72         }
73 #endif
74
75         KEY_STATE_UP(index);
76     }
77
78     if (ALL_COMBO_KEYS_ARE_DOWN && is_combo_active) {
79         SEND_KEY(combo->action);
80         reset_combo(combo);
81     } 
82     
83     if(NO_COMBO_KEYS_ARE_DOWN && !is_combo_active) {
84         reset_combo(combo);
85     }
86
87     return is_combo_active;
88 }
89
90 bool process_combo(uint16_t keycode, keyrecord_t *record)
91 {
92     bool is_combo_key = false;
93
94     for (int i = 0; i < COMBO_COUNT; ++i) {
95         combo_t *combo = &key_combos[i];
96         is_combo_key |= process_single_combo(combo, keycode, record);
97     }    
98
99     return !is_combo_key;
100 }
101
102 void matrix_scan_combo(void)
103 {
104 #if COMBO_TERM
105     for (int i = 0; i < COMBO_COUNT; ++i) {
106         combo_t *combo = &key_combos[i];
107         if (combo->timer && 
108             combo->timer != COMBO_TIMER_ELAPSED && 
109             timer_elapsed(combo->timer) > COMBO_TERM) {
110
111             combo->timer = COMBO_TIMER_ELAPSED;
112             unregister_code16(combo->key);
113             register_code16(combo->key);
114         }
115     }
116 #endif
117 }