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

feat: Parameterised Mod Morph and Tap Dance #2724

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/dts/behaviors.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <behaviors/none.dtsi>
#include <behaviors/mod_tap.dtsi>
#include <behaviors/layer_tap.dtsi>
#include <behaviors/tap_dance.dtsi>
#include <behaviors/mod_morph.dtsi>
#include <behaviors/gresc.dtsi>
#include <behaviors/sticky_key.dtsi>
#include <behaviors/momentary_layer.dtsi>
Expand Down
24 changes: 24 additions & 0 deletions app/dts/behaviors/mod_morph.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#include <dt-bindings/zmk/behaviors.h>
#include <dt-bindings/zmk/keys.h>

/ {
behaviors {
#if ZMK_BEHAVIOR_OMIT(MODMORPH)
/omit-if-no-ref/
#endif
mm: mod_morph_kp {
compatible = "zmk,behavior-mod-morph-param";
#binding-cells = <2>;
bindings = <&kp PLACEHOLDER>, <&kp PLACEHOLDER>;
display-name = "Mod-Morph";
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
binding-params = <BINDING_PARAM(1,0) BINDING_PARAM(2,0)>;
};
};
};
23 changes: 23 additions & 0 deletions app/dts/behaviors/tap_dance.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#include <dt-bindings/zmk/behaviors.h>

/ {
behaviors {
#if ZMK_BEHAVIOR_OMIT(TAPDANCE)
/omit-if-no-ref/
#endif
td: tap_dance_kp {
compatible = "zmk,behavior-tap-dance-param";
#binding-cells = <2>;
bindings = <&kp PLACEHOLDER>, <&kp PLACEHOLDER>;
display-name = "Tap-Dance";
tapping-term-ms = <200>;
binding-params = <BINDING_PARAM(1,0) BINDING_PARAM(2,0)>;
};
};
};
6 changes: 6 additions & 0 deletions app/dts/bindings/behaviors/binding_params.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT

properties:
binding-params:
type: array
13 changes: 13 additions & 0 deletions app/dts/bindings/behaviors/mod_morph_base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT

properties:
bindings:
type: phandle-array
required: true
mods:
type: int
required: true
keep-mods:
type: int
required: false
10 changes: 10 additions & 0 deletions app/dts/bindings/behaviors/tap_dance_base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT

properties:
bindings:
type: phandle-array
required: true
tapping-term-ms:
type: int
default: 200
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT

description: Mod Morph Behavior

compatible: "zmk,behavior-mod-morph-param"

include: [two_param.yaml, mod_morph_base.yaml, binding_params.yaml]
13 changes: 1 addition & 12 deletions app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,4 @@ description: Mod Morph Behavior

compatible: "zmk,behavior-mod-morph"

include: zero_param.yaml

properties:
bindings:
type: phandle-array
required: true
mods:
type: int
required: true
keep-mods:
type: int
required: false
include: [zero_param.yaml, mod_morph_base.yaml]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT

description: Tap Dance Behavior

compatible: "zmk,behavior-tap-dance-param"

include: [two_param.yaml, tap_dance_base.yaml, binding_params.yaml]
10 changes: 1 addition & 9 deletions app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,4 @@ description: Tap Dance Behavior

compatible: "zmk,behavior-tap-dance"

include: zero_param.yaml

properties:
bindings:
type: phandle-array
required: true
tapping-term-ms:
type: int
default: 200
include: [zero_param.yaml, tap_dance_base.yaml]
5 changes: 4 additions & 1 deletion app/include/dt-bindings/zmk/behaviors.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@

