~ruther/qmk_firmware

bfd7fe586297d70f824a402fd476c3daa889fa56 — tmk 12 years ago 66d5dd2
Add oneshot modifier action.
3 files changed, 236 insertions(+), 175 deletions(-)

M common/action.c
M common/action.h
M keyboard/hhkb/keymap.c
M common/action.c => common/action.c +132 -69
@@ 97,6 97,40 @@ static void waiting_buffer_process(void)
{
}

/* Oneshot modifier
 *
 * Problem: Want to capitalize like 'The' but the result tends to be 'THe'.
 * Solution: Oneshot modifier have its effect on only one key coming next.
 *           Tap Shift, then type 't', 'h' and 'e'. Not need to hold Shift key.
 *
 *  Hold:       works as normal modifier.
 *  Tap:        one shot modifier.
 *  2 Tap:      cancel one shot modifier.
 *  5-Tap:      toggles enable/disable oneshot feature.
 */
static struct {
    uint8_t mods;
    uint8_t time;
    bool    ready;
    bool    disabled;
}   oneshot_state;
static void oneshot_start(uint8_t mods, uint16_t time)
{
    oneshot_state.mods = mods;
    oneshot_state.time = time;
    oneshot_state.ready = true;
}
static void oneshot_cancel(void)
{
    oneshot_state.mods = 0;
    oneshot_state.time = 0;
    oneshot_state.ready = false;
}
static void oneshot_toggle(void)
{
    oneshot_state.disabled = !oneshot_state.disabled;
}


