diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 37341017..b05d16bf 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -30,7 +30,6 @@ target_include_directories(app PRIVATE include) target_sources(app PRIVATE src/kscan.c) target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/keymap.c) -target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/hid.c) target_sources(app PRIVATE src/sensors.c) target_sources_ifdef(CONFIG_ZMK_DISPLAY app PRIVATE src/display.c) @@ -56,5 +55,5 @@ target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c) target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c) target_sources(app PRIVATE src/endpoints.c) +target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/main.c) - diff --git a/app/Kconfig b/app/Kconfig index 01384a0f..55c5bae7 100644 --- a/app/Kconfig +++ b/app/Kconfig @@ -40,8 +40,8 @@ menuconfig ZMK_BLE select BT_PERIPHERAL select BT_GATT_DIS select BT_GATT_BAS - select SETTINGS - select BT_SETTINGS + # select SETTINGS + # select BT_SETTINGS if ZMK_BLE diff --git a/app/include/zmk/event-manager.h b/app/include/zmk/event-manager.h index 39381715..43d3f299 100644 --- a/app/include/zmk/event-manager.h +++ b/app/include/zmk/event-manager.h @@ -72,8 +72,12 @@ struct zmk_event_subscription { #define ZMK_EVENT_RAISE(ev) \ zmk_event_manager_raise((struct zmk_event_header *)ev); +#define ZMK_EVENT_RAISE_AFTER(ev, mod) \ + zmk_event_manager_raise_after((struct zmk_event_header *)ev, &zmk_listener_##mod); + #define ZMK_EVENT_RELEASE(ev) \ zmk_event_manager_release((struct zmk_event_header *)ev); int zmk_event_manager_raise(struct zmk_event_header *event); +int zmk_event_manager_raise_after(struct zmk_event_header *event, const struct zmk_listener *listener); int zmk_event_manager_release(struct zmk_event_header *event); diff --git a/app/src/behaviors/behavior_key_press.c b/app/src/behaviors/behavior_key_press.c index 34df1c0b..7404c798 100644 --- a/app/src/behaviors/behavior_key_press.c +++ b/app/src/behaviors/behavior_key_press.c @@ -28,27 +28,17 @@ static int behavior_key_press_init(struct device *dev) static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t keycode, u32_t _) { const struct behavior_key_press_config *cfg = dev->config_info; - struct keycode_state_changed *ev; LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", position, cfg->usage_page, keycode); - ev = new_keycode_state_changed(); - ev->usage_page = cfg->usage_page; - ev->keycode = keycode; - ev->state = true; - return ZMK_EVENT_RAISE(ev); + return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, keycode, true)); } static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t keycode, u32_t _) { const struct behavior_key_press_config *cfg = dev->config_info; - struct keycode_state_changed *ev; LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", position, cfg->usage_page, keycode); - ev = new_keycode_state_changed(); - ev->usage_page = cfg->usage_page; - ev->keycode = keycode; - ev->state = false; - return ZMK_EVENT_RAISE(ev); + return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, keycode, false)); } static const struct behavior_driver_api behavior_key_press_driver_api = { diff --git a/app/src/behaviors/behavior_mod_tap.c b/app/src/behaviors/behavior_mod_tap.c index 62604eba..6151f7e9 100644 --- a/app/src/behaviors/behavior_mod_tap.c +++ b/app/src/behaviors/behavior_mod_tap.c @@ -10,25 +10,124 @@ #include #include +#include +#include #include #include #include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#define ZMK_BHV_MOD_TAP_MAX_HELD 4 +#define ZMK_BHV_MOD_TAP_MAX_PENDING_KC 4 + +struct active_mod_tap_item { + u32_t keycode; + u8_t mods; + bool pending; + zmk_mod_flags active_mods; +}; + +struct captured_keycode_state_change_item { + struct keycode_state_changed* event; + zmk_mod_flags active_mods; +}; + struct behavior_mod_tap_config { }; struct behavior_mod_tap_data { - u16_t pending_press_positions; + struct active_mod_tap_item active_mod_taps[ZMK_BHV_MOD_TAP_MAX_HELD]; + struct captured_keycode_state_change_item captured_keycode_events[ZMK_BHV_MOD_TAP_MAX_PENDING_KC]; }; +bool have_pending_mods(char *label) { + struct device *dev = device_get_binding(label); + struct behavior_mod_tap_data *data = dev->driver_data; + + for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) { + if (data->active_mod_taps[i].mods) { + LOG_DBG("Found pending mods for %d and keycode 0x%02X", data->active_mod_taps[i].mods, data->active_mod_taps[i].keycode); + return true; + } + } + + return false; +} + +struct captured_keycode_state_change_item* find_pending_keycode(struct behavior_mod_tap_data *data, u32_t keycode) +{ + for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_PENDING_KC; i++) { + if (data->captured_keycode_events[i].event == NULL) { + continue; + } + + if (data->captured_keycode_events[i].event->keycode == keycode) { + return &data->captured_keycode_events[i]; + } + } + + return NULL; +} + +zmk_mod_flags behavior_mod_tap_active_mods(struct behavior_mod_tap_data *data) +{ + zmk_mod_flags mods = 0; + + for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) { + mods |= data->active_mod_taps[i].mods; + } + + return mods; +} + +int behavior_mod_tap_capture_keycode_event(struct behavior_mod_tap_data *data, struct keycode_state_changed *ev) +{ + for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_PENDING_KC; i++) { + if (data->captured_keycode_events[i].event != NULL) { + continue; + } + + data->captured_keycode_events[i].event = ev; + data->captured_keycode_events[i].active_mods = behavior_mod_tap_active_mods(data); + return 0; + } + + return -ENOMEM; +} + +void behavior_mod_tap_update_active_mods_state(struct behavior_mod_tap_data *data, zmk_mod_flags used_flags) +{ + for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) { + if ((data->active_mod_taps[i].mods & used_flags) == data->active_mod_taps[i].mods) { + data->active_mod_taps[i].pending = false; + } + } +} + +// How to pass context to subscription?! int behavior_mod_tap_listener(const struct zmk_event_header *eh) { - if (is_keycode_state_changed(eh)) { + if (is_keycode_state_changed(eh) && have_pending_mods(DT_INST_LABEL(0))) { struct device *dev = device_get_binding(DT_INST_LABEL(0)); - const struct keycode_state_changed *ev = cast_keycode_state_changed(eh); + struct keycode_state_changed *ev = cast_keycode_state_changed(eh); + struct behavior_mod_tap_data *data = dev->driver_data; + struct captured_keycode_state_change_item* pending_keycode; if (ev->state) { - struct behavior_mod_tap_data *data = dev->driver_data; - data->pending_press_positions = 0; + LOG_DBG("Have pending mods, capturing keycode 0x%02X event to ressend later", ev->keycode); + behavior_mod_tap_capture_keycode_event(data, ev); + return ZMK_EV_EVENT_CAPTURED; + } else if ((pending_keycode = find_pending_keycode(data, ev->keycode)) != NULL) { + LOG_DBG("Key released, going to activate mods 0x%02X then send pending key press for keycode 0x%02X", + pending_keycode->active_mods, pending_keycode->event->keycode); + + zmk_hid_register_mods(pending_keycode->active_mods); + behavior_mod_tap_update_active_mods_state(data, pending_keycode->active_mods); + + ZMK_EVENT_RELEASE(pending_keycode->event); + k_msleep(10); + + pending_keycode->event = NULL; + pending_keycode->active_mods = 0; } } return 0; @@ -46,9 +145,25 @@ static int behavior_mod_tap_init(struct device *dev) static int on_keymap_binding_pressed(struct device *dev, u32_t position, u32_t mods, u32_t keycode) { struct behavior_mod_tap_data *data = dev->driver_data; - LOG_DBG("mods: %d, keycode: %d", mods, keycode); - WRITE_BIT(data->pending_press_positions, position, true); - return ZMK_EVENT_RAISE(create_modifiers_state_changed(mods, true)); + LOG_DBG("mods: %d, keycode: 0x%02X", mods, keycode); + for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) { + if (data->active_mod_taps[i].mods != 0) { + continue; + } + + zmk_mod_flags active_mods = behavior_mod_tap_active_mods(data); + + data->active_mod_taps[i].active_mods = active_mods; + data->active_mod_taps[i].mods = mods; + data->active_mod_taps[i].keycode = keycode; + data->active_mod_taps[i].pending = true; + + return 0; + } + + LOG_WRN("Failed to record mod-tap activation, at maximum concurrent mod-tap activations"); + + return -ENOMEM; } static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t mods, u32_t keycode) @@ -56,12 +171,66 @@ static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t struct behavior_mod_tap_data *data = dev->driver_data; LOG_DBG("mods: %d, keycode: %d", mods, keycode); - ZMK_EVENT_RAISE(create_modifiers_state_changed(mods, false)); - k_msleep(10); // TODO: Better approach than k_msleep to avoid USB send failures? Retries in the USB endpoint layer? - if (data->pending_press_positions & BIT(position)) { - ZMK_EVENT_RAISE(create_keycode_state_changed(USAGE_KEYPAD, keycode, true)); - k_msleep(10); - ZMK_EVENT_RAISE(create_keycode_state_changed(USAGE_KEYPAD, keycode, false)); + for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) { + struct active_mod_tap_item *item = &data->active_mod_taps[i]; + if (item->mods == mods && item->keycode == keycode) { + if (item->pending) { + LOG_DBG("Sending un-triggered mod-tap for keycode: 0x%02X", keycode); + + if (item->active_mods) { + LOG_DBG("Registering recorded active mods captured when mod-tap initially activated: 0x%02X", item->active_mods); + behavior_mod_tap_update_active_mods_state(data, item->active_mods); + zmk_hid_register_mods(item->active_mods); + } + + struct keycode_state_changed *key_press = create_keycode_state_changed(USAGE_KEYPAD, item->keycode, true); + ZMK_EVENT_RAISE_AFTER(key_press, behavior_mod_tap); + k_msleep(10); + + for (int j = 0; j < ZMK_BHV_MOD_TAP_MAX_PENDING_KC; j++) { + if (data->captured_keycode_events[j].event == NULL) { + continue; + } + + struct keycode_state_changed *ev = data->captured_keycode_events[j].event; + data->captured_keycode_events[i].event = NULL; + data->captured_keycode_events[i].active_mods = 0; + LOG_DBG("Re-sending latched key press for usage page 0x%02X keycode 0x%02X state %s", ev->usage_page, ev->keycode, (ev->state ? "pressed" : "released")); + ZMK_EVENT_RELEASE(ev); + k_msleep(10); + } + + struct keycode_state_changed *key_release = create_keycode_state_changed(USAGE_KEYPAD, keycode, false); + LOG_DBG("Sending un-triggered mod-tap release for keycode: 0x%02X", keycode); + ZMK_EVENT_RAISE_AFTER(key_release, behavior_mod_tap); + k_msleep(10); + + if (item->active_mods) { + LOG_DBG("Unregistering recorded active mods captured when mod-tap initially activated: 0x%02X", item->active_mods); + zmk_hid_unregister_mods(item->active_mods); + zmk_endpoints_send_report(USAGE_KEYPAD); + } + + + } else { + LOG_DBG("Releasing triggered mods: %d", mods); + zmk_hid_unregister_mods(mods); + zmk_endpoints_send_report(USAGE_KEYPAD); + } + + item->mods = 0; + item->keycode = 0; + item->active_mods = 0; + + LOG_DBG("Removing mods %d from active_mods for other held mod-taps", mods); + for (int j = 0; j < ZMK_BHV_MOD_TAP_MAX_HELD; j++) { + if (data->active_mod_taps[j].active_mods & mods) { + LOG_DBG("Removing 0x%02X from active mod tap mods 0x%02X keycode 0x%02X", mods, data->active_mod_taps[j].mods, data->active_mod_taps[j].keycode); + data->active_mod_taps[j].active_mods &= ~mods; + } + } + break; + } } return 0; diff --git a/app/src/event_manager.c b/app/src/event_manager.c index 3edba10a..5d2e9ed5 100644 --- a/app/src/event_manager.c +++ b/app/src/event_manager.c @@ -55,6 +55,22 @@ int zmk_event_manager_raise(struct zmk_event_header *event) return zmk_event_manager_handle_from(event, 0); } +int zmk_event_manager_raise_after(struct zmk_event_header *event, const struct zmk_listener *listener) +{ + u8_t len = __event_subscriptions_end - __event_subscriptions_start; + for (int i = 0; i < len; i++) { + struct zmk_event_subscription *ev_sub = __event_subscriptions_start + i; + + if (ev_sub->event_type == event->event && ev_sub->listener == listener) { + return zmk_event_manager_handle_from(event, i+1); + } + } + + LOG_WRN("Unable to find where to raise this after event"); + + return -EINVAL; +} + int zmk_event_manager_release(struct zmk_event_header *event) { return zmk_event_manager_handle_from(event, event->last_listener_index + 1); diff --git a/app/src/events/keycode_state_changed.c b/app/src/events/keycode_state_changed.c index 964b24aa..73508e16 100644 --- a/app/src/events/keycode_state_changed.c +++ b/app/src/events/keycode_state_changed.c @@ -7,4 +7,4 @@ #include #include -ZMK_EVENT_IMPL(keycode_state_changed); \ No newline at end of file +ZMK_EVENT_IMPL(keycode_state_changed);