#define ZMK_BEHAVIOR_OMIT(_name) \
!(defined(ZMK_BEHAVIORS_KEEP_##_name) || \
(defined(ZMK_BEHAVIORS_KEEP_ALL) && !defined(ZMK_BEHAVIORS_OMIT_##_name)))
(defined(ZMK_BEHAVIORS_KEEP_ALL) && !defined(ZMK_BEHAVIORS_OMIT_##_name)))

#define PLACEHOLDER 0
#define BINDING_PARAM(arg1, arg2) ((arg1 << 2) | arg2)
62 changes: 42 additions & 20 deletions app/src/behaviors/behavior_mod_morph.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
* SPDX-License-Identifier: MIT
*/

#define DT_DRV_COMPAT zmk_behavior_mod_morph

#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
Expand All @@ -21,13 +19,15 @@

LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#if DT_HAS_COMPAT_STATUS_OKAY(zmk_behavior_mod_morph) || \
DT_HAS_COMPAT_STATUS_OKAY(zmk_behavior_mod_morph_param)

struct behavior_mod_morph_config {
struct zmk_behavior_binding normal_binding;
struct zmk_behavior_binding morph_binding;
zmk_mod_flags_t mods;
zmk_mod_flags_t masked_mods;
uint8_t binding_params;
};

struct behavior_mod_morph_data {
Expand All @@ -39,6 +39,7 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding,
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
const struct behavior_mod_morph_config *cfg = dev->config;
struct behavior_mod_morph_data *data = dev->data;
uint8_t map = cfg->binding_params;

if (data->pressed_binding != NULL) {
LOG_ERR("Can't press the same mod-morph twice");
Expand All @@ -50,7 +51,12 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding,
data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding;
} else {
data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding;
map = map >> 4;
}
if (map & 0b1100)
data->pressed_binding->param1 = (map & 0b0100) ? binding->param1 : binding->param2;
if (map & 0b0011)
data->pressed_binding->param2 = (map & 0b0001) ? binding->param1 : binding->param2;
return zmk_behavior_invoke_binding(data->pressed_binding, event, true);
}

Expand Down Expand Up @@ -84,26 +90,42 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; }

#define _TRANSFORM_ENTRY(idx, node) \
{ \
.behavior_dev = DEVICE_DT_NAME(DT_INST_PHANDLE_BY_IDX(node, bindings, idx)), \
.param1 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \
(DT_INST_PHA_BY_IDX(node, bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_INST_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \
(DT_INST_PHA_BY_IDX(node, bindings, idx, param2))), \
.behavior_dev = DEVICE_DT_NAME(DT_PHANDLE_BY_IDX(node, bindings, idx)), \
.param1 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param1), (0), \
(DT_PHA_BY_IDX(node, bindings, idx, param1))), \
.param2 = COND_CODE_0(DT_PHA_HAS_CELL_AT_IDX(node, bindings, idx, param2), (0), \
(DT_PHA_BY_IDX(node, bindings, idx, param2))), \
}

#define KP_INST(n) \
static struct behavior_mod_morph_config behavior_mod_morph_config_##n = { \
.normal_binding = _TRANSFORM_ENTRY(0, n), \
.morph_binding = _TRANSFORM_ENTRY(1, n), \
.mods = DT_INST_PROP(n, mods), \
.masked_mods = COND_CODE_0(DT_INST_NODE_HAS_PROP(n, keep_mods), (DT_INST_PROP(n, mods)), \
(DT_INST_PROP(n, mods) & ~DT_INST_PROP(n, keep_mods))), \
#define VALIDATE_BINDING_PARAMS(inst, prop, idx) \
BUILD_ASSERT( \
(DT_PROP_LEN(inst, bindings) == DT_PROP_LEN(inst, binding_params)), \
DT_NODE_FULL_NAME(inst) ": The number of binding params must match the bindings"); \
BUILD_ASSERT(((((DT_PROP_BY_IDX(inst, prop, idx) >> 2) & 3) <= 2) && \
((DT_PROP_BY_IDX(inst, prop, idx) & 3) <= 2)), \
DT_NODE_FULL_NAME( \
inst) ": Invalid binding parameters at index " #idx ". " \
"Use the BINDING_PARAM(x,y) macro with valid arguments (0, 1, or 2).");

#define MM_INST(inst) \
COND_CODE_0(DT_NODE_HAS_PROP(inst, binding_params), (), \
(DT_FOREACH_PROP_ELEM(inst, binding_params, VALIDATE_BINDING_PARAMS))); \
static struct behavior_mod_morph_config behavior_mod_morph_config_##inst = { \
.normal_binding = _TRANSFORM_ENTRY(0, inst), \
.morph_binding = _TRANSFORM_ENTRY(1, inst), \
.mods = DT_PROP(inst, mods), \
.masked_mods = COND_CODE_0(DT_NODE_HAS_PROP(inst, keep_mods), (DT_PROP(inst, mods)), \
(DT_PROP(inst, mods) & ~DT_PROP(inst, keep_mods))), \
.binding_params = (COND_CODE_0(DT_NODE_HAS_PROP(inst, binding_params), (0), \
((DT_PROP_BY_IDX(inst, binding_params, 0) << 4) | \
(DT_PROP_BY_IDX(inst, binding_params, 1))))), \
}; \
static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \
BEHAVIOR_DT_INST_DEFINE(n, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##n, \
&behavior_mod_morph_config_##n, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mod_morph_driver_api);
static struct behavior_mod_morph_data behavior_mod_morph_data_##inst = {}; \
BEHAVIOR_DT_DEFINE(inst, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##inst, \
&behavior_mod_morph_config_##inst, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mod_morph_driver_api);

DT_INST_FOREACH_STATUS_OKAY(KP_INST)
DT_FOREACH_STATUS_OKAY(zmk_behavior_mod_morph, MM_INST)
DT_FOREACH_STATUS_OKAY(zmk_behavior_mod_morph_param, MM_INST)

#endif
Loading
Loading