Implement control animation and basic animation behaviors

This commit is contained in:
Kuba Birecki 2022-01-18 22:52:24 +01:00
parent 69de72c6f4
commit fc8b91a2ba
9 changed files with 383 additions and 1 deletions

View file

@ -82,9 +82,11 @@ target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c)
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/color.c)
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/animation_compose.c)
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/animation_control.c)
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/animation_ripple.c)
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/animation_solid.c)
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/animation.c)
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/behaviors/behavior_animation.c)
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_status_changed.c)
add_subdirectory(src/split)

View file

@ -18,4 +18,5 @@
#include <behaviors/caps_word.dtsi>
#include <behaviors/key_repeat.dtsi>
#include <behaviors/backlight.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/animation.dtsi>

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
/ {
behaviors {
/omit-if-no-ref/ animation: behavior_animation {
compatible = "zmk,behavior-animation";
label = "ANIMATION";
#binding-cells = <1>;
};
};
};

View file

@ -0,0 +1,23 @@
# Copyright (c) 2020, The ZMK Contributors
# SPDX-License-Identifier: MIT
description: |
Higher-order animation which allows for controlling animation drivers
placed underneath it by turning them on and off, cycling though them,
or changing the brightness.
compatible: "zmk,animation-control"
properties:
animations:
type: phandles
required: true
description: |
Animations to be combined.
brightness-steps:
type: int
required: false
default: 5
description: |
How many brightness steps should be supported.

View file

@ -0,0 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Animation Action
compatible: "zmk,behavior-animation"
include: one_param.yaml

View file

@ -13,3 +13,44 @@
#else
#define HSL(h, s, l) (h + (s << 16) + (l << 24))
#endif
/**
* Animation blending modes
*/
#define BLENDING_MODE_NORMAL 0
#define BLENDING_MODE_MULTIPLY 1
#define BLENDING_MODE_LIGHTEN 2
#define BLENDING_MODE_DARKEN 3
#define BLENDING_MODE_SCREEN 4
#define BLENDING_MODE_SUBTRACT 5
/**
* Animation control commands
*/
#define ANIMATION_CMD_TOGGLE 0
#define ANIMATION_CMD_NEXT 1
#define ANIMATION_CMD_PREVIOUS 2
#define ANIMATION_CMD_SELECT 3
#define ANIMATION_CMD_BRIGHTEN 4
#define ANIMATION_CMD_DIM 5
#define ANIMATION_CMD_NEXT_CONTROL_ZONE 6
#define ANIMATION_CMD_PREVIOUS_CONTROL_ZONE 7
/**
* Generic animation command macro
*/
#define ANIMATION_CONTROL_CMD(command, zone, param) ((zone << 24) | (command << 16) | (param))
/**
* Animation behavior helpers
*/
#define ANIMATION_TOGGLE(zone) ANIMATION_CONTROL_CMD(ANIMATION_CMD_TOGGLE, zone, 0)
#define ANIMATION_NEXT(zone) ANIMATION_CONTROL_CMD(ANIMATION_CMD_NEXT, zone, 0)
#define ANIMATION_PREVIOUS(zone) ANIMATION_CONTROL_CMD(ANIMATION_CMD_PREVIOUS, zone, 0)
#define ANIMATION_SELECT(zone, target_animation) \
ANIMATION_CONTROL_CMD(ANIMATION_CMD_SELECT, zone, target_animation)
#define ANIMATION_BRIGHTEN(zone) ANIMATION_CONTROL_CMD(ANIMATION_CMD_BRIGHTEN, zone, 0)
#define ANIMATION_DIM(zone) ANIMATION_CONTROL_CMD(ANIMATION_CMD_DIM, zone, 0)
#define ANIMATION_NEXT_CONTROL_ZONE ANIMATION_CONTROL_CMD(ANIMATION_CMD_NEXT_CONTROL_ZONE, 0, 0)
#define ANIMATION_PREVIOUS_CONTROL_ZONE \
ANIMATION_CONTROL_CMD(ANIMATION_CMD_PREVIOUS_CONTROL_ZONE, 0, 0)

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr.h>
#include <device.h>
/**
* Animation control commands
*/
#define ANIMATION_CMD_TOGGLE 0
#define ANIMATION_CMD_NEXT 1
#define ANIMATION_CMD_PREVIOUS 2
#define ANIMATION_CMD_SELECT 3
#define ANIMATION_CMD_BRIGHTEN 4
#define ANIMATION_CMD_DIM 5
#define ANIMATION_CMD_NEXT_CONTROL_ZONE 6
#define ANIMATION_CMD_PREVIOUS_CONTROL_ZONE 7
int animation_control_handle_command(const struct device *dev, uint8_t command, uint8_t param);

View file

