From 2bbf3d5820734eabbcf47c3072be6fdcaa9e36cc Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Thu, 28 Apr 2016 23:23:33 -0400 Subject: stops forcing debug_action --- tmk_core/common/action.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tmk_core/common/action.c') diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index 0162fbd63..f9e6c17dc 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -27,11 +27,11 @@ along with this program. If not, see . #include "action_util.h" #include "action.h" -//#ifdef DEBUG_ACTION +#ifdef DEBUG_ACTION #include "debug.h" -//#else -//#include "nodebug.h" -//#endif +#else +#include "nodebug.h" +#endif void action_exec(keyevent_t event) -- cgit v1.3.1 From 74e97eefd7ae76f9ddcb76890a30aa9038804cdb Mon Sep 17 00:00:00 2001 From: Thiago Alves Date: Thu, 5 May 2016 18:41:37 -0700 Subject: 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. --- tmk_core/common/action.c | 97 +++++++++++++++++++++++++++++++++++++++---- tmk_core/common/action_code.h | 5 ++- tmk_core/common/action_util.c | 70 +++++++++++++++++++++++++++++-- tmk_core/common/action_util.h | 20 +++++++++ tmk_core/doc/keymap.md | 8 +++- 5 files changed, 186 insertions(+), 14 deletions(-) (limited to 'tmk_core/common/action.c') diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index f9e6c17dc..081e90b2d 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -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; diff --git a/tmk_core/common/action_code.h b/tmk_core/common/action_code.h index 2b0b0b077..ca729aaec 100644 --- a/tmk_core/common/action_code.h +++ b/tmk_core/common/action_code.h @@ -76,7 +76,8 @@ along with this program. If not, see . * 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)) diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c index a2d6577b2..61ff202be 100644 --- a/tmk_core/common/action_util.c +++ b/tmk_core/common/action_util.c @@ -18,6 +18,7 @@ along with this program. If not, see . #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 */ diff --git a/tmk_core/common/action_util.h b/tmk_core/common/action_util.h index 1a95cec10..dd0c4c2bf 100644 --- a/tmk_core/common/action_util.h +++ b/tmk_core/common/action_util.h @@ -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); diff --git a/tmk_core/doc/keymap.md b/tmk_core/doc/keymap.md index d4a129b20..4d42fbe5c 100644 --- a/tmk_core/doc/keymap.md +++ b/tmk_core/doc/keymap.md @@ -528,14 +528,20 @@ This is a feature to assign both toggle layer and momentary switch layer action ### 4.3 Oneshot Modifier -This runs onetime effects which modify only on just one following key. It works as normal modifier key when holding down while oneshot modifier when tapping. +This runs onetime effects which modify only on just one following key. It works as normal modifier key when holding down while oneshot modifier when tapping. The behavior of oneshot modifiers is similar to the [sticky keys](https://en.wikipedia.org/wiki/StickyKeys) functionality found in most operating systems. ACTION_MODS_ONESHOT(MOD_LSFT) +Oneshot layer key: + + ACTION_LAYER_ONESHOT(MY_LAYER) + Say you want to type 'The', you have to push and hold Shift key before type 't' then release it before type 'h' and 'e', otherwise you'll get 'THe' or 'the' unintentionally. With Oneshot Modifier you can tap Shift then type 't', 'h' and 'e' normally, you don't need to holding Shift key properly here. This mean you can release Shift before 't' is pressed down. Oneshot effect is cancel unless following key is pressed down within `ONESHOT_TIMEOUT` of `config.h`. No timeout when it is `0` or not defined. +Most implementations of sticky keys allow you to lock a modifier by double tapping the modifier. The layer then remains locked untill the modifier is tapped again. To enable this behaviour for oneshot modifiers set `ONESHOT_TAP_TOGGLE` to the number taps required. The feature is disabled if `ONESHOT_TAP_TOGGLE<2` or not defined. + ### 4.4 Tap Toggle Mods Similar to layer tap toggle, this works as a momentary modifier when holding, but toggles on with several taps. A single tap will 'unstick' the modifier again. -- cgit v1.3.1 From 1a8c0dd22d6a2255511d0db6a456315541b5815b Mon Sep 17 00:00:00 2001 From: Erez Zukerman Date: Sun, 15 May 2016 00:27:32 -0400 Subject: Leader key implementation (#326) * implements leader key for planck experimental * allows override of leader timeout * adds ability to use the leader key in seq * fixes leader keycode * adds chording prototype * fixes keycode detection * moves music mode to quantum.c * disables chording by default * updates process_action functions to return bool --- keyboard/atomic/atomic.c | 7 +- keyboard/atomic/atomic.h | 2 +- keyboard/gh60_rev_c/gh60.c | 7 +- keyboard/gh60_rev_c/gh60.h | 2 +- keyboard/planck/keymaps/experimental/keymap.c | 48 +++++--- keyboard/planck/planck.c | 8 +- keyboard/planck/planck.h | 16 +-- keyboard/preonic/preonic.c | 8 +- keyboard/preonic/preonic.h | 2 +- quantum/keymap_common.c | 2 +- quantum/keymap_common.h | 4 +- quantum/matrix.c | 8 +- quantum/quantum.c | 167 ++++++++++++++++++++++++++ quantum/quantum.h | 48 ++++++++ quantum/quantum.mk | 3 +- quantum/template/template.c | 7 +- quantum/template/template.h | 2 +- tmk_core/common/action.c | 7 +- tmk_core/common/action.h | 2 +- tmk_core/common/matrix.h | 4 +- 20 files changed, 290 insertions(+), 64 deletions(-) create mode 100644 quantum/quantum.c create mode 100644 quantum/quantum.h (limited to 'tmk_core/common/action.c') diff --git a/keyboard/atomic/atomic.c b/keyboard/atomic/atomic.c index b4b261457..fa218a48f 100644 --- a/keyboard/atomic/atomic.c +++ b/keyboard/atomic/atomic.c @@ -11,8 +11,9 @@ void matrix_scan_user(void) { } __attribute__ ((weak)) -void process_action_user(keyrecord_t *record) { +bool process_action_user(keyrecord_t *record) { // leave this function blank - it can be defined in a keymap file + return true; } __attribute__ ((weak)) @@ -45,11 +46,11 @@ void matrix_scan_kb(void) { matrix_scan_user(); } -void process_action_kb(keyrecord_t *record) { +bool process_action_kb(keyrecord_t *record) { // put your per-action keyboard code here // runs for every action, just before processing by the firmware - process_action_user(record); + return process_action_user(record); } void led_set_kb(uint8_t usb_led) { diff --git a/keyboard/atomic/atomic.h b/keyboard/atomic/atomic.h index 845a9043e..2d6b4c6cb 100644 --- a/keyboard/atomic/atomic.h +++ b/keyboard/atomic/atomic.h @@ -29,7 +29,7 @@ void matrix_init_user(void); void matrix_scan_user(void); -void process_action_user(keyrecord_t *record); +bool process_action_user(keyrecord_t *record); void led_set_user(uint8_t usb_led); void backlight_init_ports(void); diff --git a/keyboard/gh60_rev_c/gh60.c b/keyboard/gh60_rev_c/gh60.c index 8e7219bfe..6da4d8ee3 100644 --- a/keyboard/gh60_rev_c/gh60.c +++ b/keyboard/gh60_rev_c/gh60.c @@ -12,8 +12,9 @@ void matrix_scan_user(void) { } __attribute__ ((weak)) -void process_action_user(keyrecord_t *record) { +bool process_action_user(keyrecord_t *record) { // leave this function blank - it can be defined in a keymap file + return true; } __attribute__ ((weak)) @@ -35,11 +36,11 @@ void matrix_scan_kb(void) { matrix_scan_user(); } -void process_action_kb(keyrecord_t *record) { +bool process_action_kb(keyrecord_t *record) { // put your per-action keyboard code here // runs for every action, just before processing by the firmware - process_action_user(record); + return process_action_user(record); } void led_set_kb(uint8_t usb_led) { diff --git a/keyboard/gh60_rev_c/gh60.h b/keyboard/gh60_rev_c/gh60.h index 2373ad333..95e5e1ebc 100644 --- a/keyboard/gh60_rev_c/gh60.h +++ b/keyboard/gh60_rev_c/gh60.h @@ -75,7 +75,7 @@ inline void gh60_wasd_leds_off(void) { DDRF &= ~(1<<7); PORTF &= ~(1<<7); } void matrix_init_user(void); void matrix_scan_user(void); -void process_action_user(keyrecord_t *record); +bool process_action_user(keyrecord_t *record); void led_set_user(uint8_t usb_led); #endif diff --git a/keyboard/planck/keymaps/experimental/keymap.c b/keyboard/planck/keymaps/experimental/keymap.c index fc3ac4a97..8dc158c73 100644 --- a/keyboard/planck/keymaps/experimental/keymap.c +++ b/keyboard/planck/keymaps/experimental/keymap.c @@ -6,6 +6,7 @@ #ifdef AUDIO_ENABLE #include "audio.h" #endif + #include "eeconfig.h" extern keymap_config_t keymap_config; @@ -78,7 +79,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { {KC_TAB, KC_Q, KC_W, KC_F, KC_P, KC_G, KC_J, KC_L, KC_U, KC_Y, KC_SCLN, KC_BSPC}, {KC_ESC, KC_A, KC_R, KC_S, KC_T, KC_D, KC_H, KC_N, KC_E, KC_I, KC_O, KC_QUOT}, {KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_K, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT }, - {M(M_BL), KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT} + {KC_LEAD, KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT} }, /* Dvorak @@ -291,7 +292,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) case 8: if (record->event.pressed) { #ifdef AUDIO_ENABLE - layer_off(_MUSIC); + music_activated = false; stop_all_notes(); #endif } @@ -300,7 +301,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) if (record->event.pressed) { #ifdef AUDIO_ENABLE PLAY_NOTE_ARRAY(music_scale, false, 0); - layer_on(_MUSIC); + music_activated = true; #endif } break; @@ -360,24 +361,35 @@ void matrix_init_user(void) { } #ifdef AUDIO_ENABLE -void play_goodbye_tone() -{ - PLAY_NOTE_ARRAY(goodbye, false, 0); - _delay_ms(150); -} + void play_goodbye_tone(void) + { + PLAY_NOTE_ARRAY(goodbye, false, 0); + _delay_ms(150); + } +#endif -uint8_t starting_note = 0x0C; -int offset = 0; +LEADER_EXTERNS(); -void process_action_user(keyrecord_t *record) { +#define LEADER_TIMEOUT 300 - if (IS_LAYER_ON(_MUSIC)) { - if (record->event.pressed) { - play_note(((double)220.0)*pow(2.0, -4.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row)), 0xF); - } else { - stop_note(((double)220.0)*pow(2.0, -4.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row))); +void matrix_scan_user(void) { + LEADER_DICTIONARY() { + leading = false; + leader_end(); + + SEQ_ONE_KEY(KC_F) { + register_code(KC_S); + unregister_code(KC_S); + } + SEQ_TWO_KEYS(KC_A, KC_S) { + register_code(KC_H); + unregister_code(KC_H); + } + SEQ_THREE_KEYS(KC_A, KC_S, KC_D) { + register_code(KC_LGUI); + register_code(KC_S); + unregister_code(KC_S); + unregister_code(KC_LGUI); } } - } -#endif diff --git a/keyboard/planck/planck.c b/keyboard/planck/planck.c index 446353dbf..da7b3a170 100644 --- a/keyboard/planck/planck.c +++ b/keyboard/planck/planck.c @@ -7,7 +7,9 @@ __attribute__ ((weak)) void matrix_scan_user(void) {} __attribute__ ((weak)) -void process_action_user(keyrecord_t *record) {} +bool process_action_user(keyrecord_t *record) { + return true; +} __attribute__ ((weak)) void led_set_user(uint8_t usb_led) {} @@ -32,8 +34,8 @@ void matrix_scan_kb(void) { matrix_scan_user(); } -void process_action_kb(keyrecord_t *record) { - process_action_user(record); +bool process_action_kb(keyrecord_t *record) { + return process_action_user(record); } void led_set_kb(uint8_t usb_led) { diff --git a/keyboard/planck/planck.h b/keyboard/planck/planck.h index cfd4956bf..8aec6b262 100644 --- a/keyboard/planck/planck.h +++ b/keyboard/planck/planck.h @@ -1,19 +1,7 @@ #ifndef PLANCK_H #define PLANCK_H -#include "matrix.h" -#include "keymap_common.h" -#ifdef BACKLIGHT_ENABLE - #include "backlight.h" -#endif -#ifdef RGBLIGHT_ENABLE - #include "rgblight.h" -#endif -#include -#include -#ifdef MIDI_ENABLE - #include -#endif +#include "quantum.h" #define PLANCK_MIT( \ k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \ @@ -43,7 +31,7 @@ void matrix_init_user(void); void matrix_scan_user(void); -void process_action_user(keyrecord_t *record); +bool process_action_user(keyrecord_t *record); void led_set_user(uint8_t usb_led); void backlight_init_ports(void); diff --git a/keyboard/preonic/preonic.c b/keyboard/preonic/preonic.c index 211f8d029..13e05c65a 100644 --- a/keyboard/preonic/preonic.c +++ b/keyboard/preonic/preonic.c @@ -11,8 +11,8 @@ void matrix_scan_user(void) { }; __attribute__ ((weak)) -void process_action_user(keyrecord_t *record) { - +bool process_action_user(keyrecord_t *record) { + return true; }; void matrix_init_kb(void) { @@ -36,8 +36,8 @@ void matrix_scan_kb(void) { matrix_scan_user(); }; -void process_action_kb(keyrecord_t *record) { - process_action_user(record); +bool process_action_kb(keyrecord_t *record) { + return process_action_user(record); } #ifdef BACKLIGHT_ENABLE diff --git a/keyboard/preonic/preonic.h b/keyboard/preonic/preonic.h index 030acdadb..2406a11d7 100644 --- a/keyboard/preonic/preonic.h +++ b/keyboard/preonic/preonic.h @@ -47,6 +47,6 @@ void matrix_init_user(void); void matrix_scan_user(void); -void process_action_kb(keyrecord_t *record); +bool process_action_kb(keyrecord_t *record); #endif diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c index 8f00f9cc3..0184770c4 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -251,7 +251,7 @@ static action_t keycode_to_action(uint16_t keycode) } eeconfig_update_keymap(keymap_config.raw); break; - case 0x5100 ... 0x5FFF: ; + case 0x5100 ... 0x56FF: ; // Layer movement shortcuts // See .h to see constraints/usage int type = (keycode >> 0x8) & 0xF; diff --git a/quantum/keymap_common.h b/quantum/keymap_common.h index 322fda498..2ad1ba6c6 100644 --- a/quantum/keymap_common.h +++ b/quantum/keymap_common.h @@ -159,7 +159,7 @@ extern const uint16_t fn_actions[]; #define S(kc) LSFT(kc) #define F(kc) FUNC(kc) -#define M(kc) kc | 0x3000 +#define M(kc) (kc | 0x3000) #define MACRODOWN(...) (record->event.pressed ? MACRO(__VA_ARGS__) : MACRO_NONE) @@ -191,6 +191,8 @@ extern const uint16_t fn_actions[]; #define RESET 0x5000 #define DEBUG 0x5001 +#define KC_LEAD 0x5014 + // MAGIC keycodes #define MAGIC_SWAP_CONTROL_CAPSLOCK 0x5002 diff --git a/quantum/matrix.c b/quantum/matrix.c index 7d70f728d..cab39e117 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c @@ -55,12 +55,12 @@ static void unselect_rows(void); static void select_row(uint8_t row); __attribute__ ((weak)) -void matrix_init_kb(void) { +void matrix_init_quantum(void) { } __attribute__ ((weak)) -void matrix_scan_kb(void) { +void matrix_scan_quantum(void) { } @@ -93,7 +93,7 @@ void matrix_init(void) matrix_debouncing[i] = 0; } - matrix_init_kb(); + matrix_init_quantum(); } @@ -157,7 +157,7 @@ uint8_t matrix_scan(void) } #endif - matrix_scan_kb(); + matrix_scan_quantum(); return 1; } diff --git a/quantum/quantum.c b/quantum/quantum.c new file mode 100644 index 000000000..e274d846f --- /dev/null +++ b/quantum/quantum.c @@ -0,0 +1,167 @@ +#include "quantum.h" + +__attribute__ ((weak)) +void matrix_init_kb(void) {} + +__attribute__ ((weak)) +void matrix_scan_kb(void) {} + +__attribute__ ((weak)) +bool process_action_kb(keyrecord_t *record) { + return true; +} + +__attribute__ ((weak)) +void leader_start(void) {} + +__attribute__ ((weak)) +void leader_end(void) {} + +#ifdef AUDIO_ENABLE + uint8_t starting_note = 0x0C; + int offset = 0; + bool music_activated = false; +#endif + +// Leader key stuff +bool leading = false; +uint16_t leader_time = 0; + +uint16_t leader_sequence[3] = {0, 0, 0}; +uint8_t leader_sequence_size = 0; + +// Chording stuff +#define CHORDING_MAX 4 +bool chording = false; + +uint8_t chord_keys[CHORDING_MAX] = {0}; +uint8_t chord_key_count = 0; +uint8_t chord_key_down = 0; + +bool keys_chord(uint8_t keys[]) { + uint8_t keys_size = sizeof(keys)/sizeof(keys[0]); + bool pass = true; + uint8_t in = 0; + for (uint8_t i = 0; i < chord_key_count; i++) { + bool found = false; + for (uint8_t j = 0; j < keys_size; j++) { + if (chord_keys[i] == (keys[j] & 0xFF)) { + in++; // detects key in chord + found = true; + break; + } + } + if (found) + continue; + if (chord_keys[i] != 0) { + pass = false; // makes sure rest are blank + } + } + return (pass && (in == keys_size)); +} + +bool process_action_quantum(keyrecord_t *record) { + + /* This gets the keycode from the key pressed */ + keypos_t key = record->event.key; + uint16_t keycode; + + #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) + uint8_t layer; + + if (record->event.pressed) { + layer = layer_switch_get_layer(key); + update_source_layers_cache(key, layer); + } else { + layer = read_source_layers_cache(key); + } + keycode = keymap_key_to_keycode(layer, key); + #else + keycode = keymap_key_to_keycode(layer_switch_get_layer(key), key); + #endif + + #ifdef AUDIO_ENABLE + if (music_activated) { + if (record->event.pressed) { + play_note(((double)220.0)*pow(2.0, -4.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row)), 0xF); + } else { + stop_note(((double)220.0)*pow(2.0, -4.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row))); + } + if (keycode < 0xFF) // ignores all normal keycodes, but lets RAISE, LOWER, etc through + return false; + } + #endif + + + +#ifndef DISABLE_LEADER + // Leader key set-up + if (record->event.pressed) { + if (!leading && keycode == KC_LEAD) { + leader_start(); + leading = true; + leader_time = timer_read(); + leader_sequence_size = 0; + leader_sequence[0] = 0; + leader_sequence[1] = 0; + leader_sequence[2] = 0; + return false; + } + if (leading && timer_elapsed(leader_time) < LEADER_TIMEOUT) { + leader_sequence[leader_sequence_size] = keycode; + leader_sequence_size++; + return false; + } + } +#endif + +#define DISABLE_CHORDING +#ifndef DISABLE_CHORDING + + if (keycode >= 0x5700 && keycode <= 0x57FF) { + if (record->event.pressed) { + if (!chording) { + chording = true; + for (uint8_t i = 0; i < CHORDING_MAX; i++) + chord_keys[i] = 0; + chord_key_count = 0; + chord_key_down = 0; + } + chord_keys[chord_key_count] = (keycode & 0xFF); + chord_key_count++; + chord_key_down++; + return false; + } else { + if (chording) { + chord_key_down--; + if (chord_key_down == 0) { + chording = false; + // Chord Dictionary + if (keys_chord((uint8_t[]){KC_ENTER, KC_SPACE})) { + register_code(KC_A); + unregister_code(KC_A); + return false; + } + for (uint8_t i = 0; i < chord_key_count; i++) { + register_code(chord_keys[i]); + unregister_code(chord_keys[i]); + return false; + } + } + } + } + } + +#endif + + + return process_action_kb(record); +} + +void matrix_init_quantum() { + matrix_init_kb(); +} + +void matrix_scan_quantum() { + matrix_scan_kb(); +} \ No newline at end of file diff --git a/quantum/quantum.h b/quantum/quantum.h new file mode 100644 index 000000000..db726ad42 --- /dev/null +++ b/quantum/quantum.h @@ -0,0 +1,48 @@ +#ifndef QUANTUM_H +#define QUANTUM_H + +#include "matrix.h" +#include "keymap_common.h" +#ifdef BACKLIGHT_ENABLE + #include "backlight.h" +#endif +#ifdef RGBLIGHT_ENABLE + #include "rgblight.h" +#endif +#ifdef AUDIO_ENABLE + #include "audio.h" +#endif +#ifdef MIDI_ENABLE + #include +#endif +#include "action_layer.h" +#include "eeconfig.h" +#include +#include + +extern uint32_t default_layer_state; + +#ifndef NO_ACTION_LAYER + extern uint32_t layer_state; +#endif + +bool music_activated; + +void matrix_init_kb(void); +void matrix_scan_kb(void); +bool process_action_kb(keyrecord_t *record); + +void leader_start(void); +void leader_end(void); + +#ifndef LEADER_TIMEOUT + #define LEADER_TIMEOUT 200 +#endif +#define SEQ_ONE_KEY(key) if (leader_sequence[0] == (key) && leader_sequence[1] == 0 && leader_sequence[2] == 0) +#define SEQ_TWO_KEYS(key1, key2) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == 0) +#define SEQ_THREE_KEYS(key1, key2, key3) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3)) + +#define LEADER_EXTERNS() extern bool leading; extern uint16_t leader_time; extern uint16_t leader_sequence[3]; extern uint8_t leader_sequence_size +#define LEADER_DICTIONARY() if (leading && timer_elapsed(leader_time) > LEADER_TIMEOUT) + +#endif \ No newline at end of file diff --git a/quantum/quantum.mk b/quantum/quantum.mk index 5f4c2f045..b45ad850a 100644 --- a/quantum/quantum.mk +++ b/quantum/quantum.mk @@ -1,7 +1,8 @@ QUANTUM_DIR = quantum # # project specific files -SRC += $(QUANTUM_DIR)/keymap_common.c \ +SRC += $(QUANTUM_DIR)/quantum.c \ + $(QUANTUM_DIR)/keymap_common.c \ $(QUANTUM_DIR)/led.c # ifdef KEYMAP_FILE diff --git a/quantum/template/template.c b/quantum/template/template.c index 6050a2d20..649072eb2 100644 --- a/quantum/template/template.c +++ b/quantum/template/template.c @@ -11,8 +11,9 @@ void matrix_scan_user(void) { } __attribute__ ((weak)) -void process_action_user(keyrecord_t *record) { +bool process_action_user(keyrecord_t *record) { // leave this function blank - it can be defined in a keymap file + return true; } __attribute__ ((weak)) @@ -34,11 +35,11 @@ void matrix_scan_kb(void) { matrix_scan_user(); } -void process_action_kb(keyrecord_t *record) { +bool process_action_kb(keyrecord_t *record) { // put your per-action keyboard code here // runs for every action, just before processing by the firmware - process_action_user(record); + return process_action_user(record); } void led_set_kb(uint8_t usb_led) { diff --git a/quantum/template/template.h b/quantum/template/template.h index 22742105a..8537e3b4b 100644 --- a/quantum/template/template.h +++ b/quantum/template/template.h @@ -24,7 +24,7 @@ void matrix_init_user(void); void matrix_scan_user(void); -void process_action_user(keyrecord_t *record); +bool process_action_user(keyrecord_t *record); void led_set_user(uint8_t usb_led); #endif diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index 081e90b2d..c026b96d9 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -70,7 +70,9 @@ void process_action_nocache(keyrecord_t *record) #endif __attribute__ ((weak)) -void process_action_kb(keyrecord_t *record) {} +bool process_action_quantum(keyrecord_t *record) { + return true; +} void process_action(keyrecord_t *record) { @@ -89,7 +91,8 @@ void process_action(keyrecord_t *record) } #endif - process_action_kb(record); + if (!process_action_quantum(record)) + return; action_t action = store_or_get_action(event.pressed, event.key); dprint("ACTION: "); debug_action(action); diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index 44ec3047b..7d1cbafe9 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h @@ -59,7 +59,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); /* keyboard-specific key event (pre)processing */ -void process_action_kb(keyrecord_t *record); +bool process_action_quantum(keyrecord_t *record); /* Utilities for actions. */ #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) diff --git a/tmk_core/common/matrix.h b/tmk_core/common/matrix.h index 0b013fc98..ad0871bfb 100644 --- a/tmk_core/common/matrix.h +++ b/tmk_core/common/matrix.h @@ -64,8 +64,8 @@ void matrix_power_up(void); void matrix_power_down(void); /* keyboard-specific setup/loop functionality */ -void matrix_init_kb(void); -void matrix_scan_kb(void); +void matrix_init_quantum(void); +void matrix_scan_quantum(void); #ifdef __cplusplus } -- cgit v1.3.1 From bf5c2ccee5497523c214dae7aacdc27fdbb0f235 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Sun, 15 May 2016 00:47:25 -0400 Subject: splits process_action up to handle records separately (#329) * implements leader key for planck experimental * allows override of leader timeout * adds ability to use the leader key in seq * fixes leader keycode * adds chording prototype * fixes keycode detection * moves music mode to quantum.c * disables chording by default * adds music sequencer functionality * implements audio/music functions in quantum.c * splits up process_action to allow independent processing of actions * merging? --- quantum/keymap_common.h | 3 +++ quantum/quantum.c | 11 +++++++++- tmk_core/common/action.c | 47 ++++++++++++++++++++++------------------ tmk_core/common/action.h | 7 +++--- tmk_core/common/action_tapping.c | 30 ++++++++++++------------- 5 files changed, 58 insertions(+), 40 deletions(-) (limited to 'tmk_core/common/action.c') diff --git a/quantum/keymap_common.h b/quantum/keymap_common.h index 4107d575b..0074ab164 100644 --- a/quantum/keymap_common.h +++ b/quantum/keymap_common.h @@ -191,6 +191,9 @@ extern const uint16_t fn_actions[]; #define RESET 0x5000 #define DEBUG 0x5001 +#define KC_LEAD 0x5014 + + // MAGIC keycodes #define MAGIC_SWAP_CONTROL_CAPSLOCK 0x5002 diff --git a/quantum/quantum.c b/quantum/quantum.c index cd7fdbb7f..dd5d84f82 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -70,7 +70,7 @@ static uint8_t music_sequence_position = 0; static uint16_t music_sequence_timer = 0; static uint16_t music_sequence_interval = 100; -bool process_action_quantum(keyrecord_t *record) { +bool process_record_quantum(keyrecord_t *record) { /* This gets the keycode from the key pressed */ keypos_t key = record->event.key; @@ -90,6 +90,14 @@ bool process_action_quantum(keyrecord_t *record) { keycode = keymap_key_to_keycode(layer_switch_get_layer(key), key); #endif + // This is how you use actions here + // if (keycode == KC_LEAD) { + // action_t action; + // action.code = ACTION_DEFAULT_LAYER_SET(0); + // process_action(record, action); + // return false; + // } + #ifdef AUDIO_ENABLE if (keycode == AU_ON && record->event.pressed) { audio_on(); @@ -259,5 +267,6 @@ void matrix_scan_quantum() { } #endif + matrix_scan_kb(); } \ No newline at end of file diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index c026b96d9..be6dea2b7 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -46,7 +46,7 @@ void action_exec(keyevent_t event) #ifndef NO_ACTION_TAPPING action_tapping_process(record); #else - process_action(&record); + process_record(&record); if (!IS_NOEVENT(record.event)) { dprint("processed: "); debug_record(record); dprintln(); } @@ -56,25 +56,43 @@ void action_exec(keyevent_t event) #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) bool disable_action_cache = false; -void process_action_nocache(keyrecord_t *record) +void process_record_nocache(keyrecord_t *record) { disable_action_cache = true; - process_action(record); + process_record(record); disable_action_cache = false; } #else -void process_action_nocache(keyrecord_t *record) +void process_record_nocache(keyrecord_t *record) { - process_action(record); + process_record(record); } #endif __attribute__ ((weak)) -bool process_action_quantum(keyrecord_t *record) { +bool process_record_quantum(keyrecord_t *record) { return true; } -void process_action(keyrecord_t *record) +void process_record(keyrecord_t *record) +{ + if (IS_NOEVENT(record->event)) { return; } + + if(!process_record_quantum(record)) + return; + + action_t action = store_or_get_action(record->event.pressed, record->event.key); + dprint("ACTION: "); debug_action(action); +#ifndef NO_ACTION_LAYER + dprint(" layer_state: "); layer_debug(); + dprint(" default_layer_state: "); default_layer_debug(); +#endif + dprintln(); + + process_action(record, action); +} + +void process_action(keyrecord_t *record, action_t action) { bool do_release_oneshot = false; keyevent_t event = record->event; @@ -82,8 +100,6 @@ void process_action(keyrecord_t *record) uint8_t tap_count = record->tap.count; #endif - if (IS_NOEVENT(event)) { return; } - #if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) if (has_oneshot_layer_timed_out()) { dprintf("Oneshot layer: timeout\n"); @@ -91,17 +107,6 @@ void process_action(keyrecord_t *record) } #endif - if (!process_action_quantum(record)) - return; - - action_t action = store_or_get_action(event.pressed, event.key); - dprint("ACTION: "); debug_action(action); -#ifndef NO_ACTION_LAYER - dprint(" layer_state: "); layer_debug(); - dprint(" default_layer_state: "); default_layer_debug(); -#endif - dprintln(); - if (event.pressed) { // clear the potential weak mods left by previously pressed keys clear_weak_mods(); @@ -451,7 +456,7 @@ void process_action(keyrecord_t *record) if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED ) ) { record->event.pressed = false; layer_on(get_oneshot_layer()); - process_action(record); + process_record(record); layer_off(get_oneshot_layer()); } #endif diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index 7d1cbafe9..e8aa12a7c 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h @@ -59,14 +59,15 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); /* keyboard-specific key event (pre)processing */ -bool process_action_quantum(keyrecord_t *record); +bool process_record_quantum(keyrecord_t *record); /* Utilities for actions. */ #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) extern bool disable_action_cache; #endif -void process_action_nocache(keyrecord_t *record); -void process_action(keyrecord_t *record); +void process_record_nocache(keyrecord_t *record); +void process_record(keyrecord_t *record); +void process_action(keyrecord_t *record, action_t action); void register_code(uint8_t code); void unregister_code(uint8_t code); void register_mods(uint8_t mods); diff --git a/tmk_core/common/action_tapping.c b/tmk_core/common/action_tapping.c index e6343e6da..ff78d7f2a 100644 --- a/tmk_core/common/action_tapping.c +++ b/tmk_core/common/action_tapping.c @@ -89,7 +89,7 @@ bool process_tapping(keyrecord_t *keyp) debug("Tapping: First tap(0->1).\n"); tapping_key.tap.count = 1; debug_tapping_key(); - process_action(&tapping_key); + process_record(&tapping_key); // copy tapping state keyp->tap = tapping_key.tap; @@ -103,7 +103,7 @@ bool process_tapping(keyrecord_t *keyp) */ else if (IS_RELEASED(event) && waiting_buffer_typed(event)) { debug("Tapping: End. No tap. Interfered by typing key\n"); - process_action(&tapping_key); + process_record(&tapping_key); tapping_key = (keyrecord_t){}; debug_tapping_key(); // enqueue @@ -131,7 +131,7 @@ bool process_tapping(keyrecord_t *keyp) } // Release of key should be process immediately. debug("Tapping: release event of a key pressed before tapping\n"); - process_action(keyp); + process_record(keyp); return true; } else { @@ -148,7 +148,7 @@ bool process_tapping(keyrecord_t *keyp) if (IS_TAPPING_KEY(event.key) && !event.pressed) { debug("Tapping: Tap release("); debug_dec(tapping_key.tap.count); debug(")\n"); keyp->tap = tapping_key.tap; - process_action(keyp); + process_record(keyp); tapping_key = *keyp; debug_tapping_key(); return true; @@ -157,7 +157,7 @@ bool process_tapping(keyrecord_t *keyp) if (tapping_key.tap.count > 1) { debug("Tapping: Start new tap with releasing last tap(>1).\n"); // unregister key - process_action(&(keyrecord_t){ + process_record(&(keyrecord_t){ .tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, @@ -175,7 +175,7 @@ bool process_tapping(keyrecord_t *keyp) if (!IS_NOEVENT(event)) { debug("Tapping: key event while last tap(>0).\n"); } - process_action(keyp); + process_record(keyp); return true; } } @@ -185,7 +185,7 @@ bool process_tapping(keyrecord_t *keyp) if (tapping_key.tap.count == 0) { debug("Tapping: End. Timeout. Not tap(0): "); debug_event(event); debug("\n"); - process_action(&tapping_key); + process_record(&tapping_key); tapping_key = (keyrecord_t){}; debug_tapping_key(); return false; @@ -193,7 +193,7 @@ bool process_tapping(keyrecord_t *keyp) if (IS_TAPPING_KEY(event.key) && !event.pressed) { debug("Tapping: End. last timeout tap release(>0)."); keyp->tap = tapping_key.tap; - process_action(keyp); + process_record(keyp); tapping_key = (keyrecord_t){}; return true; } @@ -201,7 +201,7 @@ bool process_tapping(keyrecord_t *keyp) if (tapping_key.tap.count > 1) { debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); // unregister key - process_action(&(keyrecord_t){ + process_record(&(keyrecord_t){ .tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, @@ -219,7 +219,7 @@ bool process_tapping(keyrecord_t *keyp) if (!IS_NOEVENT(event)) { debug("Tapping: key event while last timeout tap(>0).\n"); } - process_action(keyp); + process_record(keyp); return true; } } @@ -233,7 +233,7 @@ bool process_tapping(keyrecord_t *keyp) keyp->tap = tapping_key.tap; if (keyp->tap.count < 15) keyp->tap.count += 1; debug("Tapping: Tap press("); debug_dec(keyp->tap.count); debug(")\n"); - process_action(keyp); + process_record(keyp); tapping_key = *keyp; debug_tapping_key(); return true; @@ -253,12 +253,12 @@ bool process_tapping(keyrecord_t *keyp) // should none in buffer // FIX: interrupted when other key is pressed tapping_key.tap.interrupted = true; - process_action(keyp); + process_record(keyp); return true; } } else { if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n"); - process_action(keyp); + process_record(keyp); return true; } } else { @@ -280,7 +280,7 @@ bool process_tapping(keyrecord_t *keyp) debug_tapping_key(); return true; } else { - process_action(keyp); + process_record(keyp); return true; } } @@ -347,7 +347,7 @@ void waiting_buffer_scan_tap(void) WITHIN_TAPPING_TERM(waiting_buffer[i].event)) { tapping_key.tap.count = 1; waiting_buffer[i].tap.count = 1; - process_action(&tapping_key); + process_record(&tapping_key); debug("waiting_buffer_scan_tap: found at ["); debug_dec(i); debug("]\n"); debug_waiting_buffer(); -- cgit v1.3.1 From dd378601608849679ead6e2cccb74f7f29c131dc Mon Sep 17 00:00:00 2001 From: Joe Wasson Date: Wed, 27 Jul 2016 08:43:02 -0700 Subject: Add one-hand support. This adds an action, `ACTION_SWAP_HANDS`, that swaps the the keys on the keyboard across a keymap-defined hemisphere in order to support one-hand typing without requiring a separate one-handed layer. See updated `doc/keymap.md` for more information. --- doc/keymap.md | 18 ++++++++++++++++++ tmk_core/common.mk | 4 ++++ tmk_core/common/action.c | 33 +++++++++++++++++++++++++++++++++ tmk_core/common/action.h | 18 ++++++++++++++++++ tmk_core/common/action_code.h | 8 +++++++- 5 files changed, 80 insertions(+), 1 deletion(-) (limited to 'tmk_core/common/action.c') diff --git a/doc/keymap.md b/doc/keymap.md index d1985e567..1285ad6cd 100644 --- a/doc/keymap.md +++ b/doc/keymap.md @@ -455,6 +455,24 @@ Turn the backlight on and off without changing level. +### 2.6 Swap-Hands Action +The swap-hands action allows support for one-handed keyboards without requiring a separate layer. Set `ONEHAND_ENABLE` in the Makefile and define a `hand_swap_config` entry in your keymap. Now whenever the `ACTION_SWAP_HANDS` command is executed the keyboard is mirrored. For instance, to type "Hello, World" on QWERTY you would type `^Ge^s^s^w^c W^wr^sd` + +The configuration table is a simple 2-dimensional array to map from column/row to new column/row. Example `hand_swap_config` for Planck: + +``` +const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { + {{11, 0}, {10, 0}, {9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}}, + {{11, 1}, {10, 1}, {9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}}, + {{11, 2}, {10, 2}, {9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}}, + {{11, 3}, {10, 3}, {9, 3}, {8, 3}, {7, 3}, {6, 3}, {5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}}, +}; +``` + +Note that the array indices are reversed same as the matrix and the values are of type `keypos_t` which is `{col, row}` and all values are zero-based. In the example above, `hand_swap_config[2][4]` (third row, fifth column) would return {7, 2} (third row, eighth column). + + + ## 3. Layer switching Example There are some ways to switch layer with 'Layer' actions. diff --git a/tmk_core/common.mk b/tmk_core/common.mk index aa05b9491..429c57143 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -85,6 +85,10 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes) OPT_DEFS += -DBLUETOOTH_ENABLE endif +ifeq ($(strip $(ONEHAND_ENABLE)), yes) + OPT_DEFS += -DONEHAND_ENABLE +endif + ifeq ($(strip $(KEYMAP_SECTION_ENABLE)), yes) OPT_DEFS += -DKEYMAP_SECTION_ENABLE diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index be6dea2b7..0413b1a99 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -41,6 +41,12 @@ void action_exec(keyevent_t event) dprint("EVENT: "); debug_event(event); dprintln(); } +#ifdef ONEHAND_ENABLE + if (!IS_NOEVENT(event)) { + process_hand_swap(&event); + } +#endif + keyrecord_t record = { .event = event }; #ifndef NO_ACTION_TAPPING @@ -53,6 +59,26 @@ void action_exec(keyevent_t event) #endif } +#ifdef ONEHAND_ENABLE +bool swap_hands = false; + +void process_hand_swap(keyevent_t *event) { + static swap_state_row_t swap_state[MATRIX_ROWS]; + + keypos_t pos = event->key; + swap_state_row_t col_bit = (swap_state_row_t)1<pressed ? swap_hands : + swap_state[pos.row] & (col_bit); + + if (do_swap) { + event->key = hand_swap_config[pos.row][pos.col]; + swap_state[pos.row] |= col_bit; + } else { + swap_state[pos.row] &= ~(col_bit); + } +} +#endif + #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) bool disable_action_cache = false; @@ -439,6 +465,13 @@ void process_action(keyrecord_t *record, action_t action) break; #endif case ACT_COMMAND: + switch (action.command.id) { +#ifdef ONEHAND_ENABLE + case CMD_SWAP_HANDS: + swap_hands = event.pressed; + break; +#endif + } break; #ifndef NO_ACTION_FUNCTION case ACT_FUNCTION: diff --git a/tmk_core/common/action.h b/tmk_core/common/action.h index e8aa12a7c..b9bdfe642 100644 --- a/tmk_core/common/action.h +++ b/tmk_core/common/action.h @@ -65,6 +65,24 @@ bool process_record_quantum(keyrecord_t *record); #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS) extern bool disable_action_cache; #endif + +/* Code for handling one-handed key modifiers. */ +#ifdef ONEHAND_ENABLE +extern bool swap_hands; +extern const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; +#if (MATRIX_COLS <= 8) +typedef uint8_t swap_state_row_t; +#elif (MATRIX_COLS <= 16) +typedef uint16_t swap_state_row_t; +#elif (MATRIX_COLS <= 32) +typedef uint32_t swap_state_row_t; +#else +#error "MATRIX_COLS: invalid value" +#endif + +void process_hand_swap(keyevent_t *record); +#endif + void process_record_nocache(keyrecord_t *record); void process_record(keyrecord_t *record); void process_action(keyrecord_t *record, action_t action); diff --git a/tmk_core/common/action_code.h b/tmk_core/common/action_code.h index ca729aaec..95d2cbf3e 100644 --- a/tmk_core/common/action_code.h +++ b/tmk_core/common/action_code.h @@ -295,6 +295,10 @@ enum backlight_opt { BACKLIGHT_STEP = 3, BACKLIGHT_LEVEL = 4, }; + +enum command_id { + CMD_SWAP_HANDS = 0x14, +}; /* Macro */ #define ACTION_MACRO(id) ACTION(ACT_MACRO, (id)) #define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP<<8 | (id)) @@ -306,7 +310,7 @@ enum backlight_opt { #define ACTION_BACKLIGHT_STEP() ACTION(ACT_BACKLIGHT, BACKLIGHT_STEP << 8) #define ACTION_BACKLIGHT_LEVEL(level) ACTION(ACT_BACKLIGHT, BACKLIGHT_LEVEL << 8 | (level)) /* Command */ -#define ACTION_COMMAND(id, opt) ACTION(ACT_COMMAND, (opt)<<8 | (addr)) +#define ACTION_COMMAND(id, opt) ACTION(ACT_COMMAND, (opt)<<8 | (id)) /* Function */ enum function_opts { FUNC_TAP = 0x8, /* indciates function is tappable */ @@ -314,5 +318,7 @@ enum function_opts { #define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id)) #define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP<<8 | (id)) #define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt)<<8 | (id)) +/* OneHand Support */ +#define ACTION_SWAP_HANDS() ACTION_COMMAND(CMD_SWAP_HANDS, 0) #endif /* ACTION_CODE_H */ -- cgit v1.3.1 From 8090f6b499fd87ddeb7a191f7bc3dace9d03be23 Mon Sep 17 00:00:00 2001 From: Joe Wasson Date: Thu, 28 Jul 2016 01:24:06 -0700 Subject: Improve one-hand support by adding more actions and tap keys. --- doc/keymap.md | 13 +++++++++- tmk_core/common/action.c | 57 ++++++++++++++++++++++++++++++++++++++++--- tmk_core/common/action_code.h | 28 ++++++++++++++++++--- 3 files changed, 89 insertions(+), 9 deletions(-) (limited to 'tmk_core/common/action.c') diff --git a/doc/keymap.md b/doc/keymap.md index 1285ad6cd..6f2a663fc 100644 --- a/doc/keymap.md +++ b/doc/keymap.md @@ -456,8 +456,9 @@ Turn the backlight on and off without changing level. ### 2.6 Swap-Hands Action -The swap-hands action allows support for one-handed keyboards without requiring a separate layer. Set `ONEHAND_ENABLE` in the Makefile and define a `hand_swap_config` entry in your keymap. Now whenever the `ACTION_SWAP_HANDS` command is executed the keyboard is mirrored. For instance, to type "Hello, World" on QWERTY you would type `^Ge^s^s^w^c W^wr^sd` +The swap-hands action allows support for one-handed keyboards without requiring a separate layer. Set `ONEHAND_ENABLE` in the Makefile and define a `hand_swap_config` entry in your keymap. Now whenever the `ACTION_SWAP_HANDS` command key is pressed the keyboard is mirrored. For instance, to type "Hello, World" on QWERTY you would type `^Ge^s^s^w^c W^wr^sd` +### 2.6.1 Configuration The configuration table is a simple 2-dimensional array to map from column/row to new column/row. Example `hand_swap_config` for Planck: ``` @@ -471,6 +472,16 @@ const keypos_t hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = { Note that the array indices are reversed same as the matrix and the values are of type `keypos_t` which is `{col, row}` and all values are zero-based. In the example above, `hand_swap_config[2][4]` (third row, fifth column) would return {7, 2} (third row, eighth column). +### 2.6.2 Advanced Swap Commands +- **`ACTION_SWAP_HANDS()`** Swaps hands when pressed, returns to normal when released (momentary). +- **`ACTION_SWAP_HANDS_TOGGLE()`** Toggles swap on and off with every keypress. +- **`ACTION_SWAP_HANDS_TAP_TOGGLE()`** Toggles with a tap; momentary when held. +- **`ACTION_SWAP_HANDS_TAP_KEY(key)`** Sends `key` with a tap; momentary swap when held. +- **`ACTION_SWAP_HANDS_ON_OFF()`** Alias for `ACTION_SWAP_HANDS()` +- **`ACTION_SWAP_HANDS_OFF_ON()`** Momentarily turns off swap. +- **`ACTION_SWAP_HANDS_ON()`** Turns on swapping and leaves it on. +- **`ACTION_SWAP_HANDS_OFF()`** Turn off swapping and leaves it off. Good for returning to a known state. + ## 3. Layer switching Example diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index 0413b1a99..08ef22eb9 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -465,14 +465,55 @@ void process_action(keyrecord_t *record, action_t action) break; #endif case ACT_COMMAND: - switch (action.command.id) { + break; #ifdef ONEHAND_ENABLE - case CMD_SWAP_HANDS: + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case OP_SH_TOGGLE: + if (event.pressed) { + swap_hands = !swap_hands; + } + break; + case OP_SH_ON_OFF: swap_hands = event.pressed; break; -#endif + case OP_SH_OFF_ON: + swap_hands = !event.pressed; + break; + case OP_SH_ON: + if (!event.pressed) { + swap_hands = true; + } + break; + case OP_SH_OFF: + if (!event.pressed) { + swap_hands = false; + } + break; + #ifndef NO_ACTION_TAPPING + case OP_SH_TAP_TOGGLE: + /* tap toggle */ + if (tap_count > 0) { + if (!event.pressed) { + swap_hands = !swap_hands; + } + } else { + swap_hands = event.pressed; + } + break; + default: + if (tap_count > 0) { + if (event.pressed) { + register_code(action.swap.code); + } else { + unregister_code(action.swap.code); + } + } else { + swap_hands = event.pressed; + } + #endif } - break; +#endif #ifndef NO_ACTION_FUNCTION case ACT_FUNCTION: action_function(record, action.func.id, action.func.opt); @@ -685,6 +726,13 @@ bool is_tap_key(keypos_t key) return true; } return false; + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case 0x00 ... 0xdf: + case OP_SH_TAP_TOGGLE: + return true; + } + return false; case ACT_MACRO: case ACT_FUNCTION: if (action.func.opt & FUNC_TAP) { return true; } @@ -725,6 +773,7 @@ void debug_action(action_t action) case ACT_MACRO: dprint("ACT_MACRO"); break; case ACT_COMMAND: dprint("ACT_COMMAND"); break; case ACT_FUNCTION: dprint("ACT_FUNCTION"); break; + case ACT_SWAP_HANDS: dprint("ACT_SWAP_HANDS"); break; default: dprint("UNKNOWN"); break; } dprintf("[%X:%02X]", action.kind.param>>8, action.kind.param&0xff); diff --git a/tmk_core/common/action_code.h b/tmk_core/common/action_code.h index 95d2cbf3e..33da35f35 100644 --- a/tmk_core/common/action_code.h +++ b/tmk_core/common/action_code.h @@ -108,6 +108,8 @@ enum action_kind_id { /* Other Keys */ ACT_USAGE = 0b0100, ACT_MOUSEKEY = 0b0101, + /* One-hand Support */ + ACT_SWAP_HANDS = 0b0110, /* Layer Actions */ ACT_LAYER = 0b1000, ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */ @@ -178,6 +180,11 @@ typedef union { uint8_t opt :4; uint8_t kind :4; } func; + struct action_swap { + uint8_t code :8; + uint8_t opt :4; + uint8_t kind :4; + } swap; } action_t; @@ -296,9 +303,6 @@ enum backlight_opt { BACKLIGHT_LEVEL = 4, }; -enum command_id { - CMD_SWAP_HANDS = 0x14, -}; /* Macro */ #define ACTION_MACRO(id) ACTION(ACT_MACRO, (id)) #define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP<<8 | (id)) @@ -319,6 +323,22 @@ enum function_opts { #define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP<<8 | (id)) #define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt)<<8 | (id)) /* OneHand Support */ -#define ACTION_SWAP_HANDS() ACTION_COMMAND(CMD_SWAP_HANDS, 0) +enum swap_hands_pram_tap_op { + OP_SH_TOGGLE = 0xF0, + OP_SH_TAP_TOGGLE, + OP_SH_ON_OFF, + OP_SH_OFF_ON, + OP_SH_OFF, + OP_SH_ON, +}; + +#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF() +#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE) +#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE) +#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key) +#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF) +#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON) +#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON) +#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF) #endif /* ACTION_CODE_H */ -- cgit v1.3.1 From d4309990472895bbbe82f7ba22ed33c16f928778 Mon Sep 17 00:00:00 2001 From: Didier Loiseau Date: Fri, 23 Sep 2016 00:55:15 +0200 Subject: Fix issue #772 Meh and Hyper not working --- tmk_core/common/action.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'tmk_core/common/action.c') diff --git a/tmk_core/common/action.c b/tmk_core/common/action.c index 08ef22eb9..d485b46c7 100644 --- a/tmk_core/common/action.c +++ b/tmk_core/common/action.c @@ -155,9 +155,10 @@ void process_action(keyrecord_t *record, action_t action) action.key.mods<<4; if (event.pressed) { if (mods) { - if (IS_MOD(action.key.code)) { + if (IS_MOD(action.key.code) || action.key.code == KC_NO) { // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless. - // this also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT) + // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT). + // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO). add_mods(mods); } else { add_weak_mods(mods); @@ -168,7 +169,7 @@ void process_action(keyrecord_t *record, action_t action) } else { unregister_code(action.key.code); if (mods) { - if (IS_MOD(action.key.code)) { + if (IS_MOD(action.key.code) || action.key.code == KC_NO) { del_mods(mods); } else { del_weak_mods(mods); -- cgit v1.3.1