Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core] Tri Layer Keys #19795

Merged
merged 14 commits into from
Feb 11, 2023
1 change: 1 addition & 0 deletions builddefs/common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ ifeq ($(strip $(VIA_ENABLE)), yes)
DYNAMIC_KEYMAP_ENABLE := yes
RAW_ENABLE := yes
BOOTMAGIC_ENABLE := yes
TRI_LAYER_ENABLE := yes
SRC += $(QUANTUM_DIR)/via.c
OPT_DEFS += -DVIA_ENABLE
endif
Expand Down
1 change: 1 addition & 0 deletions builddefs/generic_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ GENERIC_FEATURES = \
VELOCIKEY \
WPM \
DYNAMIC_TAPPING_TERM \
TRI_LAYER

define HANDLE_GENERIC_FEATURE
# $$(info "Processing: $1_ENABLE $2.c")
Expand Down
3 changes: 2 additions & 1 deletion builddefs/show_options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ OTHER_OPTION_NAMES = \
PROGRAMMABLE_BUTTON_ENABLE \
SECURE_ENABLE \
CAPS_WORD_ENABLE \
AUTOCORRECT_ENABLE
AUTOCORRECT_ENABLE \
TRI_LAYER_ENABLE

define NAME_ECHO
@printf " %-30s = %-16s # %s\\n" "$1" "$($1)" "$(origin $1)"
Expand Down
18 changes: 18 additions & 0 deletions data/constants/keycodes/keycodes_0.0.2_quantum.hjson
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
}
1 change: 1 addition & 0 deletions docs/_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
* [Swap Hands](feature_swap_hands.md)
* [Tap Dance](feature_tap_dance.md)
* [Tap-Hold Configuration](tap_hold.md)
* [Tri Layer](feature_tri_layer.md)
* [Unicode](feature_unicode.md)
* [Userspace](feature_userspace.md)
* [WPM Calculation](feature_wpm.md)
Expand Down
48 changes: 48 additions & 0 deletions docs/feature_tri_layer.md
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 10 additions & 0 deletions quantum/action_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,13 @@ uint8_t layer_switch_get_layer(keypos_t key) {
action_t layer_switch_get_action(keypos_t 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));
}
21 changes: 21 additions & 0 deletions quantum/action_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,25 @@ void layer_and(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_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
# define layer_state 0

Expand All @@ -131,6 +150,8 @@ layer_state_t layer_state_set_kb(layer_state_t state);
# define layer_xor(state) (void)state
# define layer_state_set_kb(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

/* pressed actions cache */
Expand Down
6 changes: 5 additions & 1 deletion quantum/keycodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,8 @@ enum qk_keycode_defines {
QK_AUTOCORRECT_ON = 0x7C74,
QK_AUTOCORRECT_OFF = 0x7C75,
QK_AUTOCORRECT_TOGGLE = 0x7C76,
QK_TRI_LAYER_LOWER = 0x7C77,
QK_TRI_LAYER_UPPER = 0x7C78,
SAFE_RANGE = 0x7E00,

// Alias
Expand Down Expand Up @@ -1282,6 +1284,8 @@ enum qk_keycode_defines {
AC_ON = QK_AUTOCORRECT_ON,
AC_OFF = QK_AUTOCORRECT_OFF,
AC_TOGG = QK_AUTOCORRECT_TOGGLE,
TL_LOWR = QK_TRI_LAYER_LOWER,
TL_UPPR = QK_TRI_LAYER_UPPER,
};

// Range Helpers
Expand Down Expand Up @@ -1333,4 +1337,4 @@ enum qk_keycode_defines {
#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_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)
32 changes: 32 additions & 0 deletions quantum/process_keycode/process_tri_layer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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;
break;
drashna marked this conversation as resolved.
Show resolved Hide resolved
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;
break;
drashna marked this conversation as resolved.
Show resolved Hide resolved
}
return true;
}
16 changes: 16 additions & 0 deletions quantum/process_keycode/process_tri_layer.h
Original file line number Diff line number Diff line change
@@ -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);
13 changes: 3 additions & 10 deletions quantum/quantum.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ bool process_record_quantum(keyrecord_t *record) {
#endif
#ifdef AUTOCORRECT_ENABLE
process_autocorrect(keycode, record) &&
#endif
#ifdef TRI_LAYER_ENABLE
process_tri_layer(keycode, record) &&
#endif
true)) {
return false;
Expand Down Expand Up @@ -443,16 +446,6 @@ void set_single_persistent_default_layer(uint8_t 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
// different events such as startup and bootloader jump
Expand Down
7 changes: 4 additions & 3 deletions quantum/quantum.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,10 @@ extern layer_state_t layer_state;
# include "process_autocorrect.h"
#endif

// For tri-layer
void update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3);
layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3);
#ifdef TRI_LAYER_ENABLE
# include "tri_layer.h"
# include "process_tri_layer.h"
#endif

void set_single_persistent_default_layer(uint8_t default_layer);

Expand Down
39 changes: 39 additions & 0 deletions quantum/tri_layer.c
Original file line number Diff line number Diff line change
@@ -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 changes: 59 additions & 0 deletions quantum/tri_layer.h
Original file line number Diff line number Diff line change
@@ -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 layer value
drashna marked this conversation as resolved.
Show resolved Hide resolved
*
* @param layer
*/
void set_tri_layer_lower_layer(uint8_t layer);
/**
* @brief Set the tri layer upper layer layer value
drashna marked this conversation as resolved.
Show resolved Hide resolved
*
* @param layer
*/
void set_tri_layer_upper_layer(uint8_t layer);
/**
* @brief Set the tri layer adjust layer object
drashna marked this conversation as resolved.
Show resolved Hide resolved
*
* @param layer
*/
void set_tri_layer_adjust_layer(uint8_t layer);
/**
* @brief Set the tri layer layers object
drashna marked this conversation as resolved.
Show resolved Hide resolved
*
* @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 object
drashna marked this conversation as resolved.
Show resolved Hide resolved
*
* @return uint8_t
*/
uint8_t get_tri_layer_lower_layer(void);
/**
* @brief Get the tri layer upper layer object
drashna marked this conversation as resolved.
Show resolved Hide resolved
*
* @return uint8_t
*/
uint8_t get_tri_layer_upper_layer(void);
/**
* @brief Get the tri layer adjust layer object
drashna marked this conversation as resolved.
Show resolved Hide resolved
*
* @return uint8_t
*/
uint8_t get_tri_layer_adjust_layer(void);
2 changes: 2 additions & 0 deletions tests/test_common/keycode_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,5 +659,7 @@ std::map<uint16_t, std::string> KEYCODE_ID_TABLE = {
{QK_AUTOCORRECT_ON, "QK_AUTOCORRECT_ON"},
{QK_AUTOCORRECT_OFF, "QK_AUTOCORRECT_OFF"},
{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"},
};
6 changes: 6 additions & 0 deletions tests/tri_layer/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <[email protected]>
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "test_common.h"
8 changes: 8 additions & 0 deletions tests/tri_layer/test.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <[email protected]>
# 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
Loading