aboutsummaryrefslogtreecommitdiff
path: root/tmk_core
diff options
context:
space:
mode:
authorThiago Alves <talk@thiagoalves.com.br>2016-05-05 18:41:37 -0700
committerJack Humbert <jack.humb@gmail.com>2016-05-05 21:41:37 -0400
commit74e97eefd7ae76f9ddcb76890a30aa9038804cdb (patch)
tree9bdf7ce6ffb97a024c8230e5e960d53503262ced /tmk_core
parentd4520cd3ac7550fc7243e9a76824d9ba674875c6 (diff)
downloadqmk_firmware-74e97eefd7ae76f9ddcb76890a30aa9038804cdb.tar.gz
qmk_firmware-74e97eefd7ae76f9ddcb76890a30aa9038804cdb.zip
Adds oneshot layer and oneshot tap toggling (#308)
This commit is mostly a cherry-pick from `ahtn` at https://github.com/tmk/tmk_keyboard/pull/255. These are the changes: * Adds ACTION_LAYER_ONESHOT * Adds ONESHOT_TAP_TOGGLE * Mentions sticky keys in the docs on oneshot.
Diffstat (limited to 'tmk_core')
-rw-r--r--tmk_core/common/action.c97
-rw-r--r--tmk_core/common/action_code.h5
-rw-r--r--tmk_core/common/action_util.c70
-rw-r--r--tmk_core/common/action_util.h20
-rw-r--r--tmk_core/doc/keymap.md8
5 files changed, 186 insertions, 14 deletions
diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c
index f9e6c17dc..081e90b2d 100644
--- a/tmk_core/common/action.c
+++ b/tmk_core/common/action.c
@@ -74,6 +74,7 @@ void process_action_kb(keyrecord_t *record) {}
74 74
75void process_action(keyrecord_t *record) 75void process_action(keyrecord_t *record)
76{ 76{
77 bool do_release_oneshot = false;
77 keyevent_t event = record->event; 78 keyevent_t event = record->event;
78#ifndef NO_ACTION_TAPPING 79#ifndef NO_ACTION_TAPPING
79 uint8_t tap_count = record->tap.count; 80 uint8_t tap_count = record->tap.count;
@@ -81,6 +82,13 @@ void process_action(keyrecord_t *record)
81 82
82 if (IS_NOEVENT(event)) { return; } 83 if (IS_NOEVENT(event)) { return; }
83 84
85#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
86 if (has_oneshot_layer_timed_out()) {
87 dprintf("Oneshot layer: timeout\n");
88 clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
89 }
90#endif
91
84 process_action_kb(record); 92 process_action_kb(record);
85 93
86 action_t action = store_or_get_action(event.pressed, event.key); 94 action_t action = store_or_get_action(event.pressed, event.key);
@@ -95,6 +103,15 @@ void process_action(keyrecord_t *record)
95 // clear the potential weak mods left by previously pressed keys 103 // clear the potential weak mods left by previously pressed keys
96 clear_weak_mods(); 104 clear_weak_mods();
97 } 105 }
106
107#ifndef NO_ACTION_ONESHOT
108 // notice we only clear the one shot layer if the pressed key is not a modifier.
109 if (is_oneshot_layer_active() && event.pressed && !IS_MOD(action.key.code)) {
110 clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
111 do_release_oneshot = !is_oneshot_layer_active();
112 }
113#endif
114
98 switch (action.kind.id) { 115 switch (action.kind.id) {
99 /* Key and Mods */ 116 /* Key and Mods */
100 case ACT_LMODS: 117 case ACT_LMODS:
@@ -139,24 +156,37 @@ void process_action(keyrecord_t *record)
139 // Oneshot modifier 156 // Oneshot modifier
140 if (event.pressed) { 157 if (event.pressed) {
141 if (tap_count == 0) { 158 if (tap_count == 0) {
159 dprint("MODS_TAP: Oneshot: 0\n");
142 register_mods(mods); 160 register_mods(mods);
143 } 161 } else if (tap_count == 1) {
144 else if (tap_count == 1) {
145 dprint("MODS_TAP: Oneshot: start\n"); 162 dprint("MODS_TAP: Oneshot: start\n");
146 set_oneshot_mods(mods); 163 set_oneshot_mods(mods);
147 } 164 #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
148 else { 165 } else if (tap_count == ONESHOT_TAP_TOGGLE) {
166 dprint("MODS_TAP: Toggling oneshot");
167 clear_oneshot_mods();
168 set_oneshot_locked_mods(mods);
169 register_mods(mods);
170 #endif
171 } else {
149 register_mods(mods); 172 register_mods(mods);
150 } 173 }
151 } else { 174 } else {
152 if (tap_count == 0) { 175 if (tap_count == 0) {
153 clear_oneshot_mods(); 176 clear_oneshot_mods();
154 unregister_mods(mods); 177 unregister_mods(mods);
155 } 178 } else if (tap_count == 1) {
156 else if (tap_count == 1) {
157 // Retain Oneshot mods 179 // Retain Oneshot mods
158 } 180 #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
159 else { 181 if (mods & get_mods()) {
182 clear_oneshot_locked_mods();
183 clear_oneshot_mods();
184 unregister_mods(mods);
185 }
186 } else if (tap_count == ONESHOT_TAP_TOGGLE) {
187 // Toggle Oneshot Layer
188 #endif
189 } else {
160 clear_oneshot_mods(); 190 clear_oneshot_mods();
161 unregister_mods(mods); 191 unregister_mods(mods);
162 } 192 }
@@ -309,6 +339,44 @@ void process_action(keyrecord_t *record)
309 event.pressed ? layer_move(action.layer_tap.val) : 339 event.pressed ? layer_move(action.layer_tap.val) :
310 layer_clear(); 340 layer_clear();
311 break; 341 break;
342 #ifndef NO_ACTION_ONESHOT
343 case OP_ONESHOT:
344 // Oneshot modifier
345 #if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
346 do_release_oneshot = false;
347 if (event.pressed) {
348 del_mods(get_oneshot_locked_mods());
349 if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
350 reset_oneshot_layer();
351 layer_off(action.layer_tap.val);
352 break;
353 } else if (tap_count < ONESHOT_TAP_TOGGLE) {
354 layer_on(action.layer_tap.val);
355 set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
356 }
357 } else {
358 add_mods(get_oneshot_locked_mods());
359 if (tap_count >= ONESHOT_TAP_TOGGLE) {
360 reset_oneshot_layer();
361 clear_oneshot_locked_mods();
362 set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
363 } else {
364 clear_oneshot_layer_state(ONESHOT_PRESSED);
365 }
366 }
367 #else
368 if (event.pressed) {
369 layer_on(action.layer_tap.val);
370 set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
371 } else {
372 clear_oneshot_layer_state(ONESHOT_PRESSED);
373 if (tap_count > 1) {
374 clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
375 }
376 }
377 #endif
378 break;
379 #endif
312 default: 380 default:
313 /* tap key */ 381 /* tap key */
314 if (event.pressed) { 382 if (event.pressed) {
@@ -372,6 +440,18 @@ void process_action(keyrecord_t *record)
372 default: 440 default:
373 break; 441 break;
374 } 442 }
443
444#ifndef NO_ACTION_ONESHOT
445 /* Because we switch layers after a oneshot event, we need to release the
446 * key before we leave the layer or no key up event will be generated.
447 */
448 if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED ) ) {
449 record->event.pressed = false;
450 layer_on(get_oneshot_layer());
451 process_action(record);
452 layer_off(get_oneshot_layer());
453 }
454#endif
375} 455}
376 456
377 457
@@ -560,6 +640,7 @@ bool is_tap_key(keypos_t key)
560 switch (action.layer_tap.code) { 640 switch (action.layer_tap.code) {
561 case 0x00 ... 0xdf: 641 case 0x00 ... 0xdf:
562 case OP_TAP_TOGGLE: 642 case OP_TAP_TOGGLE:
643 case OP_ONESHOT:
563 return true; 644 return true;
564 } 645 }
565 return false; 646 return false;
diff --git a/tmk_core/common/action_code.h b/tmk_core/common/action_code.h
index 2b0b0b077..ca729aaec 100644
--- a/tmk_core/common/action_code.h
+++ b/tmk_core/common/action_code.h
@@ -76,7 +76,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
76 * 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP] 76 * 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP]
77 * 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP] 77 * 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP]
78 * 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP] 78 * 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP]
79 * 101E|LLLL|1111 xxxx Reserved (0xF4-FF) 79 * 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP]
80 * 101E|LLLL|1111 xxxx Reserved (0xF5-FF)
80 * ELLLL: layer 0-31(E: extra bit for layer 16-31) 81 * ELLLL: layer 0-31(E: extra bit for layer 16-31)
81 * 82 *
82 * 83 *
@@ -250,6 +251,7 @@ enum layer_pram_tap_op {
250 OP_ON_OFF, 251 OP_ON_OFF,
251 OP_OFF_ON, 252 OP_OFF_ON,
252 OP_SET_CLEAR, 253 OP_SET_CLEAR,
254 OP_ONESHOT,
253}; 255};
254#define ACTION_LAYER_BITOP(op, part, bits, on) (ACT_LAYER<<12 | (op)<<10 | (on)<<8 | (part)<<5 | ((bits)&0x1f)) 256#define ACTION_LAYER_BITOP(op, part, bits, on) (ACT_LAYER<<12 | (op)<<10 | (on)<<8 | (part)<<5 | ((bits)&0x1f))
255#define ACTION_LAYER_TAP(layer, key) (ACT_LAYER_TAP<<12 | (layer)<<8 | (key)) 257#define ACTION_LAYER_TAP(layer, key) (ACT_LAYER_TAP<<12 | (layer)<<8 | (key))
@@ -266,6 +268,7 @@ enum layer_pram_tap_op {
266#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF) 268#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF)
267#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON) 269#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON)
268#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR) 270#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR)
271#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT)
269#define ACTION_LAYER_MODS(layer, mods) ACTION_LAYER_TAP((layer), 0xe0 | ((mods)&0x0f)) 272#define ACTION_LAYER_MODS(layer, mods) ACTION_LAYER_TAP((layer), 0xe0 | ((mods)&0x0f))
270/* With Tapping */ 273/* With Tapping */
271#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key)) 274#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))
diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c
index a2d6577b2..61ff202be 100644
--- a/tmk_core/common/action_util.c
+++ b/tmk_core/common/action_util.c
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
18#include "report.h" 18#include "report.h"
19#include "debug.h" 19#include "debug.h"
20#include "action_util.h" 20#include "action_util.h"
21#include "action_layer.h"
21#include "timer.h" 22#include "timer.h"
22 23
23static inline void add_key_byte(uint8_t code); 24static inline void add_key_byte(uint8_t code);
@@ -47,11 +48,70 @@ report_keyboard_t *keyboard_report = &(report_keyboard_t){};
47 48
48#ifndef NO_ACTION_ONESHOT 49#ifndef NO_ACTION_ONESHOT
49static int8_t oneshot_mods = 0; 50static int8_t oneshot_mods = 0;
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; }
50#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) 55#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
51static int16_t oneshot_time = 0; 56static int16_t oneshot_time = 0;
57inline bool has_oneshot_mods_timed_out() {
58 return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT;
59}
52#endif 60#endif
53#endif 61#endif
54 62
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
55 115
56void send_keyboard_report(void) { 116void send_keyboard_report(void) {
57 keyboard_report->mods = real_mods; 117 keyboard_report->mods = real_mods;
@@ -60,7 +120,7 @@ void send_keyboard_report(void) {
60#ifndef NO_ACTION_ONESHOT 120#ifndef NO_ACTION_ONESHOT
61 if (oneshot_mods) { 121 if (oneshot_mods) {
62#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) 122#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
63 if (TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT) { 123 if (has_oneshot_mods_timed_out()) {
64 dprintf("Oneshot: timeout\n"); 124 dprintf("Oneshot: timeout\n");
65 clear_oneshot_mods(); 125 clear_oneshot_mods();
66 } 126 }
@@ -70,6 +130,7 @@ void send_keyboard_report(void) {
70 clear_oneshot_mods(); 130 clear_oneshot_mods();
71 } 131 }
72 } 132 }
133
73#endif 134#endif
74 host_keyboard_send(keyboard_report); 135 host_keyboard_send(keyboard_report);
75} 136}
@@ -143,11 +204,12 @@ void clear_oneshot_mods(void)
143 oneshot_time = 0; 204 oneshot_time = 0;
144#endif 205#endif
145} 206}
207uint8_t get_oneshot_mods(void)
208{
209 return oneshot_mods;
210}
146#endif 211#endif
147 212
148
149
150
151/* 213/*
152 * inspect keyboard state 214 * inspect keyboard state
153 */ 215 */
diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h
index 1a95cec10..dd0c4c2bf 100644
--- a/tmk_core/common/action_util.h
+++ b/tmk_core/common/action_util.h
@@ -56,10 +56,30 @@ void clear_macro_mods(void);
56 56
57/* oneshot modifier */ 57/* oneshot modifier */
58void set_oneshot_mods(uint8_t mods); 58void set_oneshot_mods(uint8_t mods);
59uint8_t get_oneshot_mods(void);
59void clear_oneshot_mods(void); 60void clear_oneshot_mods(void);
60void oneshot_toggle(void); 61void oneshot_toggle(void);
61void oneshot_enable(void); 62void oneshot_enable(void);
62void oneshot_disable(void); 63void oneshot_disable(void);
64bool has_oneshot_mods_timed_out(void);
65
66int8_t get_oneshot_locked_mods(void);
67void set_oneshot_locked_mods(int8_t mods);
68void clear_oneshot_locked_mods(void);
69
70typedef enum {
71 ONESHOT_PRESSED = 0b01,
72 ONESHOT_OTHER_KEY_PRESSED = 0b10,
73 ONESHOT_START = 0b11,
74 ONESHOT_TOGGLED = 0b100
75} oneshot_fullfillment_t;
76void set_oneshot_layer(uint8_t layer, uint8_t state);
77uint8_t get_oneshot_layer(void);
78void clear_oneshot_layer_state(oneshot_fullfillment_t state);
79void reset_oneshot_layer(void);
80bool is_oneshot_layer_active(void);
81uint8_t get_oneshot_layer_state(void);
82bool has_oneshot_layer_timed_out(void);
63 83
64/* inspect */ 84/* inspect */
65uint8_t has_anykey(void); 85uint8_t has_anykey(void);
diff --git a/tmk_core/doc/keymap.md b/tmk_core/doc/keymap.md
index d4a129b20..4d42fbe5c 100644
--- a/tmk_core/doc/keymap.md
+++ b/tmk_core/doc/keymap.md
@@ -528,14 +528,20 @@ This is a feature to assign both toggle layer and momentary switch layer action
528 528
529 529
530### 4.3 Oneshot Modifier 530### 4.3 Oneshot Modifier
531This runs onetime effects which modify only on just one following key. It works as normal modifier key when holding down while oneshot modifier when tapping. 531This runs onetime effects which modify only on just one following key. It works as normal modifier key when holding down while oneshot modifier when tapping. The behavior of oneshot modifiers is similar to the [sticky keys](https://en.wikipedia.org/wiki/StickyKeys) functionality found in most operating systems.
532 532
533 ACTION_MODS_ONESHOT(MOD_LSFT) 533 ACTION_MODS_ONESHOT(MOD_LSFT)
534 534
535Oneshot layer key:
536
537 ACTION_LAYER_ONESHOT(MY_LAYER)
538
535Say you want to type 'The', you have to push and hold Shift key before type 't' then release it before type 'h' and 'e', otherwise you'll get 'THe' or 'the' unintentionally. With Oneshot Modifier you can tap Shift then type 't', 'h' and 'e' normally, you don't need to holding Shift key properly here. This mean you can release Shift before 't' is pressed down. 539Say you want to type 'The', you have to push and hold Shift key before type 't' then release it before type 'h' and 'e', otherwise you'll get 'THe' or 'the' unintentionally. With Oneshot Modifier you can tap Shift then type 't', 'h' and 'e' normally, you don't need to holding Shift key properly here. This mean you can release Shift before 't' is pressed down.
536 540
537Oneshot effect is cancel unless following key is pressed down within `ONESHOT_TIMEOUT` of `config.h`. No timeout when it is `0` or not defined. 541Oneshot effect is cancel unless following key is pressed down within `ONESHOT_TIMEOUT` of `config.h`. No timeout when it is `0` or not defined.
538 542
543Most implementations of sticky keys allow you to lock a modifier by double tapping the modifier. The layer then remains locked untill the modifier is tapped again. To enable this behaviour for oneshot modifiers set `ONESHOT_TAP_TOGGLE` to the number taps required. The feature is disabled if `ONESHOT_TAP_TOGGLE<2` or not defined.
544
539 545
540### 4.4 Tap Toggle Mods 546### 4.4 Tap Toggle Mods
541Similar to layer tap toggle, this works as a momentary modifier when holding, but toggles on with several taps. A single tap will 'unstick' the modifier again. 547Similar to layer tap toggle, this works as a momentary modifier when holding, but toggles on with several taps. A single tap will 'unstick' the modifier again.