timeline: don't use ALLEGRO_TIMERs anymore, rely on delta time instead

This commit is contained in:
Sebastian Krzyszkowiak 2018-06-29 22:59:25 +02:00
parent b5d2b4a032
commit a4a1c356df
3 changed files with 83 additions and 176 deletions

View file

@ -457,12 +457,12 @@ static void DrawQueue(struct Game* game, struct TM_Action* queue, int clipX, int
struct TM_Action* pom = queue; struct TM_Action* pom = queue;
while (pom != NULL) { while (pom != NULL) {
int width = al_get_text_width(game->_priv.font_console, pom->name); int width = al_get_text_width(game->_priv.font_console, pom->name);
al_draw_filled_rectangle(pos - (10 / 3200.0) * al_get_display_width(game->display), clipY, pos + width + (10 / 3200.0) * al_get_display_width(game->display), clipY + (60 / 1800.0) * al_get_display_height(game->display), pom->active ? al_map_rgba(255, 255, 255, 192) : al_map_rgba(0, 0, 0, 0)); al_draw_filled_rectangle(pos - (10 / 3200.0) * al_get_display_width(game->display), clipY, pos + width + (10 / 3200.0) * al_get_display_width(game->display), clipY + (60 / 1800.0) * al_get_display_height(game->display), pom->started ? al_map_rgba(255, 255, 255, 192) : al_map_rgba(0, 0, 0, 0));
al_draw_rectangle(pos - (10 / 3200.0) * al_get_display_width(game->display), clipY, pos + width + (10 / 3200.0) * al_get_display_width(game->display), clipY + (60 / 1800.0) * al_get_display_height(game->display), al_map_rgb(255, 255, 255), 2); al_draw_rectangle(pos - (10 / 3200.0) * al_get_display_width(game->display), clipY, pos + width + (10 / 3200.0) * al_get_display_width(game->display), clipY + (60 / 1800.0) * al_get_display_height(game->display), al_map_rgb(255, 255, 255), 2);
al_draw_text(game->_priv.font_console, pom->active ? al_map_rgb(0, 0, 0) : al_map_rgb(255, 255, 255), pos, clipY, ALLEGRO_ALIGN_LEFT, pom->name); al_draw_text(game->_priv.font_console, pom->started ? al_map_rgb(0, 0, 0) : al_map_rgb(255, 255, 255), pos, clipY, ALLEGRO_ALIGN_LEFT, pom->name);
if (pom->delay) { if (pom->delay) {
al_draw_textf(game->_priv.font_console, al_map_rgb(255, 255, 255), pos, clipY - (50 / 1800.0) * al_get_display_height(game->display), ALLEGRO_ALIGN_LEFT, "%d", pom->delay); al_draw_textf(game->_priv.font_console, al_map_rgb(255, 255, 255), pos, clipY - (50 / 1800.0) * al_get_display_height(game->display), ALLEGRO_ALIGN_LEFT, "%d", (int)pom->delay);
} }
if (strncmp(pom->name, "TM_BackgroundAction", 19) == 0) { if (strncmp(pom->name, "TM_BackgroundAction", 19) == 0) {

View file

@ -22,6 +22,15 @@
#include "utils.h" #include "utils.h"
#include <allegro5/allegro.h> #include <allegro5/allegro.h>
static void DestroyArgs(struct TM_Arguments* args) {
struct TM_Arguments* pom;
while (args) {
pom = args->next;
free(args);
args = pom;
}
}
SYMBOL_EXPORT struct Timeline* TM_Init(struct Game* game, char* name) { SYMBOL_EXPORT struct Timeline* TM_Init(struct Game* game, char* name) {
PrintConsole(game, "Timeline Manager[%s]: init", name); PrintConsole(game, "Timeline Manager[%s]: init", name);
struct Timeline* timeline = malloc(sizeof(struct Timeline)); struct Timeline* timeline = malloc(sizeof(struct Timeline));
@ -35,26 +44,46 @@ SYMBOL_EXPORT struct Timeline* TM_Init(struct Game* game, char* name) {
} }
SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) { SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) {
/* process first element from queue // NOTICE: current implementation has no way to know how much time
if returns true, delete it // an action has "eaten". This means that if you pass a huge delta
and repeat for the next one */ // that spans across multiple actions, the end result will most likely
// differ from what you would get from calling TM_Process repeatively
// with smaller deltas that sum up to the first value. Be aware!
/* process first element from queue.
if returns true, delete it and repeat for the next one */
delta *= 1000;
bool next = true; bool next = true;
while (next) { while (next) {
if (timeline->queue) { if (timeline->queue) {
timeline->queue->delta = delta; timeline->queue->delta = delta / 1000.0;
if (*timeline->queue->function) {
if (!timeline->queue->active) { if (timeline->queue->active && timeline->queue->delay > 0.0) {
timeline->queue->delay -= delta;
if (timeline->queue->delay <= 0.0) {
timeline->queue->started = true;
if (timeline->queue->function) {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: run action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name);
(*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_START);
} else {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay reached (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name);
}
timeline->queue->delay = 0.0;
}
}
if (timeline->queue->function) {
if (!timeline->queue->started) {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: run action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); PrintConsole(timeline->game, "Timeline Manager[%s]: queue: run action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name);
(*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_START); (*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_START);
timeline->queue->started = true;
} }
timeline->queue->active = true;
if ((*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_RUNNING)) { if ((*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_RUNNING)) {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: destroy action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name); PrintConsole(timeline->game, "Timeline Manager[%s]: queue: destroy action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name);
timeline->queue->active = false;
struct TM_Action* tmp = timeline->queue; struct TM_Action* tmp = timeline->queue;
timeline->queue = timeline->queue->next; timeline->queue = timeline->queue->next;
(*tmp->function)(timeline->game, tmp, TM_ACTIONSTATE_DESTROY); (*tmp->function)(timeline->game, tmp, TM_ACTIONSTATE_DESTROY);
TM_DestroyArgs(tmp->arguments); DestroyArgs(tmp->arguments);
free(tmp->name); free(tmp->name);
free(tmp); free(tmp);
} else { } else {
@ -62,15 +91,15 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) {
} }
} else { } else {
/* delay handling */ /* delay handling */
if (timeline->queue->active) { if (timeline->queue->started) {
struct TM_Action* tmp = timeline->queue; struct TM_Action* tmp = timeline->queue;
timeline->queue = timeline->queue->next; timeline->queue = timeline->queue->next;
free(tmp->name); free(tmp->name);
free(tmp); free(tmp);
} else { } else {
if (!al_get_timer_started(timeline->queue->timer)) { if (!timeline->queue->active) {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay started %d ms (%d - %s)", timeline->name, timeline->queue->delay, timeline->queue->id, timeline->queue->name); PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay started %d ms (%d - %s)", timeline->name, (int)timeline->queue->delay, timeline->queue->id, timeline->queue->name);
al_start_timer(timeline->queue->timer); timeline->queue->active = true;
} }
next = false; next = false;
} }
@ -79,18 +108,18 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) {
next = false; next = false;
} }
} }
/* process all elements from background marked as active */
/* process all elements from background queue */
struct TM_Action *tmp, *tmp2, *pom = timeline->background; struct TM_Action *tmp, *tmp2, *pom = timeline->background;
tmp = NULL; tmp = NULL;
while (pom != NULL) { while (pom != NULL) {
bool destroy = false; bool destroy = false;
pom->delta = delta; pom->delta = delta / 1000.0;
if (pom->active) { if (pom->started) {
if (*pom->function) { if (pom->function) {
if ((*pom->function)(timeline->game, pom, TM_ACTIONSTATE_RUNNING)) { if ((pom->function)(timeline->game, pom, TM_ACTIONSTATE_RUNNING)) {
pom->active = false;
PrintConsole(timeline->game, "Timeline Manager[%s]: background: destroy action (%d - %s)", timeline->name, pom->id, pom->name); PrintConsole(timeline->game, "Timeline Manager[%s]: background: destroy action (%d - %s)", timeline->name, pom->id, pom->name);
(*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); (pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY);
if (tmp) { if (tmp) {
tmp->next = pom->next; tmp->next = pom->next;
} else { } else {
@ -107,13 +136,23 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) {
} }
destroy = true; destroy = true;
} }
} else {
pom->delay -= delta;
if (pom->delay <= 0.0) {
PrintConsole(timeline->game, "Timeline Manager[%s]: background: delay reached, run action (%d - %s)", timeline->name, pom->id, pom->name);
pom->delay = 0.0;
if (pom->function) {
pom->function(timeline->game, pom, TM_ACTIONSTATE_START);
}
pom->started = true;
}
} }
if (!destroy) { if (!destroy) {
tmp = pom; tmp = pom;
pom = pom->next; pom = pom->next;
} else { } else {
TM_DestroyArgs(pom->arguments); DestroyArgs(pom->arguments);
free(pom->name); free(pom->name);
free(pom); free(pom);
tmp2 = tmp; tmp2 = tmp;
@ -131,93 +170,6 @@ SYMBOL_EXPORT void TM_Process(struct Timeline* timeline, double delta) {
} }
} }
static void PauseTimers(struct Timeline* timeline, bool pause) {
if (timeline->queue) {
if (timeline->queue->timer) {
if (pause) {
al_stop_timer(timeline->queue->timer);
} else if (!timeline->queue->active) {
al_resume_timer(timeline->queue->timer);
}
}
}
struct TM_Action* tmp = timeline->background;
while (tmp) {
if (tmp->timer) {
if (pause) {
al_stop_timer(tmp->timer);
} else if (!tmp->active) {
al_resume_timer(tmp->timer);
}
}
tmp = tmp->next;
}
}
static void TM_Propagate(struct Timeline* timeline, enum TM_ActionState action) {
if (timeline->queue) {
if ((*timeline->queue->function) && (timeline->queue->active)) {
(*timeline->queue->function)(timeline->game, timeline->queue, action);
}
}
/* process all elements from background marked as active */
struct TM_Action* pom = timeline->background;
while (pom != NULL) {
if (pom->active) {
if (*pom->function) {
(*pom->function)(timeline->game, pom, action);
}
}
pom = pom->next;
}
}
SYMBOL_EXPORT void TM_Draw(struct Timeline* timeline) {
TM_Propagate(timeline, TM_ACTIONSTATE_DRAW);
}
SYMBOL_EXPORT void TM_Pause(struct Timeline* timeline) {
PrintConsole(timeline->game, "Timeline Manager[%s]: Pause.", timeline->name);
PauseTimers(timeline, true);
TM_Propagate(timeline, TM_ACTIONSTATE_PAUSE);
}
SYMBOL_EXPORT void TM_Resume(struct Timeline* timeline) {
PrintConsole(timeline->game, "Timeline Manager[%s]: Resume.", timeline->name);
TM_Propagate(timeline, TM_ACTIONSTATE_RESUME);
PauseTimers(timeline, false);
}
SYMBOL_EXPORT void TM_HandleEvent(struct Timeline* timeline, ALLEGRO_EVENT* ev) {
if (ev->type != ALLEGRO_EVENT_TIMER) { return; }
if (timeline->queue) {
if (ev->timer.source == timeline->queue->timer) {
timeline->queue->active = true;
al_destroy_timer(timeline->queue->timer);
timeline->queue->timer = NULL;
if (timeline->queue->function) {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: run action (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name);
(*timeline->queue->function)(timeline->game, timeline->queue, TM_ACTIONSTATE_START);
} else {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: delay reached (%d - %s)", timeline->name, timeline->queue->id, timeline->queue->name);
}
return;
}
}
struct TM_Action* pom = timeline->background;
while (pom) {
if (ev->timer.source == pom->timer) {
PrintConsole(timeline->game, "Timeline Manager[%s]: background: delay reached, run action (%d - %s)", timeline->name, pom->id, pom->name);
pom->active = true;
al_destroy_timer(pom->timer);
pom->timer = NULL;
(*pom->function)(timeline->game, pom, TM_ACTIONSTATE_START);
return;
}
pom = pom->next;
}
}
SYMBOL_EXPORT struct TM_Action* TM_AddAction(struct Timeline* timeline, TM_ActionCallback* func, struct TM_Arguments* args, char* name) { SYMBOL_EXPORT struct TM_Action* TM_AddAction(struct Timeline* timeline, TM_ActionCallback* func, struct TM_Arguments* args, char* name) {
struct TM_Action* action = malloc(sizeof(struct TM_Action)); struct TM_Action* action = malloc(sizeof(struct TM_Action));
if (timeline->queue) { if (timeline->queue) {
@ -233,13 +185,13 @@ SYMBOL_EXPORT struct TM_Action* TM_AddAction(struct Timeline* timeline, TM_Actio
action->function = func; action->function = func;
action->arguments = args; action->arguments = args;
action->name = strdup(name); action->name = strdup(name);
action->timer = NULL;
action->active = false; action->active = false;
action->delay = 0; action->started = false;
action->delay = 0.0;
action->id = ++timeline->lastid; action->id = ++timeline->lastid;
if (action->function) { if (action->function) {
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: init action (%d - %s)", timeline->name, action->id, action->name); PrintConsole(timeline->game, "Timeline Manager[%s]: queue: init action (%d - %s)", timeline->name, action->id, action->name);
(*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT); action->function(timeline->game, action, TM_ACTIONSTATE_INIT);
} }
return action; return action;
} }
@ -261,21 +213,10 @@ SYMBOL_EXPORT struct TM_Action* TM_AddBackgroundAction(struct Timeline* timeline
action->name = strdup(name); action->name = strdup(name);
action->delay = delay; action->delay = delay;
action->id = ++timeline->lastid; action->id = ++timeline->lastid;
if (delay) { action->active = true;
PrintConsole(timeline->game, "Timeline Manager[%s]: background: init action with delay %d ms (%d - %s)", timeline->name, delay, action->id, action->name); action->started = false;
(*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT); PrintConsole(timeline->game, "Timeline Manager[%s]: background: init action with delay %d ms (%d - %s)", timeline->name, delay, action->id, action->name);
action->active = false; (*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT);
action->timer = al_create_timer(delay / 1000.0);
al_register_event_source(timeline->game->_priv.event_queue, al_get_timer_event_source(action->timer));
al_start_timer(action->timer);
} else {
PrintConsole(timeline->game, "Timeline Manager[%s]: background: init action (%d - %s)", timeline->name, action->id, action->name);
(*action->function)(timeline->game, action, TM_ACTIONSTATE_INIT);
action->timer = NULL;
action->active = true;
PrintConsole(timeline->game, "Timeline Manager[%s]: background: run action (%d - %s)", timeline->name, action->id, action->name);
(*action->function)(timeline->game, action, TM_ACTIONSTATE_START);
}
return action; return action;
} }
@ -294,7 +235,7 @@ static TM_Action(RunInBackground) {
free(name); free(name);
free(delay); free(delay);
if (!(*used)) { if (!(*used)) {
TM_DestroyArgs(arguments); DestroyArgs(arguments);
} }
free(used); free(used);
} }
@ -312,8 +253,6 @@ SYMBOL_EXPORT void TM_AddDelay(struct Timeline* timeline, int delay) {
struct TM_Action* tmp = TM_AddAction(timeline, NULL, NULL, "TM_Delay"); struct TM_Action* tmp = TM_AddAction(timeline, NULL, NULL, "TM_Delay");
PrintConsole(timeline->game, "Timeline Manager[%s]: queue: adding delay %d ms (%d)", timeline->name, delay, tmp->id); PrintConsole(timeline->game, "Timeline Manager[%s]: queue: adding delay %d ms (%d)", timeline->name, delay, tmp->id);
tmp->delay = delay; tmp->delay = delay;
tmp->timer = al_create_timer(delay / 1000.0);
al_register_event_source(timeline->game->_priv.event_queue, al_get_timer_event_source(tmp->timer));
} }
SYMBOL_EXPORT void TM_CleanQueue(struct Timeline* timeline) { SYMBOL_EXPORT void TM_CleanQueue(struct Timeline* timeline) {
@ -323,11 +262,7 @@ SYMBOL_EXPORT void TM_CleanQueue(struct Timeline* timeline) {
if (*pom->function) { if (*pom->function) {
(*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY);
} }
if (pom->timer) { DestroyArgs(pom->arguments);
al_stop_timer(pom->timer);
al_destroy_timer(pom->timer);
}
TM_DestroyArgs(pom->arguments);
tmp = pom->next; tmp = pom->next;
free(pom->name); free(pom->name);
free(pom); free(pom);
@ -343,11 +278,7 @@ SYMBOL_EXPORT void TM_CleanBackgroundQueue(struct Timeline* timeline) {
if (*pom->function) { if (*pom->function) {
(*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY); (*pom->function)(timeline->game, pom, TM_ACTIONSTATE_DESTROY);
} }
if (pom->timer) { DestroyArgs(pom->arguments);
al_stop_timer(pom->timer);
al_destroy_timer(pom->timer);
}
TM_DestroyArgs(pom->arguments);
tmp = pom->next; tmp = pom->next;
free(pom->name); free(pom->name);
free(pom); free(pom);
@ -357,10 +288,8 @@ SYMBOL_EXPORT void TM_CleanBackgroundQueue(struct Timeline* timeline) {
} }
SYMBOL_EXPORT void TM_SkipDelay(struct Timeline* timeline) { SYMBOL_EXPORT void TM_SkipDelay(struct Timeline* timeline) {
if (timeline->queue && timeline->queue->timer) { if (timeline->queue && timeline->queue->delay) {
al_stop_timer(timeline->queue->timer); timeline->queue->delay = 0.0;
al_set_timer_speed(timeline->queue->timer, 0.01);
al_start_timer(timeline->queue->timer);
} }
} }
@ -410,12 +339,3 @@ SYMBOL_EXPORT void* TM_GetArg(struct TM_Arguments* args, int num) {
} }
return args->value; return args->value;
} }
SYMBOL_EXPORT void TM_DestroyArgs(struct TM_Arguments* args) {
struct TM_Arguments* pom;
while (args) {
pom = args->next;
free(args);
args = pom;
}
}

View file

@ -32,10 +32,7 @@ enum TM_ActionState {
TM_ACTIONSTATE_INIT, TM_ACTIONSTATE_INIT,
TM_ACTIONSTATE_START, TM_ACTIONSTATE_START,
TM_ACTIONSTATE_RUNNING, TM_ACTIONSTATE_RUNNING,
TM_ACTIONSTATE_DRAW, TM_ACTIONSTATE_DESTROY
TM_ACTIONSTATE_DESTROY,
TM_ACTIONSTATE_PAUSE,
TM_ACTIONSTATE_RESUME
}; };
struct TM_Action; struct TM_Action;
@ -61,27 +58,19 @@ struct TM_Arguments {
struct TM_Action { struct TM_Action {
TM_ActionCallback* function; /*!< Function callback of the action. */ TM_ActionCallback* function; /*!< Function callback of the action. */
struct TM_Arguments* arguments; /*!< Arguments of the action. */ struct TM_Arguments* arguments; /*!< Arguments of the action. */
ALLEGRO_TIMER* timer; /*!< Delay timer. */ bool active; /*!< Whether this action is being processed by the queue right now. */
bool active; /*!< If false, then this action is waiting for it's delay to finish. */ bool started; /*!< If false, then the action is waiting for its delay to finish. */
int delay; /*!< Number of miliseconds to delay before action is started. */ double delay; /*!< Number of miliseconds to delay before action is started. */
double delta; /*!< Number of miliseconds since the last TM_Process invocation. */ double delta; /*!< Number of miliseconds since the last TM_Process invocation. */
struct TM_Action* next; /*!< Pointer to next action in queue. */
unsigned int id; /*!< ID of the action. */ unsigned int id; /*!< ID of the action. */
char* name; /*!< "User friendly" name of the action. */ char* name; /*!< "User friendly" name of the action. */
struct TM_Action* next; /*!< Pointer to next action in queue. */
}; };
/*! \brief Init timeline. */ /*! \brief Init timeline. */
struct Timeline* TM_Init(struct Game* game, char* name); struct Timeline* TM_Init(struct Game* game, char* name);
/*! \brief Process current timeline actions. */ /*! \brief Process current timeline actions. */
void TM_Process(struct Timeline*, double delta); void TM_Process(struct Timeline*, double delta);
/*! \brief Ask current timeline actions to draw. */
void TM_Draw(struct Timeline*);
/*! \brief Pauses the timeline. */
void TM_Pause(struct Timeline*);
/*! \brief Resumes the timeline. */
void TM_Resume(struct Timeline*);
/*! \brief Handle timer events. */
void TM_HandleEvent(struct Timeline*, ALLEGRO_EVENT* ev);
/*! \brief Add new action to main queue. */ /*! \brief Add new action to main queue. */
struct TM_Action* TM_AddAction(struct Timeline*, TM_ActionCallback* func, struct TM_Arguments* args, char* name); struct TM_Action* TM_AddAction(struct Timeline*, TM_ActionCallback* func, struct TM_Arguments* args, char* name);
/*! \brief Add new action to background queue. */ /*! \brief Add new action to background queue. */
@ -94,15 +83,13 @@ void TM_AddDelay(struct Timeline*, int delay);
void TM_CleanQueue(struct Timeline*); void TM_CleanQueue(struct Timeline*);
/*! \brief Remove all actions from background queue. */ /*! \brief Remove all actions from background queue. */
void TM_CleanBackgroundQueue(struct Timeline*); void TM_CleanBackgroundQueue(struct Timeline*);
/*! \brief Destroy timeline. */ /*! \brief Destroy given timeline. */
void TM_Destroy(struct Timeline*); void TM_Destroy(struct Timeline*);
/*! \brief Add data to TM_Arguments queue. */ /*! \brief Add data to TM_Arguments queue (or create if NULL). */
struct TM_Arguments* TM_AddToArgs(struct TM_Arguments* args, int num, ...); struct TM_Arguments* TM_AddToArgs(struct TM_Arguments* args, int num, ...);
/*! \brief Get nth argument from TM_Arguments queue (counted from 0). */ /*! \brief Get nth argument from TM_Arguments queue (counted from 0). */
void* TM_GetArg(struct TM_Arguments* args, int num); void* TM_GetArg(struct TM_Arguments* args, int num);
/*! \brief Destroy TM_Arguments queue. */ /*! \brief Skip delay of the first action in the queue */
void TM_DestroyArgs(struct TM_Arguments* args);
/*! \brief Skip delay if it's currently the first action in the queue */
void TM_SkipDelay(struct Timeline*); void TM_SkipDelay(struct Timeline*);
/*! \brief Checks if the main queue is empty */ /*! \brief Checks if the main queue is empty */
bool TM_IsEmpty(struct Timeline* timeline); bool TM_IsEmpty(struct Timeline* timeline);