diff options
Diffstat (limited to 'users/vitoni')
-rw-r--r-- | users/vitoni/readme.adoc | 16 | ||||
-rw-r--r-- | users/vitoni/rgb_matrix_effects.c | 236 | ||||
-rw-r--r-- | users/vitoni/rgb_matrix_effects.h | 174 | ||||
-rw-r--r-- | users/vitoni/rules.mk | 4 | ||||
-rw-r--r-- | users/vitoni/utils.c | 129 | ||||
-rw-r--r-- | users/vitoni/utils.h | 80 | ||||
-rw-r--r-- | users/vitoni/vitoni.c | 131 | ||||
-rw-r--r-- | users/vitoni/vitoni.h | 30 |
8 files changed, 800 insertions, 0 deletions
diff --git a/users/vitoni/readme.adoc b/users/vitoni/readme.adoc new file mode 100644 index 0000000000..acf65793d2 --- /dev/null +++ b/users/vitoni/readme.adoc @@ -0,0 +1,16 @@ += User functions + +Functions are mostly related to changing the RGB lights depending on user interaction and when idling. + +== utils.h + +Common functions are declared in link:utils.h[]. These function are not directly RGB related but used to modify state and calculate values. + +== rgb_matrix_effects.h + +Functions in link:rgb_matrix_effects.h[] make use of common function in `utils.h` and are used to create to RGB matrix effects such as fading or breathing. + +== vitoni.h + +The functions declared in link:vitoni.h[] are used as entry points for usage of RGB effects. +One entry point is `matrix_scan` based for regular task while the other is `process_record` based for user activity tasks. diff --git a/users/vitoni/rgb_matrix_effects.c b/users/vitoni/rgb_matrix_effects.c new file mode 100644 index 0000000000..3a13e99bc7 --- /dev/null +++ b/users/vitoni/rgb_matrix_effects.c @@ -0,0 +1,236 @@ +// Copyright 2021 Victor Toni (@vitoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "rgb_matrix_effects.h" + +#include <rgb_matrix.h> +#include <lib/lib8tion/lib8tion.h> + +#include "utils.h" + +/* + Offset used to start at the right point in th curve to avoid big jumps in brightness + 0 => 0% (signed) => 50% (unsigned) + 64 => 100% (signed) => 100% (unsigned) + 128 => 0% (signed) => 50% (unsigned) + 192 => -100% (signed) => 0% (unsigned) +*/ +enum PHASE { + PHASE_ZERO_RAISING + ,PHASE_HIGH + ,PHASE_ZERO_FALLING + ,PHASE_LOW +}; + +/** + * @brief Calculates the offset so that a specific time is aligned to a specific point in the sine curve. + * @param[in] time The time for which the offset shopuld be calculated. + * @param[in] phase Phase which should be reached with the offset + * @see PHASE + */ +uint8_t offset_for_time(const uint8_t time, const uint8_t phase) { + switch (phase) { + case PHASE_ZERO_RAISING: + return 0 - time; + case PHASE_HIGH: + return 64 - time; + case PHASE_ZERO_FALLING: + return 128 - time; + case PHASE_LOW: + return 192 - time; + default: + return 0; + } +} + +/** + * @brief Scales down `g_rgb_timer` so that it can be used for RGB effects. + * @return scaled down timer + * @see rgb_time_2_scale_w_factor() + */ +uint8_t rgb_time_2_scale(void) { + static const uint8_t factor = 1; + return rgb_time_2_scale_w_factor(factor); +} + +/* + * Used to slow down RGB speed. + */ +static const uint8_t rgb_speed_divisor = 8; + +/** + * @brief Scales down `g_rgb_timer` so that it can be used for RGB effects. + * @details Usually these calculations aredone internally by some RGB effects. + This method exposed to scaling so that all effects to have same timebase. If `rgb_matrix_config.speed` all effects are affected the same. + * @param[in] factor The factor can be used to speed up some operations in relation to others. + * @return scaled down timer taking into account the given factor + * @see g_rgb_timer + * @see rgb_matrix_config.speed + */ +uint8_t rgb_time_2_scale_w_factor(const uint8_t rgb_speed_factor) { + const uint8_t scaled_time = scale16by8(g_rgb_timer, rgb_matrix_config.speed * rgb_speed_factor / rgb_speed_divisor); + + return scaled_time; +} + +/** + * @brief Inverse function to calculate time required to execute `timer` steps. + * @details This method allows calculation of the time needed to execute N `timer`steps. + Usefull when using a scaled down time but requiring the time needed to perform these steps. + * @param[in] scaled_time scaled down timer to inverse to time + * @return time corresponding to scaled down time + * @see rgb_time_2_scale() + */ +uint16_t scale_2_rgb_time(const uint8_t scaled_time) { + const uint16_t time = scaled_time * rgb_speed_divisor * UINT8_MAX / rgb_matrix_config.speed; + + return time; +} + +bool fade_in_ranged(const uint8_t time, const uint8_t range_min, const uint8_t range_max) { + static const uint8_t max_delta = 1; + return scaled_sin_up(time, range_min, range_max, max_delta, &(rgb_matrix_config.hsv.v)); +} + +bool fade_out_ranged(const uint8_t time, const uint8_t range_min, const uint8_t range_max) { + static const uint8_t max_delta = 1; + return scaled_sin_down(time, range_min, range_max, max_delta, &(rgb_matrix_config.hsv.v)); +} + +/** + * @brief Convenience method to eventually skip the value part when setting HSV. + * @details When setting HSV this includes the value/brightness. + As changing brightness might interfer with fading or breathing effects, + this method can skip the value part of HSV (depending on the preprocessor flag: RGB_FADE_IN). + * @param[in] hue Hue + * @param[in] sat Saturation + * @param[in] hue Value (brightness) + * @see rgb_matrix_sethsv_noeeprom() + */ +void rgb_matrix_sethsv_noeeprom_user(const uint16_t hue, const uint8_t sat, const uint8_t val) { +#if defined(RGB_FADE_IN) || defined(RGB_IDLE_TIMEOUT) + rgb_matrix_config.hsv.h = hue; + rgb_matrix_config.hsv.s = sat; + // omitting setting the value to avoid interfering with effects +// rgb_matrix_config.hsv.v = val; +#else + rgb_matrix_sethsv_noeeprom(hue, sat, val); +#endif +} + +#if defined(RGB_FADE_IN) || defined(RGB_IDLE_TIMEOUT) +/** + * @brief Calculates the time offset required by fade in. + * @details Using an arbitrary timer any point on the sine curve might be pointed to. + * The offest is calculated so that + * a) the point is at the lowest point in the curve and the curve is raising + * b) the point is near the current brightness (eg. fade in might be called while fading out and the lowest value has not yet been reached). + * @param[in] time Current time usually represented by (usually scaled) timer + * @return Offset required so that time matches the current brightness + */ +uint8_t calc_fade_in_offset(const uint8_t time) { + static const uint8_t max_steps = UINT8_MAX/2; + static const uint8_t range_min = 0; + static const uint8_t range_max = RGB_MATRIX_MAXIMUM_BRIGHTNESS; + + // start at the right point in the sine curve + uint8_t time_offset = offset_for_time(time, PHASE_LOW); + + // find the right offset to match the current brightness + for (int i = 1; i < max_steps; i++) { + const uint8_t value = scaled_sin(time + time_offset + 1, range_min, range_max); + if (in_range(value, range_min, range_max) && value < rgb_matrix_config.hsv.v) { + time_offset++; + } else { + break; + } + } + + return time_offset; +} + +/** + * @brief Increases value/brightness until reaching RGB_MATRIX_MAXIMUM_BRIGHTNESS based on given timer. + * @param[in] time A (usually scaled) timer + * @return Returns `true` if RGB_MATRIX_MAXIMUM_BRIGHTNESS has been reached, `false` otherwise. + */ +bool fade_in(const uint8_t time) { + static const uint8_t range_min = 0; + static const uint8_t range_max = RGB_MATRIX_MAXIMUM_BRIGHTNESS; + + return fade_in_ranged(time, range_min, range_max); +} +#endif + +#if defined(RGB_DISABLE_WITH_FADE_OUT) || defined(RGB_IDLE_TIMEOUT) +/** + * @brief Calculates the time offset required by fade out. + * @details Using an arbitrary timer any point on the Sinus curve might be pointed to. + * The offest is calculated so that + * a) the point is at the highest point in the curve and the curve is failing + * b) the point is near the current brightness (eg. fade out might be called while on breath effect). + * @param[in] time Current time usually represented by a(usually scaled) timer + * @return Offset required so that time matches the current brightness + */ +uint8_t calc_fade_out_offset(const uint8_t time) { + static const uint8_t range_min = 0; + static const uint8_t range_max = RGB_MATRIX_MAXIMUM_BRIGHTNESS; + + // start at the right point in the sin() curve + uint8_t time_offset = offset_for_time(time, PHASE_HIGH); + + // find the right offset to match the current brightness + for (int i = 1; i < 127; i++) { + const uint8_t value = scaled_sin(time + time_offset + 1, range_min, range_max); + if (in_range(value, range_min, range_max) && rgb_matrix_config.hsv.v < value) { + time_offset++; + } else { + break; + } + } + + return time_offset; +} +#endif + +#if defined(RGB_DISABLE_WITH_FADE_OUT) +/** + * @brief Decreases value/brightness until reaching 0 based on given timer. + * @param[in] time A (usually scaled) timer + * @return Returns `true` if 0 has been reached, `false` otherwise. + */ +bool fade_out(const uint8_t time) { + static const uint8_t range_min = 0; + static const uint8_t range_max = RGB_MATRIX_MAXIMUM_BRIGHTNESS; + + return fade_out_ranged(time, range_min, range_max); +} +#endif + +#if defined(RGB_IDLE_TIMEOUT) +/** + * @brief Decreases value/brightness until reaching `RGB_IDLE_MINIMUM_BRIGHTNESS` based on given timer. + * @param[in] time A (usually scaled) timer + * @return Returns `true` if `RGB_IDLE_MINIMUM_BRIGHTNESS` has been reached, `false` otherwise. + */ +bool idle_fade_out(const uint8_t time) { + static const uint8_t range_min = RGB_IDLE_MINIMUM_BRIGHTNESS; + static const uint8_t range_max = RGB_MATRIX_MAXIMUM_BRIGHTNESS; + + return fade_out_ranged(time, range_min, range_max); +} + +#if defined(RGB_IDLE_BREATHE) +/** + * @brief Changes value/brightness to create a breathing effect based on given timer. + * @details Brightness will breathe in the range starting from `RGB_IDLE_MINIMUM_BRIGHTNESS` to `RGB_IDLE_MAXIMUM_BRIGHTNESS`. + * @param[in] time A (usually scaled) timer + */ +void idle_breathe(const uint8_t time) { + static const uint8_t range_min = RGB_IDLE_MINIMUM_BRIGHTNESS; + static const uint8_t range_max = RGB_IDLE_MAXIMUM_BRIGHTNESS; + + rgb_matrix_config.hsv.v = scaled_sin(time, range_min, range_max); +} +#endif // RGB_IDLE_BREATHE +#endif // RGB_IDLE_TIMEOUT diff --git a/users/vitoni/rgb_matrix_effects.h b/users/vitoni/rgb_matrix_effects.h new file mode 100644 index 0000000000..ed74500b18 --- /dev/null +++ b/users/vitoni/rgb_matrix_effects.h @@ -0,0 +1,174 @@ +// Copyright 2021 Victor Toni (@vitoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +/** + * States reflecting the state of the keyboard. + * Dependeing on these states various effects can set for the RGB matrix. + */ +enum states { + REGULAR //!< when in regular use +#if defined(RGB_IDLE_TIMEOUT) + ,IDLE_FADE_OUT //!< when started idling + ,IDLE //!< when idling +#endif +#if defined(RGB_FADE_IN) || defined(RGB_IDLE_TIMEOUT) + ,FADE_IN //!< when starting initially or before going back to REGULAR +#endif +#if defined(RGB_DISABLE_WITH_FADE_OUT) + ,FADE_OUT //!< before supending +#endif + ,SUSPENDED //!< expecting to be suspended by RGB_DISABLE_TIMEOUT any time +}; + +/** + * @brief Scales down `g_rgb_timer` so that it can be used for RGB effects. + * @details Usually these calculations aredone internally by some RGB effects. + This method exposed to scaling so that all effects to have same timebase. If `rgb_matrix_config.speed` all effects are affected the same. + * @param[in] factor The factor can be used to speed up some operations in relation to others. + * @return scaled down timer taking into account the given factor + * @see g_rgb_timer + * @see rgb_matrix_config.speed + */ +uint8_t rgb_time_2_scale_w_factor(const uint8_t factor); + +/** + * @brief Scales down `g_rgb_timer` so that it can be used for RGB effects. + * @return scaled down timer + * @see rgb_time_2_scale_w_factor() + */ +uint8_t rgb_time_2_scale(void); + +/** + * @brief Inverse function to calculate time required to execute `timer` steps. + * @details This method allows calculation of the time needed to execute N `timer`steps. + Usefull when using a scaled down time but requiring the time needed to perform these steps. + * @param[in] scaled_time scaled down timer to inverse to time + * @return time corresponding to scaled down time + * @see rgb_time_2_scale() + */ +uint16_t scale_2_rgb_time(const uint8_t scaled_time); + +/** + * @brief Convenience method to eventually skip the value part when setting HSV. + * @details When setting HSV this includes the value/brightness. + As changing brightness might interfer with fading or breathing effects, + this method can skip the value part of HSV (depending on the preprocessor flag: RGB_FADE_IN). + * @param[in] hue Hue + * @param[in] sat Saturation + * @param[in] hue Value (brightness) + * @see rgb_matrix_sethsv_noeeprom() + */ +void rgb_matrix_sethsv_noeeprom_user(const uint16_t hue, const uint8_t sat, const uint8_t val); + +#if defined(RGB_FADE_IN) || defined(RGB_DISABLE_WITH_FADE_OUT) || defined(RGB_IDLE_TIMEOUT) +# if defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) +# if (RGB_MATRIX_MAXIMUM_BRIGHTNESS) < 1 +# error "RGB_MATRIX_MAXIMUM_BRIGHTNESS must not be less than ONE" +# endif +# if UINT8_MAX < (RGB_MATRIX_MAXIMUM_BRIGHTNESS) +# error "RGB_MATRIX_MAXIMUM_BRIGHTNESS must not be larger than UINT8_MAX" +# endif +# else +# define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 +# endif +#endif + +#if defined(RGB_FADE_IN) || defined(RGB_IDLE_TIMEOUT) +/** + * @brief Calculates the time offset required by fade in. + * @details Using an arbitrary timer any point on the sine curve might be pointed to. + * The offset is calculated so that + * a) the point is at the lowest point in the curve and the curve is raising + * b) the point is near the current brightness (eg. fade in might be called while fading out and the lowest value has not yet been reached). + * @param[in] time Current time usually represented by a(usually scaled) timer + * @return Offset required so that time matches the current brightness + */ +uint8_t calc_fade_in_offset(const uint8_t time); + +/** + * @brief Increases value/brightness until reaching RGB_MATRIX_MAXIMUM_BRIGHTNESS based on given timer. + * @param[in] time A (usually scaled) timer + * @return Returns `true` if RGB_MATRIX_MAXIMUM_BRIGHTNESS has been reached, `false` otherwise. + */ +bool fade_in(const uint8_t time); +#endif + +#if defined(RGB_DISABLE_WITH_FADE_OUT) +# if !defined(RGB_DISABLE_TIMEOUT) +# warning "RGB_DISABLE_WITH_FADE_OUT expects RGB_DISABLE_TIMEOUT to be defined" +# endif +#endif + +#if defined(RGB_DISABLE_WITH_FADE_OUT) || defined(RGB_IDLE_TIMEOUT) +/** + * @brief Calculates the time offset required by fade out. + * @details Using an arbitrary timer any point on the Sinus curve might be pointed to. + * The offest is calculated so that + * a) the point is at the highest point in the curve and the curve is failing + * b) the point is near the current brightness (eg. fade out might be called while on breath effect). + * @param[in] time Current time usually represented by a(usually scaled) timer + * @return Offset required so that time matches the current brightness + */ +uint8_t calc_fade_out_offset(const uint8_t time); +#endif + +#if defined(RGB_DISABLE_WITH_FADE_OUT) +/** + * @brief Decreases value/brightness until reaching 0 based on given timer. + * @param[in] time A (usually scaled) timer + * @return Returns `true` if 0 has been reached, `false` otherwise. + */ +bool fade_out(const uint8_t time); +#endif + +#if defined(RGB_IDLE_TIMEOUT) +# if RGB_IDLE_TIMEOUT < 0 +# error "RGB_IDLE_TIMEOUT must not be less than ZERO" +# endif +# if !defined(RGB_IDLE_MINIMUM_BRIGHTNESS) + // minimum brightness when idling +# define RGB_IDLE_MINIMUM_BRIGHTNESS (RGB_MATRIX_MAXIMUM_BRIGHTNESS/5) +# endif +# if RGB_IDLE_MINIMUM_BRIGHTNESS < 0 +# error "RGB_IDLE_MINIMUM_BRIGHTNESS must not be less than ZERO" +# endif // RGB_IDLE_MINIMUM_BRIGHTNESS < 0 +# if RGB_MATRIX_MAXIMUM_BRIGHTNESS <= RGB_IDLE_MINIMUM_BRIGHTNESS +# error "RGB_IDLE_MINIMUM_BRIGHTNESS must be less than RGB_MATRIX_MAXIMUM_BRIGHTNESS" +# endif // RGB_MATRIX_MAXIMUM_BRIGHTNESS <= RGB_IDLE_MINIMUM_BRIGHTNESS + +/** + * @brief Decreases value/brightness until reaching `RGB_IDLE_MINIMUM_BRIGHTNESS` based on given timer. + * @param[in] time A (usually scaled) timer + * @return Returns `true` if `RGB_IDLE_MINIMUM_BRIGHTNESS` has been reached, `false` otherwise. + */ +bool idle_fade_out(const uint8_t time); + +#if defined(RGB_IDLE_BREATHE) +# if !defined(RGB_IDLE_MAXIMUM_BRIGHTNESS) + // maximum brightness when idling +# define RGB_IDLE_MAXIMUM_BRIGHTNESS (RGB_MATRIX_MAXIMUM_BRIGHTNESS*3/5) +# endif +# if !(0 <= RGB_IDLE_MAXIMUM_BRIGHTNESS) +# error "RGB_IDLE_MINIMUM_BRIGHTNESS must not be less than ZERO, was: " RGB_IDLE_MAXIMUM_BRIGHTNESS +# endif // RGB_IDLE_MAXIMUM_BRIGHTNESS < 0 +# if !(RGB_IDLE_MINIMUM_BRIGHTNESS < RGB_IDLE_MAXIMUM_BRIGHTNESS) +# error "RGB_IDLE_MINIMUM_BRIGHTNESS must be less than RGB_IDLE_MAXIMUM_BRIGHTNESS" +# endif // RGB_IDLE_MAXIMUM_BRIGHTNESS <= RGB_IDLE_MINIMUM_BRIGHTNESS +# if !(RGB_IDLE_MAXIMUM_BRIGHTNESS <= RGB_MATRIX_MAXIMUM_BRIGHTNESS) +# error "RGB_IDLE_MAXIMUM_BRIGHTNESS must be less than or equal to RGB_MATRIX_MAXIMUM_BRIGHTNESS" +# endif // RGB_MATRIX_MAXIMUM_BRIGHTNESS <= RGB_IDLE_MAXIMUM_BRIGHTNESS + +/** + * @brief Changes value/brightness to create a breathing effect based on given timer. + * @details Brightness will breathe in the range starting from `RGB_IDLE_MINIMUM_BRIGHTNESS` to `RGB_IDLE_MAXIMUM_BRIGHTNESS`. + * @param[in] time A (usually scaled) timer + */ +void idle_breathe(const uint8_t time); +#endif // RGB_IDLE_BREATHE + +#endif // RGB_IDLE_TIMEOUT diff --git a/users/vitoni/rules.mk b/users/vitoni/rules.mk new file mode 100644 index 0000000000..2f3b0d15e5 --- /dev/null +++ b/users/vitoni/rules.mk @@ -0,0 +1,4 @@ +SRC += \ + vitoni.c \ + utils.c \ + rgb_matrix_effects.c diff --git a/users/vitoni/utils.c b/users/vitoni/utils.c new file mode 100644 index 0000000000..fb011570e4 --- /dev/null +++ b/users/vitoni/utils.c @@ -0,0 +1,129 @@ +// Copyright 2021 Victor Toni (@vitoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "utils.h" + +#include <lib/lib8tion/lib8tion.h> + +/** +* @brief Changes `*value` to `new_value`. +* @param[in,out] value Pointer to variable to be changed. +* @param[in] new_value Value to be changed. +* @param[in,out] changed Flag indicating `*value` and `new_value` were different. +*/ +void update_value(uint8_t *value, const uint8_t new_value, bool *changed) { + if (new_value != (*value)) { + (*changed) = true; + (*value) = new_value; + } +} + +/** +* @brief Checks whether a value is in the given range. +* @param[in] value Value to be checked. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @return `true` if (range_min <= value <= range_max), `false` otherwise +*/ +bool in_range(const uint8_t value, const uint8_t range_min, const uint8_t range_max) { + return range_min <= value && value <= range_max; +} + +/** +* @brief Calculates the sine value based on sin8() and scales it to the given range (unsigned). +* +* Table of values for unscaled sin8() eg. a theta of 0 results to 128 and a theta of 255 (240+15) results to 125. + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +---------------------------------------------------------------- + 0: 128 131 134 137 140 143 146 149 152 155 158 161 164 167 170 173 + 16: 177 179 182 184 187 189 192 194 197 200 202 205 207 210 212 215 + 32: 218 219 221 223 224 226 228 229 231 233 234 236 238 239 241 243 + 48: 245 245 246 246 247 248 248 249 250 250 251 251 252 253 253 254 + 64: 255 254 253 253 252 251 251 250 250 249 248 248 247 246 246 245 + 80: 245 243 241 239 238 236 234 233 231 229 228 226 224 223 221 219 + 96: 218 215 212 210 207 205 202 200 197 194 192 189 187 184 182 179 + 112: 177 173 170 167 164 161 158 155 152 149 146 143 140 137 134 131 + 128: 128 125 122 119 116 113 110 107 104 101 98 95 92 89 86 83 + 144: 79 77 74 72 69 67 64 62 59 56 54 51 49 46 44 41 + 160: 38 37 35 33 32 30 28 27 25 23 22 20 18 17 15 13 + 176: 11 11 10 10 9 8 8 7 6 6 5 5 4 3 3 2 + 192: 1 2 3 3 4 5 5 6 6 7 8 8 9 10 10 11 + 208: 11 13 15 17 18 20 22 23 25 27 28 30 32 33 35 37 + 224: 38 41 44 46 49 51 54 56 59 62 64 67 69 72 74 77 + 240: 79 83 86 89 92 95 98 101 104 107 110 113 116 119 122 125 +* +* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sine calculation. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @return Calculated sine value mapped to the given range. +*/ +uint8_t scaled_sin(const uint8_t theta, const uint8_t range_min, const uint8_t range_max) { + const uint8_t range = range_max - range_min; + return scale8(sin8(theta), range) + range_min; +} + +/** +* @brief Increases the given value until reaching range_max. +* The increments occur following an upwards sine wave (scaled from range_min to range_max). +* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sine calculation. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @param[in] max_delta Maximum delta between value and range_max (due to values being integers and eventually not fully matching). +* @param[in,out] value Reference of variable to be increased +* @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise +* @see scaled_sin() +*/ +bool scaled_sin_up(const uint8_t theta, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value) { + // ensure upper range bound + if (range_max <= (*value)) { + (*value) = range_max; + return true; + } + + const uint8_t new_value = scaled_sin(theta, range_min, range_max); + if (in_range(new_value, range_min, range_max) && (*value) < new_value) { + (*value) = new_value; + + return range_max == (*value); + } + + const uint8_t delta = range_max - (*value); + if (delta <= max_delta) { + (*value) = range_max; + } + + return delta <= max_delta; +} + +/** +* @brief Decreases the given value until reaching range_min. +* The decrements occur following an downwards sinus wave (scaled from range_min to range_max). +* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sinus calculation. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @param[in] max_delta Maximum delta between value and range_min (due to values being integers and eventually not fully matching). +* @param[in,out] value Reference of variable to be decreased +* @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise +* @see scaled_sin() +*/ +bool scaled_sin_down(const uint8_t theta, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value) { + // ensure lower range bound + if ((*value) <= range_min) { + (*value) = range_min; + return true; + } + + const uint8_t new_value = scaled_sin(theta, range_min, range_max); + if (in_range(new_value, range_min, range_max) && new_value < (*value)) { + (*value) = new_value; + + return range_min == (*value); + } + + const uint8_t delta = (*value) - range_min; + if (delta <= max_delta) { + (*value) = range_min; + } + + return delta <= max_delta; +} diff --git a/users/vitoni/utils.h b/users/vitoni/utils.h new file mode 100644 index 0000000000..987b612d58 --- /dev/null +++ b/users/vitoni/utils.h @@ -0,0 +1,80 @@ +// Copyright 2021 Victor Toni (@vitoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +/** +* @brief Changes `*value` to `new_value`. +* @param[in,out] value Pointer to variable to be changed. +* @param[in] new_value Value to be changed. +* @param[in,out] changed Flag indicating `*value` and `new_value` were different. +*/ +void update_value(uint8_t *value, const uint8_t new_value, bool *changed); + +/** +* @brief Checks whether a value is in the given range. +* @param[in] value Value to be checked. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @return `true` if (range_min <= value <= range_max), `false` otherwise +*/ +bool in_range(const uint8_t value, const uint8_t range_min, const uint8_t range_max); + +/** +* @brief Calculates the sine value based on sin8() and scales it to the given range (unsigned). +* +* Table of values for unscaled sin8() eg. a theta of 0 results to 128 and a theta of 255 (240+15) results to 125. + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +---------------------------------------------------------------- + 0: 128 131 134 137 140 143 146 149 152 155 158 161 164 167 170 173 + 16: 177 179 182 184 187 189 192 194 197 200 202 205 207 210 212 215 + 32: 218 219 221 223 224 226 228 229 231 233 234 236 238 239 241 243 + 48: 245 245 246 246 247 248 248 249 250 250 251 251 252 253 253 254 + 64: 255 254 253 253 252 251 251 250 250 249 248 248 247 246 246 245 + 80: 245 243 241 239 238 236 234 233 231 229 228 226 224 223 221 219 + 96: 218 215 212 210 207 205 202 200 197 194 192 189 187 184 182 179 + 112: 177 173 170 167 164 161 158 155 152 149 146 143 140 137 134 131 + 128: 128 125 122 119 116 113 110 107 104 101 98 95 92 89 86 83 + 144: 79 77 74 72 69 67 64 62 59 56 54 51 49 46 44 41 + 160: 38 37 35 33 32 30 28 27 25 23 22 20 18 17 15 13 + 176: 11 11 10 10 9 8 8 7 6 6 5 5 4 3 3 2 + 192: 1 2 3 3 4 5 5 6 6 7 8 8 9 10 10 11 + 208: 11 13 15 17 18 20 22 23 25 27 28 30 32 33 35 37 + 224: 38 41 44 46 49 51 54 56 59 62 64 67 69 72 74 77 + 240: 79 83 86 89 92 95 98 101 104 107 110 113 116 119 122 125 +* +* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sine calculation. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @return Calculated sine value mapped to the given range. +*/ +uint8_t scaled_sin(const uint8_t theta, const uint8_t range_min, const uint8_t range_max); + +/** +* @brief Increases the given value until reaching range_max. +* The increments occur following an upwards sine wave (scaled from range_min to range_max). +* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sine calculation. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @param[in] max_delta Maximum delta between value and range_max (due to values being integers and eventually not fully matching). +* @param[in,out] value Reference of variable to be increased +* @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise +* @see scaled_sin() +*/ +bool scaled_sin_up(const uint8_t thea, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value); + +/** +* @brief Decreases the given value until reaching range_min. +* The decrements occur following an downwards sinus wave (scaled from range_min to range_max). +* @param[in] theta Angle (a full circle mapped to 0-255) used as input for sinus calculation. +* @param[in] range_min Lower bound of range (inclusive). +* @param[in] range_max Upper bound of range (inclusive). +* @param[in] max_delta Maximum delta between value and range_min (due to values being integers and eventually not fully matching). +* @param[in,out] value Reference of variable to be decreased +* @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise +* @see scaled_sin() +*/ +bool scaled_sin_down(const uint8_t theta, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value); diff --git a/users/vitoni/vitoni.c b/users/vitoni/vitoni.c new file mode 100644 index 0000000000..2a0ff5c46f --- /dev/null +++ b/users/vitoni/vitoni.c @@ -0,0 +1,131 @@ +// Copyright 2021 Victor Toni (@vitoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "vitoni.h" + +#include <rgb_matrix.h> +#include <lib/lib8tion/lib8tion.h> + +#include "rgb_matrix_effects.h" +#include "utils.h" + +#if defined(RGB_FADE_IN) || defined(RGB_DISABLE_WITH_FADE_OUT) || defined(RGB_IDLE_TIMEOUT) +static uint8_t state; + +// flag used to indicate that offset calculation is needed to adjust the timer, +// so that it matches the index used for sine calculation +static bool calc_offset; + +void matrix_scan_user_rgb(void) { +#if defined(RGB_DISABLE_WITH_FADE_OUT) || defined(RGB_IDLE_TIMEOUT) + const uint8_t time = rgb_time_2_scale(); +#endif + static uint8_t time_offset; + + const uint32_t inactivity_millis = last_input_activity_elapsed(); + +#if defined(RGB_IDLE_TIMEOUT) + if (IDLE != state && RGB_IDLE_TIMEOUT <= inactivity_millis) { + update_value(&state, IDLE_FADE_OUT, &calc_offset); + } +#endif +#if defined(RGB_DISABLE_WITH_FADE_OUT) + const uint32_t fade_out_duration = scale_2_rgb_time(128); + const uint32_t start_fade_out_after_millis = (RGB_DISABLE_TIMEOUT) > fade_out_duration + ? (RGB_DISABLE_TIMEOUT) - fade_out_duration + : 0; + + if (start_fade_out_after_millis <= inactivity_millis) { + update_value(&state, FADE_OUT, &calc_offset); + } +#elif defined(RGB_DISABLE_TIMEOUT) + // having to set brightness "manually" to black as starting point for fade in + // for the time when returning from suspended state + if (RGB_DISABLE_TIMEOUT <= inactivity_millis + 15) { + rgb_matrix_config.hsv.v = 0; + state = SUSPENDED; + } +#endif + + switch(state) { +#if defined(RGB_IDLE_TIMEOUT) + case IDLE_FADE_OUT: + if (calc_offset) { + time_offset = calc_fade_out_offset(time); + + // resetting flag for subsequent calls + calc_offset = false; + } + if (idle_fade_out(time + time_offset)) { + update_value(&state, IDLE, &calc_offset); + } + break; + case IDLE: +#if defined(RGB_IDLE_BREATHE) + if (calc_offset) { + // no need to calculate time_offset since we are aligned already due to IDLE_FADE_OUT + // resetting flag for subsequent calls + calc_offset = false; + } + idle_breathe(time + time_offset); +#endif + break; +#endif +#if defined(RGB_DISABLE_WITH_FADE_OUT) + case FADE_OUT: + if (calc_offset) { + time_offset = calc_fade_out_offset(time); + + // resetting flag for subsequent calls + calc_offset = false; + } + if (fade_out(time + time_offset)) { + update_value(&state, SUSPENDED, &calc_offset); + } + break; +#endif +#if defined(RGB_FADE_IN) || defined(RGB_IDLE_TIMEOUT) + case FADE_IN: + { + // since we want to be active, fade in should be faster than e.g. fading out + const uint8_t fade_in_time = rgb_time_2_scale_w_factor(4); + if (calc_offset) { + time_offset = calc_fade_in_offset(fade_in_time); + + // resetting flag for subsequent calls + calc_offset = false; + } + if (fade_in(fade_in_time + time_offset)) { + update_value(&state, REGULAR, &calc_offset); + } + } + break; +#endif + default: + break; + } +} + +#if defined(RGB_FADE_IN) || defined(RGB_IDLE_TIMEOUT) +bool process_record_user_rgb(const uint16_t keycode, const keyrecord_t *record) { + // if we are in a non regular state we might have faded out (eventually partially) + // so we restore brightness (to max as we don't keep track of manually changed brightness) + // if (REGULAR != state && FADE_IN != state) { + if (FADE_IN != state && REGULAR != state) { + update_value(&state, FADE_IN, &calc_offset); + } + + return true; // Process all other keycodes normally +} + +void suspend_wakeup_init_user(void) { + if (FADE_IN != state) { + // setting brightness to black as starting point for fade in + rgb_matrix_config.hsv.v = 0; + + update_value(&state, FADE_IN, &calc_offset); + } +} +#endif // defined(RGB_FADE_IN) + +#endif // defined(RGB_FADE_IN) || defined(RGB_DISABLE_WITH_FADE_OUT) diff --git a/users/vitoni/vitoni.h b/users/vitoni/vitoni.h new file mode 100644 index 0000000000..1f26037135 --- /dev/null +++ b/users/vitoni/vitoni.h @@ -0,0 +1,30 @@ +// Copyright 2021 Victor Toni (@vitoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <quantum/action.h> + +#include "rgb_matrix_effects.h" + +/** + * @brief Executes periodic tasks, eg. fading or checking for upcoming supend. + * @details Function declaration as weak as the implementation might "disappear" depending on the RGB settings used. + * The weak declaration avoids having to change `keymap.c`. + */ +__attribute__((weak)) +void matrix_scan_user_rgb(void); + +/** + * @brief Executes tasks based on user activity, eg. fading in. + * @details Function declaration as weak as the implementation might "disappear" depending on the RGB settings used. + * The weak declaration avoids having to change `keymap.c`. + * @param[in] keycode + * @param[in] record + * @return `false` if further processing should be stopped, `true` otherwise + */ +__attribute__((weak)) +bool process_record_user_rgb(const uint16_t keycode, const keyrecord_t *record); |