aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Humbert <jack.humb@gmail.com>2017-02-06 19:31:45 -0500
committerGitHub <noreply@github.com>2017-02-06 19:31:45 -0500
commit4348fb54d6eb80e8d82c0724be647631bdd524d3 (patch)
tree7e51344981e0e9daef487c3cbe7b4508735f91bf
parentb6ffda484971306264c8c95facf75eec4c85b62a (diff)
parent40abf8bc9ce22cab472f79e3a97c413ac5648986 (diff)
downloadqmk_firmware-4348fb54d6eb80e8d82c0724be647631bdd524d3.tar.gz
qmk_firmware-4348fb54d6eb80e8d82c0724be647631bdd524d3.zip
Merge pull request #960 from ofples/feature/combos
Keyboard combination triggers
-rw-r--r--build_keyboard.mk5
-rw-r--r--quantum/process_keycode/process_combo.c134
-rw-r--r--quantum/process_keycode/process_combo.h43
-rw-r--r--quantum/quantum.c8
-rw-r--r--quantum/quantum.h4
5 files changed, 194 insertions, 0 deletions
diff --git a/build_keyboard.mk b/build_keyboard.mk
index b85557d4a..2c64e93a2 100644
--- a/build_keyboard.mk
+++ b/build_keyboard.mk
@@ -144,6 +144,11 @@ ifeq ($(strip $(MIDI_ENABLE)), yes)
144 SRC += $(QUANTUM_DIR)/process_keycode/process_midi.c 144 SRC += $(QUANTUM_DIR)/process_keycode/process_midi.c
145endif 145endif
146 146
147ifeq ($(strip $(COMBO_ENABLE)), yes)
148 OPT_DEFS += -DCOMBO_ENABLE
149 SRC += $(QUANTUM_DIR)/process_keycode/process_combo.c
150endif
151
147ifeq ($(strip $(VIRTSER_ENABLE)), yes) 152ifeq ($(strip $(VIRTSER_ENABLE)), yes)
148 OPT_DEFS += -DVIRTSER_ENABLE 153 OPT_DEFS += -DVIRTSER_ENABLE
149endif 154endif
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
new file mode 100644
index 000000000..e2189ad98
--- /dev/null
+++ b/quantum/process_keycode/process_combo.c
@@ -0,0 +1,134 @@
1#include "process_combo.h"
2#include "print.h"
3
4
5#define COMBO_TIMER_ELAPSED -1
6
7
8__attribute__ ((weak))
9combo_t key_combos[] = {
10
11};
12
13__attribute__ ((weak))
14void process_combo_event(uint8_t combo_index, bool pressed) {
15
16}
17
18static uint8_t current_combo_index = 0;
19
20static 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)
37static 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
99bool 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
111void 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}
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
new file mode 100644
index 000000000..847f2b737
--- /dev/null
+++ b/quantum/process_keycode/process_combo.h
@@ -0,0 +1,43 @@
1#ifndef PROCESS_COMBO_H
2#define PROCESS_COMBO_H
3
4#include <stdint.h>
5#include "progmem.h"
6#include "quantum.h"
7
8typedef struct
9{
10 const uint16_t *keys;
11 uint16_t keycode;
12#ifdef EXTRA_EXTRA_LONG_COMBOS
13 uint32_t state;
14#elif EXTRA_LONG_COMBOS
15 uint16_t state;
16#else
17 uint8_t state;
18#endif
19 uint16_t timer;
20#ifdef COMBO_ALLOW_ACTION_KEYS
21 keyrecord_t prev_record;
22#else
23 uint16_t prev_key;
24#endif
25} combo_t;
26
27
28#define COMBO(ck, ca) {.keys = &(ck)[0], .keycode = (ca)}
29#define COMBO_ACTION(ck) {.keys = &(ck)[0]}
30
31#define COMBO_END 0
32#ifndef COMBO_COUNT
33#define COMBO_COUNT 0
34#endif
35#ifndef COMBO_TERM
36#define COMBO_TERM TAPPING_TERM
37#endif
38
39bool process_combo(uint16_t keycode, keyrecord_t *record);
40void matrix_scan_combo(void);
41void process_combo_event(uint8_t combo_index, bool pressed);
42
43#endif
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 0aecd238e..b83ae433e 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -158,6 +158,9 @@ bool process_record_quantum(keyrecord_t *record) {
158 #ifndef DISABLE_CHORDING 158 #ifndef DISABLE_CHORDING
159 process_chording(keycode, record) && 159 process_chording(keycode, record) &&
160 #endif 160 #endif
161 #ifdef COMBO_ENABLE
162 process_combo(keycode, record) &&
163 #endif
161 #ifdef UNICODE_ENABLE 164 #ifdef UNICODE_ENABLE
162 process_unicode(keycode, record) && 165 process_unicode(keycode, record) &&
163 #endif 166 #endif
@@ -536,6 +539,11 @@ void matrix_scan_quantum() {
536 #ifdef TAP_DANCE_ENABLE 539 #ifdef TAP_DANCE_ENABLE
537 matrix_scan_tap_dance(); 540 matrix_scan_tap_dance();
538 #endif 541 #endif
542
543 #ifdef COMBO_ENABLE
544 matrix_scan_combo();
545 #endif
546
539 matrix_scan_kb(); 547 matrix_scan_kb();
540} 548}
541 549
diff --git a/quantum/quantum.h b/quantum/quantum.h
index e6adf974a..8614c053a 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -63,6 +63,10 @@ extern uint32_t default_layer_state;
63 #include "process_printer.h" 63 #include "process_printer.h"
64#endif 64#endif
65 65
66#ifdef COMBO_ENABLE
67 #include "process_combo.h"
68#endif
69
66#define SEND_STRING(str) send_string(PSTR(str)) 70#define SEND_STRING(str) send_string(PSTR(str))
67void send_string(const char *str); 71void send_string(const char *str);
68 72