@ -0,0 +1,175 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_animation_control
#include <zephyr.h>
#include <device.h>
#include <drivers/animation.h>
#include <logging/log.h>
#include <zmk/animation.h>
#include <zmk/animation/animation_control.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
// Zephyr 2.7.0 comes with DT_INST_FOREACH_PROP_ELEM
// that we can't use quite yet as we're still on 2.5.*
#define ZMK_DT_INST_FOREACH_PROP_ELEM(inst, prop, fn) \
UTIL_LISTIFY(DT_INST_PROP_LEN(inst, prop), fn, DT_DRV_INST(inst), prop)
#define PHANDLE_TO_DEVICE(idx, node_id, prop) DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)),
struct animation_control_config {
const struct device **animations;
const size_t animations_size;
const uint8_t brightness_steps;
};
struct animation_control_data {
bool active;
uint8_t brightness;
size_t current_animation;
};
int animation_control_handle_command(const struct device *dev, uint8_t command, uint8_t param) {
const struct animation_control_config *config = dev->config;
struct animation_control_data *data = dev->data;
switch (command) {
case ANIMATION_CMD_TOGGLE:
data->active = !data->active;
if (data->active) {
animation_start(config->animations[data->current_animation]);
return 0;
}
animation_stop(config->animations[data->current_animation]);
break;
case ANIMATION_CMD_NEXT:
data->current_animation++;
if (data->current_animation == config->animations_size) {
data->current_animation = 0;
}
break;
case ANIMATION_CMD_PREVIOUS:
if (data->current_animation == 0) {
data->current_animation = config->animations_size;
}
data->current_animation--;
break;
case ANIMATION_CMD_SELECT:
if (config->animations_size < param) {
return -ENOTSUP;
}
data->current_animation = param;
break;
case ANIMATION_CMD_DIM:
if (data->brightness == 0) {
return 0;
}
data->brightness--;
if (data->brightness == 0) {
animation_stop(config->animations[data->current_animation]);
}
break;
case ANIMATION_CMD_BRIGHTEN:
if (data->brightness == config->brightness_steps) {
return 0;
}
if (data->brightness == 0) {
animation_start(config->animations[data->current_animation]);
}
data->brightness++;
break;
}
// Force refresh
zmk_animation_request_frames(1);
return 0;
}
void animation_control_start(const struct device *dev) {
const struct animation_control_config *config = dev->config;
const struct animation_control_data *data = dev->data;
if (!data->active) {
return;
}
animation_start(config->animations[data->current_animation]);
}
void animation_control_stop(const struct device *dev) {
const struct animation_control_config *config = dev->config;
const struct animation_control_data *data = dev->data;
animation_stop(config->animations[data->current_animation]);
}
void animation_control_render_frame(const struct device *dev, struct animation_pixel *pixels,
size_t num_pixels) {
const struct animation_control_config *config = dev->config;
const struct animation_control_data *data = dev->data;
if (!data->active) {
return;
}
animation_render_frame(config->animations[data->current_animation], pixels, num_pixels);
if (data->brightness == config->brightness_steps) {
return;
}
float brightness = (float)data->brightness / (float)config->brightness_steps;
for (size_t i = 0; i < num_pixels; ++i) {
pixels[i].value.r *= brightness;
pixels[i].value.g *= brightness;
pixels[i].value.b *= brightness;
}
}
static int animation_control_init(const struct device *dev) { return 0; }
static const struct animation_api animation_control_api = {
.on_start = animation_control_start,
.on_stop = animation_control_stop,
.render_frame = animation_control_render_frame,
};
#define ANIMATION_CONTROL_DEVICE(idx) \
\
static const struct device *animation_control_##idx##_animations[] = { \
ZMK_DT_INST_FOREACH_PROP_ELEM(idx, animations, PHANDLE_TO_DEVICE)}; \
\
static const struct animation_control_config animation_control_##idx##_config = { \
.animations = animation_control_##idx##_animations, \
.animations_size = DT_INST_PROP_LEN(idx, animations), \
.brightness_steps = DT_INST_PROP(idx, brightness_steps) - 1, \
}; \
\
static struct animation_control_data animation_control_##idx##_data = { \
.active = true, \
.brightness = DT_INST_PROP(idx, brightness_steps) - 1, \
.current_animation = 0, \
}; \
\
DEVICE_DT_INST_DEFINE(idx, &animation_control_init, NULL, &animation_control_##idx##_data, \
&animation_control_##idx##_config, POST_KERNEL, \
CONFIG_APPLICATION_INIT_PRIORITY, &animation_control_api);
DT_INST_FOREACH_STATUS_OKAY(ANIMATION_CONTROL_DEVICE);

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr.h>
#include <device.h>
#include <drivers/behavior.h>
#include <logging/log.h>
#include <zmk/animation/animation_control.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#define DT_DRV_COMPAT zmk_animation_control
#define DEVICE_ADDR(idx) DEVICE_DT_GET(DT_INST(idx, zmk_animation_control)),
/**
* Control animation instance pointers.
*/
static const struct device *control_animations[] = {DT_INST_FOREACH_STATUS_OKAY(DEVICE_ADDR)};
/**
* Size of control animation instance pointers array.
*/
static const uint8_t control_animations_size = sizeof(control_animations);
/**
* Index of the current default control animation instance.
*/
static uint8_t current_zone = 0;
#define DT_DRV_COMPAT zmk_behavior_animation
static int
on_keymap_binding_convert_central_state_dependent_params(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
if ((binding->param1 >> 24) == 0xff) {
binding->param1 = (current_zone << 24) | (binding->param1 & 0x00ffffff);
}
return 0;
}
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
uint8_t value = binding->param1 & 0xff;
uint8_t command = (binding->param1 >> 16) & 0xff;
uint8_t zone = (binding->param1 >> 24) & 0xff;
if (command == ANIMATION_CMD_NEXT_CONTROL_ZONE) {
current_zone++;
if (current_zone == control_animations_size) {
current_zone = 0;
}
return 0;
}
if (command == ANIMATION_CMD_PREVIOUS_CONTROL_ZONE) {
if (current_zone == 0) {
current_zone = control_animations_size;
}
current_zone--;
return 0;
}
if (control_animations_size <= zone) {
return -ENOTSUP;
}
return animation_control_handle_command(control_animations[zone], command, value);
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
return ZMK_BEHAVIOR_OPAQUE;
}
static int behavior_animation_init(const struct device *dev) { return 0; }
static const struct behavior_driver_api behavior_animation_driver_api = {
.binding_convert_central_state_dependent_params =
on_keymap_binding_convert_central_state_dependent_params,
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
};
DEVICE_DT_INST_DEFINE(0, behavior_animation_init, device_pm_control_nop, NULL, NULL, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_animation_driver_api);