forked from mirrors/qmk_userspace
		
	[Core] Tri Layer Keys (#19795)
Co-authored-by: wilba <wilba@wilba.tech> Co-authored-by: Pablo Martínez <58857054+elpekenin@users.noreply.github.com> Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Nick Brassel <nick@tzarc.org>
This commit is contained in:
		
					parent
					
						
							
								4002843797
							
						
					
				
			
			
				commit
				
					
						fe02abc479
					
				
			
		
					 19 changed files with 377 additions and 15 deletions
				
			
		| 
						 | 
					@ -620,6 +620,7 @@ ifeq ($(strip $(VIA_ENABLE)), yes)
 | 
				
			||||||
    DYNAMIC_KEYMAP_ENABLE := yes
 | 
					    DYNAMIC_KEYMAP_ENABLE := yes
 | 
				
			||||||
    RAW_ENABLE := yes
 | 
					    RAW_ENABLE := yes
 | 
				
			||||||
    BOOTMAGIC_ENABLE := yes
 | 
					    BOOTMAGIC_ENABLE := yes
 | 
				
			||||||
 | 
					    TRI_LAYER_ENABLE := yes
 | 
				
			||||||
    SRC += $(QUANTUM_DIR)/via.c
 | 
					    SRC += $(QUANTUM_DIR)/via.c
 | 
				
			||||||
    OPT_DEFS += -DVIA_ENABLE
 | 
					    OPT_DEFS += -DVIA_ENABLE
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,7 @@ GENERIC_FEATURES = \
 | 
				
			||||||
    VELOCIKEY \
 | 
					    VELOCIKEY \
 | 
				
			||||||
    WPM \
 | 
					    WPM \
 | 
				
			||||||
    DYNAMIC_TAPPING_TERM \
 | 
					    DYNAMIC_TAPPING_TERM \
 | 
				
			||||||
 | 
					    TRI_LAYER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define HANDLE_GENERIC_FEATURE
 | 
					define HANDLE_GENERIC_FEATURE
 | 
				
			||||||
    # $$(info "Processing: $1_ENABLE $2.c")
 | 
					    # $$(info "Processing: $1_ENABLE $2.c")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,8 @@ OTHER_OPTION_NAMES = \
 | 
				
			||||||
  PROGRAMMABLE_BUTTON_ENABLE \
 | 
					  PROGRAMMABLE_BUTTON_ENABLE \
 | 
				
			||||||
  SECURE_ENABLE \
 | 
					  SECURE_ENABLE \
 | 
				
			||||||
  CAPS_WORD_ENABLE \
 | 
					  CAPS_WORD_ENABLE \
 | 
				
			||||||
  AUTOCORRECT_ENABLE
 | 
					  AUTOCORRECT_ENABLE \
 | 
				
			||||||
 | 
					  TRI_LAYER_ENABLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
define NAME_ECHO
 | 
					define NAME_ECHO
 | 
				
			||||||
       @printf "  %-30s = %-16s # %s\\n" "$1" "$($1)" "$(origin $1)"
 | 
					       @printf "  %-30s = %-16s # %s\\n" "$1" "$($1)" "$(origin $1)"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								data/constants/keycodes/keycodes_0.0.2_quantum.hjson
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								data/constants/keycodes/keycodes_0.0.2_quantum.hjson
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    "keycodes": {
 | 
				
			||||||
 | 
					        "0x7C77": {
 | 
				
			||||||
 | 
					            "group": "quantum",
 | 
				
			||||||
 | 
					            "key": "QK_TRI_LAYER_LOWER",
 | 
				
			||||||
 | 
					            "aliases": [
 | 
				
			||||||
 | 
					                "TL_LOWR"
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "0x7C78": {
 | 
				
			||||||
 | 
					            "group": "quantum",
 | 
				
			||||||
 | 
					            "key": "QK_TRI_LAYER_UPPER",
 | 
				
			||||||
 | 
					            "aliases": [
 | 
				
			||||||
 | 
					                "TL_UPPR"
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -93,6 +93,7 @@
 | 
				
			||||||
    * [Swap Hands](feature_swap_hands.md)
 | 
					    * [Swap Hands](feature_swap_hands.md)
 | 
				
			||||||
    * [Tap Dance](feature_tap_dance.md)
 | 
					    * [Tap Dance](feature_tap_dance.md)
 | 
				
			||||||
    * [Tap-Hold Configuration](tap_hold.md)
 | 
					    * [Tap-Hold Configuration](tap_hold.md)
 | 
				
			||||||
 | 
					    * [Tri Layer](feature_tri_layer.md)
 | 
				
			||||||
    * [Unicode](feature_unicode.md)
 | 
					    * [Unicode](feature_unicode.md)
 | 
				
			||||||
    * [Userspace](feature_userspace.md)
 | 
					    * [Userspace](feature_userspace.md)
 | 
				
			||||||
    * [WPM Calculation](feature_wpm.md)
 | 
					    * [WPM Calculation](feature_wpm.md)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										48
									
								
								docs/feature_tri_layer.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								docs/feature_tri_layer.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					# Tri Layers :id=tri-layers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This enables support for the OLKB style "Tri Layer" keycodes.  These function similar to the `MO` (momentary) function key, but if both the "Lower" and "Upper" keys are pressed, it activates a third "Adjust" layer.  To enable this functionality, add this line to your `rules.mk`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```make
 | 
				
			||||||
 | 
					TRI_LAYER_ENABLE = yes
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the "upper", "lower" and "adjust" names don't have a particular significance, they are just used to identify and clarify the behavior. Layers are processed from highest numeric value to lowest, however the values are not required to be consecutive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For a detailed explanation of how the layer stack works, check out [Keymap Overview](keymap.md#keymap-and-layers).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Keycodes :id=keycodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Keycode              | Alias     | Description                                                                                             |
 | 
				
			||||||
 | 
					|----------------------|-----------|---------------------------------------------------------------------------------------------------------|
 | 
				
			||||||
 | 
					| `QK_TRI_LAYER_LOWER` | `TL_LOWR` | Momentarily enables the "lower" layer. Enables the "adjust" layer if the "upper" layer is also enabled" |
 | 
				
			||||||
 | 
					| `QK_TRI_LAYER_UPPER` | `TL_UPPR` | Momentarily enables the "upper" layer. Enables the "adjust" layer if the "lower" layer is also enabled" |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To change the default values for the layers, you can change these defines, in your `config.h`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Config name              | Default | Description                              |
 | 
				
			||||||
 | 
					|--------------------------|---------|------------------------------------------|
 | 
				
			||||||
 | 
					| `TRI_LAYER_LOWER_LAYER`  | `1`     | Sets the default for the "lower" layer.  |
 | 
				
			||||||
 | 
					| `TRI_LAYER_UPPER_LAYER`  | `2`     | Sets the default for the "upper" layer.  |
 | 
				
			||||||
 | 
					| `TRI_LAYER_ADJUST_LAYER` | `3`     | Sets the default for the "adjust" layer. |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Eg, if you wanted to set the "Adjust" layer to be layer 5, you'd add this to your `config.h`: 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```c
 | 
				
			||||||
 | 
					#define TRI_LAYER_ADJUST_LAYER 5
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Function name                                |  Description                                    |
 | 
				
			||||||
 | 
					|----------------------------------------------|-------------------------------------------------|
 | 
				
			||||||
 | 
					| `set_tri_layer_lower_layer(layer)`           | Changes the "lower" layer*.                     |
 | 
				
			||||||
 | 
					| `set_tri_layer_upper_layer(layer)`           | Changes the "upper" layer*.                     |
 | 
				
			||||||
 | 
					| `set_tri_layer_adjust_layer(layer)`          | Changes the "adjust" layer*.                    |
 | 
				
			||||||
 | 
					| `set_tri_layer_layers(lower, upper, adjust)` | Stes the "lower", "upper" and "adjust" layers*. |
 | 
				
			||||||
 | 
					| `get_tri_layer_lower_layer()`                | Gets the current "lower" layer.                 |
 | 
				
			||||||
 | 
					| `get_tri_layer_upper_layer()`                | Gets the current "upper" layer.                 |
 | 
				
			||||||
 | 
					| `get_tri_layer_adjust_layer()`               | Gets the current "adjust" layer.                |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					!> Note: these settings are not persisent, and will be reset to the default on power loss or power cycling of the controller.
 | 
				
			||||||
| 
						 | 
					@ -349,3 +349,13 @@ uint8_t layer_switch_get_layer(keypos_t key) {
 | 
				
			||||||
action_t layer_switch_get_action(keypos_t key) {
 | 
					action_t layer_switch_get_action(keypos_t key) {
 | 
				
			||||||
    return action_for_key(layer_switch_get_layer(key), key);
 | 
					    return action_for_key(layer_switch_get_layer(key), key);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3) {
 | 
				
			||||||
 | 
					    layer_state_t mask12 = ((layer_state_t)1 << layer1) | ((layer_state_t)1 << layer2);
 | 
				
			||||||
 | 
					    layer_state_t mask3  = (layer_state_t)1 << layer3;
 | 
				
			||||||
 | 
					    return (state & mask12) == mask12 ? (state | mask3) : (state & ~mask3);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3) {
 | 
				
			||||||
 | 
					    layer_state_set(update_tri_layer_state(layer_state, layer1, layer2, layer3));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,6 +113,25 @@ void          layer_and(layer_state_t state);
 | 
				
			||||||
void          layer_xor(layer_state_t state);
 | 
					void          layer_xor(layer_state_t state);
 | 
				
			||||||
layer_state_t layer_state_set_user(layer_state_t state);
 | 
					layer_state_t layer_state_set_user(layer_state_t state);
 | 
				
			||||||
layer_state_t layer_state_set_kb(layer_state_t state);
 | 
					layer_state_t layer_state_set_kb(layer_state_t state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Applies the tri layer to global layer state. Not be used in layer_state_set_(kb|user) functions.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param layer1 First layer to check for tri layer
 | 
				
			||||||
 | 
					 * @param layer2 Second layer to check for tri layer
 | 
				
			||||||
 | 
					 * @param layer3 Layer to activate if both other layers are enabled
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Applies the tri layer behavior to supplied layer bitmask, without using layer functions.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param state Original layer bitmask to check and modify
 | 
				
			||||||
 | 
					 * @param layer1 First layer to check for tri layer
 | 
				
			||||||
 | 
					 * @param layer2 Second layer to check for tri layer
 | 
				
			||||||
 | 
					 * @param layer3 Layer to activate if both other layers are enabled
 | 
				
			||||||
 | 
					 * @return layer_state_t returns a modified layer bitmask with tri layer modifications applied
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#    define layer_state 0
 | 
					#    define layer_state 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,6 +150,8 @@ layer_state_t layer_state_set_kb(layer_state_t state);
 | 
				
			||||||
#    define layer_xor(state) (void)state
 | 
					#    define layer_xor(state) (void)state
 | 
				
			||||||
#    define layer_state_set_kb(state) (void)state
 | 
					#    define layer_state_set_kb(state) (void)state
 | 
				
			||||||
#    define layer_state_set_user(state) (void)state
 | 
					#    define layer_state_set_user(state) (void)state
 | 
				
			||||||
 | 
					#    define update_tri_layer(layer1, layer2, layer3)
 | 
				
			||||||
 | 
					#    define update_tri_layer_state(state, layer1, layer2, layer3) (void)state
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* pressed actions cache */
 | 
					/* pressed actions cache */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -717,6 +717,8 @@ enum qk_keycode_defines {
 | 
				
			||||||
    QK_AUTOCORRECT_ON = 0x7C74,
 | 
					    QK_AUTOCORRECT_ON = 0x7C74,
 | 
				
			||||||
    QK_AUTOCORRECT_OFF = 0x7C75,
 | 
					    QK_AUTOCORRECT_OFF = 0x7C75,
 | 
				
			||||||
    QK_AUTOCORRECT_TOGGLE = 0x7C76,
 | 
					    QK_AUTOCORRECT_TOGGLE = 0x7C76,
 | 
				
			||||||
 | 
					    QK_TRI_LAYER_LOWER = 0x7C77,
 | 
				
			||||||
 | 
					    QK_TRI_LAYER_UPPER = 0x7C78,
 | 
				
			||||||
    SAFE_RANGE = 0x7E00,
 | 
					    SAFE_RANGE = 0x7E00,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Alias
 | 
					// Alias
 | 
				
			||||||
| 
						 | 
					@ -1282,6 +1284,8 @@ enum qk_keycode_defines {
 | 
				
			||||||
    AC_ON      = QK_AUTOCORRECT_ON,
 | 
					    AC_ON      = QK_AUTOCORRECT_ON,
 | 
				
			||||||
    AC_OFF     = QK_AUTOCORRECT_OFF,
 | 
					    AC_OFF     = QK_AUTOCORRECT_OFF,
 | 
				
			||||||
    AC_TOGG    = QK_AUTOCORRECT_TOGGLE,
 | 
					    AC_TOGG    = QK_AUTOCORRECT_TOGGLE,
 | 
				
			||||||
 | 
					    TL_LOWR    = QK_TRI_LAYER_LOWER,
 | 
				
			||||||
 | 
					    TL_UPPR    = QK_TRI_LAYER_UPPER,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Range Helpers
 | 
					// Range Helpers
 | 
				
			||||||
| 
						 | 
					@ -1333,4 +1337,4 @@ enum qk_keycode_defines {
 | 
				
			||||||
#define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31)
 | 
					#define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31)
 | 
				
			||||||
#define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING)
 | 
					#define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING)
 | 
				
			||||||
#define IS_RGB_KEYCODE(code) ((code) >= RGB_TOG && (code) <= RGB_MODE_TWINKLE)
 | 
					#define IS_RGB_KEYCODE(code) ((code) >= RGB_TOG && (code) <= RGB_MODE_TWINKLE)
 | 
				
			||||||
#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_AUTOCORRECT_TOGGLE)
 | 
					#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_TRI_LAYER_UPPER)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										30
									
								
								quantum/process_keycode/process_tri_layer.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								quantum/process_keycode/process_tri_layer.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					// Copyright 2023 QMK
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "process_tri_layer.h"
 | 
				
			||||||
 | 
					#include "tri_layer.h"
 | 
				
			||||||
 | 
					#include "action_layer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool process_tri_layer(uint16_t keycode, keyrecord_t *record) {
 | 
				
			||||||
 | 
					    switch (keycode) {
 | 
				
			||||||
 | 
					        case QK_TRI_LAYER_LOWER:
 | 
				
			||||||
 | 
					            if (record->event.pressed) {
 | 
				
			||||||
 | 
					                layer_on(get_tri_layer_lower_layer());
 | 
				
			||||||
 | 
					                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                layer_off(get_tri_layer_lower_layer());
 | 
				
			||||||
 | 
					                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        case QK_TRI_LAYER_UPPER:
 | 
				
			||||||
 | 
					            if (record->event.pressed) {
 | 
				
			||||||
 | 
					                layer_on(get_tri_layer_upper_layer());
 | 
				
			||||||
 | 
					                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                layer_off(get_tri_layer_upper_layer());
 | 
				
			||||||
 | 
					                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										16
									
								
								quantum/process_keycode/process_tri_layer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								quantum/process_keycode/process_tri_layer.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					// Copyright 2023 QMK
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "action.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Handles tri layer behavior
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param keycode the keycode
 | 
				
			||||||
 | 
					 * @param record the key record structure
 | 
				
			||||||
 | 
					 * @return true continue handling keycodes
 | 
				
			||||||
 | 
					 * @return false stop handling keycodes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool process_tri_layer(uint16_t keycode, keyrecord_t *record);
 | 
				
			||||||
| 
						 | 
					@ -342,6 +342,9 @@ bool process_record_quantum(keyrecord_t *record) {
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#ifdef AUTOCORRECT_ENABLE
 | 
					#ifdef AUTOCORRECT_ENABLE
 | 
				
			||||||
            process_autocorrect(keycode, record) &&
 | 
					            process_autocorrect(keycode, record) &&
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifdef TRI_LAYER_ENABLE
 | 
				
			||||||
 | 
					            process_tri_layer(keycode, record) &&
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
            true)) {
 | 
					            true)) {
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
| 
						 | 
					@ -443,16 +446,6 @@ void set_single_persistent_default_layer(uint8_t default_layer) {
 | 
				
			||||||
    default_layer_set((layer_state_t)1 << default_layer);
 | 
					    default_layer_set((layer_state_t)1 << default_layer);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3) {
 | 
					 | 
				
			||||||
    layer_state_t mask12 = ((layer_state_t)1 << layer1) | ((layer_state_t)1 << layer2);
 | 
					 | 
				
			||||||
    layer_state_t mask3  = (layer_state_t)1 << layer3;
 | 
					 | 
				
			||||||
    return (state & mask12) == mask12 ? (state | mask3) : (state & ~mask3);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3) {
 | 
					 | 
				
			||||||
    layer_state_set(update_tri_layer_state(layer_state, layer1, layer2, layer3));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//------------------------------------------------------------------------------
 | 
					//------------------------------------------------------------------------------
 | 
				
			||||||
// Override these functions in your keymap file to play different tunes on
 | 
					// Override these functions in your keymap file to play different tunes on
 | 
				
			||||||
// different events such as startup and bootloader jump
 | 
					// different events such as startup and bootloader jump
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -240,9 +240,10 @@ extern layer_state_t layer_state;
 | 
				
			||||||
#    include "process_autocorrect.h"
 | 
					#    include "process_autocorrect.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// For tri-layer
 | 
					#ifdef TRI_LAYER_ENABLE
 | 
				
			||||||
void          update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3);
 | 
					#    include "tri_layer.h"
 | 
				
			||||||
layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3);
 | 
					#    include "process_tri_layer.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void set_single_persistent_default_layer(uint8_t default_layer);
 | 
					void set_single_persistent_default_layer(uint8_t default_layer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										39
									
								
								quantum/tri_layer.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								quantum/tri_layer.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					// Copyright 2023 QMK
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "tri_layer.h"
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static uint8_t tri_layer_lower_layer  = TRI_LAYER_LOWER_LAYER;
 | 
				
			||||||
 | 
					static uint8_t tri_layer_upper_layer  = TRI_LAYER_UPPER_LAYER;
 | 
				
			||||||
 | 
					static uint8_t tri_layer_adjust_layer = TRI_LAYER_ADJUST_LAYER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_tri_layer_lower_layer(uint8_t layer) {
 | 
				
			||||||
 | 
					    tri_layer_lower_layer = layer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_tri_layer_upper_layer(uint8_t layer) {
 | 
				
			||||||
 | 
					    tri_layer_upper_layer = layer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_tri_layer_adjust_layer(uint8_t layer) {
 | 
				
			||||||
 | 
					    tri_layer_adjust_layer = layer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_tri_layer_layers(uint8_t lower, uint8_t raise, uint8_t adjust) {
 | 
				
			||||||
 | 
					    tri_layer_lower_layer  = lower;
 | 
				
			||||||
 | 
					    tri_layer_upper_layer  = raise;
 | 
				
			||||||
 | 
					    tri_layer_adjust_layer = adjust;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint8_t get_tri_layer_lower_layer(void) {
 | 
				
			||||||
 | 
					    return tri_layer_lower_layer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint8_t get_tri_layer_upper_layer(void) {
 | 
				
			||||||
 | 
					    return tri_layer_upper_layer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint8_t get_tri_layer_adjust_layer(void) {
 | 
				
			||||||
 | 
					    return tri_layer_adjust_layer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										59
									
								
								quantum/tri_layer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								quantum/tri_layer.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					// Copyright 2023 QMK
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef TRI_LAYER_LOWER_LAYER
 | 
				
			||||||
 | 
					#    define TRI_LAYER_LOWER_LAYER 1
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifndef TRI_LAYER_UPPER_LAYER
 | 
				
			||||||
 | 
					#    define TRI_LAYER_UPPER_LAYER 2
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifndef TRI_LAYER_ADJUST_LAYER
 | 
				
			||||||
 | 
					#    define TRI_LAYER_ADJUST_LAYER 3
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Set the tri layer lower layer index
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param layer
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void set_tri_layer_lower_layer(uint8_t layer);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Set the tri layer upper layer index
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param layer
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void set_tri_layer_upper_layer(uint8_t layer);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Set the tri layer adjust layer index
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param layer
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void set_tri_layer_adjust_layer(uint8_t layer);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Set the tri layer indices
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param lower
 | 
				
			||||||
 | 
					 * @param upper
 | 
				
			||||||
 | 
					 * @param adjust
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void set_tri_layer_layers(uint8_t lower, uint8_t upper, uint8_t adjust);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Get the tri layer lower layer index
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return uint8_t
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					uint8_t get_tri_layer_lower_layer(void);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Get the tri layer upper layer index
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return uint8_t
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					uint8_t get_tri_layer_upper_layer(void);
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Get the tri layer adjust layer index
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @return uint8_t
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					uint8_t get_tri_layer_adjust_layer(void);
 | 
				
			||||||
| 
						 | 
					@ -659,5 +659,7 @@ std::map<uint16_t, std::string> KEYCODE_ID_TABLE = {
 | 
				
			||||||
    {QK_AUTOCORRECT_ON, "QK_AUTOCORRECT_ON"},
 | 
					    {QK_AUTOCORRECT_ON, "QK_AUTOCORRECT_ON"},
 | 
				
			||||||
    {QK_AUTOCORRECT_OFF, "QK_AUTOCORRECT_OFF"},
 | 
					    {QK_AUTOCORRECT_OFF, "QK_AUTOCORRECT_OFF"},
 | 
				
			||||||
    {QK_AUTOCORRECT_TOGGLE, "QK_AUTOCORRECT_TOGGLE"},
 | 
					    {QK_AUTOCORRECT_TOGGLE, "QK_AUTOCORRECT_TOGGLE"},
 | 
				
			||||||
 | 
					    {QK_TRI_LAYER_LOWER, "QK_TRI_LAYER_LOWER"},
 | 
				
			||||||
 | 
					    {QK_TRI_LAYER_UPPER, "QK_TRI_LAYER_UPPER"},
 | 
				
			||||||
    {SAFE_RANGE, "SAFE_RANGE"},
 | 
					    {SAFE_RANGE, "SAFE_RANGE"},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								tests/tri_layer/config.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/tri_layer/config.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "test_common.h"
 | 
				
			||||||
							
								
								
									
										8
									
								
								tests/tri_layer/test.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/tri_layer/test.mk
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					# Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# --------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					# Keep this file, even if it is empty, as a marker that this folder contains tests
 | 
				
			||||||
 | 
					# --------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TRI_LAYER_ENABLE = yes
 | 
				
			||||||
							
								
								
									
										103
									
								
								tests/tri_layer/test_tri_layer.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								tests/tri_layer/test_tri_layer.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,103 @@
 | 
				
			||||||
 | 
					// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "test_common.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using testing::_;
 | 
				
			||||||
 | 
					using testing::InSequence;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TriLayer : public TestFixture {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_F(TriLayer, TriLayerLowerTest) {
 | 
				
			||||||
 | 
					    TestDriver driver;
 | 
				
			||||||
 | 
					    KeymapKey  lower_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_LOWER};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set_keymap({lower_layer_key, KeymapKey{1, 0, 0, KC_TRNS}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Press Lower. */
 | 
				
			||||||
 | 
					    EXPECT_NO_REPORT(driver);
 | 
				
			||||||
 | 
					    lower_layer_key.press();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					    VERIFY_AND_CLEAR(driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Release Lower. */
 | 
				
			||||||
 | 
					    EXPECT_NO_REPORT(driver);
 | 
				
			||||||
 | 
					    lower_layer_key.release();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					    VERIFY_AND_CLEAR(driver);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_F(TriLayer, TriLayerUpperTest) {
 | 
				
			||||||
 | 
					    TestDriver driver;
 | 
				
			||||||
 | 
					    KeymapKey  upper_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_UPPER};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set_keymap({upper_layer_key, KeymapKey{2, 0, 0, KC_TRNS}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Press Raise. */
 | 
				
			||||||
 | 
					    EXPECT_NO_REPORT(driver);
 | 
				
			||||||
 | 
					    upper_layer_key.press();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					    VERIFY_AND_CLEAR(driver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Release Raise. */
 | 
				
			||||||
 | 
					    EXPECT_NO_REPORT(driver);
 | 
				
			||||||
 | 
					    upper_layer_key.release();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					    VERIFY_AND_CLEAR(driver);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST_F(TriLayer, TriLayerAdjustTest) {
 | 
				
			||||||
 | 
					    TestDriver driver;
 | 
				
			||||||
 | 
					    KeymapKey  lower_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_LOWER};
 | 
				
			||||||
 | 
					    KeymapKey  upper_layer_key = KeymapKey{0, 1, 0, QK_TRI_LAYER_UPPER};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    set_keymap({
 | 
				
			||||||
 | 
					        upper_layer_key,
 | 
				
			||||||
 | 
					        lower_layer_key,
 | 
				
			||||||
 | 
					        KeymapKey{1, 0, 0, KC_TRNS},
 | 
				
			||||||
 | 
					        KeymapKey{1, 1, 0, KC_TRNS},
 | 
				
			||||||
 | 
					        KeymapKey{2, 0, 0, KC_TRNS},
 | 
				
			||||||
 | 
					        KeymapKey{2, 1, 0, KC_TRNS},
 | 
				
			||||||
 | 
					        KeymapKey{3, 0, 0, KC_TRNS},
 | 
				
			||||||
 | 
					        KeymapKey{3, 1, 0, KC_TRNS},
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Press Lower, then upper, and release upper and then lower. */
 | 
				
			||||||
 | 
					    EXPECT_NO_REPORT(driver);
 | 
				
			||||||
 | 
					    lower_layer_key.press();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    upper_layer_key.press();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_TRUE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    lower_layer_key.release();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    upper_layer_key.release();
 | 
				
			||||||
 | 
					    run_one_scan_loop();
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));
 | 
				
			||||||
 | 
					    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));
 | 
				
			||||||
 | 
					    VERIFY_AND_CLEAR(driver);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue