diff options
| -rw-r--r-- | common_features.mk | 7 | ||||
| -rw-r--r-- | keyboards/nyquist/keymaps/333fred/Makefile | 2 | ||||
| -rw-r--r-- | keyboards/nyquist/keymaps/333fred/keymap.c | 2 | ||||
| -rw-r--r-- | quantum/process_keycode/process_key_lock.c | 120 | ||||
| -rw-r--r-- | quantum/process_keycode/process_key_lock.h | 24 | ||||
| -rw-r--r-- | quantum/quantum.c | 4 | ||||
| -rw-r--r-- | quantum/quantum.h | 4 | ||||
| -rw-r--r-- | quantum/quantum_keycodes.h | 4 | 
8 files changed, 165 insertions, 2 deletions
| diff --git a/common_features.mk b/common_features.mk index 0adf81afac..f405d5c07b 100644 --- a/common_features.mk +++ b/common_features.mk @@ -104,6 +104,11 @@ ifeq ($(strip $(TAP_DANCE_ENABLE)), yes)      SRC += $(QUANTUM_DIR)/process_keycode/process_tap_dance.c  endif +ifeq ($(strip $(KEY_LOCK_ENABLE)), yes) +    OPT_DEFS += -DKEY_LOCK_ENABLE +    SRC += $(QUANTUM_DIR)/process_keycode/process_key_lock.c +endif +  ifeq ($(strip $(PRINTING_ENABLE)), yes)      OPT_DEFS += -DPRINTING_ENABLE      SRC += $(QUANTUM_DIR)/process_keycode/process_printer.c @@ -156,4 +161,4 @@ QUANTUM_SRC:= \  ifndef CUSTOM_MATRIX      QUANTUM_SRC += $(QUANTUM_DIR)/matrix.c -endif
\ No newline at end of file +endif diff --git a/keyboards/nyquist/keymaps/333fred/Makefile b/keyboards/nyquist/keymaps/333fred/Makefile index 457a3d01d4..471eac8b5e 100644 --- a/keyboards/nyquist/keymaps/333fred/Makefile +++ b/keyboards/nyquist/keymaps/333fred/Makefile @@ -1,3 +1,5 @@ +KEY_LOCK_ENABLE = yes +  ifndef QUANTUM_DIR  	include ../../../../Makefile  endif diff --git a/keyboards/nyquist/keymaps/333fred/keymap.c b/keyboards/nyquist/keymaps/333fred/keymap.c index 07434d93fa..20bf298876 100644 --- a/keyboards/nyquist/keymaps/333fred/keymap.c +++ b/keyboards/nyquist/keymaps/333fred/keymap.c @@ -39,7 +39,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {    KC_TAB,        KC_Q,         KC_W,  KC_E,    KC_R,           KC_T,    KC_Y,    KC_U,    KC_I,    KC_O,   KC_P,            KC_BSLASH, \    KC_ESC,        KC_A,         KC_S,  KC_D,    LT(_VIM, KC_F), KC_G,    KC_H,    KC_J,    KC_K,    KC_L,   KC_SCLN,         KC_QUOT, \    OSM(MOD_LSFT), LCTL_T(KC_Z), KC_X,  KC_C,    KC_V,           KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT, RCTL_T(KC_SLSH), OSM(MOD_RSFT), \ -  KC_LCTL,       KC_LALT,      KC_F4, KC_LGUI, OSL(_LOWER),    KC_BSPC, KC_SPC,  KC_ENT,  KC_RALT, KC_EQL, TG(_GAME),       KC_DEL \ +  KC_LCTL,       KC_LALT,      KC_F4, KC_LGUI, OSL(_LOWER),    KC_BSPC, KC_SPC,  KC_ENT,  KC_LOCK, KC_EQL, TG(_GAME),       KC_DEL \  ),  /* Lower diff --git a/quantum/process_keycode/process_key_lock.c b/quantum/process_keycode/process_key_lock.c new file mode 100644 index 0000000000..60b0fcd9b1 --- /dev/null +++ b/quantum/process_keycode/process_key_lock.c @@ -0,0 +1,120 @@ +/* Copyright 2017 Fredric Silberberg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "inttypes.h" +#include "stdint.h" +#include "process_key_lock.h" + +#define SHIFT(shift) (((uint64_t)1) << (shift)) +#define GET_KEY_ARRAY(code) (((code) < 0x40) ? key_state[0] : \ +                             ((code) < 0x80) ? key_state[1] : \ +                             ((code) < 0xC0) ? key_state[2] : key_state[3]) +#define GET_CODE_INDEX(code) (((code) < 0x40) ? (code) : \ +                              ((code) < 0x80) ? (code) - 0x40 : \ +                              ((code) < 0xC0) ? (code) - 0x80 : (code) - 0xC0) +#define KEY_STATE(code)  (GET_KEY_ARRAY(code) & SHIFT(GET_CODE_INDEX(code))) == SHIFT(GET_CODE_INDEX(code)) +#define SET_KEY_ARRAY_STATE(code, val) do { \ +    switch (code) { \ +        case 0x00 ... 0x3F: \ +            key_state[0] = (val); \ +            break; \ +        case 0x40 ... 0x7F: \ +            key_state[1] = (val); \ +            break; \ +        case 0x80 ... 0xBF: \ +            key_state[2] = (val); \ +            break; \ +        case 0xC0 ... 0xFF: \ +            key_state[3] = (val); \ +            break; \ +    } \ +} while(0) +#define SET_KEY_STATE(code) SET_KEY_ARRAY_STATE(code, (GET_KEY_ARRAY(code) | SHIFT(GET_CODE_INDEX(code)))) +#define UNSET_KEY_STATE(code) SET_KEY_ARRAY_STATE(code, (GET_KEY_ARRAY(code)) & ~(SHIFT(GET_CODE_INDEX(code)))) +#define IS_STANDARD_KEYCODE(code) ((code) <= 0xFF) +#define print_hex64(num) do { print_hex32((num & 0xFFFFFFFF00000000) >> 32); print_hex32(num & 0x00000000FFFFFFFF); } while (0) + +// Locked key state. This is an array of 256 bits, one for each of the standard keys supported qmk. +uint64_t key_state[4] = { 0x0, 0x0, 0x0, 0x0 }; +bool watching = false; + +bool process_key_lock(uint16_t keycode, keyrecord_t *record) { +    // We start by categorizing the keypress event. In the event of a down +    // event, there are several possibilities: +    // 1. The key is not being locked, and we are not watching for new keys. +    //    In this case, we bail immediately. This is the common case for down events. +    // 2. The key was locked, and we need to unlock it. In this case, we will +    //    reset the state in our map and return false. When the user releases the +    //    key, the up event will no longer be masked and the OS will observe the +    //    released key. +    // 3. KC_LOCK was just pressed. In this case, we set up the state machine +    //    to watch for the next key down event, and finish processing +    // 4. The keycode is below 0xFF, and we are watching for new keys. In this case, +    //    we will send the key down event to the os, and set the key_state for that +    //    key to mask the up event. +    // 5. The keycode is above 0xFF, and we're wathing for new keys. In this case, +    //    the user pressed a key that we cannot "lock", as it's a series of keys, +    //    or a macro invocation, or a layer transition, or a custom-defined key, or +    //    or some other arbitrary code. In this case, we bail immediately, reset +    //    our watch state, and return true. +    // +    // In the event of an up event, there are these possibilities: +    // 1. The key is not being locked. In this case, we return true and bail +    //    immediately. This is the common case. +    // 2. The key is being locked. In this case, we will mask the up event +    //    by returning false, so the OS never sees that the key was released +    //    until the user pressed the key again. +    if (record->event.pressed) { +        // Non-standard keycode, reset and return +        if (!(IS_STANDARD_KEYCODE(keycode) || keycode == KC_LOCK)) { +            watching = false; +            return true; +        } + +        // If we're already watching, turn off the watch. +        if (keycode == KC_LOCK) { +            watching = !watching; +            return false; +        } +         +        if (IS_STANDARD_KEYCODE(keycode)) { +            // We check watching first. This is so that in the following scenario, we continue to +            // hold the key: KC_LOCK, KC_F, KC_LOCK, KC_F +            // If we checked in reverse order, we'd end up holding the key pressed after the second +            // KC_F press is registered, when the user likely meant to hold F +            if (watching) { +                watching = false; +                SET_KEY_STATE(keycode); +                // Let the standard keymap send the keycode down event. The up event will be masked. +                return true; +            } +             +            if (KEY_STATE(keycode)) { +                UNSET_KEY_STATE(keycode); +                // The key is already held, stop this process. The up event will be sent when the user +                // releases the key. +                return false; +            } +        } +         +        // Either the key isn't a standard key, or we need to send the down event. Continue standard +        // processing +        return true; +    } else { +        // Stop processing if it's a standard key and we're masking up. +        return !(IS_STANDARD_KEYCODE(keycode) && KEY_STATE(keycode)); +    } +} diff --git a/quantum/process_keycode/process_key_lock.h b/quantum/process_keycode/process_key_lock.h new file mode 100644 index 0000000000..237e103bcd --- /dev/null +++ b/quantum/process_keycode/process_key_lock.h @@ -0,0 +1,24 @@ +/* Copyright 2017 Fredric Silberberg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PROCESS_KEY_LOCK_H +#define PROCESS_KEY_LOCK_H + +#include "quantum.h" + +bool process_key_lock(uint16_t keycode, keyrecord_t *record); + +#endif // PROCESS_KEY_LOCK_H diff --git a/quantum/quantum.c b/quantum/quantum.c index 1f8ce6c46f..c71a97bf28 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -193,6 +193,10 @@ bool process_record_quantum(keyrecord_t *record) {      // }    if (!( +  #if defined(KEY_LOCK_ENABLE) +    // Must run first to be able to mask key_up events. +    process_key_lock(keycode, record) && +  #endif      process_record_kb(keycode, record) &&    #if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)      process_midi(keycode, record) && diff --git a/quantum/quantum.h b/quantum/quantum.h index 453cb43f88..9a6d691a15 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -99,6 +99,10 @@ extern uint32_t default_layer_state;  	#include "process_combo.h"  #endif +#ifdef KEY_LOCK_ENABLE +	#include "process_key_lock.h" +#endif +  #define SEND_STRING(str) send_string(PSTR(str))  extern const bool ascii_to_shift_lut[0x80];  extern const uint8_t ascii_to_keycode_lut[0x80]; diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index acdb9248d6..1bb6706ba4 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -419,6 +419,10 @@ enum quantum_keycodes {      OUT_BT,  #endif +#ifdef KEY_LOCK_ENABLE +    KC_LOCK, +#endif +      // always leave at the end      SAFE_RANGE  }; | 
