f40ca745252b0724d302e645482f96e224b5e1c3
1 /* Copyright 2016 Jack Humbert
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 #include "process_combo.h"
20 __attribute__((weak
)) combo_t key_combos
[COMBO_COUNT
] = {
24 __attribute__((weak
)) void process_combo_event(uint8_t combo_index
, bool pressed
) {}
26 static uint16_t timer
= 0;
27 static uint8_t current_combo_index
= 0;
28 static bool drop_buffer
= false;
29 static bool is_active
= false;
30 static bool b_combo_enable
= true; // defaults to enabled
32 static uint8_t buffer_size
= 0;
33 #ifdef COMBO_ALLOW_ACTION_KEYS
34 static keyrecord_t key_buffer
[MAX_COMBO_LENGTH
];
36 static uint16_t key_buffer
[MAX_COMBO_LENGTH
];
39 static inline void send_combo(uint16_t action
, bool pressed
) {
42 register_code16(action
);
44 unregister_code16(action
);
47 process_combo_event(current_combo_index
, pressed
);
51 static inline void dump_key_buffer(bool emit
) {
52 if (buffer_size
== 0) {
57 for (uint8_t i
= 0; i
< buffer_size
; i
++) {
58 #ifdef COMBO_ALLOW_ACTION_KEYS
59 const action_t action
= store_or_get_action(key_buffer
[i
].event
.pressed
, key_buffer
[i
].event
.key
);
60 process_action(&(key_buffer
[i
]), action
);
62 register_code16(key_buffer
[i
]);
63 send_keyboard_report();
71 #define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == combo->state)
72 #define KEY_STATE_DOWN(key) \
74 combo->state |= (1 << key); \
76 #define KEY_STATE_UP(key) \
78 combo->state &= ~(1 << key); \
81 static bool process_single_combo(combo_t
*combo
, uint16_t keycode
, keyrecord_t
*record
) {
84 /* Find index of keycode and number of combo keys */
85 for (const uint16_t *keys
= combo
->keys
;; ++count
) {
86 uint16_t key
= pgm_read_word(&keys
[count
]);
87 if (keycode
== key
) index
= count
;
88 if (COMBO_END
== key
) break;
91 /* Continue processing if not a combo key */
92 if (-1 == (int8_t)index
) return false;
94 bool is_combo_active
= is_active
;
96 if (record
->event
.pressed
) {
97 KEY_STATE_DOWN(index
);
99 if (is_combo_active
) {
100 if (ALL_COMBO_KEYS_ARE_DOWN
) { /* Combo was pressed */
101 send_combo(combo
->keycode
, true);
106 if (ALL_COMBO_KEYS_ARE_DOWN
) { /* Combo was released */
107 send_combo(combo
->keycode
, false);
109 /* continue processing without immediately returning */
110 is_combo_active
= false;
116 return is_combo_active
;
119 #define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
121 bool process_combo(uint16_t keycode
, keyrecord_t
*record
) {
122 bool is_combo_key
= false;
124 bool no_combo_keys_pressed
= true;
126 if (keycode
== CMB_ON
&& record
->event
.pressed
) {
131 if (keycode
== CMB_OFF
&& record
->event
.pressed
) {
136 if (keycode
== CMB_TOG
&& record
->event
.pressed
) {
141 if (!is_combo_enabled()) {
145 for (current_combo_index
= 0; current_combo_index
< COMBO_COUNT
; ++current_combo_index
) {
146 combo_t
*combo
= &key_combos
[current_combo_index
];
147 is_combo_key
|= process_single_combo(combo
, keycode
, record
);
148 no_combo_keys_pressed
= no_combo_keys_pressed
&& NO_COMBO_KEYS_ARE_DOWN
;
152 /* buffer is only dropped when we complete a combo, so we refresh the timer
154 timer
= timer_read();
155 dump_key_buffer(false);
156 } else if (!is_combo_key
) {
157 /* if no combos claim the key we need to emit the keybuffer */
158 dump_key_buffer(true);
160 // reset state if there are no combo keys pressed at all
161 if (no_combo_keys_pressed
) {
165 } else if (record
->event
.pressed
&& is_active
) {
166 /* otherwise the key is consumed and placed in the buffer */
167 timer
= timer_read();
169 if (buffer_size
< MAX_COMBO_LENGTH
) {
170 #ifdef COMBO_ALLOW_ACTION_KEYS
171 key_buffer
[buffer_size
++] = *record
;
173 key_buffer
[buffer_size
++] = keycode
;
178 return !is_combo_key
;
181 void matrix_scan_combo(void) {
182 if (b_combo_enable
&& is_active
&& timer
&& timer_elapsed(timer
) > COMBO_TERM
) {
183 /* This disables the combo, meaning key events for this
184 * combo will be handled by the next processors in the chain
187 dump_key_buffer(true);
191 void combo_enable(void) { b_combo_enable
= true; }
193 void combo_disable(void) {
194 b_combo_enable
= is_active
= false;
196 dump_key_buffer(true);
199 void combo_toggle(void) {
200 if (b_combo_enable
) {
207 bool is_combo_enabled(void) { return b_combo_enable
; }