feat(behaviors): Add reusable sensor behaviors.

* Add new sensor behaviors that either take full bindings
  add definition, or accept parameters when bound in the
  keymap.
* Remove existing hard-coded key press sensor behavior
  and instead leverage new generic sensor behaviors to
  achieve the same functionality.

Co-authored-by: nick@conway.dev
This commit is contained in:
Nick Conway
2022-05-23 16:33:08 -04:00
committed by Pete Johanson
parent 9a73650041
commit 3db163aa2c
17 changed files with 301 additions and 107 deletions

View File

@@ -51,7 +51,9 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/behaviors/behavior_to_layer.c)
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
target_sources(app PRIVATE src/behaviors/behavior_none.c)
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c)
target_sources(app PRIVATE src/combo.c)
target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c)
target_sources(app PRIVATE src/behavior_queue.c)

View File

@@ -350,11 +350,7 @@ config ZMK_BEHAVIORS_QUEUE_SIZE
int "Maximum number of behaviors to allow queueing from a macro or other complex behavior"
default 64
DT_COMPAT_ZMK_BEHAVIOR_KEY_TOGGLE := zmk,behavior-key-toggle
config ZMK_BEHAVIOR_KEY_TOGGLE
bool
default $(dt_compat_enabled,$(DT_COMPAT_ZMK_BEHAVIOR_KEY_TOGGLE))
rsource "Kconfig.behaviors"
config ZMK_MACRO_DEFAULT_WAIT_MS
int "Default time to wait (in milliseconds) before triggering the next behavior in macros"

24
app/Kconfig.behaviors Normal file
View File

@@ -0,0 +1,24 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT
config ZMK_BEHAVIOR_KEY_TOGGLE
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_KEY_TOGGLE_ENABLED
config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
bool
default n
config ZMK_BEHAVIOR_SENSOR_ROTATE
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_SENSOR_ROTATE_ENABLED
select ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
config ZMK_BEHAVIOR_SENSOR_ROTATE_VAR
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR_ENABLED
select ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON

View File

@@ -8,9 +8,10 @@
behaviors {
/* DEPRECATED: `inc_dec_cp` will be removed in the future */
/omit-if-no-ref/ inc_dec_cp: inc_dec_kp: behavior_sensor_rotate_key_press {
compatible = "zmk,behavior-sensor-rotate-key-press";
compatible = "zmk,behavior-sensor-rotate-var";
label = "ENC_KEY_PRESS";
#sensor-binding-cells = <2>;
bindings = <&kp>, <&kp>;
};
};
};

View File

@@ -1,19 +0,0 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Sensor rotate key press/release behavior
compatible: "zmk,behavior-sensor-rotate-key-press"
properties:
label:
type: string
required: true
"#sensor-binding-cells":
type: int
required: true
const: 2
sensor-binding-cells:
- param1
- param2

View File

@@ -0,0 +1,25 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Sensor rotate behavior
compatible: "zmk,behavior-sensor-rotate-var"
properties:
label:
type: string
required: true
"#sensor-binding-cells":
type: int
required: true
const: 2
bindings:
type: phandles
required: true
tap-ms:
type: int
default: 5
sensor-binding-cells:
- param1
- param2

View File

@@ -0,0 +1,21 @@
# Copyright (c) 2022 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Sensor rotate behavior
compatible: "zmk,behavior-sensor-rotate"
properties:
label:
type: string
required: true
"#sensor-binding-cells":
type: int
required: true
const: 0
bindings:
type: phandle-array
required: true
tap-ms:
type: int
default: 5

View File

