implement interval for behaviors - fixes #383 default tip

Fri, 13 Jun 2025 18:09:49 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 13 Jun 2025 18:09:49 +0200
changeset 148
9f030f402699
parent 147
4908cc6c2e01

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);
 }
 

mercurial