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.
This commit is contained in:
parent
d4520cd3ac
commit
74e97eefd7
5 changed files with 186 additions and 14 deletions
|
@ -74,6 +74,7 @@ void process_action_kb(keyrecord_t *record) {}
|
|||
|
||||
void process_action(keyrecord_t *record)
|
||||
{
|
||||
bool do_release_oneshot = false;
|
||||
keyevent_t event = record->event;
|
||||
#ifndef NO_ACTION_TAPPING
|
||||
uint8_t tap_count = record->tap.count;
|
||||
|
@ -81,6 +82,13 @@ void process_action(keyrecord_t *record)
|
|||
|
||||
if (IS_NOEVENT(event)) { return; }
|
||||
|
||||
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
|
||||
if (has_oneshot_layer_timed_out()) {
|
||||
dprintf("Oneshot layer: timeout\n");
|
||||
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
|
||||
}
|
||||
#endif
|
||||
|
||||
process_action_kb(record);
|
||||
|
||||
action_t action = store_or_get_action(event.pressed, event.key);
|
||||
|
@ -95,6 +103,15 @@ void process_action(keyrecord_t *record)
|
|||
// clear the potential weak mods left by previously pressed keys
|
||||
clear_weak_mods();
|
||||
}
|
||||
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
// notice we only clear the one shot layer if the pressed key is not a modifier.
|
||||
if (is_oneshot_layer_active() && event.pressed && !IS_MOD(action.key.code)) {
|
||||
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
|
||||
do_release_oneshot = !is_oneshot_layer_active();
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (action.kind.id) {
|
||||
/* Key and Mods */
|
||||
case ACT_LMODS:
|
||||
|
@ -139,24 +156,37 @@ void process_action(keyrecord_t *record)
|
|||
// Oneshot modifier
|
||||
if (event.pressed) {
|
||||
if (tap_count == 0) {
|
||||
dprint("MODS_TAP: Oneshot: 0\n");
|
||||
register_mods(mods);
|
||||
}
|
||||
else if (tap_count == 1) {
|
||||
} else if (tap_count == 1) {
|
||||
dprint("MODS_TAP: Oneshot: start\n");
|
||||
set_oneshot_mods(mods);
|
||||
}
|
||||
else {
|
||||
#if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
|
||||
} else if (tap_count == ONESHOT_TAP_TOGGLE) {
|
||||
dprint("MODS_TAP: Toggling oneshot");
|
||||
clear_oneshot_mods();
|
||||
set_oneshot_locked_mods(mods);
|
||||
register_mods(mods);
|
||||
#endif
|
||||
} else {
|
||||
register_mods(mods);
|
||||
}
|
||||
} else {
|
||||
if (tap_count == 0) {
|
||||
clear_oneshot_mods();
|
||||
unregister_mods(mods);
|
||||
}
|
||||
else if (tap_count == 1) {
|
||||
} else if (tap_count == 1) {
|
||||
// Retain Oneshot mods
|
||||
}
|
||||
else {
|
||||
#if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
|
||||
if (mods & get_mods()) {
|
||||
clear_oneshot_locked_mods();
|
||||
clear_oneshot_mods();
|
||||
unregister_mods(mods);
|
||||
}
|
||||
} else if (tap_count == ONESHOT_TAP_TOGGLE) {
|
||||
// Toggle Oneshot Layer
|
||||
#endif
|
||||
} else {
|
||||
clear_oneshot_mods();
|
||||
unregister_mods(mods);
|
||||
}
|
||||
|
@ -309,6 +339,44 @@ void process_action(keyrecord_t *record)
|
|||
event.pressed ? layer_move(action.layer_tap.val) :
|
||||
layer_clear();
|
||||
break;
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
case OP_ONESHOT:
|
||||
// Oneshot modifier
|
||||
#if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
|
||||
do_release_oneshot = false;
|
||||
if (event.pressed) {
|
||||
del_mods(get_oneshot_locked_mods());
|
||||
if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
|
||||
reset_oneshot_layer();
|
||||
layer_off(action.layer_tap.val);
|
||||
break;
|
||||
} else if (tap_count < ONESHOT_TAP_TOGGLE) {
|
||||
layer_on(action.layer_tap.val);
|
||||
set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
|
||||
}
|
||||
} else {
|
||||
add_mods(get_oneshot_locked_mods());
|
||||
if (tap_count >= ONESHOT_TAP_TOGGLE) {
|
||||
reset_oneshot_layer();
|
||||
clear_oneshot_locked_mods();
|
||||
set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
|
||||
} else {
|
||||
clear_oneshot_layer_state(ONESHOT_PRESSED);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (event.pressed) {
|
||||
layer_on(action.layer_tap.val);
|
||||
set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
|
||||
} else {
|
||||
clear_oneshot_layer_state(ONESHOT_PRESSED);
|
||||
if (tap_count > 1) {
|
||||
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* tap key */
|
||||
if (event.pressed) {
|
||||
|
@ -372,6 +440,18 @@ void process_action(keyrecord_t *record)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
/* Because we switch layers after a oneshot event, we need to release the
|
||||
* key before we leave the layer or no key up event will be generated.
|
||||
*/
|
||||
if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED ) ) {
|
||||
record->event.pressed = false;
|
||||
layer_on(get_oneshot_layer());
|
||||
process_action(record);
|
||||
layer_off(get_oneshot_layer());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -560,6 +640,7 @@ bool is_tap_key(keypos_t key)
|
|||
switch (action.layer_tap.code) {
|
||||
case 0x00 ... 0xdf:
|
||||
case OP_TAP_TOGGLE:
|
||||
case OP_ONESHOT:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -76,7 +76,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
* 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP]
|
||||
* 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP]
|
||||
* 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP]
|
||||
* 101E|LLLL|1111 xxxx Reserved (0xF4-FF)
|
||||
* 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP]
|
||||
* 101E|LLLL|1111 xxxx Reserved (0xF5-FF)
|
||||
* ELLLL: layer 0-31(E: extra bit for layer 16-31)
|
||||
*
|
||||
*
|
||||
|
@ -250,6 +251,7 @@ enum layer_pram_tap_op {
|
|||
OP_ON_OFF,
|
||||
OP_OFF_ON,
|
||||
OP_SET_CLEAR,
|
||||
OP_ONESHOT,
|
||||
};
|
||||
#define ACTION_LAYER_BITOP(op, part, bits, on) (ACT_LAYER<<12 | (op)<<10 | (on)<<8 | (part)<<5 | ((bits)&0x1f))
|
||||
#define ACTION_LAYER_TAP(layer, key) (ACT_LAYER_TAP<<12 | (layer)<<8 | (key))
|
||||
|
@ -266,6 +268,7 @@ enum layer_pram_tap_op {
|
|||
#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF)
|
||||
#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON)
|
||||
#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR)
|
||||
#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT)
|
||||
#define ACTION_LAYER_MODS(layer, mods) ACTION_LAYER_TAP((layer), 0xe0 | ((mods)&0x0f))
|
||||
/* With Tapping */
|
||||
#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))
|
||||
|
|
|
@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "report.h"
|
||||
#include "debug.h"
|
||||
#include "action_util.h"
|
||||
#include "action_layer.h"
|
||||
#include "timer.h"
|
||||
|
||||
static inline void add_key_byte(uint8_t code);
|
||||
|
@ -47,11 +48,70 @@ report_keyboard_t *keyboard_report = &(report_keyboard_t){};
|
|||
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
static int8_t oneshot_mods = 0;
|
||||
static int8_t oneshot_locked_mods = 0;
|
||||
int8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; }
|
||||
void set_oneshot_locked_mods(int8_t mods) { oneshot_locked_mods = mods; }
|
||||
void clear_oneshot_locked_mods(void) { oneshot_locked_mods = 0; }
|
||||
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
|
||||
static int16_t oneshot_time = 0;
|
||||
inline bool has_oneshot_mods_timed_out() {
|
||||
return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* oneshot layer */
|
||||
#ifndef NO_ACTION_ONESHOT
|
||||
/* oneshot_layer_data bits
|
||||
* LLLL LSSS
|
||||
* where:
|
||||
* L => are layer bits
|
||||
* S => oneshot state bits
|
||||
*/
|
||||
static int8_t oneshot_layer_data = 0;
|
||||
|
||||
inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; }
|
||||
inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; }
|
||||
|
||||
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
|
||||
static int16_t oneshot_layer_time = 0;
|
||||
inline bool has_oneshot_layer_timed_out() {
|
||||
return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT &&
|
||||
!(get_oneshot_layer_state() & ONESHOT_TOGGLED);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Oneshot layer */
|
||||
void set_oneshot_layer(uint8_t layer, uint8_t state)
|
||||
{
|
||||
oneshot_layer_data = layer << 3 | state;
|
||||
layer_on(layer);
|
||||
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
|
||||
oneshot_layer_time = timer_read();
|
||||
#endif
|
||||
}
|
||||
void reset_oneshot_layer(void) {
|
||||
oneshot_layer_data = 0;
|
||||
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
|
||||
oneshot_layer_time = 0;
|
||||
#endif
|
||||
}
|
||||
void clear_oneshot_layer_state(oneshot_fullfillment_t state)
|
||||
{
|
||||
uint8_t start_state = oneshot_layer_data;
|
||||
oneshot_layer_data &= ~state;
|
||||
if (!get_oneshot_layer_state() && start_state != oneshot_layer_data) {
|
||||
layer_off(get_oneshot_layer());
|
||||
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
|
||||
oneshot_layer_time = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
bool is_oneshot_layer_active(void)
|
||||
{
|
||||
return get_oneshot_layer_state();
|
||||
}
|
||||
#endif
|
||||
|
||||
void send_keyboard_report(void) {
|
||||
keyboard_report->mods = real_mods;
|
||||
|
@ -60,7 +120,7 @@ void send_keyboard_report(void) {
|
|||
#ifndef NO_ACTION_ONESHOT
|
||||
if (oneshot_mods) {
|
||||
#if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
|
||||
if (TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT) {
|
||||
if (has_oneshot_mods_timed_out()) {
|
||||
dprintf("Oneshot: timeout\n");
|
||||
clear_oneshot_mods();
|
||||
}
|
||||
|
@ -70,6 +130,7 @@ void send_keyboard_report(void) {
|
|||
clear_oneshot_mods();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
host_keyboard_send(keyboard_report);
|
||||
}
|
||||
|
@ -143,11 +204,12 @@ void clear_oneshot_mods(void)
|
|||
oneshot_time = 0;
|
||||
#endif
|
||||
}
|
||||
uint8_t get_oneshot_mods(void)
|
||||
{
|
||||
return oneshot_mods;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* inspect keyboard state
|
||||
*/
|
||||
|
|
|
@ -56,10 +56,30 @@ void clear_macro_mods(void);
|
|||
|
||||
/* oneshot modifier */
|
||||
void set_oneshot_mods(uint8_t mods);
|
||||
uint8_t get_oneshot_mods(void);
|
||||
void clear_oneshot_mods(void);
|
||||
void oneshot_toggle(void);
|
||||
void oneshot_enable(void);
|
||||
void oneshot_disable(void);
|
||||
bool has_oneshot_mods_timed_out(void);
|
||||
|
||||
int8_t get_oneshot_locked_mods(void);
|
||||
void set_oneshot_locked_mods(int8_t mods);
|
||||
void clear_oneshot_locked_mods(void);
|
||||
|
||||
typedef enum {
|
||||
ONESHOT_PRESSED = 0b01,
|
||||
ONESHOT_OTHER_KEY_PRESSED = 0b10,
|
||||
ONESHOT_START = 0b11,
|
||||
ONESHOT_TOGGLED = 0b100
|
||||
} oneshot_fullfillment_t;
|
||||
void set_oneshot_layer(uint8_t layer, uint8_t state);
|
||||
uint8_t get_oneshot_layer(void);
|
||||
void clear_oneshot_layer_state(oneshot_fullfillment_t state);
|
||||
void reset_oneshot_layer(void);
|
||||
bool is_oneshot_layer_active(void);
|
||||
uint8_t get_oneshot_layer_state(void);
|
||||
bool has_oneshot_layer_timed_out(void);
|
||||
|
||||
/* inspect */
|
||||
uint8_t has_anykey(void);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue