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