Fri, 13 Jun 2025 18:09:49 +0200
implement interval for behaviors - fixes #383
src/ascension/behavior.h | file | annotate | diff | comparison | revisions | |
src/ascension/datatypes.h | file | annotate | diff | comparison | revisions | |
src/behavior.c | file | annotate | diff | comparison | revisions | |
src/scene.c | file | annotate | diff | comparison | revisions | |
test/snake/snake.c | file | annotate | diff | comparison | revisions |
--- a/src/ascension/behavior.h Fri Jun 13 17:45:19 2025 +0200 +++ b/src/ascension/behavior.h Fri Jun 13 18:09:49 2025 +0200 @@ -37,18 +37,33 @@ typedef void(*asc_behavior_destroy_func)(AscBehavior*); struct asc_behavior_s { + AscSceneNode *node; asc_behavior_func func; asc_behavior_destroy_func destroy_func; - AscSceneNode *node; void *data; + uint64_t last_execution; + uint64_t interval; bool enabled; - // TODO: more useful attributes, e.g. timing settings + // TODO: more useful attributes }; struct asc_behavior_create_args { - asc_behavior_func func; + /** + * The function that is invoked when the behavior is destroyed. + */ asc_behavior_destroy_func destroy_func; + /** + * The function that is invoked when the behavior is triggered. + */ + asc_behavior_func func; + /** + * Pointer to additional custom data. + */ void *data; + /** + * Minimum delay between two successive executions in nanoseconds. + */ + uint64_t interval; }; AscBehavior *asc_behavior_add_(AscSceneNode *node, struct asc_behavior_create_args args); @@ -56,4 +71,14 @@ // TODO: asc_behavior_remove() +void asc_behavior_trigger(AscBehavior *behavior); + +static inline void asc_behavior_enable(AscBehavior *behavior) { + behavior->enabled = true; +} + +static inline void asc_behavior_disable(AscBehavior *behavior) { + behavior->enabled = false; +} + #endif
--- a/src/ascension/datatypes.h Fri Jun 13 17:45:19 2025 +0200 +++ b/src/ascension/datatypes.h Fri Jun 13 18:09:49 2025 +0200 @@ -40,9 +40,10 @@ // Useful Macros // -------------------------------------------------------------------------- -#define ASC_NANOS_SECOND 1000000000ull -#define ASC_NANOS_MILLISECOND 1000000ull -#define ASC_NANOS_MICROSECOND 1000ull +#define asc_nanoseconds(t) (t) +#define asc_microseconds(t) (t*1000ull) +#define asc_milliseconds(t) (t*1000000ull) +#define asc_seconds(t) (t*1000000000ull) /** * Tests a value if it is zero and returns an alternative when it is.
--- a/src/behavior.c Fri Jun 13 17:45:19 2025 +0200 +++ b/src/behavior.c Fri Jun 13 18:09:49 2025 +0200 @@ -26,6 +26,7 @@ */ #include "ascension/behavior.h" +#include "ascension/context.h" #include "ascension/error.h" #include "ascension/scene.h" @@ -61,5 +62,15 @@ behavior->func = args.func; behavior->destroy_func = args.destroy_func; behavior->data = args.data; + behavior->interval = args.interval; + behavior->last_execution = 0; return behavior; } + +void asc_behavior_trigger(AscBehavior *behavior) { + if (!behavior->enabled) return; + if (behavior->last_execution + behavior->interval > asc_context.total_nanos) return; + + behavior->func(behavior); + behavior->last_execution = asc_context.total_nanos; +} \ No newline at end of file
--- a/src/scene.c Fri Jun 13 17:45:19 2025 +0200 +++ b/src/scene.c Fri Jun 13 18:09:49 2025 +0200 @@ -71,9 +71,7 @@ cx_foreach(AscSceneNode*, node, iter) { CxIterator behavior_iter = cxListIterator(node->behaviors); cx_foreach(AscBehavior*, behavior, behavior_iter) { - if (behavior->enabled) { - behavior->func(behavior); - } + asc_behavior_trigger(behavior); } } }
--- a/test/snake/snake.c Fri Jun 13 17:45:19 2025 +0200 +++ b/test/snake/snake.c Fri Jun 13 18:09:49 2025 +0200 @@ -57,22 +57,19 @@ static void update_fps_counter(AscBehavior *behavior) { AscSceneNode *node = behavior->node; + // update text static uint64_t last_fps = 0; - static uint64_t debounce = ASC_NANOS_SECOND - 1; - debounce += asc_context.frame_nanos; - // only update text every second - if (debounce >= ASC_NANOS_SECOND) { - debounce = 0; - uint64_t fps = ASC_NANOS_SECOND; - fps /= asc_context.frame_nanos; - if (fps != last_fps) { - last_fps = fps; - asc_text_printf(node, "%"PRIu64" FPS", fps); - } + uint64_t fps = asc_seconds(1) / asc_context.frame_nanos; + if (fps != last_fps) { + last_fps = fps; + asc_text_printf(node, "%"PRIu64" FPS", fps); } - // tie to bottom right of the screen - if (asc_test_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED) - || asc_active_window->resized) { +} + +static void tie_fps_counter_to_corner(AscBehavior *behavior) { + // TODO: this should be replaced with some sort of UI layout manager + AscSceneNode *node = behavior->node; + if (asc_test_flag(node->flags, ASC_SCENE_NODE_GRAPHICS_UPDATED) || asc_active_window->resized) { asc_vec2u bottom_right = asc_active_window->dimensions; asc_vec2u text_size = ((AscText*)node)->dimension; asc_set_position2d( @@ -105,7 +102,8 @@ asc_font(ASC_FONT_REGULAR, 12); asc_ink_rgb(255, 255, 255); AscSceneNode *node = asc_text(.name = "FPS Counter"); - asc_behavior_add(node, .func = update_fps_counter); + asc_behavior_add(node, .func = update_fps_counter, .interval = asc_seconds(1)); + asc_behavior_add(node, .func = tie_fps_counter_to_corner); asc_add_ui_node(node); }