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:
parent
18b8b9b3a5
commit
ef2e6e9156
28 changed files with 425 additions and 11 deletions
|
@ -14,3 +14,6 @@ properties:
|
||||||
mods:
|
mods:
|
||||||
type: int
|
type: int
|
||||||
required: true
|
required: true
|
||||||
|
keep-mods:
|
||||||
|
type: int
|
||||||
|
required: false
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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, \
|
||||||
|
|
|
@ -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;
|
||||||
|
|
8
app/tests/mod-morph/1-no-morph/events.patterns
Normal file
8
app/tests/mod-morph/1-no-morph/events.patterns
Normal 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
|
5
app/tests/mod-morph/1-no-morph/keycode_events.snapshot
Normal file
5
app/tests/mod-morph/1-no-morph/keycode_events.snapshot
Normal 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
|
11
app/tests/mod-morph/1-no-morph/native_posix_64.keymap
Normal file
11
app/tests/mod-morph/1-no-morph/native_posix_64.keymap
Normal 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)
|
||||||
|
>;
|
||||||
|
};
|
8
app/tests/mod-morph/2a-masked-morph/events.patterns
Normal file
8
app/tests/mod-morph/2a-masked-morph/events.patterns
Normal 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
|
12
app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot
Normal file
12
app/tests/mod-morph/2a-masked-morph/keycode_events.snapshot
Normal 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
|
13
app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap
Normal file
13
app/tests/mod-morph/2a-masked-morph/native_posix_64.keymap
Normal 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)
|
||||||
|
>;
|
||||||
|
};
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||||
|
>;
|
||||||
|
};
|
|
@ -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
|
|
@ -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
|
|
@ -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>, << 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
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -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
|
|
@ -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
|
|
@ -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>, << 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
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
8
app/tests/mod-morph/3-unmasked-morph/events.patterns
Normal file
8
app/tests/mod-morph/3-unmasked-morph/events.patterns
Normal 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
|
12
app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot
Normal file
12
app/tests/mod-morph/3-unmasked-morph/keycode_events.snapshot
Normal 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
|
37
app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap
Normal file
37
app/tests/mod-morph/3-unmasked-morph/native_posix_64.keymap
Normal 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)
|
||||||
|
>;
|
||||||
|
};
|
23
app/tests/mod-morph/behavior_keymap.dtsi
Normal file
23
app/tests/mod-morph/behavior_keymap.dtsi
Normal 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
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -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
|
||||||
|
|
|
@ -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)>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
Loading…
Reference in a new issue