summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOkke Formsma <okke@formsma.nl>2020-12-26 14:46:56 +0100
committerPete Johanson <peter@peterjohanson.com>2021-01-22 19:27:18 -0500
commit5eeb310b2f0ccf1adf91b448573001559696a1d7 (patch)
treea3a711afe29b2df3dc781a8103ce58b21626433c
parent58ad5d4e865341e736f4f80e6c3ec31d168fc509 (diff)
feat(grave-escape): implement grave-escape
closes #85
-rw-r--r--app/CMakeLists.txt1
-rw-r--r--app/dts/behaviors.dtsi1
-rw-r--r--app/dts/behaviors/gresc.dtsi19
-rw-r--r--app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml16
-rw-r--r--app/include/zmk/hid.h1
-rw-r--r--app/src/behaviors/behavior_mod_morph.c99
-rw-r--r--app/src/hid.c2
-rw-r--r--app/tests/gresc/gresc-press-release/events.patterns1
-rw-r--r--app/tests/gresc/gresc-press-release/keycode_events.snapshot18
-rw-r--r--app/tests/gresc/gresc-press-release/native_posix.keymap49
-rw-r--r--app/tests/gresc/gresc-two-instances/events.patterns2
-rw-r--r--app/tests/gresc/gresc-two-instances/keycode_events.snapshot6
-rw-r--r--app/tests/gresc/gresc-two-instances/native_posix.keymap43
13 files changed, 258 insertions, 0 deletions
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index b217a1a..83cf1bb 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -45,6 +45,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c)
target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c)
+ target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c)
target_sources(app PRIVATE src/behaviors/behavior_outputs.c)
target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c)
target_sources(app PRIVATE src/behaviors/behavior_to_layer.c)
diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi
index daa073f..4333cee 100644
--- a/app/dts/behaviors.dtsi
+++ b/app/dts/behaviors.dtsi
@@ -3,6 +3,7 @@
#include <behaviors/none.dtsi>
#include <behaviors/mod_tap.dtsi>
#include <behaviors/layer_tap.dtsi>
+#include <behaviors/gresc.dtsi>
#include <behaviors/sticky_key.dtsi>
#include <behaviors/momentary_layer.dtsi>
#include <behaviors/toggle_layer.dtsi>
diff --git a/app/dts/behaviors/gresc.dtsi b/app/dts/behaviors/gresc.dtsi
new file mode 100644
index 0000000..2959388
--- /dev/null
+++ b/app/dts/behaviors/gresc.dtsi
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <dt-bindings/zmk/keys.h>
+
+/ {
+ behaviors {
+ /omit-if-no-ref/ gresc: grave_escape {
+ compatible = "zmk,behavior-mod-morph";
+ label = "GRAVE_ESCAPE";
+ #binding-cells = <0>;
+ bindings = <&kp ESC>, <&kp GRAVE>;
+ mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
+ };
+ };
+};
diff --git a/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml
new file mode 100644
index 0000000..66b452a
--- /dev/null
+++ b/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml
@@ -0,0 +1,16 @@
+# Copyright (c) 2020 The ZMK Contributors
+# SPDX-License-Identifier: MIT
+
+description: Keyboard Reset Behavior
+
+compatible: "zmk,behavior-mod-morph"
+
+include: zero_param.yaml
+
+properties:
+ bindings:
+ type: phandle-array
+ required: true
+ mods:
+ type: int
+ required: true
diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h
index 1ec5126..aca3cc4 100644
--- a/app/include/zmk/hid.h
+++ b/app/include/zmk/hid.h
@@ -166,6 +166,7 @@ struct zmk_hid_consumer_report {
struct zmk_hid_consumer_report_body body;
} __packed;
+zmk_mod_flags_t zmk_hid_get_explicit_mods();
int zmk_hid_register_mod(zmk_mod_t modifier);
int zmk_hid_unregister_mod(zmk_mod_t modifier);
int zmk_hid_implicit_modifiers_press(zmk_mod_flags_t implicit_modifiers);
diff --git a/app/src/behaviors/behavior_mod_morph.c b/app/src/behaviors/behavior_mod_morph.c
new file mode 100644
index 0000000..36d109b
--- /dev/null
+++ b/app/src/behaviors/behavior_mod_morph.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_behavior_mod_morph
+
+#include <device.h>
+#include <drivers/behavior.h>
+#include <logging/log.h>
+#include <zmk/behavior.h>
+
+#include <zmk/matrix.h>
+#include <zmk/endpoints.h>
+#include <zmk/event_manager.h>
+#include <zmk/events/position_state_changed.h>
+#include <zmk/events/keycode_state_changed.h>
+#include <zmk/events/modifiers_state_changed.h>
+#include <zmk/hid.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
+
+struct behavior_mod_morph_config {
+ struct zmk_behavior_binding normal_binding;
+ struct zmk_behavior_binding morph_binding;
+ zmk_mod_flags_t mods;
+};
+
+struct behavior_mod_morph_data {
+ struct zmk_behavior_binding *pressed_binding;
+};
+
+static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding,
+ struct zmk_behavior_binding_event event) {
+ const struct device *dev = device_get_binding(binding->behavior_dev);
+ const struct behavior_mod_morph_config *cfg = dev->config;
+ struct behavior_mod_morph_data *data = dev->data;
+
+ if (data->pressed_binding != NULL) {
+ LOG_ERR("Can't press the same mod-morph twice");
+ return -ENOTSUP;
+ }
+
+ if (zmk_hid_get_explicit_mods() & cfg->mods) {
+ data->pressed_binding = (struct zmk_behavior_binding *)&cfg->morph_binding;
+ } else {
+ data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding;
+ }
+ return behavior_keymap_binding_pressed(data->pressed_binding, event);
+}
+
+static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding,
+ struct zmk_behavior_binding_event event) {
+ const struct device *dev = device_get_binding(binding->behavior_dev);
+ struct behavior_mod_morph_data *data = dev->data;
+
+ if (data->pressed_binding == NULL) {
+ LOG_ERR("Mod-morph already released");
+ return -ENOTSUP;
+ }
+
+ struct zmk_behavior_binding *pressed_binding = data->pressed_binding;
+ data->pressed_binding = NULL;
+ return behavior_keymap_binding_released(pressed_binding, event);
+}
+
+static const struct behavior_driver_api behavior_mod_morph_driver_api = {
+ .binding_pressed = on_mod_morph_binding_pressed,
+ .binding_released = on_mod_morph_binding_released,
+};
+
+static int behavior_mod_morph_init(const struct device *dev) { return 0; }
+
+#define _TRANSFORM_ENTRY(idx, node) \
+ { \
+ .behavior_dev = DT_LABEL(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))), \
+ }
+
+#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), \
+ }; \
+ static struct behavior_mod_morph_data behavior_mod_morph_data_##n = {}; \
+ DEVICE_AND_API_INIT(behavior_mod_morph_##n, DT_INST_LABEL(n), behavior_mod_morph_init, \
+ &behavior_mod_morph_data_##n, &behavior_mod_morph_config_##n, APPLICATION, \
+ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mod_morph_driver_api);
+
+DT_INST_FOREACH_STATUS_OKAY(KP_INST)
+
+#endif \ No newline at end of file
diff --git a/app/src/hid.c b/app/src/hid.c
index b9ddfc5..65eabd9 100644
--- a/app/src/hid.c
+++ b/app/src/hid.c
@@ -26,6 +26,8 @@ static zmk_mod_flags_t explicit_modifiers = 0;
LOG_DBG("Modifiers set to 0x%02X", keyboard_report.body.modifiers); \
}
+zmk_mod_flags_t zmk_hid_get_explicit_mods() { return explicit_modifiers; }
+
int zmk_hid_register_mod(zmk_mod_t modifier) {
explicit_modifier_counts[modifier]++;
LOG_DBG("Modifier %d count %d", modifier, explicit_modifier_counts[modifier]);
diff --git a/app/tests/gresc/gresc-press-release/events.patterns b/app/tests/gresc/gresc-press-release/events.patterns
new file mode 100644
index 0000000..b1342af
--- /dev/null
+++ b/app/tests/gresc/gresc-press-release/events.patterns
@@ -0,0 +1 @@
+s/.*hid_listener_keycode_//p
diff --git a/app/tests/gresc/gresc-press-release/keycode_events.snapshot b/app/tests/gresc/gresc-press-release/keycode_events.snapshot
new file mode 100644
index 0000000..ebeba59
--- /dev/null
+++ b/app/tests/gresc/gresc-press-release/keycode_events.snapshot
@@ -0,0 +1,18 @@
+pressed: usage_page 0x07 keycode 0x29 mods 0x00
+released: usage_page 0x07 keycode 0x29 mods 0x00
+pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+pressed: usage_page 0x07 keycode 0x35 mods 0x00
+released: usage_page 0x07 keycode 0x35 mods 0x00
+released: usage_page 0x07 keycode 0xe1 mods 0x00
+pressed: usage_page 0x07 keycode 0xe3 mods 0x00
+pressed: usage_page 0x07 keycode 0x35 mods 0x00
+released: usage_page 0x07 keycode 0x35 mods 0x00
+released: usage_page 0x07 keycode 0xe3 mods 0x00
+pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+pressed: usage_page 0x07 keycode 0x35 mods 0x00
+released: usage_page 0x07 keycode 0xe1 mods 0x00
+released: usage_page 0x07 keycode 0x35 mods 0x00
+pressed: usage_page 0x07 keycode 0xe3 mods 0x00
+pressed: usage_page 0x07 keycode 0x35 mods 0x00
+released: usage_page 0x07 keycode 0xe3 mods 0x00
+released: usage_page 0x07 keycode 0x35 mods 0x00
diff --git a/app/tests/gresc/gresc-press-release/native_posix.keymap b/app/tests/gresc/gresc-press-release/native_posix.keymap
new file mode 100644
index 0000000..7ca3d77
--- /dev/null
+++ b/app/tests/gresc/gresc-press-release/native_posix.keymap
@@ -0,0 +1,49 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &gresc &none
+ &kp LEFT_SHIFT &kp LEFT_GUI
+ >;
+ };
+ };
+};
+
+&kscan {
+ events = <
+ /* esc */
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+
+ /* ~ */
+ ZMK_MOCK_PRESS(1,0,10)
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ ZMK_MOCK_RELEASE(1,0,10)
+
+ /* LGUI+` */
+ ZMK_MOCK_PRESS(1,1,10)
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ ZMK_MOCK_RELEASE(1,1,10)
+
+ /* ~ */
+ ZMK_MOCK_PRESS(1,0,10)
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_RELEASE(1,0,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+
+ /* LGUI+` */
+ ZMK_MOCK_PRESS(1,1,10)
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_RELEASE(1,1,10)
+ ZMK_MOCK_RELEASE(0,0,10)
+ >;
+}; \ No newline at end of file
diff --git a/app/tests/gresc/gresc-two-instances/events.patterns b/app/tests/gresc/gresc-two-instances/events.patterns
new file mode 100644
index 0000000..ef7b795
--- /dev/null
+++ b/app/tests/gresc/gresc-two-instances/events.patterns
@@ -0,0 +1,2 @@
+s/.*hid_listener_keycode_//p
+s/.*on_mod_morph_binding_/morph_binding_/p \ No newline at end of file
diff --git a/app/tests/gresc/gresc-two-instances/keycode_events.snapshot b/app/tests/gresc/gresc-two-instances/keycode_events.snapshot
new file mode 100644
index 0000000..170e0cb
--- /dev/null
+++ b/app/tests/gresc/gresc-two-instances/keycode_events.snapshot
@@ -0,0 +1,6 @@
+pressed: usage_page 0x07 keycode 0x29 mods 0x00
+released: usage_page 0x07 keycode 0x29 mods 0x00
+pressed: usage_page 0x07 keycode 0xe1 mods 0x00
+pressed: usage_page 0x07 keycode 0x35 mods 0x00
+released: usage_page 0x07 keycode 0xe1 mods 0x00
+released: usage_page 0x07 keycode 0x35 mods 0x00
diff --git a/app/tests/gresc/gresc-two-instances/native_posix.keymap b/app/tests/gresc/gresc-two-instances/native_posix.keymap
new file mode 100644
index 0000000..0c38721
--- /dev/null
+++ b/app/tests/gresc/gresc-two-instances/native_posix.keymap
@@ -0,0 +1,43 @@
+#include <dt-bindings/zmk/keys.h>
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/kscan-mock.h>
+
+/*
+This test checks nothing breaks if two grave-escapes are pressed at the same time.
+If someone ever really needs two, they can make a second behavior definition.
+
+The second gresc that is pressed is ignored.
+The first gresc that is released releases the key.
+*/
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &gresc &gresc
+ &kp LEFT_SHIFT &kp LEFT_GUI
+ >;
+ };
+ };
+};
+
+&kscan {
+ events = <
+ /* esc */
+ ZMK_MOCK_PRESS(0,0,10)
+ ZMK_MOCK_PRESS(0,1,10) /* the second gresc is ignored */
+ ZMK_MOCK_RELEASE(0,0,10)
+ ZMK_MOCK_RELEASE(0,1,10) /* the second gresc is ignored */
+
+ /* ~ */
+ ZMK_MOCK_PRESS(1,0,10)
+ ZMK_MOCK_PRESS(0,1,10)
+ ZMK_MOCK_PRESS(0,0,10) /* the second gresc is ignored */
+ ZMK_MOCK_RELEASE(1,0,10)
+ ZMK_MOCK_RELEASE(0,1,10)
+ ZMK_MOCK_RELEASE(0,0,10) /* the second gresc is ignored */
+ >;
+}; \ No newline at end of file