/*
 * Rule to judge tap:


@@ 271,83 305,102 @@ static void process(keyrecord_t *record)
    switch (action.kind.id) {
        /* Key and Mods */
        case ACT_LMODS:
            //               |pressed                          |released
            // --------------+---------------------------------+------------
            // key           |down(key)                        |up(key)
            // mods          |add(mods)                        |del(mods)
            // key with mods |add(mods), down(key), unset(mods)|up(key)
            if (event.pressed) {
                uint8_t tmp_mods = host_get_mods();
                if (action.key.mods) {
                    host_add_mods(action.key.mods);
                    host_send_keyboard_report();
                }
                register_code(action.key.code);
                if (action.key.mods && action.key.code) {
                    host_set_mods(tmp_mods);
                    host_send_keyboard_report();
                }
            } else {
                if (action.key.mods && !action.key.code) {
                    host_del_mods(action.key.mods);
                    host_send_keyboard_report();
                }
                unregister_code(action.key.code);
            }
            break;
        case ACT_RMODS:
            //               |pressed                          |released
            // --------------+---------------------------------+------------
            // key           |down(key)                        |up(key)
            // mods          |add(mods)                        |del(mods)
            // key with mods |add(mods), down(key), unset(mods)|up(key)
            if (event.pressed) {
                uint8_t tmp_mods = host_get_mods();
                if (action.key.mods) {
                    host_add_mods(action.key.mods<<4);
                    host_send_keyboard_report();
                }
                register_code(action.key.code);
                if (action.key.mods && action.key.code) {
                    host_set_mods(tmp_mods);
                    host_send_keyboard_report();
                }
            } else {
                if (action.key.mods && !action.key.code) {
                    host_del_mods(action.key.mods<<4);
                    host_send_keyboard_report();
            {
                uint8_t mods = (action.kind.id == ACT_LMODS) ?  action.key.mods :
                                                                action.key.mods<<4;
                if (event.pressed) {
                    uint8_t tmp_mods = host_get_mods();
                    if (mods) {
                        host_add_mods(mods);
                        host_send_keyboard_report();
                    }
                    register_code(action.key.code);
                    if (mods && action.key.code) {
                        host_set_mods(tmp_mods);
                        host_send_keyboard_report();
                    }
                } else {
                    if (mods && !action.key.code) {
                        host_del_mods(mods);
                        host_send_keyboard_report();
                    }
                    unregister_code(action.key.code);
                }
                unregister_code(action.key.code);
            }
            break;
        case ACT_LMODS_TAP:
        case ACT_RMODS_TAP:
            {
                uint8_t tmp_mods = (action.kind.id == ACT_LMODS_TAP) ?  action.key.mods :
                                                                        action.key.mods<<4;
                if (event.pressed) {
                    if (IS_TAPPING_KEY(event.key) && tap_count > 0) {
                        if (waiting_buffer_has_anykey_pressed()) {
                            debug("MODS_TAP: Tap: Cancel: add_mods\n");
                            // ad hoc: set 0 to cancel tap
                            record->tap_count = 0;
                            add_mods(tmp_mods);
                uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ?  action.key.mods :
                                                                    action.key.mods<<4;
                switch (action.layer.code) {
                    case 0x00:
                        // Oneshot modifier
                        if (event.pressed) {
                            if (tap_count == 0) {
                                debug("MODS_TAP: Oneshot: add_mods\n");
                                add_mods(mods);
                            }
                            else if (tap_count == 1) {
                                debug("MODS_TAP: Oneshot: start\n");
                                oneshot_start(mods, event.time);
                            }
                            else if (tap_count == 5) {
                                debug("MODS_TAP: Oneshot: toggle\n");
                                oneshot_toggle();
                            }
                            else {
                                debug("MODS_TAP: Oneshot: cancel&add_mods\n");
                                // double tap cancels oneshot and works as normal modifier.
                                oneshot_cancel();
                                add_mods(mods);
                            }
                        } else {
                            debug("MODS_TAP: Tap: register_code\n");
                            register_code(action.key.code);
                            if (tap_count == 0) {
                                debug("MODS_TAP: Oneshot: cancel/del_mods\n");
                                // cancel oneshot by holding.
                                oneshot_cancel();
                                del_mods(mods);
                            }
                            else if (tap_count == 1) {
                                debug("MODS_TAP: Oneshot: del_mods\n");
                                // retain Oneshot
                                del_mods(mods);
                            }
                            else {
                                debug("MODS_TAP: Oneshot: del_mods\n");
                                // cancel Mods
                                del_mods(mods);
                            }
                        }
                    } else {
                        debug("MODS_TAP: No tap: add_mods\n");
                        add_mods(tmp_mods);
                    }
                } else {
                    if (IS_TAPPING_KEY(event.key) && tap_count > 0) {
                        debug("MODS_TAP: Tap: unregister_code\n");
                        unregister_code(action.key.code);
                    } else {
                        debug("MODS_TAP: No tap: add_mods\n");
                        del_mods(tmp_mods);
                    }
                        break;
                    default:
                        if (event.pressed) {
                            if (tap_count > 0) {
                                if (waiting_buffer_has_anykey_pressed()) {
                                    debug("MODS_TAP: Tap: Cancel: add_mods\n");
                                    // ad hoc: set 0 to cancel tap
                                    record->tap_count = 0;
                                    add_mods(mods);
                                } else {
                                    debug("MODS_TAP: Tap: register_code\n");
                                    register_code(action.key.code);
                                }
                            } else {
                                debug("MODS_TAP: No tap: add_mods\n");
                                add_mods(mods);
                            }
                        } else {
                            if (tap_count > 0) {
                                debug("MODS_TAP: Tap: unregister_code\n");
                                unregister_code(action.key.code);
                            } else {
                                debug("MODS_TAP: No tap: add_mods\n");
                                del_mods(mods);
                            }
                        }
                        break;
                }
            }
            break;


@@ 579,7 632,17 @@ void register_code(uint8_t code)
    }
    else if IS_KEY(code) {
        // TODO: should push command_proc out of this block?
        if (!command_proc(code)) {
        if (command_proc(code)) return;

        if (oneshot_state.mods && oneshot_state.ready && !oneshot_state.disabled) {
            uint8_t tmp_mods = host_get_mods();
            host_add_mods(oneshot_state.mods);
            host_add_key(code);
            host_send_keyboard_report();

            host_set_mods(tmp_mods);
            oneshot_state.ready = false;
        } else {
            host_add_key(code);
            host_send_keyboard_report();
        }

M common/action.h => common/action.h +95 -103
@@ 3,10 3,79 @@

#include "keyboard.h"


/* Action struct.
 *
 * In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15). 
 * AVR looks like a little endian in avr-gcc.
 *
 * TODO: not portable across compiler/endianness?
 * Byte order and bit order of 0x1234:
 * Big endian:     15 ...  8 7 ... 210
 *                |  0x12   |  0x34   |
 *                 0001 0010 0011 0100
 * Little endian:  012 ... 7  8 ... 15
 *                |  0x34   |  0x12   |
 *                 0010 1100 0100 1000
 */
typedef union {
    uint16_t code;
    struct action_kind {
        uint16_t param  :12;
        uint16_t id     :4;
    } kind;
    struct action_key {
        uint16_t code   :8;
        uint16_t mods   :4;
        uint16_t kind   :4;
    } key;
    struct action_layer {
        uint16_t code   :8;
        uint16_t opt    :4;
        uint16_t kind   :4;
    } layer;
    struct action_usage {
        uint16_t code   :10;
        uint16_t page   :2;
        uint16_t kind   :4;
    } usage;
    struct action_command {
        uint16_t id     :8;
        uint16_t option :4;
        uint16_t kind   :4;
    } command;
    struct action_function {
        uint8_t  id     :8;
        uint8_t  opt    :4;
        uint8_t  kind   :4;
    } func;
} action_t;

/* Action record. For internal use. */
typedef struct {
    keyevent_t  event;
    uint8_t     tap_count;
} keyrecord_t;


/* Tap count: Tap is comprised of press and release within TAP_TERM.
 * 0    means no tap.
 * >1   means tap.
 */
extern uint8_t tap_count;

/* current tap key event */
extern keyevent_t tapping_event;


/* action function */
typedef void (*action_func_t)(keyevent_t event, uint8_t opt);

// TODO: legacy keymap support
void action_exec(keyevent_t event);
void action_call_function(keyevent_t event, uint8_t id);


/*
 * Utilities for actions.
 */


@@ 25,33 94,36 @@ bool is_tap_key(key_t key);


/*
Action codes
16bit code: action_kind(4bit) + action_parameter(12bit)

 * Action codes
 * ============
 * 16bit code: action_kind(4bit) + action_parameter(12bit)
 *
Keyboard Keys
-------------
ACT_LMODS(0000):
0000|0000|000000|00    No action
0000|mods|000000|00    Left mods Momentary
0000|mods|000000|01    Left mods OneShot
0000|mods|000000|10    (reserved)
0000|mods|000000|11    (reserved)
0000|0000| keycode     Key
0010|mods|000000|00    Left mods Momentary
0000|mods| keycode     Key+Left mods

ACT_RMODS(0001):
0001|0000|000000|00    No action
0001|0000| keycode     Key(no used)
0001|mods|000000|00    Right mods Momentary
0001|mods|000000|01    Right mods OneShot
0001|mods|000000|10    (reserved)
0001|mods|000000|11    (reserved)
0001|0000| keycode     Key
0001|mods| keycode     Key+Right mods

ACT_LMODS_TAP(0010):
0010|mods|000000|00    Left mods OneShot
0010|mods|000000|01    (reserved)
0010|mods|000000|10    (reserved)
0010|mods|000000|11    (reserved)
0010|mods| keycode     Left mods+tap Key

ACT_RMODS_TAP(0011):
0011|mods|000000|00    Right mods OneShot
0011|mods|000000|01    (reserved)
0011|mods|000000|10    (reserved)
0011|mods|000000|11    (reserved)
0011|mods| keycode     Right mods+tap Key
 



@@ 108,24 180,22 @@ Extensions(11XX)
NOTE: NOT FIXED

ACT_MACRO(1100):
1100|opt | id(8)      Macro play
1100|1111| id(8)      Macro record
1100|opt | id(8)      Macro play?
1100|1111| id(8)      Macro record?

ACT_COMMAND(1110):
1110|opt | id(8)      Built-in Command exec

ACT_FUNCTION(1111):
1111| address(12)     Function
1111|opt | id(8)      Function
                                                    Macro record(dynamicly)
                                                    Macro play(dynamicly)
TODO: modifier + [tap key /w mod]
    : layerkey + [tap key /w mod]
1111| address(12)     Function?
1111|opt | id(8)      Function?

TODO: modifier + function by tap?
    for example: LShift + '('[Shift+9] and RShift + ')'[Shift+0]
    http://deskthority.net/workshop-f7/tmk-keyboard-firmware-collection-t4478.html#p90052
*/
 */

enum action_id {
enum action_kind_id {
    ACT_LMODS           = 0b0000,
    ACT_RMODS           = 0b0001,
    ACT_LMODS_TAP       = 0b0010,


@@ 144,74 214,11 @@ enum action_id {
    ACT_FUNCTION        = 0b1111
};

// TODO: not portable across compiler/endianness?
/*
In avr-gcc bit fields seems to be assigned from LSB(bit0) to MSB(bit15). 
AVR looks like a little endian in avr-gcc.

Byte order and bit order of 0x1234:
Big endian:     15 ...  8 7 ... 210
               |  0x12   |  0x34   |
                0001 0010 0011 0100
Little endian:  012 ... 7  8 ... 15
               |  0x34   |  0x12   |
                0010 1100 0100 1000
*/
typedef union {
    uint16_t code;
    struct action_kind {
        uint16_t param  :12;
        uint16_t id     :4;
    } kind;
    struct action_key {
        uint16_t code   :8;
        uint16_t mods   :4;
        uint16_t kind   :4;
    } key;
    struct action_layer {
        uint16_t code   :8;
        uint16_t opt    :4;
        uint16_t kind   :4;
    } layer;
    struct action_usage {
        uint16_t code   :10;
        uint16_t page   :2;
        uint16_t kind   :4;
    } usage;
    struct action_command {
        uint16_t id     :8;
        uint16_t option :4;
        uint16_t kind   :4;
    } command;
    struct action_function {
        uint8_t  id     :8;
        uint8_t  opt    :4;
        uint8_t  kind   :4;
    } func;
} action_t;


enum stroke_cmd {
    STROKE_DOWN,
    STROKE_UP,
    STROKE_ALLUP, /* release all keys in reverse order */
enum acion_param {
    ONE_SHOT            = 0x00,
};

typedef struct {
    keyevent_t  event;
    uint8_t     tap_count;
} keyrecord_t;

/* action function */
typedef void (*action_func_t)(keyevent_t event, uint8_t opt);


// TODO: legacy keymap support
void action_exec(keyevent_t event);
void action_call_function(keyevent_t event, uint8_t id);


// TODO: proper names
/* action_t utility */
#define ACTION_NO                       0
#define ACTION(kind, param)             ((kind)<<12 | (param))


@@ 221,16 228,12 @@ void action_call_function(keyevent_t event, uint8_t id);
#define ACTION_KEY(key)                 ACTION(ACT_LMODS,    key)
#define ACTION_LMODS(mods)              ACTION(ACT_LMODS,    (mods)<<8 | 0x00)
#define ACTION_LMODS_KEY(mods, key)     ACTION(ACT_LMODS,    (mods)<<8 | (key))
#define ACTION_LMODS_ONESHOT(mods)      ACTION(ACT_LMODS,    (mods)<<8 | 0x01)
#define ACTION_LMODS_SWITCH(mods, tap)  ACTION(ACT_LMODS,    (mods)<<8 | 0xF0 | (tap))
#define ACTION_LMODS_TOGGLE(mods, tap)  ACTION(ACT_LMODS,    (mods)<<8 | 0xF1 | (tap))
#define ACTION_RMODS(mods)              ACTION(ACT_RMODS,    (mods)<<8 | 0x00)
#define ACTION_RMODS_KEY(mods, key)     ACTION(ACT_RMODS,    (mods)<<8 | (key))
#define ACTION_RMODS_ONESHOT(mods)      ACTION(ACT_RMODS,    (mods)<<8 | 0x01)
#define ACTION_RMODS_SWITCH(mods, tap)  ACTION(ACT_RMODS,    (mods)<<8 | 0xF0 | (tap))
#define ACTION_RMODS_TOGGLE(mods, tap)  ACTION(ACT_RMODS,    (mods)<<8 | 0xF1 | (tap))

/* Mods + Tap key */
#define ACTION_LMODS_TAP(mods, key)     ACTION(ACT_LMODS_TAP, MOD_BITS(mods)<<8 | (key))
#define ACTION_LMODS_ONESHOT(mods)      ACTION(ACT_LMODS_TAP, MOD_BITS(mods)<<8 | ONE_SHOT)
#define ACTION_RMODS_TAP(mods, key)     ACTION(ACT_RMODS_TAP, MOD_BITS(mods)<<8 | (key))

/* Layer Switch */


@@ 268,15 271,4 @@ void action_call_function(keyevent_t event, uint8_t id);
/* Function */
#define ACTION_FUNCTION(id, opt)        ACTION(ACT_FUNCTION, (opt)<<8 | id)


/* helpers for readability */
#define LAYER(layer)    (layer)
#define TAP(tap)        (tap)
#define DOUBLE_TAP      2
#define TRIPLE_TAP      3
#define QUADRUPLE_TAP   4
#define QUINTUPLE_TAP   5
#define DOWN(key)       (key)
#define UP(key)         STROKE_UP, (key)

#endif  /* ACTION_H */

M keyboard/hhkb/keymap.c => keyboard/hhkb/keymap.c +9 -3
@@ 61,6 61,8 @@ static const uint16_t PROGMEM fn_actions[] = {
    ACTION_LAYER_SET_TAP_KEY(5, KC_SPC),    // Fn5
    ACTION_LMODS_TAP(MOD_BIT(KC_LCTL), KC_BSPC), // Fn6
    ACTION_RMODS_TAP(MOD_BIT(KC_RCTL), KC_ENT), // Fn7

    ACTION_LMODS_TAP(MOD_BIT(KC_LSFT), ONE_SHOT),   // Fn8
};




@@ 81,7 83,7 @@ static const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    KEYMAP(ESC, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, BSLS,GRV, \
           TAB, Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC, \
           FN6, A,   S,   D,   F,   G,   H,   J,   K,   L,   FN3, QUOT,FN7, \
           LSFT,Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, FN2, RSFT,FN1, \
           FN8, Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, FN2, RSFT,FN1, \
                LGUI,LALT,          FN5,                RALT,FN4),

    /* Layer 1: HHKB mode (HHKB Fn)


@@ 205,8 207,12 @@ action_t keymap_get_action(uint8_t layer, uint8_t row, uint8_t col) {
            action.code = ACTION_RMODS(MOD_BIT(key)>>4);
            break;
*/
        case KC_FN0 ... KC_FN7:
            action.code = pgm_read_word(&fn_actions[FN_INDEX(key)]);
        case KC_FN0 ... FN_MAX:
            if (FN_INDEX(key) < sizeof(fn_actions) / sizeof(fn_actions[0])) {
                action.code = pgm_read_word(&fn_actions[FN_INDEX(key)]);
            } else {
                action.code = ACTION_NO;
            }
            break;
        case KC_NO ... KC_UNDEFINED:
        default: