@@ -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(); | |||
} |
@@ -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 */ |
@@ -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: |