From 8b29f6d34556d98df60f529e84ee66e49e6bf0c0 Mon Sep 17 00:00:00 2001
From: Peter Johanson <peter@peterjohanson.com>
Date: Thu, 4 May 2023 00:04:20 +0000
Subject: [PATCH] refactor(sensors): Split data handling from triggers.

* All sensor behaviors should see sensor data, then selectively
  only have some trigger their behaviors.
---
 app/include/drivers/behavior.h                | 59 ++++++++++++--
 app/src/behaviors/behavior_sensor_rotate.c    |  3 +-
 .../behaviors/behavior_sensor_rotate_common.c | 31 +++++--
 .../behaviors/behavior_sensor_rotate_common.h | 13 ++-
 .../behaviors/behavior_sensor_rotate_var.c    |  3 +-
 app/src/keymap.c                              | 81 +++++++++++--------
 6 files changed, 135 insertions(+), 55 deletions(-)

diff --git a/app/include/drivers/behavior.h b/app/include/drivers/behavior.h
index 0aa5d85e..d7e57d02 100644
--- a/app/include/drivers/behavior.h
+++ b/app/include/drivers/behavior.h
@@ -23,9 +23,17 @@
  * (Internal use only.)
  */
 
+enum behavior_sensor_binding_process_mode {
+    BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER,
+    BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD,
+};
+
 typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *binding,
                                                   struct zmk_behavior_binding_event event);
