#include "liquid.h" #include "rom/queue.h" #include "scenes.h" #include #include #include static const char *TAG = "Liquid"; struct RunningScene { struct Scene *scene; SLIST_ENTRY(RunningScene) next; } cmd_item_t; struct Liquid { SLIST_HEAD(, RunningScene) stack; // stack with the topmost scene as the first element / head }; static struct SceneEvent Default_onChildReturn(struct Scene *scene, uint32_t tag, uint32_t status, void *data) { if (data) free(data); return SceneEvent_Repaint(); } static void addChild(struct Liquid *container, struct Scene *child) { struct RunningScene *elm = calloc(1, sizeof(struct RunningScene)); if (!child->onChildReturn) child->onChildReturn = Default_onChildReturn; assert(child->paint != NULL); elm->scene = child; SLIST_INSERT_HEAD(&container->stack, elm, next); } struct Liquid *Liquid_start() { struct Liquid *container = calloc(1, sizeof(struct Liquid)); addChild(container, NewScene_Root()); Liquid_paint(container); return container; } static struct SceneEvent handleSceneEvent(struct Liquid *container, struct SceneEvent event) { struct RunningScene *topmost = SLIST_FIRST(&container->stack); switch (event.kind) { case SceneEventKind_Close: assert(SLIST_NEXT(topmost, next) != NULL); SLIST_REMOVE_HEAD(&container->stack, next); uint32_t tag = topmost->scene->tag; if (topmost->scene->options) { free(topmost->scene->options); } free(topmost->scene); free(topmost); topmost = SLIST_FIRST(&container->stack); // this is always set. return topmost->scene->onChildReturn(topmost->scene, tag, event.close.status, event.close.data); case SceneEventKind_OpenChild:; if (!event.open.scene) { ESP_LOGE(TAG, "Attempt to open NULL scene!"); return SceneEvent_None(); } struct Scene *newScene = event.open.scene; newScene->tag = event.open.tag; addChild(container, newScene); return SceneEvent_Repaint(); case SceneEventKind_RequestRepaint: case SceneEventKind_None: default: // this shouldn't get here return event; } } bool processReturnValue(struct Liquid *container, struct SceneEvent result) { while (result.kind != SceneEventKind_None) { if (result.kind == SceneEventKind_RequestRepaint) { return 1; // repaint } result = handleSceneEvent(container, result); } return 0; } bool Liquid_handleInput(struct Liquid *container, struct InputEvent event) { struct RunningScene *topmost = SLIST_FIRST(&container->stack); assert(topmost != NULL); assert(topmost->scene != NULL); if (topmost->scene->onInput) { struct SceneEvent result = topmost->scene->onInput(topmost->scene, event); return processReturnValue(container, result); } else { return false; } } bool Liquid_handleTick(struct Liquid *container) { struct RunningScene *topmost = SLIST_FIRST(&container->stack); assert(topmost != NULL); assert(topmost->scene != NULL); if (topmost->scene->onTick) { struct SceneEvent result = topmost->scene->onTick(topmost->scene); return processReturnValue(container, result); } else { return false; } } void Liquid_paint(struct Liquid *container) { struct RunningScene *topmost = SLIST_FIRST(&container->stack); assert(topmost != NULL); topmost->scene->paint(topmost->scene); }