1
0
Fork 0

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:
Thiago Alves 2016-05-05 18:41:37 -07:00 committed by Jack Humbert
parent d4520cd3ac
commit 74e97eefd7
5 changed files with 186 additions and 14 deletions

View file

@ -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;

View file

@ -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))

View file

@ -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
*/

View file

@ -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);