Skip to content

Commit

Permalink
feat: Add parameterised mod morph
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick-Munnich committed Dec 18, 2024
1 parent d0016b3 commit 9e78e7b
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 33 deletions.
1 change: 1 addition & 0 deletions app/dts/behaviors.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <behaviors/transparent.dtsi>
#include <behaviors/none.dtsi>
#include <behaviors/mod_tap.dtsi>
#include <behaviors/mod_morph.dtsi>
#include <behaviors/layer_tap.dtsi>
#include <behaviors/gresc.dtsi>
#include <behaviors/sticky_key.dtsi>
Expand Down
23 changes: 23 additions & 0 deletions app/dts/behaviors/mod_morph.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(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)>;
};
};
};
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
13 changes: 13 additions & 0 deletions app/dts/bindings/behaviors/zmk,behavior-mod-morph-param.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

description: Mod Morph Behavior

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

include: [two_param.yaml, mod_morph_base.yaml]

properties:
binding-params:
type: array
required: true
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]
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)
56 changes: 36 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,36 @@ 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 KP_INST(inst) \
BUILD_ASSERT((COND_CODE_0(DT_NODE_HAS_PROP(inst, binding_params), (0), \
((((DT_PROP_BY_IDX(inst, binding_params, 0) >> 2) & \
(DT_PROP_BY_IDX(inst, binding_params, 0))) | \
((DT_PROP_BY_IDX(inst, binding_params, 1) >> 2) & \
(DT_PROP_BY_IDX(inst, binding_params, 1)))))) == 0), \
"Invalid binding parameters"); \
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, KP_INST)
DT_FOREACH_STATUS_OKAY(zmk_behavior_mod_morph_param, KP_INST)

#endif
8 changes: 8 additions & 0 deletions app/tests/mod-morph/4a-morph-params-singles/events.patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p
s/.*hid_listener_keycode_released.*keycode/released: keycode/p
s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p
s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p
s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p
s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p
s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p
s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
pressed: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x00
pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x02
reg implicit: Modifiers set to 0x02
mask mods: Modifiers set to 0x00
pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x02
released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00
pressed: keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x00
unmask mods: Modifiers set to 0x00
pressed: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x01
reg implicit: Modifiers set to 0x01
mask mods: Modifiers set to 0x00
pressed: keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x07 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x01
unmask mods: Modifiers set to 0x01
released: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00
pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x02
reg implicit: Modifiers set to 0x02
mask mods: Modifiers set to 0x00
pressed: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x02
unmask mods: Modifiers set to 0x02
pressed: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x03
reg implicit: Modifiers set to 0x03
mask mods: Modifiers set to 0x02
mask mods: Modifiers set to 0x01
pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x01
released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x01
unmask mods: Modifiers set to 0x03
unmask mods: Modifiers set to 0x03
released: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x02
unreg implicit: Modifiers set to 0x02
released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00
51 changes: 51 additions & 0 deletions app/tests/mod-morph/4a-morph-params-singles/native_posix_64.keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>

/ {
behaviors {
mm1: mod_morph_param1 {
compatible = "zmk,behavior-mod-morph-param";
#binding-cells = <2>;
bindings = <&mm C PLACEHOLDER>, <&mm D PLACEHOLDER>;
mods = <(MOD_LCTL|MOD_RCTL)>;
binding-params = <BINDING_PARAM(0,1) BINDING_PARAM(0,2)>;
};
};

keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&kp LEFT_CONTROL &mm1 A B
&kp LEFT_SHIFT &mm A B
>;
};
};
};


&kscan {
events = <
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(1,0,10)
>;
};
8 changes: 8 additions & 0 deletions app/tests/mod-morph/4b-morph-params-doubles/events.patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
s/.*hid_listener_keycode_pressed.*keycode/pressed: keycode/p
s/.*hid_listener_keycode_released.*keycode/released: keycode/p
s/.*hid_register_mod.*Modifiers set to /reg explicit: Modifiers set to /p
s/.*hid_unregister_mod.*Modifiers set to /unreg explicit: Modifiers set to /p
s/.*hid_implicit_modifiers_press.*Modifiers set to /reg implicit: Modifiers set to /p
s/.*hid_implicit_modifiers_release.*Modifiers set to /unreg implicit: Modifiers set to /p
s/.*hid_masked_modifiers_set.*Modifiers set to /mask mods: Modifiers set to /p
s/.*hid_masked_modifiers_clear.*Modifiers set to /unmask mods: Modifiers set to /p
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
pressed: keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x00
unmask mods: Modifiers set to 0x00
pressed: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x01
reg implicit: Modifiers set to 0x01
mask mods: Modifiers set to 0x00
pressed: keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x01
unmask mods: Modifiers set to 0x01
released: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00
pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x02
reg implicit: Modifiers set to 0x02
mask mods: Modifiers set to 0x00
pressed: keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x02
unmask mods: Modifiers set to 0x02
pressed: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x03
reg implicit: Modifiers set to 0x03
mask mods: Modifiers set to 0x02
mask mods: Modifiers set to 0x01
pressed: keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x01
released: keycode 0x08 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x01
unmask mods: Modifiers set to 0x03
unmask mods: Modifiers set to 0x03
released: keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x02
unreg implicit: Modifiers set to 0x02
released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00
pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x02
reg implicit: Modifiers set to 0x02
mask mods: Modifiers set to 0x00
pressed: keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x00
released: keycode 0x09 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x00
unmask mods: Modifiers set to 0x02
unmask mods: Modifiers set to 0x02
released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00
Loading

0 comments on commit 9e78e7b

Please sign in to comment.