feat(behaviors): Add mod-morph `keep-mods`

* Update docs for mod-morph
* Add unit tests for mod-morph
* Add keep-mods to DT binding

Co-authored-by: Martin Aumüller <aumuell@reserv.at>
Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
This commit is contained in:
Robert U 2022-10-14 21:40:28 -04:00 committed by GitHub
parent 18b8b9b3a5
commit ef2e6e9156
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 425 additions and 11 deletions

View File

@ -14,3 +14,6 @@ properties:
mods: mods:
type: int type: int
required: true required: true
keep-mods:
type: int
required: false

View File

@ -135,6 +135,8 @@ int zmk_hid_register_mods(zmk_mod_flags_t explicit_modifiers);
int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers); int zmk_hid_unregister_mods(zmk_mod_flags_t explicit_modifiers);
int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers); int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers);
int zmk_hid_implicit_modifiers_release(); int zmk_hid_implicit_modifiers_release();
int zmk_hid_masked_modifiers_set(zmk_mod_flags_t masked_modifiers);
int zmk_hid_masked_modifiers_clear();
int zmk_hid_keyboard_press(zmk_key_t key); int zmk_hid_keyboard_press(zmk_key_t key);
int zmk_hid_keyboard_release(zmk_key_t key); int zmk_hid_keyboard_release(zmk_key_t key);

View File