-typedef int (*behavior_sensor_keymap_binding_callback_t)(
+typedef int (*behavior_sensor_keymap_binding_process_callback_t)(
+    struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
+    enum behavior_sensor_binding_process_mode mode);
+typedef int (*behavior_sensor_keymap_binding_data_callback_t)(
     struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
     const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
     const struct zmk_sensor_channel_data channel_data[channel_data_size]);
@@ -41,7 +49,8 @@ __subsystem struct behavior_driver_api {
     behavior_keymap_binding_callback_t binding_convert_central_state_dependent_params;
     behavior_keymap_binding_callback_t binding_pressed;
     behavior_keymap_binding_callback_t binding_released;
-    behavior_sensor_keymap_binding_callback_t sensor_binding_triggered;
+    behavior_sensor_keymap_binding_data_callback_t sensor_binding_data;
+    behavior_sensor_keymap_binding_process_callback_t sensor_binding_process;
 };
 /**
  * @endcond
@@ -151,7 +160,7 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
 }
 
 /**
- * @brief Handle the a sensor keymap binding being triggered
+ * @brief Handle the a sensor keymap binding processing any incoming data from the sensor
  * @param binding Sensor keymap binding which was triggered.
  * @param sensor Pointer to the sensor device structure for the sensor driver instance.
  * @param virtual_key_position ZMK_KEYMAP_LEN + sensor number
@@ -160,12 +169,12 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
  * @retval 0 If successful.
  * @retval Negative errno code if failure.
  */
-__syscall int behavior_sensor_keymap_binding_triggered(
+__syscall int behavior_sensor_keymap_binding_data(
     struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
     const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
     const struct zmk_sensor_channel_data *channel_data);
 
-static inline int z_impl_behavior_sensor_keymap_binding_triggered(
+static inline int z_impl_behavior_sensor_keymap_binding_data(
     struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
     const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
     const struct zmk_sensor_channel_data *channel_data) {
@@ -177,12 +186,46 @@ static inline int z_impl_behavior_sensor_keymap_binding_triggered(
 
     const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
 
-    if (api->sensor_binding_triggered == NULL) {
+    if (api->sensor_binding_data == NULL) {
         return -ENOTSUP;
     }
 
-    return api->sensor_binding_triggered(binding, event, sensor_config, channel_data_size,
-                                         channel_data);
+    return api->sensor_binding_data(binding, event, sensor_config, channel_data_size, channel_data);
+}
+
+/**
+ * @brief Handle the keymap sensor binding being triggered after updating any local data
+ * @param dev Pointer to the device structure for the driver instance.
+ * @param param1 User parameter specified at time of behavior binding.
+ * @param param2 User parameter specified at time of behavior binding.
+ *
+ * @retval 0 If successful.
+ * @retval Negative errno code if failure.
+ */
+// clang-format off
+__syscall int behavior_sensor_keymap_binding_process(
+    struct zmk_behavior_binding *binding,
+    struct zmk_behavior_binding_event event,
+    enum behavior_sensor_binding_process_mode mode);
+// clang-format on
+
+static inline int
+z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *binding,
+                                              struct zmk_behavior_binding_event event,
+                                              enum behavior_sensor_binding_process_mode mode) {
+    const struct device *dev = device_get_binding(binding->behavior_dev);
+
+    if (dev == NULL) {
+        return -EINVAL;
+    }
+
+    const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;
+
+    if (api->sensor_binding_process == NULL) {
+        return -ENOTSUP;
+    }
+
+    return api->sensor_binding_process(binding, event, mode);
 }
 
 /**
diff --git a/app/src/behaviors/behavior_sensor_rotate.c b/app/src/behaviors/behavior_sensor_rotate.c
index 86846d5b..3b5bef62 100644
--- a/app/src/behaviors/behavior_sensor_rotate.c
+++ b/app/src/behaviors/behavior_sensor_rotate.c
@@ -13,7 +13,8 @@
 #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};
+    .sensor_binding_data = zmk_behavior_sensor_rotate_common_data,
+    .sensor_binding_process = zmk_behavior_sensor_rotate_common_process};
 
 static int behavior_sensor_rotate_init(const struct device *dev) { return 0; };
 
diff --git a/app/src/behaviors/behavior_sensor_rotate_common.c b/app/src/behaviors/behavior_sensor_rotate_common.c
index 99e4e019..0a2b619d 100644
--- a/app/src/behaviors/behavior_sensor_rotate_common.c
+++ b/app/src/behaviors/behavior_sensor_rotate_common.c
@@ -11,13 +11,12 @@
 
 LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
 
-int zmk_behavior_sensor_rotate_common_trigger(struct zmk_behavior_binding *binding,
-                                              struct zmk_behavior_binding_event event,
-                                              const struct zmk_sensor_config *sensor_config,
-                                              size_t channel_data_size,
-                                              const struct zmk_sensor_channel_data *channel_data) {
+int zmk_behavior_sensor_rotate_common_data(struct zmk_behavior_binding *binding,
+                                           struct zmk_behavior_binding_event event,
+                                           const struct zmk_sensor_config *sensor_config,
+                                           size_t channel_data_size,
+                                           const struct zmk_sensor_channel_data *channel_data) {
     const struct device *dev = device_get_binding(binding->behavior_dev);
-    const struct behavior_sensor_rotate_config *cfg = dev->config;
     struct behavior_sensor_rotate_data *data = dev->data;
 
     const struct sensor_value value = channel_data[0].value;
@@ -52,6 +51,26 @@ int zmk_behavior_sensor_rotate_common_trigger(struct zmk_behavior_binding *bindi
         value.val1, value.val2, data->remainder[sensor_position].val1,
         data->remainder[sensor_position].val2, triggers, binding->param1, binding->param2);
 
+    data->triggers[sensor_position] = triggers;
+    return 0;
+}
+
+int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding,
+                                              struct zmk_behavior_binding_event event,
+                                              enum behavior_sensor_binding_process_mode mode) {
+    const struct device *dev = device_get_binding(binding->behavior_dev);
+    const struct behavior_sensor_rotate_config *cfg = dev->config;
+    struct behavior_sensor_rotate_data *data = dev->data;
+
+    const int sensor_position = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position);
+
+    if (mode != BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER) {
+        data->triggers[sensor_position] = 0;
+        return 0;
+    }
+
+    int triggers = data->triggers[sensor_position];
+
     struct zmk_behavior_binding triggered_binding;
     if (triggers > 0) {
         triggered_binding = cfg->cw_binding;
diff --git a/app/src/behaviors/behavior_sensor_rotate_common.h b/app/src/behaviors/behavior_sensor_rotate_common.h
index eab443a3..d9d4d855 100644
--- a/app/src/behaviors/behavior_sensor_rotate_common.h
+++ b/app/src/behaviors/behavior_sensor_rotate_common.h
@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: MIT
  */
 
+#include <drivers/behavior.h>
 #include <zmk/behavior.h>
 #include <zmk/sensors.h>
 
@@ -16,10 +17,14 @@ struct behavior_sensor_rotate_config {
 
 struct behavior_sensor_rotate_data {
     struct sensor_value remainder[ZMK_KEYMAP_SENSORS_LEN];
+    int triggers[ZMK_KEYMAP_SENSORS_LEN];
 };
 
-int zmk_behavior_sensor_rotate_common_trigger(struct zmk_behavior_binding *binding,
+int zmk_behavior_sensor_rotate_common_data(struct zmk_behavior_binding *binding,
+                                           struct zmk_behavior_binding_event event,
+                                           const struct zmk_sensor_config *sensor_config,
+                                           size_t channel_data_size,
+                                           const struct zmk_sensor_channel_data *channel_data);
+int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding,
                                               struct zmk_behavior_binding_event event,
-                                              const struct zmk_sensor_config *sensor_config,
-                                              size_t channel_data_size,
-                                              const struct zmk_sensor_channel_data *channel_data);
\ No newline at end of file
+                                              enum behavior_sensor_binding_process_mode mode);
\ No newline at end of file
diff --git a/app/src/behaviors/behavior_sensor_rotate_var.c b/app/src/behaviors/behavior_sensor_rotate_var.c
index 95bb9961..3c2373b0 100644
--- a/app/src/behaviors/behavior_sensor_rotate_var.c
+++ b/app/src/behaviors/behavior_sensor_rotate_var.c
@@ -13,7 +13,8 @@
 #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};
+    .sensor_binding_data = zmk_behavior_sensor_rotate_common_data,
+    .sensor_binding_process = zmk_behavior_sensor_rotate_common_process};
 
 static int behavior_sensor_rotate_var_init(const struct device *dev) { return 0; };
 
diff --git a/app/src/keymap.c b/app/src/keymap.c
index 16543d37..c8121176 100644
--- a/app/src/keymap.c
+++ b/app/src/keymap.c
@@ -252,48 +252,59 @@ int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pr
 }
 
 #if ZMK_KEYMAP_HAS_SENSORS
-int zmk_keymap_sensor_triggered(
-    uint8_t sensor_position, size_t channel_data_size,
-    const struct zmk_sensor_channel_data channel_data[channel_data_size], int64_t timestamp) {
-    for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= _zmk_keymap_layer_default; layer--) {
-        if (zmk_keymap_layer_active(layer)) {
-            struct zmk_behavior_binding *binding = &zmk_sensor_keymap[layer][sensor_position];
-            const struct device *behavior;
-            int ret;
+int zmk_keymap_sensor_event(uint8_t sensor_position, size_t channel_data_size,
+                            const struct zmk_sensor_channel_data channel_data[channel_data_size],
+                            int64_t timestamp) {
+    bool opaque_response = false;
 
-            LOG_DBG("layer: %d sensor_position: %d, binding name: %s", layer, sensor_position,
-                    binding->behavior_dev);
+    for (int layer = ZMK_KEYMAP_LAYERS_LEN - 1; layer >= 0; layer--) {
+        struct zmk_behavior_binding *binding = &zmk_sensor_keymap[layer][sensor_position];
+        const struct device *behavior;
+        int ret;
 
-            behavior = device_get_binding(binding->behavior_dev);
+        LOG_DBG("layer: %d sensor_position: %d, binding name: %s", layer, sensor_position,
+                binding->behavior_dev);
 
-            if (!behavior) {
-                LOG_DBG("No behavior assigned to %d on layer %d", sensor_position, layer);
-                continue;
-            }
+        behavior = device_get_binding(binding->behavior_dev);
 
-            struct zmk_behavior_binding_event event = {
-                .layer = layer,
-                .position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_position),
-                .timestamp = timestamp,
-            };
+        if (!behavior) {
+            LOG_DBG("No behavior assigned to %d on layer %d", sensor_position, layer);
+            continue;
+        }
 
-            ret = behavior_sensor_keymap_binding_triggered(
-                binding, event, zmk_sensors_get_config_at_position(sensor_position),
-                channel_data_size, channel_data);
+        struct zmk_behavior_binding_event event = {
+            .layer = layer,
+            .position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_position),
+            .timestamp = timestamp,
+        };
 
-            if (ret > 0) {
-                LOG_DBG("behavior processing to continue to next layer");
-                continue;
-            } else if (ret < 0) {
-                LOG_DBG("Behavior returned error: %d", ret);
-                return ret;
-            } else {
-                return ret;
-            }
+        ret = behavior_sensor_keymap_binding_data(
+            binding, event, zmk_sensors_get_config_at_position(sensor_position), channel_data_size,
+            channel_data);
+
+        if (ret > 0) {
+            LOG_DBG("behavior processing to continue to next layer");
+            continue;
+        }
+
+        enum behavior_sensor_binding_process_mode mode =
+            (!opaque_response && layer >= _zmk_keymap_layer_default &&
+             zmk_keymap_layer_active(layer))
+                ? BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER
+                : BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD;
+
+        ret = behavior_sensor_keymap_binding_process(binding, event, mode);
+
+        if (ret == ZMK_BEHAVIOR_OPAQUE) {
+            LOG_DBG("sensor event processing complete, behavior response was opaque");
+            opaque_response = true;
+        } else if (ret < 0) {
+            LOG_DBG("Behavior returned error: %d", ret);
+            return ret;
         }
     }
 
-    return -ENOTSUP;
+    return 0;
 }
 
 #endif /* ZMK_KEYMAP_HAS_SENSORS */
@@ -308,8 +319,8 @@ int keymap_listener(const zmk_event_t *eh) {
 #if ZMK_KEYMAP_HAS_SENSORS
     const struct zmk_sensor_event *sensor_ev;
     if ((sensor_ev = as_zmk_sensor_event(eh)) != NULL) {
-        return zmk_keymap_sensor_triggered(sensor_ev->sensor_position, sensor_ev->channel_data_size,
-                                           sensor_ev->channel_data, sensor_ev->timestamp);
+        return zmk_keymap_sensor_event(sensor_ev->sensor_position, sensor_ev->channel_data_size,
+                                       sensor_ev->channel_data, sensor_ev->timestamp);
     }
 #endif /* ZMK_KEYMAP_HAS_SENSORS */