Adds oneshot layer and oneshot tap toggling (#308)
[jackhill/qmk/firmware.git] / tmk_core / common / action_util.c
CommitLineData
a074364c 1/*
2Copyright 2013 Jun Wako <wakojun@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17#include "host.h"
18#include "report.h"
19#include "debug.h"
20#include "action_util.h"
74e97eef 21#include "action_layer.h"
a074364c 22#include "timer.h"
23
24static inline void add_key_byte(uint8_t code);
25static inline void del_key_byte(uint8_t code);
26#ifdef NKRO_ENABLE
27static inline void add_key_bit(uint8_t code);
28static inline void del_key_bit(uint8_t code);
29#endif
30
31static uint8_t real_mods = 0;
32static uint8_t weak_mods = 0;
b7a81f04 33static uint8_t macro_mods = 0;
a074364c 34
35#ifdef USB_6KRO_ENABLE
36#define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
37#define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
38#define RO_INC(a) RO_ADD(a, 1)
39#define RO_DEC(a) RO_SUB(a, 1)
40static int8_t cb_head = 0;
41static int8_t cb_tail = 0;
42static int8_t cb_count = 0;
43#endif
44
45// TODO: pointer variable is not needed
46//report_keyboard_t keyboard_report = {};
47report_keyboard_t *keyboard_report = &(report_keyboard_t){};
48
49#ifndef NO_ACTION_ONESHOT
50static int8_t oneshot_mods = 0;
74e97eef
TA
51static int8_t oneshot_locked_mods = 0;
52int8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
53void set_oneshot_locked_mods(int8_t mods) { oneshot_locked_mods = mods; }
54void clear_oneshot_locked_mods(void) { oneshot_locked_mods = 0; }
a074364c 55#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
56static int16_t oneshot_time = 0;
74e97eef
TA
57inline bool has_oneshot_mods_timed_out() {
58 return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT;
59}
a074364c 60#endif
61#endif
62
74e97eef
TA
63/* oneshot layer */
64#ifndef NO_ACTION_ONESHOT
65/* oneshot_layer_data bits
66* LLLL LSSS
67* where:
68* L => are layer bits
69* S => oneshot state bits
70*/
71static int8_t oneshot_layer_data = 0;
72
73inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
74inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }
75
76#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
77static int16_t oneshot_layer_time = 0;
78inline bool has_oneshot_layer_timed_out() {
79 return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT &&
80 !(get_oneshot_layer_state() & ONESHOT_TOGGLED);
81}
82#endif
83
84/* Oneshot layer */
85void set_oneshot_layer(uint8_t layer, uint8_t state)
86{
87 oneshot_layer_data = layer << 3 | state;
88 layer_on(layer);
89#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
90 oneshot_layer_time = timer_read();
91#endif
92}
93void reset_oneshot_layer(void) {
94 oneshot_layer_data = 0;
95#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
96 oneshot_layer_time = 0;
97#endif
98}
99void clear_oneshot_layer_state(oneshot_fullfillment_t state)
100{
101 uint8_t start_state = oneshot_layer_data;
102 oneshot_layer_data &= ~state;
103 if (!get_oneshot_layer_state() && start_state != oneshot_layer_data) {
104 layer_off(get_oneshot_layer());
105#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
106 oneshot_layer_time = 0;
107#endif
108 }
109}
110bool is_oneshot_layer_active(void)
111{
112 return get_oneshot_layer_state();
113}
114#endif
a074364c 115
116void send_keyboard_report(void) {
117 keyboard_report->mods = real_mods;
118 keyboard_report->mods |= weak_mods;
b7a81f04 119 keyboard_report->mods |= macro_mods;
a074364c 120#ifndef NO_ACTION_ONESHOT
121 if (oneshot_mods) {
122#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
74e97eef 123 if (has_oneshot_mods_timed_out()) {
a074364c 124 dprintf("Oneshot: timeout\n");
125 clear_oneshot_mods();
126 }
127#endif
128 keyboard_report->mods |= oneshot_mods;
129 if (has_anykey()) {
130 clear_oneshot_mods();
131 }
132 }
74e97eef 133
a074364c 134#endif
135 host_keyboard_send(keyboard_report);
136}
137
138/* key */
139void add_key(uint8_t key)
140{
141#ifdef NKRO_ENABLE
ed9766a7 142 if (keyboard_protocol && keyboard_nkro) {
a074364c 143 add_key_bit(key);
144 return;
145 }
146#endif
147 add_key_byte(key);
148}
149
150void del_key(uint8_t key)
151{
152#ifdef NKRO_ENABLE
ed9766a7 153 if (keyboard_protocol && keyboard_nkro) {
a074364c 154 del_key_bit(key);
155 return;
156 }
157#endif
158 del_key_byte(key);
159}
160
161void clear_keys(void)
162{
163 // not clear mods
164 for (int8_t i = 1; i < KEYBOARD_REPORT_SIZE; i++) {
165 keyboard_report->raw[i] = 0;
166 }
167}
168
169
170/* modifier */
171uint8_t get_mods(void) { return real_mods; }
172void add_mods(uint8_t mods) { real_mods |= mods; }
173void del_mods(uint8_t mods) { real_mods &= ~mods; }
174void set_mods(uint8_t mods) { real_mods = mods; }
175void clear_mods(void) { real_mods = 0; }
176
177/* weak modifier */
178uint8_t get_weak_mods(void) { return weak_mods; }
179void add_weak_mods(uint8_t mods) { weak_mods |= mods; }
180void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; }
181void set_weak_mods(uint8_t mods) { weak_mods = mods; }
182void clear_weak_mods(void) { weak_mods = 0; }
183
b7a81f04
DL
184/* macro modifier */
185uint8_t get_macro_mods(void) { return macro_mods; }
186void add_macro_mods(uint8_t mods) { macro_mods |= mods; }
187void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; }
188void set_macro_mods(uint8_t mods) { macro_mods = mods; }
189void clear_macro_mods(void) { macro_mods = 0; }
190
a074364c 191/* Oneshot modifier */
192#ifndef NO_ACTION_ONESHOT
193void set_oneshot_mods(uint8_t mods)
194{
195 oneshot_mods = mods;
196#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
197 oneshot_time = timer_read();
198#endif
199}
200void clear_oneshot_mods(void)
201{
202 oneshot_mods = 0;
203#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
204 oneshot_time = 0;
205#endif
206}
74e97eef
TA
207uint8_t get_oneshot_mods(void)
208{
209 return oneshot_mods;
210}
a074364c 211#endif
212
a074364c 213/*
214 * inspect keyboard state
215 */
216uint8_t has_anykey(void)
217{
218 uint8_t cnt = 0;
219 for (uint8_t i = 1; i < KEYBOARD_REPORT_SIZE; i++) {
220 if (keyboard_report->raw[i])
221 cnt++;
222 }
223 return cnt;
224}
225
226uint8_t has_anymod(void)
227{
228 return bitpop(real_mods);
229}
230
231uint8_t get_first_key(void)
232{
233#ifdef NKRO_ENABLE
ed9766a7 234 if (keyboard_protocol && keyboard_nkro) {
a074364c 235 uint8_t i = 0;
236 for (; i < KEYBOARD_REPORT_BITS && !keyboard_report->nkro.bits[i]; i++)
237 ;
238 return i<<3 | biton(keyboard_report->nkro.bits[i]);
239 }
240#endif
241#ifdef USB_6KRO_ENABLE
242 uint8_t i = cb_head;
243 do {
244 if (keyboard_report->keys[i] != 0) {
245 break;
246 }
247 i = RO_INC(i);
248 } while (i != cb_tail);
249 return keyboard_report->keys[i];
250#else
251 return keyboard_report->keys[0];
252#endif
253}
254
255
256
257/* local functions */
258static inline void add_key_byte(uint8_t code)
259{
260#ifdef USB_6KRO_ENABLE
261 int8_t i = cb_head;
262 int8_t empty = -1;
263 if (cb_count) {
264 do {
265 if (keyboard_report->keys[i] == code) {
266 return;
267 }
268 if (empty == -1 && keyboard_report->keys[i] == 0) {
269 empty = i;
270 }
271 i = RO_INC(i);
272 } while (i != cb_tail);
273 if (i == cb_tail) {
274 if (cb_tail == cb_head) {
275 // buffer is full
276 if (empty == -1) {
277 // pop head when has no empty space
278 cb_head = RO_INC(cb_head);
279 cb_count--;
280 }
281 else {
282 // left shift when has empty space
283 uint8_t offset = 1;
284 i = RO_INC(empty);
285 do {
286 if (keyboard_report->keys[i] != 0) {
287 keyboard_report->keys[empty] = keyboard_report->keys[i];
288 keyboard_report->keys[i] = 0;
289 empty = RO_INC(empty);
290 }
291 else {
292 offset++;
293 }
294 i = RO_INC(i);
295 } while (i != cb_tail);
296 cb_tail = RO_SUB(cb_tail, offset);
297 }
298 }
299 }
300 }
301 // add to tail
302 keyboard_report->keys[cb_tail] = code;
303 cb_tail = RO_INC(cb_tail);
304 cb_count++;
305#else
306 int8_t i = 0;
307 int8_t empty = -1;
308 for (; i < KEYBOARD_REPORT_KEYS; i++) {
309 if (keyboard_report->keys[i] == code) {
310 break;
311 }
312 if (empty == -1 && keyboard_report->keys[i] == 0) {
313 empty = i;
314 }
315 }
316 if (i == KEYBOARD_REPORT_KEYS) {
317 if (empty != -1) {
318 keyboard_report->keys[empty] = code;
319 }
320 }
321#endif
322}
323
324static inline void del_key_byte(uint8_t code)
325{
326#ifdef USB_6KRO_ENABLE
327 uint8_t i = cb_head;
328 if (cb_count) {
329 do {
330 if (keyboard_report->keys[i] == code) {
331 keyboard_report->keys[i] = 0;
332 cb_count--;
333 if (cb_count == 0) {
334 // reset head and tail
335 cb_tail = cb_head = 0;
336 }
337 if (i == RO_DEC(cb_tail)) {
338 // left shift when next to tail
339 do {
340 cb_tail = RO_DEC(cb_tail);
341 if (keyboard_report->keys[RO_DEC(cb_tail)] != 0) {
342 break;
343 }
344 } while (cb_tail != cb_head);
345 }
346 break;
347 }
348 i = RO_INC(i);
349 } while (i != cb_tail);
350 }
351#else
352 for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) {
353 if (keyboard_report->keys[i] == code) {
354 keyboard_report->keys[i] = 0;
355 }
356 }
357#endif
358}
359
360#ifdef NKRO_ENABLE
361static inline void add_key_bit(uint8_t code)
362{
363 if ((code>>3) < KEYBOARD_REPORT_BITS) {
364 keyboard_report->nkro.bits[code>>3] |= 1<<(code&7);
365 } else {
366 dprintf("add_key_bit: can't add: %02X\n", code);
367 }
368}
369
370static inline void del_key_bit(uint8_t code)
371{
372 if ((code>>3) < KEYBOARD_REPORT_BITS) {
373 keyboard_report->nkro.bits[code>>3] &= ~(1<<(code&7));
374 } else {
375 dprintf("del_key_bit: can't del: %02X\n", code);
376 }
377}
378#endif