diff --git a/desktop_version/CMakeLists.txt b/desktop_version/CMakeLists.txt index 0804d654..b07babb1 100644 --- a/desktop_version/CMakeLists.txt +++ b/desktop_version/CMakeLists.txt @@ -125,6 +125,7 @@ SET(VVV_SRC src/WarpClass.cpp src/XMLUtils.cpp src/main.cpp + src/DeferCallbacks.c src/Network.c src/ThirdPartyDeps.c ) diff --git a/desktop_version/src/DeferCallbacks.c b/desktop_version/src/DeferCallbacks.c new file mode 100644 index 00000000..4345bda8 --- /dev/null +++ b/desktop_version/src/DeferCallbacks.c @@ -0,0 +1,84 @@ +#include "DeferCallbacks.h" + +#include + +/* Callbacks to be deferred to the end of each sequence of gamestate functions + * in main. Useful for fixing frame-flicker glitches when doing a state + * transition in a function that gets executed before the render function. + * + * We store a linked list of callbacks, to allow for the possibility of having + * more than one callback active at a time (otherwise we could easily just + * have a single pointer here and the header would only be 1 line and an + * include guard) and to do it without having to allocate memory at runtime. + */ + +static struct DEFER_Callback* head = NULL; + +/* Add a callback. Don't call this directly; use the DEFER_CALLBACK macro. */ +void DEFER_add_callback(struct DEFER_Callback* callback) +{ + struct DEFER_Callback* node; + + /* Are we adding the first node? */ + if (head == NULL) + { + head = callback; + return; + } + + /* Time to walk the linked list */ + node = head; + + if (node == callback) + { + goto fail; + } + + while (node->next != NULL) + { + node = node->next; + + if (node == callback) + { + goto fail; + } + } + + /* We're at the end */ + node->next = callback; + + /* Success! */ + return; + +fail: + /* Having multiple instances of a callback isn't well-defined + * and is a bit complicated to reason about */ + SDL_assert(0 && "Duplicate callback added!"); +} + +/* Call each callback in the list, along with deleting the entire list. */ +void DEFER_execute_callbacks(void) +{ + struct DEFER_Callback* node = head; + struct DEFER_Callback* next; + + head = NULL; + + if (node == NULL) + { + return; + } + + next = node->next; + node->func(); + node->next = NULL; + + while (next != NULL) + { + node = next; + next = node->next; + + node->func(); + node->next = NULL; + } +} diff --git a/desktop_version/src/DeferCallbacks.h b/desktop_version/src/DeferCallbacks.h new file mode 100644 index 00000000..20a9ffbe --- /dev/null +++ b/desktop_version/src/DeferCallbacks.h @@ -0,0 +1,31 @@ +#ifndef DEFERCALLBACKS_H +#define DEFERCALLBACKS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct DEFER_Callback +{ + void (*func)(void); + struct DEFER_Callback* next; +}; + +void DEFER_add_callback(struct DEFER_Callback* callback); + +void DEFER_execute_callbacks(void); + +#define DEFER_CALLBACK(FUNC) \ + do \ + { \ + static struct DEFER_Callback callback = {FUNC, NULL}; \ + \ + DEFER_add_callback(&callback); \ + } while (0) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DEFERCALLBACKS_H */ diff --git a/desktop_version/src/main.cpp b/desktop_version/src/main.cpp index 112ebc29..fa5d1d37 100644 --- a/desktop_version/src/main.cpp +++ b/desktop_version/src/main.cpp @@ -1,6 +1,7 @@ #include #include +#include "DeferCallbacks.h" #include "editor.h" #include "Enums.h" #include "Entity.h" @@ -226,6 +227,9 @@ static enum IndexCode increment_gamestate_func_index(void) &num_gamestate_funcs ); + /* Also run callbacks that were deferred to end of func sequence. */ + DEFER_execute_callbacks(); + gamestate_func_index = 0; return Index_end;