Initial stab at mod-tap improvements.
* Not working: Roll over + mod-tap with multiple mod-tap bindings!
This commit is contained in:
parent
f269b26ea1
commit
f548f2a87c
7 changed files with 188 additions and 32 deletions
|
@ -30,7 +30,6 @@ target_include_directories(app PRIVATE include)
|
||||||
target_sources(app PRIVATE src/kscan.c)
|
target_sources(app PRIVATE src/kscan.c)
|
||||||
target_sources(app PRIVATE src/matrix_transform.c)
|
target_sources(app PRIVATE src/matrix_transform.c)
|
||||||
target_sources(app PRIVATE src/keymap.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/hid.c)
|
||||||
target_sources(app PRIVATE src/sensors.c)
|
target_sources(app PRIVATE src/sensors.c)
|
||||||
target_sources_ifdef(CONFIG_ZMK_DISPLAY app PRIVATE src/display.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_BLE app PRIVATE src/hog.c)
|
||||||
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.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/endpoints.c)
|
||||||
|
target_sources(app PRIVATE src/hid_listener.c)
|
||||||
target_sources(app PRIVATE src/main.c)
|
target_sources(app PRIVATE src/main.c)
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,8 @@ menuconfig ZMK_BLE
|
||||||
select BT_PERIPHERAL
|
select BT_PERIPHERAL
|
||||||
select BT_GATT_DIS
|
select BT_GATT_DIS
|
||||||
select BT_GATT_BAS
|
select BT_GATT_BAS
|
||||||
select SETTINGS
|
# select SETTINGS
|
||||||
select BT_SETTINGS
|
# select BT_SETTINGS
|
||||||
|
|
||||||
if ZMK_BLE
|
if ZMK_BLE
|
||||||
|
|
||||||
|
|
|
@ -72,8 +72,12 @@ struct zmk_event_subscription {
|
||||||
#define ZMK_EVENT_RAISE(ev) \
|
#define ZMK_EVENT_RAISE(ev) \
|
||||||
zmk_event_manager_raise((struct zmk_event_header *)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) \
|
#define ZMK_EVENT_RELEASE(ev) \
|
||||||
zmk_event_manager_release((struct zmk_event_header *)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(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);
|
int zmk_event_manager_release(struct zmk_event_header *event);
|
||||||
|
|
|
@ -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 _)
|
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;
|
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);
|
LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", position, cfg->usage_page, keycode);
|
||||||
|
|
||||||
ev = new_keycode_state_changed();
|
return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, keycode, true));
|
||||||
ev->usage_page = cfg->usage_page;
|
|
||||||
ev->keycode = keycode;
|
|
||||||
ev->state = true;
|
|
||||||
return ZMK_EVENT_RAISE(ev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t keycode, u32_t _)
|
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;
|
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);
|
LOG_DBG("position %d usage_page 0x%02X keycode 0x%02X", position, cfg->usage_page, keycode);
|
||||||
|
|
||||||
ev = new_keycode_state_changed();
|
return ZMK_EVENT_RAISE(create_keycode_state_changed(cfg->usage_page, keycode, false));
|
||||||
ev->usage_page = cfg->usage_page;
|
|
||||||
ev->keycode = keycode;
|
|
||||||
ev->state = false;
|
|
||||||
return ZMK_EVENT_RAISE(ev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct behavior_driver_api behavior_key_press_driver_api = {
|
static const struct behavior_driver_api behavior_key_press_driver_api = {
|
||||||
|
|
|
@ -10,25 +10,110 @@
|
||||||
#include <drivers/behavior.h>
|
#include <drivers/behavior.h>
|
||||||
#include <logging/log.h>
|
#include <logging/log.h>
|
||||||
|
|
||||||
|
#include <zmk/matrix.h>
|
||||||
|
#include <zmk/endpoints.h>
|
||||||
#include <zmk/event-manager.h>
|
#include <zmk/event-manager.h>
|
||||||
#include <zmk/events/keycode-state-changed.h>
|
#include <zmk/events/keycode-state-changed.h>
|
||||||
#include <zmk/events/modifiers-state-changed.h>
|
#include <zmk/events/modifiers-state-changed.h>
|
||||||
|
#include <zmk/hid.h>
|
||||||
|
|
||||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||||
|
|
||||||
struct behavior_mod_tap_config { };
|
#define ZMK_BHV_MOD_TAP_MAX_HELD 4
|
||||||
struct behavior_mod_tap_data {
|
#define ZMK_BHV_MOD_TAP_MAX_PENDING_KC 4
|
||||||
u16_t pending_press_positions;
|
|
||||||
|
#define TOGGLE_FIRST(arr, len, match, val) \
|
||||||
|
for (int idx = 0; idx < len; idx++) \
|
||||||
|
{ \
|
||||||
|
if (arr[idx] != match) \
|
||||||
|
{ \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
arr[idx] = val; \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pending_mod_tap_item {
|
||||||
|
u32_t keycode;
|
||||||
|
u8_t mods;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct behavior_mod_tap_config { };
|
||||||
|
struct behavior_mod_tap_data {
|
||||||
|
struct pending_mod_tap_item pending_mod_taps[ZMK_BHV_MOD_TAP_MAX_HELD];
|
||||||
|
struct pending_mod_tap_item triggered_mod_taps[ZMK_BHV_MOD_TAP_MAX_HELD];
|
||||||
|
struct keycode_state_changed* pending_key_presses[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->pending_mod_taps[i].mods) {
|
||||||
|
LOG_DBG("Found pending mods for %d and keycode 0x%02X", data->pending_mod_taps[i].mods, data->pending_mod_taps[i].keycode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool have_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->pending_key_presses[i] == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->pending_key_presses[i]->keycode == keycode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// How to pass context to subscription?!
|
||||||
int behavior_mod_tap_listener(const struct zmk_event_header *eh)
|
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));
|
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;
|
||||||
if (ev->state) {
|
if (ev->state) {
|
||||||
struct behavior_mod_tap_data *data = dev->driver_data;
|
LOG_DBG("Have pending mods, capturing keycode 0x%02X event to ressend later", ev->keycode);
|
||||||
data->pending_press_positions = 0;
|
TOGGLE_FIRST(data->pending_key_presses, ZMK_BHV_MOD_TAP_MAX_PENDING_KC, NULL, ev);
|
||||||
|
return ZMK_EV_EVENT_CAPTURED;
|
||||||
|
} else if (have_pending_keycode(data, ev->keycode)) {
|
||||||
|
zmk_mod_flags mods = 0;
|
||||||
|
|
||||||
|
LOG_DBG("Key released, going to activate mods then send pending key presses");
|
||||||
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) {
|
||||||
|
memcpy(&data->triggered_mod_taps[i], &data->pending_mod_taps[i], sizeof(struct pending_mod_tap_item));
|
||||||
|
data->pending_mod_taps[i].mods = 0;
|
||||||
|
data->pending_mod_taps[i].keycode = 0;
|
||||||
|
}
|
||||||
|
LOG_DBG("After swapping, do I have pending mods? %s", (have_pending_mods(DT_INST_LABEL(0)) ? "true" : "false"));
|
||||||
|
|
||||||
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) {
|
||||||
|
mods |= data->triggered_mod_taps[i].mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Setting mods: %d", mods);
|
||||||
|
|
||||||
|
zmk_hid_register_mods(mods);
|
||||||
|
|
||||||
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_PENDING_KC; i++) {
|
||||||
|
if (data->pending_key_presses[i] == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct keycode_state_changed *ev = data->pending_key_presses[i];
|
||||||
|
data->pending_key_presses[i] = NULL;
|
||||||
|
ZMK_EVENT_RAISE(ev);
|
||||||
|
k_msleep(10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -46,22 +131,84 @@ 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)
|
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;
|
struct behavior_mod_tap_data *data = dev->driver_data;
|
||||||
LOG_DBG("mods: %d, keycode: %d", mods, keycode);
|
LOG_DBG("mods: %d, keycode: 0x%02X", mods, keycode);
|
||||||
WRITE_BIT(data->pending_press_positions, position, true);
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) {
|
||||||
return ZMK_EVENT_RAISE(create_modifiers_state_changed(mods, true));
|
if (data->pending_mod_taps[i].mods != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->pending_mod_taps[i].mods = mods;
|
||||||
|
data->pending_mod_taps[i].keycode = keycode;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t mods, u32_t keycode)
|
static int on_keymap_binding_released(struct device *dev, u32_t position, u32_t mods, u32_t keycode)
|
||||||
{
|
{
|
||||||
struct behavior_mod_tap_data *data = dev->driver_data;
|
struct behavior_mod_tap_data *data = dev->driver_data;
|
||||||
|
bool sending_keycode = false;
|
||||||
|
bool sent_pending_key_presses = false;
|
||||||
|
struct keycode_state_changed *pending_key_presses[ZMK_BHV_MOD_TAP_MAX_PENDING_KC];
|
||||||
LOG_DBG("mods: %d, keycode: %d", mods, keycode);
|
LOG_DBG("mods: %d, keycode: %d", mods, keycode);
|
||||||
|
|
||||||
ZMK_EVENT_RAISE(create_modifiers_state_changed(mods, false));
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) {
|
||||||
k_msleep(10); // TODO: Better approach than k_msleep to avoid USB send failures? Retries in the USB endpoint layer?
|
if (data->triggered_mod_taps[i].mods == mods && data->triggered_mod_taps[i].keycode == keycode) {
|
||||||
if (data->pending_press_positions & BIT(position)) {
|
LOG_DBG("Releasing triggered mods: %d", mods);
|
||||||
ZMK_EVENT_RAISE(create_keycode_state_changed(USAGE_KEYPAD, keycode, true));
|
zmk_hid_unregister_mods(mods);
|
||||||
|
data->triggered_mod_taps[i].mods = 0;
|
||||||
|
data->triggered_mod_taps[i].keycode = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_HELD; i++) {
|
||||||
|
if (data->pending_mod_taps[i].mods == mods && data->pending_mod_taps[i].keycode == keycode) {
|
||||||
|
sending_keycode = true;
|
||||||
|
data->pending_mod_taps[i].mods = 0;
|
||||||
|
data->pending_mod_taps[i].keycode = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_PENDING_KC; i++) {
|
||||||
|
pending_key_presses[i] = data->pending_key_presses[i];
|
||||||
|
data->pending_key_presses[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (sending_keycode) {
|
||||||
|
struct keycode_state_changed *key_press = create_keycode_state_changed(USAGE_KEYPAD, keycode, true);
|
||||||
|
LOG_DBG("Sending un-triggered mod-tap for keycode: 0x%02X", keycode);
|
||||||
|
ZMK_EVENT_RAISE(key_press);
|
||||||
|
k_msleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < ZMK_BHV_MOD_TAP_MAX_PENDING_KC; i++) {
|
||||||
|
if (pending_key_presses[i] == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct keycode_state_changed *ev = pending_key_presses[i];
|
||||||
|
sent_pending_key_presses = true;
|
||||||
|
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);
|
k_msleep(10);
|
||||||
ZMK_EVENT_RAISE(create_keycode_state_changed(USAGE_KEYPAD, keycode, false));
|
}
|
||||||
|
|
||||||
|
if (sending_keycode) {
|
||||||
|
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(key_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sending_keycode && !sent_pending_key_presses) {
|
||||||
|
// Need to ensure the mod release is propagated.
|
||||||
|
zmk_endpoints_send_report(USAGE_KEYPAD);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -55,6 +55,22 @@ int zmk_event_manager_raise(struct zmk_event_header *event)
|
||||||
return zmk_event_manager_handle_from(event, 0);
|
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)
|
int zmk_event_manager_release(struct zmk_event_header *event)
|
||||||
{
|
{
|
||||||
return zmk_event_manager_handle_from(event, event->last_listener_index + 1);
|
return zmk_event_manager_handle_from(event, event->last_listener_index + 1);
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
#include <kernel.h>
|
#include <kernel.h>
|
||||||
#include <zmk/events/keycode-state-changed.h>
|
#include <zmk/events/keycode-state-changed.h>
|
||||||
|
|
||||||
ZMK_EVENT_IMPL(keycode_state_changed);
|
ZMK_EVENT_IMPL(keycode_state_changed);
|
||||||
|
|
Loading…
Reference in a new issue