From 36b5559b997cedd14a352aa70891558936b8b3a3 Mon Sep 17 00:00:00 2001 From: Drashna Jaelre Date: Wed, 20 Nov 2024 22:31:54 -0800 Subject: [Core] Add Layer Lock feature (#23430) Co-authored-by: Daniel <1767914+iamdanielv@users.noreply.github.com> Co-authored-by: Pascal Getreuer Co-authored-by: Pascal Getreuer <50221757+getreuer@users.noreply.github.com> --- quantum/process_keycode/process_layer_lock.c | 95 ++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 quantum/process_keycode/process_layer_lock.c (limited to 'quantum/process_keycode/process_layer_lock.c') diff --git a/quantum/process_keycode/process_layer_lock.c b/quantum/process_keycode/process_layer_lock.c new file mode 100644 index 0000000000..1e36d8844e --- /dev/null +++ b/quantum/process_keycode/process_layer_lock.c @@ -0,0 +1,95 @@ +// Copyright 2022-2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @file layer_lock.c + * @brief Layer Lock implementation + * + * For full documentation, see + * + */ + +#include "layer_lock.h" +#include "process_layer_lock.h" +#include "quantum_keycodes.h" +#include "action_util.h" + +// The current lock state. The kth bit is on if layer k is locked. +extern layer_state_t locked_layers; +#if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0 +extern uint32_t layer_lock_timer; +#endif + +// Handles an event on an `MO` or `TT` layer switch key. +static bool handle_mo_or_tt(uint8_t layer, keyrecord_t* record) { + if (is_layer_locked(layer)) { + if (record->event.pressed) { // On press, unlock the layer. + layer_lock_invert(layer); + } + return false; // Skip default handling. + } + return true; +} + +bool process_layer_lock(uint16_t keycode, keyrecord_t* record) { +#ifndef NO_ACTION_LAYER +# if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0 + layer_lock_timer = timer_read32(); +# endif // LAYER_LOCK_IDLE_TIMEOUT > 0 + + // The intention is that locked layers remain on. If something outside of + // this feature turned any locked layers off, unlock them. + if ((locked_layers & ~layer_state) != 0) { + layer_lock_set_kb(locked_layers &= layer_state); + } + + if (keycode == QK_LAYER_LOCK) { + if (record->event.pressed) { // The layer lock key was pressed. + layer_lock_invert(get_highest_layer(layer_state)); + } + return false; + } + + switch (keycode) { + case QK_MOMENTARY ... QK_MOMENTARY_MAX: // `MO(layer)` keys. + return handle_mo_or_tt(QK_MOMENTARY_GET_LAYER(keycode), record); + + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: // `TT(layer)`. + return handle_mo_or_tt(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode), record); + + case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: { // `LM(layer, mod)`. + uint8_t layer = QK_LAYER_MOD_GET_LAYER(keycode); + if (is_layer_locked(layer)) { + if (record->event.pressed) { // On press, unlock the layer. + layer_lock_invert(layer); + } else { // On release, clear the mods. + clear_mods(); + send_keyboard_report(); + } + return false; // Skip default handling. + } + } break; + +# ifndef NO_ACTION_TAPPING + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: // `LT(layer, key)` keys. + if (record->tap.count == 0 && !record->event.pressed && is_layer_locked(QK_LAYER_TAP_GET_LAYER(keycode))) { + // Release event on a held layer-tap key where the layer is locked. + return false; // Skip default handling so that layer stays on. + } + break; +# endif // NO_ACTION_TAPPING + } +#endif // NO_ACTION_LAYER + return true; +} -- cgit v1.2.3 From 968a611476c7add787f737be9521d2968d1f4451 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Thu, 21 Nov 2024 13:02:49 +0000 Subject: Review fixes for layer lock feature (#24627) --- quantum/keyboard.c | 6 ++-- quantum/layer_lock.c | 29 ++++++++++++++--- quantum/layer_lock.h | 45 +++----------------------- quantum/process_keycode/process_layer_lock.c | 17 ++-------- quantum/process_keycode/process_layer_lock.h | 48 ---------------------------- quantum/quantum.c | 2 +- quantum/quantum.h | 2 +- 7 files changed, 35 insertions(+), 114 deletions(-) (limited to 'quantum/process_keycode/process_layer_lock.c') diff --git a/quantum/keyboard.c b/quantum/keyboard.c index 8db81a4b39..d7836cf36e 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -140,9 +140,9 @@ along with this program. If not, see . #ifdef OS_DETECTION_ENABLE # include "os_detection.h" #endif -#if defined(LAYER_LOCK_ENABLE) && LAYER_LOCK_IDLE_TIMEOUT > 0 +#ifdef LAYER_LOCK_ENABLE # include "layer_lock.h" -#endif // LAYER_LOCK_ENABLE +#endif static uint32_t last_input_modification_time = 0; uint32_t last_input_activity_time(void) { @@ -659,7 +659,7 @@ void quantum_task(void) { secure_task(); #endif -#if defined(LAYER_LOCK_ENABLE) && LAYER_LOCK_IDLE_TIMEOUT > 0 +#ifdef LAYER_LOCK_ENABLE layer_lock_task(); #endif } diff --git a/quantum/layer_lock.c b/quantum/layer_lock.c index 9ee3c307dc..9fc84bcfca 100644 --- a/quantum/layer_lock.c +++ b/quantum/layer_lock.c @@ -23,12 +23,18 @@ layer_state_t locked_layers = 0; # if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0 uint32_t layer_lock_timer = 0; -void layer_lock_task(void) { +void layer_lock_timeout_task(void) { if (locked_layers && timer_elapsed32(layer_lock_timer) > LAYER_LOCK_IDLE_TIMEOUT) { layer_lock_all_off(); layer_lock_timer = timer_read32(); } } +void layer_lock_activity_trigger(void) { + layer_lock_timer = timer_read32(); +} +# else +void layer_lock_timeout_task(void) {} +void layer_lock_activity_trigger(void) {} # endif // LAYER_LOCK_IDLE_TIMEOUT > 0 bool is_layer_locked(uint8_t layer) { @@ -44,9 +50,7 @@ void layer_lock_invert(uint8_t layer) { } # endif // NO_ACTION_ONESHOT layer_on(layer); -# if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0 - layer_lock_timer = timer_read32(); -# endif // LAYER_LOCK_IDLE_TIMEOUT > 0 + layer_lock_activity_trigger(); } else { // Layer is being unlocked. layer_off(layer); } @@ -72,10 +76,25 @@ void layer_lock_all_off(void) { layer_lock_set_kb(locked_layers); } +#else // NO_ACTION_LAYER +bool is_layer_locked(uint8_t layer) { + return false; +} +void layer_lock_on(uint8_t layer) {} +void layer_lock_off(uint8_t layer) {} +void layer_lock_all_off(void) {} +void layer_lock_invert(uint8_t layer) {} +void layer_lock_timeout_task(void) {} +void layer_lock_activity_trigger(void) {} +#endif // NO_ACTION_LAYER + __attribute__((weak)) bool layer_lock_set_kb(layer_state_t locked_layers) { return layer_lock_set_user(locked_layers); } __attribute__((weak)) bool layer_lock_set_user(layer_state_t locked_layers) { return true; } -#endif // NO_ACTION_LAYER + +void layer_lock_task(void) { + layer_lock_timeout_task(); +} diff --git a/quantum/layer_lock.h b/quantum/layer_lock.h index 6d7285da2a..97f6c60d70 100644 --- a/quantum/layer_lock.h +++ b/quantum/layer_lock.h @@ -49,13 +49,6 @@ * * #define LAYER_LOCK_IDLE_TIMEOUT 60000 // Turn off after 60 seconds. * - * and call `layer_lock_task()` from your `matrix_scan_user()` in keymap.c: - * - * void matrix_scan_user(void) { - * layer_lock_task(); - * // Other tasks... - * } - * * For full documentation, see * */ @@ -67,24 +60,6 @@ #include "action_layer.h" #include "action_util.h" -/** - * Handler function for Layer Lock. - * - * In your keymap, define a custom keycode to use for Layer Lock. Then handle - * Layer Lock from your `process_record_user` function by calling - * `process_layer_lock`, passing your custom keycode for the `lock_keycode` arg: - * - * #include "features/layer_lock.h" - * - * bool process_record_user(uint16_t keycode, keyrecord_t* record) { - * if (!process_layer_lock(keycode, record, LLOCK)) { return false; } - * // Your macros ... - * - * return true; - * } - */ - -#ifndef NO_ACTION_LAYER /** Returns true if `layer` is currently locked. */ bool is_layer_locked(uint8_t layer); @@ -116,20 +91,8 @@ void layer_lock_invert(uint8_t layer); bool layer_lock_set_kb(layer_state_t locked_layers); bool layer_lock_set_user(layer_state_t locked_layers); +/** Handle various background tasks */ void layer_lock_task(void); -#else // NO_ACTION_LAYER -static inline bool is_layer_locked(uint8_t layer) { - return false; -} -static inline void layer_lock_on(uint8_t layer) {} -static inline void layer_lock_off(uint8_t layer) {} -static inline void layer_lock_all_off(void) {} -static inline void layer_lock_invert(uint8_t layer) {} -static inline bool layer_lock_set_kb(layer_state_t locked_layers) { - return true; -} -static inline bool layer_lock_set_user(layer_state_t locked_layers) { - return true; -} -static inline void layer_lock_task(void) {} -#endif // NO_ACTION_LAYER + +/** Update any configured timeouts */ +void layer_lock_activity_trigger(void); diff --git a/quantum/process_keycode/process_layer_lock.c b/quantum/process_keycode/process_layer_lock.c index 1e36d8844e..6946d3c886 100644 --- a/quantum/process_keycode/process_layer_lock.c +++ b/quantum/process_keycode/process_layer_lock.c @@ -12,14 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/** - * @file layer_lock.c - * @brief Layer Lock implementation - * - * For full documentation, see - * - */ - #include "layer_lock.h" #include "process_layer_lock.h" #include "quantum_keycodes.h" @@ -27,12 +19,9 @@ // The current lock state. The kth bit is on if layer k is locked. extern layer_state_t locked_layers; -#if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0 -extern uint32_t layer_lock_timer; -#endif // Handles an event on an `MO` or `TT` layer switch key. -static bool handle_mo_or_tt(uint8_t layer, keyrecord_t* record) { +static inline bool handle_mo_or_tt(uint8_t layer, keyrecord_t* record) { if (is_layer_locked(layer)) { if (record->event.pressed) { // On press, unlock the layer. layer_lock_invert(layer); @@ -44,9 +33,7 @@ static bool handle_mo_or_tt(uint8_t layer, keyrecord_t* record) { bool process_layer_lock(uint16_t keycode, keyrecord_t* record) { #ifndef NO_ACTION_LAYER -# if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0 - layer_lock_timer = timer_read32(); -# endif // LAYER_LOCK_IDLE_TIMEOUT > 0 + layer_lock_activity_trigger(); // The intention is that locked layers remain on. If something outside of // this feature turned any locked layers off, unlock them. diff --git a/quantum/process_keycode/process_layer_lock.h b/quantum/process_keycode/process_layer_lock.h index b54c0f6f10..6795110029 100644 --- a/quantum/process_keycode/process_layer_lock.h +++ b/quantum/process_keycode/process_layer_lock.h @@ -12,54 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -/** - * @file layer_lock.h - * @brief Layer Lock, a key to stay in the current layer. - * - * Overview - * -------- - * - * Layers are often accessed by holding a button, e.g. with a momentary layer - * switch `MO(layer)` or layer tap `LT(layer, key)` key. But you may sometimes - * want to "lock" or "toggle" the layer so that it stays on without having to - * hold down a button. One way to do that is with a tap-toggle `TT` layer key, - * but here is an alternative. - * - * This library implements a "Layer Lock key". When tapped, it "locks" the - * highest layer to stay active, assuming the layer was activated by one of the - * following keys: - * - * * `MO(layer)` momentary layer switch - * * `LT(layer, key)` layer tap - * * `OSL(layer)` one-shot layer - * * `TT(layer)` layer tap toggle - * * `LM(layer, mod)` layer-mod key (the layer is locked, but not the mods) - * - * Tapping the Layer Lock key again unlocks and turns off the layer. - * - * @note When a layer is "locked", other layer keys such as `TO(layer)` or - * manually calling `layer_off(layer)` will override and unlock the layer. - * - * Configuration - * ------------- - * - * Optionally, a timeout may be defined so that Layer Lock disables - * automatically if not keys are pressed for `LAYER_LOCK_IDLE_TIMEOUT` - * milliseconds. Define `LAYER_LOCK_IDLE_TIMEOUT` in your config.h, for instance - * - * #define LAYER_LOCK_IDLE_TIMEOUT 60000 // Turn off after 60 seconds. - * - * and call `layer_lock_task()` from your `matrix_scan_user()` in keymap.c: - * - * void matrix_scan_user(void) { - * layer_lock_task(); - * // Other tasks... - * } - * - * For full documentation, see - * - */ - #pragma once #include diff --git a/quantum/quantum.c b/quantum/quantum.c index 811ad2e715..4aef26a6a5 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -78,7 +78,7 @@ #ifdef LAYER_LOCK_ENABLE # include "process_layer_lock.h" -#endif // LAYER_LOCK_ENABLE +#endif #ifdef AUDIO_ENABLE # ifndef GOODBYE_SONG diff --git a/quantum/quantum.h b/quantum/quantum.h index 71cf900f2d..9db88a54d4 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -242,7 +242,7 @@ extern layer_state_t layer_state; #ifdef LAYER_LOCK_ENABLE # include "layer_lock.h" -#endif // LAYER_LOCK_ENABLE +#endif void set_single_default_layer(uint8_t default_layer); void set_single_persistent_default_layer(uint8_t default_layer); -- cgit v1.2.3