@ -27,6 +27,7 @@ struct behavior_mod_morph_config {
struct zmk_behavior_binding normal_binding; struct zmk_behavior_binding normal_binding;
struct zmk_behavior_binding morph_binding; struct zmk_behavior_binding morph_binding;
zmk_mod_flags_t mods; zmk_mod_flags_t mods;
zmk_mod_flags_t masked_mods;
}; };
struct behavior_mod_morph_data { struct behavior_mod_morph_data {
@ -45,6 +46,7 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding,
} }
if (zmk_hid_get_explicit_mods() & cfg->mods) { if (zmk_hid_get_explicit_mods() & cfg->mods) {
zmk_hid_masked_modifiers_set(cfg->masked_mods);
data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding; data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding;
} else { } else {
data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding; data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding;
@ -64,7 +66,10 @@ static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding *pressed_binding = data->pressed_binding; struct zmk_behavior_binding *pressed_binding = data->pressed_binding;
data->pressed_binding = NULL; data->pressed_binding = NULL;
return behavior_keymap_binding_released(pressed_binding, event); int err;
err = behavior_keymap_binding_released(pressed_binding, event);
zmk_hid_masked_modifiers_clear();
return err;
} }
static const struct behavior_driver_api behavior_mod_morph_driver_api = { static const struct behavior_driver_api behavior_mod_morph_driver_api = {
@ -88,6 +93,8 @@ static int behavior_mod_morph_init(const struct device *dev) { return 0; }
.normal_binding = _TRANSFORM_ENTRY(0, n), \ .normal_binding = _TRANSFORM_ENTRY(0, n), \
.morph_binding = _TRANSFORM_ENTRY(1, n), \ .morph_binding = _TRANSFORM_ENTRY(1, n), \
.mods = DT_INST_PROP(n, mods), \ .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))), \
}; \ }; \
static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \ static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \
DEVICE_DT_INST_DEFINE(n, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##n, \ DEVICE_DT_INST_DEFINE(n, behavior_mod_morph_init, NULL, &behavior_mod_morph_data_##n, \

View File

@ -20,10 +20,12 @@ static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body =
// Only release the modifier if the count is 0. // Only release the modifier if the count is 0.
static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0}; static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
static zmk_mod_flags_t explicit_modifiers = 0; static zmk_mod_flags_t explicit_modifiers = 0;
static zmk_mod_flags_t implicit_modifiers = 0;
static zmk_mod_flags_t masked_modifiers = 0;
#define SET_MODIFIERS(mods) \ #define SET_MODIFIERS(mods) \
{ \ { \
keyboard_report.body.modifiers = mods; \ keyboard_report.body.modifiers = (mods & ~masked_modifiers) | implicit_modifiers; \
LOG_DBG("Modifiers set to 0x%02X", keyboard_report.body.modifiers); \ LOG_DBG("Modifiers set to 0x%02X", keyboard_report.body.modifiers); \
} }
@ -158,13 +160,29 @@ static inline int check_keyboard_usage(zmk_key_t usage) {
} \ } \
} }
int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers) { int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t new_implicit_modifiers) {
implicit_modifiers = new_implicit_modifiers;
zmk_mod_flags_t current = GET_MODIFIERS; zmk_mod_flags_t current = GET_MODIFIERS;
SET_MODIFIERS(explicit_modifiers | implicit_modifiers); SET_MODIFIERS(explicit_modifiers);
return current == GET_MODIFIERS ? 0 : 1; return current == GET_MODIFIERS ? 0 : 1;
} }
int zmk_hid_implicit_modifiers_release() { int zmk_hid_implicit_modifiers_release() {
implicit_modifiers = 0;
zmk_mod_flags_t current = GET_MODIFIERS;
SET_MODIFIERS(explicit_modifiers);
return current == GET_MODIFIERS ? 0 : 1;
}
int zmk_hid_masked_modifiers_set(zmk_mod_flags_t new_masked_modifiers) {
masked_modifiers = new_masked_modifiers;
zmk_mod_flags_t current = GET_MODIFIERS;
SET_MODIFIERS(explicit_modifiers);
return current == GET_MODIFIERS ? 0 : 1;
}
int zmk_hid_masked_modifiers_clear() {
masked_modifiers = 0;
zmk_mod_flags_t current = GET_MODIFIERS; zmk_mod_flags_t current = GET_MODIFIERS;
SET_MODIFIERS(explicit_modifiers); SET_MODIFIERS(explicit_modifiers);
return current == GET_MODIFIERS ? 0 : 1; return current == GET_MODIFIERS ? 0 : 1;

View File

@ -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

View File

@ -0,0 +1,5 @@
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

View File

@ -0,0 +1,11 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

View File

@ -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

View File

@ -0,0 +1,12 @@
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

View File

@ -0,0 +1,13 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(1,0,10)
>;
};

View File

@ -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

View File

@ -0,0 +1,12 @@
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 0x02 explicit_mods 0x00
reg implicit: Modifiers set to 0x02
released: keycode 0x05 implicit_mods 0x02 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

View File

@ -0,0 +1,37 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
mod_morph: mod_morph {
compatible = "zmk,behavior-mod-morph";
label = "MOD_MORPH_TEST";
#binding-cells = <0>;
bindings = <&kp A>, <&kp LS(B)>; // implict mod overwrite
mods = <(MOD_LSFT|MOD_RSFT)>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp LEFT_ALT &mod_morph
&kp LEFT_SHIFT &kp RIGHT_SHIFT
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(1,0,10)
>;
};

View File

@ -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

View File

@ -0,0 +1,18 @@
pressed: keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x04
reg implicit: Modifiers set to 0x04
pressed: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
reg explicit: Modifiers set to 0x06
reg implicit: Modifiers set to 0x06
mask mods: Modifiers set to 0x04
pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x04
released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: Modifiers set to 0x04
unmask mods: Modifiers set to 0x06
released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x04
unreg implicit: Modifiers set to 0x04
released: keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00

View File

@ -0,0 +1,15 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View File

@ -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

View File

@ -0,0 +1,12 @@
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

View File

@ -0,0 +1,45 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
&kscan {
events = <
/* Shift + tap &mod_morph --> expect B (but get Shift + B) */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};
/ {
behaviors {
mod_morph: mod_morph {
compatible = "zmk,behavior-mod-morph";
label = "MOD_MORPH_TEST";
#binding-cells = <0>;
bindings = <&kp A>, <&lt 1 B>;
mods = <(MOD_LSFT|MOD_RSFT)>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp LEFT_SHIFT &mod_morph
&kp C &none
>;
};
second_layer {
bindings = <
&trans &trans
&kp D &trans
>;
};
};
};

View File

@ -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

View File

@ -0,0 +1,12 @@
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 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 0x02
released: keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
unreg explicit: Modifiers set to 0x00
unreg implicit: Modifiers set to 0x00

View File

@ -0,0 +1,47 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
&kscan {
events = <
/* Shift + hold &mod_morph --> expect and get D (no shift) */
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,200)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};
/ {
behaviors {
mod_morph: mod_morph {
compatible = "zmk,behavior-mod-morph";
label = "MOD_MORPH_TEST";
#binding-cells = <0>;
bindings = <&kp A>, <&lt 1 B>;
mods = <(MOD_LSFT|MOD_RSFT)>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp LEFT_SHIFT &mod_morph
&kp C &none
>;
};
second_layer {
bindings = <
&trans &trans
&kp D &trans
>;
};
};
};

