diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 3e0560b7..3ede36b9 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -39,6 +39,7 @@ if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) target_sources(app PRIVATE src/behaviors/behavior_key_press.c) target_sources(app PRIVATE src/behaviors/behavior_reset.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) + target_sources(app PRIVATE src/behaviors/behavior_one_shot.c) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 202202b4..028f5af8 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/app/dts/behaviors/one_shot.dtsi b/app/dts/behaviors/one_shot.dtsi new file mode 100644 index 00000000..01b6f8c3 --- /dev/null +++ b/app/dts/behaviors/one_shot.dtsi @@ -0,0 +1,20 @@ +/ { + behaviors { + osk: behavior_one_shot_key { + compatible = "zmk,behavior-one-shot"; + label = "ONE_SHOT_KEY"; + #binding-cells = <1>; + release-after-ms = <1000>; + bindings = <&kp>; + }; + osl: behavior_one_shot_layer { + compatible = "zmk,behavior-one-shot"; + label = "ONE_SHOT_LAYER"; + #binding-cells = <1>; + release-after-ms = <1000>; + bindings = <&mo>; + }; + }; + +}; + diff --git a/app/dts/bindings/behaviors/zmk,behavior-one-shot.yaml b/app/dts/bindings/behaviors/zmk,behavior-one-shot.yaml new file mode 100644 index 00000000..2d9b8392 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-one-shot.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2020 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: One Shot behavior + +compatible: "zmk,behavior-one-shot" + +include: one_param.yaml + +properties: + bindings: + type: phandles + required: true + release-after-ms: + type: int \ No newline at end of file diff --git a/app/src/behaviors/behavior_one_shot.c b/app/src/behaviors/behavior_one_shot.c new file mode 100644 index 00000000..9577d3ee --- /dev/null +++ b/app/src/behaviors/behavior_one_shot.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_one_shot + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_NODE_EXISTS(DT_DRV_INST(0)) + +#define ZMK_BHV_ONE_SHOT_MAX_HELD 10 + +// increase this if you need more keys in the board +#define ZMK_BHV_ONE_SHOT_POSITION_NOT_USED 9999 + +struct behavior_one_shot_config { + s32_t release_after_ms; + struct zmk_behavior_binding behavior; +}; + +// this data is specific for each hold-tap +struct active_one_shot { + s32_t position; + s32_t param1; + s32_t param2; + const struct behavior_one_shot_config *config; + // timer data. + s32_t release_at; + struct k_delayed_work release_after_timer; + bool timer_is_cancelled; + // usage page and keycode for the key that is being modified by this one shot + u32_t modified_key_position; +}; + +struct active_one_shot active_one_shots[ZMK_BHV_ONE_SHOT_MAX_HELD] = {}; + +static struct active_one_shot *store_one_shot(u32_t position, u32_t param1, u32_t param2, + const struct behavior_one_shot_config *config) { + for (int i = 0; i < ZMK_BHV_ONE_SHOT_MAX_HELD; i++) { + if (active_one_shots[i].position != ZMK_BHV_ONE_SHOT_POSITION_NOT_USED) { + continue; + } + active_one_shots[i].position = position; + active_one_shots[i].param1 = param1; + active_one_shots[i].param2 = param2; + active_one_shots[i].config = config; + active_one_shots[i].release_at = 0; + active_one_shots[i].timer_is_cancelled = false; + active_one_shots[i].modified_key_position = ZMK_BHV_ONE_SHOT_POSITION_NOT_USED; + return &active_one_shots[i]; + } + return NULL; +} + +static void clear_one_shot(struct active_one_shot *one_shot) { + one_shot->position = ZMK_BHV_ONE_SHOT_POSITION_NOT_USED; +} + +static struct active_one_shot *find_one_shot(u32_t position) { + for (int i = 0; i < ZMK_BHV_ONE_SHOT_MAX_HELD; i++) { + if (active_one_shots[i].position == position) { + return &active_one_shots[i]; + } + } + return NULL; +} + +static inline int press_one_shot_behavior(struct active_one_shot *one_shot, s32_t timestamp) { + const struct zmk_behavior_binding *behavior = &one_shot->config->behavior; + struct device *behavior_device = device_get_binding(behavior->behavior_dev); + return behavior_keymap_binding_pressed(behavior_device, one_shot->position, one_shot->param1, + one_shot->param2, timestamp); +} + +static inline int release_one_shot_behavior(struct active_one_shot *one_shot, s32_t timestamp) { + const struct zmk_behavior_binding *behavior = &one_shot->config->behavior; + struct device *behavior_device = device_get_binding(behavior->behavior_dev); + return behavior_keymap_binding_released(behavior_device, one_shot->position, one_shot->param1, + one_shot->param2, timestamp); +} + +static int stop_timer(struct active_one_shot *one_shot) { + int timer_cancel_result = k_delayed_work_cancel(&one_shot->release_after_timer); + if (timer_cancel_result == -EINPROGRESS) { + // too late to cancel, we'll let the timer handler clear up. + one_shot->timer_is_cancelled = true; + } + return timer_cancel_result; +} + +static int on_one_shot_binding_pressed(struct device *dev, u32_t position, u32_t param1, + u32_t param2, s64_t timestamp) { + const struct behavior_one_shot_config *cfg = dev->config_info; + + struct active_one_shot *one_shot = store_one_shot(position, param1, param2, cfg); + if (one_shot == NULL) { + LOG_ERR("unable to store one-shot info, did you press more than %d one_shots?", + ZMK_BHV_ONE_SHOT_MAX_HELD); + return 0; + } + + press_one_shot_behavior(one_shot, timestamp); + LOG_DBG("%d new one_shot", position); + return 0; +} + +static int on_one_shot_binding_released(struct device *dev, u32_t position, u32_t _, u32_t __, + s64_t timestamp) { + struct active_one_shot *one_shot = find_one_shot(position); + if (one_shot == NULL) { + LOG_ERR("ACTIVE ONE SHOT CLEARED TOO EARLY"); + return 0; + } + + if (one_shot->modified_key_position != ZMK_BHV_ONE_SHOT_POSITION_NOT_USED) { + // Another key was pressed while the one-shot was pressed. Act like a normal key. + int retval = release_one_shot_behavior(one_shot, timestamp); + clear_one_shot(one_shot); + return retval; + } + + // No other key was pressed. Start the timer. + one_shot->release_at = timestamp + one_shot->config->release_after_ms; + // adjust timer in case this behavior was queued by a hold-tap + s32_t ms_left = one_shot->release_at - k_uptime_get(); + if (ms_left > 0) { + k_delayed_work_submit(&one_shot->release_after_timer, K_MSEC(ms_left)); + } + return 0; +} + +static const struct behavior_driver_api behavior_one_shot_driver_api = { + .binding_pressed = on_one_shot_binding_pressed, + .binding_released = on_one_shot_binding_released, +}; + +static int one_shot_keycode_state_changed_listener(const struct zmk_event_header *eh) { + if (!is_keycode_state_changed(eh)) { + return 0; + } + struct keycode_state_changed *ev = cast_keycode_state_changed(eh); + for (int i = 0; i < ZMK_BHV_ONE_SHOT_MAX_HELD; i++) { + struct active_one_shot *one_shot = &active_one_shots[i]; + if (one_shot->position == ZMK_BHV_ONE_SHOT_POSITION_NOT_USED || + one_shot->position == ev->position) { + continue; + } + // If events were queued, the timer event may be queued late or not at all. + // Release the one-shot if the timer should've run out in the meantime. + if (one_shot->release_at != 0 && ev->timestamp > one_shot->release_at) { + release_one_shot_behavior(one_shot, one_shot->release_at); + if (stop_timer(one_shot)) { + clear_one_shot(one_shot); + } + continue; + } + + if (ev->state) { // key down + if (one_shot->modified_key_position != ZMK_BHV_ONE_SHOT_POSITION_NOT_USED) { + continue; + } + one_shot->modified_key_position = ev->position; + if (one_shot->release_at) { + stop_timer(one_shot); + } + } else { // key up + if (one_shot->modified_key_position != ev->position || one_shot->release_at == 0) { + continue; + } + release_one_shot_behavior(one_shot, ev->timestamp); + } + } + return 0; +} + +ZMK_LISTENER(behavior_one_shot, one_shot_keycode_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_one_shot, keycode_state_changed); + +void behavior_one_shot_timer_handler(struct k_work *item) { + struct active_one_shot *one_shot = + CONTAINER_OF(item, struct active_one_shot, release_after_timer); + if (one_shot->position == ZMK_BHV_ONE_SHOT_POSITION_NOT_USED) { + return; + } + if (!one_shot->timer_is_cancelled) { + release_one_shot_behavior(one_shot, k_uptime_get()); + } + clear_one_shot(one_shot); +} + +static int behavior_one_shot_init(struct device *dev) { + static bool init_first_run = true; + if (init_first_run) { + for (int i = 0; i < ZMK_BHV_ONE_SHOT_MAX_HELD; i++) { + k_delayed_work_init(&active_one_shots[i].release_after_timer, + behavior_one_shot_timer_handler); + active_one_shots[i].position = ZMK_BHV_ONE_SHOT_POSITION_NOT_USED; + } + } + init_first_run = false; + return 0; +} + +struct behavior_one_shot_data {}; +static struct behavior_one_shot_data behavior_one_shot_data; + +#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_one_shot_config behavior_one_shot_config_##n = { \ + .behavior = _TRANSFORM_ENTRY(0, n).release_after_ms = DT_INST_PROP(n, release_after_ms), \ + }; \ + DEVICE_AND_API_INIT(behavior_one_shot_##n, DT_INST_LABEL(n), behavior_one_shot_init, \ + &behavior_one_shot_data, &behavior_one_shot_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_one_shot_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif \ No newline at end of file diff --git a/app/tests/one-shot/1-osk-dn-up/events.patterns b/app/tests/one-shot/1-osk-dn-up/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/one-shot/1-osk-dn-up/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/one-shot/1-osk-dn-up/keycode_events.snapshot b/app/tests/one-shot/1-osk-dn-up/keycode_events.snapshot new file mode 100644 index 00000000..0516e845 --- /dev/null +++ b/app/tests/one-shot/1-osk-dn-up/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0x08 +released: usage_page 0x07 keycode 0x08 +pressed: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x04 diff --git a/app/tests/one-shot/1-osk-dn-up/native_posix.keymap b/app/tests/one-shot/1-osk-dn-up/native_posix.keymap new file mode 100644 index 00000000..dd2e6ea5 --- /dev/null +++ b/app/tests/one-shot/1-osk-dn-up/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,1200) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/events.patterns b/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/keycode_events.snapshot b/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/keycode_events.snapshot new file mode 100644 index 00000000..8dd2f9c5 --- /dev/null +++ b/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0x08 +pressed: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x08 +released: usage_page 0x07 keycode 0x04 diff --git a/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/native_posix.keymap b/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/native_posix.keymap new file mode 100644 index 00000000..588f6a1a --- /dev/null +++ b/app/tests/one-shot/2-osk-dn-up-kcdn-kcup/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/events.patterns b/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/keycode_events.snapshot b/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/keycode_events.snapshot new file mode 100644 index 00000000..0a4d9bde --- /dev/null +++ b/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0x08 +pressed: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x08 diff --git a/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/native_posix.keymap b/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/native_posix.keymap new file mode 100644 index 00000000..3c33908f --- /dev/null +++ b/app/tests/one-shot/3a-osk-dn-kcdn-kcup-up/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/events.patterns b/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/keycode_events.snapshot b/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/keycode_events.snapshot new file mode 100644 index 00000000..8dd2f9c5 --- /dev/null +++ b/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0x08 +pressed: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x08 +released: usage_page 0x07 keycode 0x04 diff --git a/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/native_posix.keymap b/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/native_posix.keymap new file mode 100644 index 00000000..e9290a31 --- /dev/null +++ b/app/tests/one-shot/3b-osk-dn-kcdn-up-kcup/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/events.patterns b/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/keycode_events.snapshot b/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/keycode_events.snapshot new file mode 100644 index 00000000..8dd2f9c5 --- /dev/null +++ b/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0x08 +pressed: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x08 +released: usage_page 0x07 keycode 0x04 diff --git a/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/native_posix.keymap b/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/native_posix.keymap new file mode 100644 index 00000000..84daad69 --- /dev/null +++ b/app/tests/one-shot/4-osk-dn-up-kcdn-timer-kcup/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,800) + ZMK_MOCK_PRESS(1,0,400) + ZMK_MOCK_RELEASE(1,0,10) + >; +}; \ No newline at end of file diff --git a/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/events.patterns b/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/keycode_events.snapshot b/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/keycode_events.snapshot new file mode 100644 index 00000000..239ed17c --- /dev/null +++ b/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/keycode_events.snapshot @@ -0,0 +1,4 @@ +pressed: usage_page 0x07 keycode 0x04 +pressed: usage_page 0x07 keycode 0x08 +released: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x08 diff --git a/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/native_posix.keymap b/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/native_posix.keymap new file mode 100644 index 00000000..71a0a1bb --- /dev/null +++ b/app/tests/one-shot/5-osk-kcdn-dn-kcup-up/native_posix.keymap @@ -0,0 +1,13 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(1,0,10) + ZMK_MOCK_RELEASE(0,0,1100) + >; +}; \ No newline at end of file diff --git a/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/events.patterns b/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/keycode_events.snapshot b/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/keycode_events.snapshot new file mode 100644 index 00000000..bfa9c974 --- /dev/null +++ b/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/keycode_events.snapshot @@ -0,0 +1,6 @@ +pressed: usage_page 0x07 keycode 0x08 +pressed: usage_page 0x07 keycode 0x04 +pressed: usage_page 0x07 keycode 0x05 +released: usage_page 0x07 keycode 0x08 +released: usage_page 0x07 keycode 0x04 +released: usage_page 0x07 keycode 0x05 diff --git a/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/native_posix.keymap b/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/native_posix.keymap new file mode 100644 index 00000000..7a9901bf --- /dev/null +++ b/app/tests/one-shot/7-osk-dn-up-kc1dn-kc2dn-kc1up-kc2up/native_posix.keymap @@ -0,0 +1,15 @@ +#include +#include +#include +#include "../behavior_keymap.dtsi" + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,10) + ZMK_MOCK_RELEASE(0,0,10) + ZMK_MOCK_PRESS(1,0,10) + ZMK_MOCK_PRESS(1,1,10) + ZMK_MOCK_RELEASE(1,0,100) + ZMK_MOCK_RELEASE(1,1,100) + >; +}; \ No newline at end of file diff --git a/app/tests/one-shot/behavior_keymap.dtsi b/app/tests/one-shot/behavior_keymap.dtsi new file mode 100644 index 00000000..c0a55ef8 --- /dev/null +++ b/app/tests/one-shot/behavior_keymap.dtsi @@ -0,0 +1,22 @@ +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &osk E &mo 1 + &kp A &kp B>; + }; + + lower_layer { + bindings = < + &osk LCTL &kp X + &kp Y &kp Z>; + }; + }; +};