/* * Copyright (C) 2018-2019 - Vito Caputo - * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "stage.h" struct stage_t { stage_t *parent; /* NULL when root stage */ dll_t layer; /* node on parent->layers[layer] when parent != NULL */ dll_t layers[STAGE_LAYERS_MAX]; char name[STAGE_NAME_MAX]; float alpha; /* alpha for the stage */ unsigned active:1; /* stage is active */ unsigned locked:1; /* stage is locked */ stage_render_func_t *render; /* render object */ stage_free_func_t *free; /* free object */ stage_match_func_t *match; /* match object against key */ void *object; /* object */ }; /* minimally initialize a stage to carry an object */ static void _stage_set_object(stage_t *stage, const char *name, void *object, stage_render_func_t *render_func, stage_free_func_t *free_func, stage_match_func_t *match_func) { assert(stage); assert(name); strncpy(stage->name, name, sizeof(stage->name)); stage->render = render_func; stage->free = free_func; stage->match = match_func; stage->object = object; } /* allocate a stage, this purely creates a stage in isolation and assigns its associated name, object and functions */ static stage_t * _stage_new(const char *name, void *object, stage_render_func_t *render_func, stage_free_func_t *free_func, stage_match_func_t *match_func) { stage_t *stage; stage = calloc(1, sizeof(stage_t)); if (!stage) return NULL; dll_init(&stage->layer); for (int i = 0; i < STAGE_LAYERS_MAX; i++) dll_init(&stage->layers[i]); _stage_set_object(stage, name, object, render_func, free_func, match_func); return stage; } /* free a stage, no list manipulation occurs, this is purely cleanup of the stage and its object */ static void _stage_free(stage_t *stage) { assert(stage); if (stage->free) stage->free(stage, stage->object); free(stage); } /* returns a new stage, attached at the specified layer under parent if supplied */ /* layer has no effect when parent == NULL */ stage_t * stage_new(stage_t *parent, int layer, const char *name, void *object, stage_render_func_t *render_func, stage_free_func_t *free_func, stage_match_func_t *match_func) { stage_t *stage; assert(parent || !layer); assert(layer < STAGE_LAYERS_MAX); stage = _stage_new(name, object, render_func, free_func, match_func); if (!stage) return NULL; if (parent) { stage->parent = parent; (void) dll_add_pre(&parent->layers[layer], &stage->layer); } return stage; } /* replaces a given stage's object-related properties but otherwise keeping the existing state */ void stage_replace(stage_t *stage, const char *name, void *object, stage_render_func_t *render_func, stage_free_func_t *free_func, stage_match_func_t *match_func) { assert(stage); if (stage->free) stage->free(stage, stage->object); _stage_set_object(stage, name, object, render_func, free_func, match_func); } static void _stage_free_all(stage_t *stage, int force); /* recursively free the layers of this stage, but not the stage itself */ static void _stage_free_layers(stage_t *stage, int force) { assert(stage); for (int i = 0; i < STAGE_LAYERS_MAX; i++) { stage_t *s, *_s; DLL_FOR_EACH_ENTRY_SAFE(&stage->layers[i], s, _s, stage_t, layer) _stage_free_all(s, force); } } /* free the stage recursively, skipping locked stages if !force */ static void _stage_free_all(stage_t *stage, int force) { assert(stage); if (stage->locked && !force) return; _stage_free_layers(stage, force); dll_del(&stage->layer); _stage_free(stage); } /* frees a given stage and all its descendants, removing from its * parent if not root. */ stage_t * stage_free(stage_t *stage) { if (stage) _stage_free_all(stage, 1); return NULL; } /* replace a given stage's object only - retain render/free hooks and everything else, invalidates cache - DOES NOT FREE OLD OBJECT */ void stage_set_object(stage_t *stage, void *object) { assert(stage); stage->object = object; } void * stage_get_object(const stage_t *stage) { assert(stage); return stage->object; } /* set the alpha on a stage */ void stage_set_alpha(stage_t *stage, float alpha) { assert(stage); stage->alpha = alpha; } /* get the current alpha from a stage */ float stage_get_alpha(const stage_t *stage) { assert(stage); return stage->alpha; } /* set a stage's active state (participates in rendering) */ void stage_set_active(stage_t *stage, int active) { assert(stage); stage->active = active; } /* get a stage's active state */ int stage_get_active(const stage_t *stage) { assert(stage); return stage->active; } /* set a stage's locked state (doesn't get freed by clears) */ void stage_set_locked(stage_t *stage, int locked) { assert(stage); stage->locked = locked; } /* get a stage's locked state */ int stage_get_locked(const stage_t *stage) { assert(stage); return stage->locked; } /* set a stage's layer (must not be root stage) */ /* TODO: maybe support supplying a parent here for switching parents? */ void stage_set_layer(stage_t *stage, int layer) { assert(stage); assert(stage->parent); (void) dll_del(&stage->layer); (void) dll_add_pre(&stage->parent->layers[layer], &stage->layer); } /* free everything in the stage, but keep the stage around */ /* probably good hygiene to clear everything when there's no intention of * reusing stages at the start of a new context, in case something is left * around unintentionally. */ void stage_clear(stage_t *stage) { _stage_free_layers(stage, 0); } static void _render_stage(const stage_t *stage, float alpha) { float a = alpha * stage->alpha; assert(stage); if (stage->render) stage->render(stage, stage->object, a); for (int i = 0; i < STAGE_LAYERS_MAX; i++) { stage_t *s; DLL_FOR_EACH_ENTRY(&stage->layers[i], s, stage_t, layer) { if (!s->active) continue; _render_stage(s, a); } } } /* recursively render the supplied stage tree, skipping inactive branches */ void stage_render(const stage_t *stage) { assert(stage); if (!stage->active) return; _render_stage(stage, 1.f); } /* lookup a stage from a name, returns first match */ stage_t * stage_lookup_name(stage_t *stage, const char *name) { assert(stage); assert(name); if (!strncmp(stage->name, name, sizeof(stage->name))) return stage; /* FIXME: this should probably search layers in descending order */ for (int i = 0; i < STAGE_LAYERS_MAX; i++) { stage_t *s; DLL_FOR_EACH_ENTRY(&stage->layers[i], s, stage_t, layer) { s = stage_lookup_name(s, name); if (s) return s; } } return NULL; }