View File

@ -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

View File

@ -0,0 +1,12 @@
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 0x02
pressed: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
reg implicit: Modifiers set to 0x02
released: keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
unreg implicit: 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

View File

@ -0,0 +1,37 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
/ {
behaviors {
mod_morph: mod_morph {
compatible = "zmk,behavior-mod-morph";
label = "MOD_MORPH_TEST";
#binding-cells = <0>;
bindings = <&kp A>, <&kp B>;
mods = <(MOD_LSFT|MOD_RSFT)>;
keep-mods = <(MOD_LSFT|MOD_RSFT)>; // no masking
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp LEFT_ALT &mod_morph
&kp LEFT_SHIFT &kp RIGHT_SHIFT
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(1,0,10)
>;
};

View File

@ -0,0 +1,23 @@
/ {
behaviors {
mod_morph: mod_morph {
compatible = "zmk,behavior-mod-morph";
label = "MOD_MORPH_TEST";
#binding-cells = <0>;
bindings = <&kp A>, <&kp B>;
mods = <(MOD_LSFT|MOD_RSFT)>;
};
};
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&kp LEFT_ALT &mod_morph
&kp LEFT_SHIFT &kp RIGHT_SHIFT
>;
};
};
};

View File

@ -7,7 +7,7 @@ mods: Modifiers set to 0x03
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00 released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
unreg: Modifier 0 count: 0 unreg: Modifier 0 count: 0
unreg: Modifier 0 released unreg: Modifier 0 released
unreg: Modifiers set to 0x00 unreg: Modifiers set to 0x02
mods: Modifiers set to 0x00 mods: Modifiers set to 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x02 explicit_mods 0x00 released: usage_page 0x07 keycode 0x05 implicit_mods 0x02 explicit_mods 0x00
mods: Modifiers set to 0x00 mods: Modifiers set to 0x00

View File

@ -14,8 +14,6 @@ The Mod-Morph behavior sends a different keypress, depending on whether a specif
The Mod-Morph behavior acts as one of two keycodes, depending on if the required modifier is being held during the keypress. The Mod-Morph behavior acts as one of two keycodes, depending on if the required modifier is being held during the keypress.
When the modifier is being held it is sent along with the morphed keycode. This can cause problems when the morphed keycode and modifier have an existing relationship (such as `shift-delete` or `ctrl-v` on many operating systems).
### Configuration ### Configuration
An example of how to implement the mod-morph "Grave Escape": An example of how to implement the mod-morph "Grave Escape":
@ -31,10 +29,6 @@ An example of how to implement the mod-morph "Grave Escape":
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
}; };
}; };
keymap {
...
};
}; };
``` ```
@ -71,3 +65,26 @@ Example:
``` ```
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
``` ```
### Advanced configuration
`keep-mods`
When a modifier specified in `mods` is being held, it won't be sent along with the morphed keycode unless it is also specified in `keep-mods`. By default `keep-mods` equals `0`, which means no modifier specified in `mods` will be sent along with the morphed keycode.
For example, the following configuration morphs `LEFT_SHIFT` + `BACKSPACE` into `DELETE`, and morphs `RIGHT_SHIFT` + `BACKSPACE` into `RIGHT_SHIFT` + `DELETE`.
```
/ {
behaviors {
bspc_del: backspace_delete {
compatible = "zmk,behavior-mod-morph";
label = "BACKSPACE_DELETE";
#binding-cells = <0>;
bindings = <&kp BACKSPACE>, <&kp DELETE>;
mods = <(MOD_LSFT|MOD_RSFT)>;
keep-mods = <(MOD_RSFT)>;
};
};
};
```