]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/process_keycode/process_combo.c
Merge pull request #1057 from priyadi/selectable_output
[qmk_firmware.git] / quantum / process_keycode / process_combo.c
1 #include "process_combo.h"
2 #include "print.h"
3
4
5 #define COMBO_TIMER_ELAPSED -1
6
7
8 __attribute__ ((weak))
9 combo_t key_combos[] = {
10
11 };
12
13 __attribute__ ((weak))
14 void process_combo_event(uint8_t combo_index, bool pressed) {
15
16 }
17
18 static uint8_t current_combo_index = 0;
19
20 static inline void send_combo(uint16_t action, bool pressed)
21 {
22     if (action) {
23         if (pressed) {
24             register_code16(action);
25         } else {
26             unregister_code16(action);
27         }
28     } else {
29         process_combo_event(current_combo_index, pressed);
30     }
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 == (int8_t)index) return false;
50
51     /* The combos timer is used to signal whether the combo is active */
52     bool is_combo_active = COMBO_TIMER_ELAPSED == combo->timer ? false : true;
53
54     if (record->event.pressed) {
55         KEY_STATE_DOWN(index);
56
57         if (is_combo_active) {
58             if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */
59                 send_combo(combo->keycode, true);
60                 combo->timer = COMBO_TIMER_ELAPSED;
61             } else { /* Combo key was pressed */
62                 combo->timer = timer_read();
63 #ifdef COMBO_ALLOW_ACTION_KEYS
64                 combo->prev_record = *record;
65 #else
66                 combo->prev_key = keycode;
67 #endif
68             }
69         }
70     } else {
71         if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */
72             send_combo(combo->keycode, false);
73         }
74
75         if (is_combo_active) { /* Combo key was tapped */
76 #ifdef COMBO_ALLOW_ACTION_KEYS
77             record->event.pressed = true;
78             process_action(record, store_or_get_action(record->event.pressed, record->event.key));
79             record->event.pressed = false;
80             process_action(record, store_or_get_action(record->event.pressed, record->event.key));
81 #else
82             register_code16(keycode);
83             send_keyboard_report();
84             unregister_code16(keycode);
85 #endif
86             combo->timer = 0;            
87         }
88
89         KEY_STATE_UP(index);        
90     }
91
92     if (NO_COMBO_KEYS_ARE_DOWN) {
93         combo->timer = 0;
94     }
95
96     return is_combo_active;
97 }
98
99 bool process_combo(uint16_t keycode, keyrecord_t *record)
100 {
101     bool is_combo_key = false;
102
103     for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) {
104         combo_t *combo = &key_combos[current_combo_index];
105         is_combo_key |= process_single_combo(combo, keycode, record);
106     }    
107
108     return !is_combo_key;
109 }
110
111 void matrix_scan_combo(void)
112 {
113     for (int i = 0; i < COMBO_COUNT; ++i) {
114         combo_t *combo = &key_combos[i];
115         if (combo->timer && 
116             combo->timer != COMBO_TIMER_ELAPSED && 
117             timer_elapsed(combo->timer) > COMBO_TERM) {
118             
119             /* This disables the combo, meaning key events for this
120              * combo will be handled by the next processors in the chain 
121              */
122             combo->timer = COMBO_TIMER_ELAPSED;
123
124 #ifdef COMBO_ALLOW_ACTION_KEYS
125             process_action(&combo->prev_record, 
126                 store_or_get_action(combo->prev_record.event.pressed, 
127                                     combo->prev_record.event.key));
128 #else
129             unregister_code16(combo->prev_key);
130             register_code16(combo->prev_key);
131 #endif
132         }
133     }
134 }