@@ -26,8 +26,7 @@ typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *b
struct zmk_behavior_binding_event event);
typedef int (*behavior_sensor_keymap_binding_callback_t)(struct zmk_behavior_binding *binding,
const struct device *sensor,
uint32_t virtual_key_position,
int64_t timestamp);
struct zmk_behavior_binding_event event);
enum behavior_locality {
BEHAVIOR_LOCALITY_CENTRAL,
@@ -161,13 +160,12 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
*/
__syscall int behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding,
const struct device *sensor,
uint32_t virtual_key_position,
int64_t timestamp);
struct zmk_behavior_binding_event event);
static inline int
z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *binding,
const struct device *sensor,
uint32_t virtual_key_position, int64_t timestamp) {
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
if (dev == NULL) {
@@ -180,7 +178,7 @@ z_impl_behavior_sensor_keymap_binding_triggered(struct zmk_behavior_binding *bin
return -ENOTSUP;
}
return api->sensor_binding_triggered(binding, sensor, virtual_key_position, timestamp);
return api->sensor_binding_triggered(binding, sensor, event);
}
/**

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_sensor_rotate
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include "behavior_sensor_rotate_common.h"
static const struct behavior_driver_api behavior_sensor_rotate_driver_api = {
.sensor_binding_triggered = zmk_behavior_sensor_rotate_common_trigger};
static int behavior_sensor_rotate_init(const struct device *dev) { return 0; };
#define _TRANSFORM_ENTRY(idx, node) \
{ \
.behavior_dev = DT_PROP(DT_INST_PHANDLE_BY_IDX(node, bindings, idx), label), \
.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 SENSOR_ROTATE_INST(n) \
static struct behavior_sensor_rotate_config behavior_sensor_rotate_config_##n = { \
.cw_binding = _TRANSFORM_ENTRY(0, n), \
.ccw_binding = _TRANSFORM_ENTRY(1, n), \
.tap_ms = DT_INST_PROP_OR(n, tap_ms, 5), \
.override_params = false, \
}; \
DEVICE_DT_INST_DEFINE( \
n, behavior_sensor_rotate_init, NULL, NULL, &behavior_sensor_rotate_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sensor_rotate_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_INST)

View File

@@ -0,0 +1,52 @@
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
#include <zmk/behavior_queue.h>
#include "behavior_sensor_rotate_common.h"
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
int zmk_behavior_sensor_rotate_common_trigger(struct zmk_behavior_binding *binding,
const struct device *sensor,
struct zmk_behavior_binding_event event) {
const struct device *dev = device_get_binding(binding->behavior_dev);
const struct behavior_sensor_rotate_config *cfg = dev->config;
struct sensor_value value;
const int err = sensor_channel_get(sensor, SENSOR_CHAN_ROTATION, &value);
if (err < 0) {
LOG_WRN("Failed to get sensor rotation value: %d", err);
return err;
}
struct zmk_behavior_binding triggered_binding;
switch (value.val1) {
case 1:
triggered_binding = cfg->cw_binding;
if (cfg->override_params) {
triggered_binding.param1 = binding->param1;
}
break;
case -1:
triggered_binding = cfg->ccw_binding;
if (cfg->override_params) {
triggered_binding.param1 = binding->param2;
}
break;
default:
return -ENOTSUP;
}
LOG_DBG("Sensor binding: %s", binding->behavior_dev);
zmk_behavior_queue_add(event.position, triggered_binding, true, cfg->tap_ms);
zmk_behavior_queue_add(event.position, triggered_binding, false, 0);
return ZMK_BEHAVIOR_OPAQUE;
}

View File

@@ -0,0 +1,13 @@
#include <zmk/behavior.h>
struct behavior_sensor_rotate_config {
struct zmk_behavior_binding cw_binding;
struct zmk_behavior_binding ccw_binding;
int tap_ms;
bool override_params;
};
int zmk_behavior_sensor_rotate_common_trigger(struct zmk_behavior_binding *binding,
const struct device *sensor,
struct zmk_behavior_binding_event event);

View File

@@ -1,69 +0,0 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_sensor_rotate_key_press
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <drivers/behavior.h>
#include <zmk/event_manager.h>
#include <zmk/events/keycode_state_changed.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int behavior_sensor_rotate_key_press_init(const struct device *dev) { return 0; };
static int on_sensor_binding_triggered(struct zmk_behavior_binding *binding,
const struct device *sensor, uint32_t virtual_key_position,
int64_t timestamp) {
struct sensor_value value;
int err;
uint32_t keycode;
LOG_DBG("inc keycode 0x%02X dec keycode 0x%02X", binding->param1, binding->param2);
err = sensor_channel_get(sensor, SENSOR_CHAN_ROTATION, &value);
if (err) {
LOG_WRN("Failed to ge sensor rotation value: %d", err);
return err;
}
switch (value.val1) {
case 1:
keycode = binding->param1;
break;
case -1:
keycode = binding->param2;
break;
default:
return -ENOTSUP;
}
LOG_DBG("SEND %d", keycode);
ZMK_EVENT_RAISE(zmk_keycode_state_changed_from_encoded(keycode, true, timestamp));
// TODO: Better way to do this?
k_msleep(5);
return ZMK_EVENT_RAISE(zmk_keycode_state_changed_from_encoded(keycode, false, timestamp));
}
static const struct behavior_driver_api behavior_sensor_rotate_key_press_driver_api = {
.sensor_binding_triggered = on_sensor_binding_triggered};
#define KP_INST(n) \
DEVICE_DT_INST_DEFINE(n, behavior_sensor_rotate_key_press_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_sensor_rotate_key_press_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_sensor_rotate_var
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include "behavior_sensor_rotate_common.h"
static const struct behavior_driver_api behavior_sensor_rotate_var_driver_api = {
.sensor_binding_triggered = zmk_behavior_sensor_rotate_common_trigger};
static int behavior_sensor_rotate_var_init(const struct device *dev) { return 0; };
#define SENSOR_ROTATE_VAR_INST(n) \
static struct behavior_sensor_rotate_config behavior_sensor_rotate_var_config_##n = { \
.cw_binding = {.behavior_dev = DT_PROP(DT_INST_PHANDLE_BY_IDX(n, bindings, 0), label)}, \
.ccw_binding = {.behavior_dev = DT_PROP(DT_INST_PHANDLE_BY_IDX(n, bindings, 1), label)}, \
.tap_ms = DT_INST_PROP(n, tap_ms), \
.override_params = true, \
}; \
DEVICE_DT_INST_DEFINE( \
n, behavior_sensor_rotate_var_init, NULL, NULL, &behavior_sensor_rotate_var_config_##n, \
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sensor_rotate_var_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_VAR_INST)

View File

@@ -270,8 +270,9 @@ int zmk_keymap_sensor_triggered(uint8_t sensor_number, const struct device *sens
continue;
}
const uint32_t position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_number);
ret = behavior_sensor_keymap_binding_triggered(binding, sensor, position, timestamp);
struct zmk_behavior_binding_event event = {
.position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_number), .timestamp = timestamp};
ret = behavior_sensor_keymap_binding_triggered(binding, sensor, event);
if (ret > 0) {
LOG_DBG("behavior processing to continue to next layer");