commit 33598d2c0a36ad67c497df0d55309aaade64e65d Author: r4 Date: Sun Nov 13 01:12:35 2022 +0100 Init diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..22e5695 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +example: example.c coro.h + $(CC) -o $@ $< `pkg-config --libs --cflags raylib` -lm + +.PHONY: clean +clean: + rm -f example diff --git a/coro.h b/coro.h new file mode 100644 index 0000000..e04aaf4 --- /dev/null +++ b/coro.h @@ -0,0 +1,106 @@ +#ifndef _CORO_H_ +#define _CORO_H_ + +#include + +// Basic usage example: +// Define a specific coroutine struct holding all your variables and return values (variables defined normally within the function will be reset): +// typedef struct { +// Coro coro; +// int x_pos, y_pos; +// } ExampleCoro; +// +// Define a specific coroutine function taking our custom type as a parameter: +// void example(ExampleCoro *c) +// CORO_BEGIN(&c->coro) // The coroutine initialization simply takes a reference to the core coroutine struct +// // Draw the object for a total of 1s +// CORO_BEGIN_REPEAT(1.0) +// // Rapidly alternate between states A and B displaying each for 0.1s +// CORO_BEGIN_REPEAT(0.1) +// DrawThingA(c->x_pos, c->y_pos); +// CORO_END_REPEAT() +// CORO_BEGIN_REPEAT(0.1) +// DrawThingB(c->x_pos, c->y_pos); +// CORO_END_REPEAT() +// CORO_END_REPEAT() +// // Print a message +// printf("Dying in 1s\n"); +// CORO_WAIT(1.0) +// // Print another message after waiting for 1s +// printf("XD\n"); +// // Done; c->done is now set to true and the coroutine will no longer run any code on subsequent calls +// CORO_END() +// +// Initialization (zero value is ready-to-use): +// ExampleCoro coro; +// coro = (ExampleCoro){0}; +// +// Each frame: +// example(&coro) +// coro.time += delta_time; + +#ifndef CORO_MAX_DEPTH +#define CORO_MAX_DEPTH 8 +#endif + +#ifndef CORO_FATAL +#include +#include +#define CORO_FATAL(...) { fprintf(stderr, "[FATAL] Coro: "__VA_ARGS__); abort(); } +#endif + +#define CORO_BEGIN(_coro) \ + { \ + Coro *_coro_instance = _coro; \ + int _coro_goto_line = 0; \ + if (_coro_instance->done) \ + return; \ + if (_coro_instance->stack_size > 0 && !_coro_instance->stack[_coro_instance->stack_size-1].repeat) { \ + if (_coro_instance->time < _coro_instance->stack[_coro_instance->stack_size-1].timeout) \ + return; \ + } \ + if (_coro_instance->stack_size > 0) \ + _coro_goto_line = _coro_instance->stack[_coro_instance->stack_size-1].line; \ + switch (_coro_goto_line) { \ + case 0: +#define CORO_END() \ + if (_coro_instance->stack_size != 0) { CORO_FATAL("programmer error in %s:%d: CORO_END() called without closing all loops\n", __FILE__, __LINE__); } \ + _coro_instance->done = true; \ + }} +// Do nothing until the specified amount of time has passed +#define CORO_WAIT(_time) \ + if (_coro_instance->stack_size+1 > CORO_MAX_DEPTH) { CORO_FATAL("programmer error in %s:%d: maximum depth of %d exceeded\n", __FILE__, __LINE__, CORO_MAX_DEPTH); } \ + _coro_instance->stack_size++; \ + _coro_instance->stack[_coro_instance->stack_size-1].repeat = false; \ + _coro_instance->stack[_coro_instance->stack_size-1].timeout = _coro_instance->time + _time; \ + case __LINE__: _coro_instance->stack[_coro_instance->stack_size-1].line = __LINE__; \ + if (_coro_instance->time < _coro_instance->stack[_coro_instance->stack_size-1].timeout) \ + return; \ + else \ + _coro_instance->stack_size--; +// Repeat a piece of code each frame until the specified amount of time has passed +#define CORO_BEGIN_REPEAT(_time) \ + if (_coro_instance->stack_size+1 > CORO_MAX_DEPTH) { CORO_FATAL("programmer error in %s:%d: maximum depth of %d exceeded\n", __FILE__, __LINE__, CORO_MAX_DEPTH); } \ + _coro_instance->stack_size++; \ + _coro_instance->stack[_coro_instance->stack_size-1].repeat = true; \ + _coro_instance->stack[_coro_instance->stack_size-1].timeout = _coro_instance->time + _time; \ + case __LINE__: _coro_instance->stack[_coro_instance->stack_size-1].line = __LINE__; +#define CORO_END_REPEAT() \ + if (_coro_instance->stack_size == 0) { CORO_FATAL("programmer error in %s:%d: CORO_END_REPEAT() called without matching CORO_BEGIN_REPEAT()\n", __FILE__, __LINE__); } \ + if (_coro_instance->time < _coro_instance->stack[_coro_instance->stack_size-1].timeout) \ + return; \ + else \ + _coro_instance->stack_size--; + +typedef struct { + struct { + unsigned int line; // line to return to for wait/repeat + bool repeat; // whether this is a repeat or simply a wait + double timeout; // when the repeat/wait is over + } stack[CORO_MAX_DEPTH]; // stack for keeping track of waits and repeats + unsigned int stack_size; + double time; // current time; meaning and unit defined by user + bool done; // set to true when coroutine is finished executing +} Coro; + +#endif /* !_CORO_H_ */ diff --git a/example.c b/example.c new file mode 100644 index 0000000..7f93851 --- /dev/null +++ b/example.c @@ -0,0 +1,88 @@ +#include + +#include "coro.h" + +typedef struct { + Coro coro; + int x_pos, y_pos; + bool alive; +} DrawCoro; + +static void draw(DrawCoro *c) +CORO_BEGIN(&c->coro) + CORO_BEGIN_REPEAT(1.0) + CORO_BEGIN_REPEAT(0.1) + DrawText("Now", c->x_pos, c->y_pos, 20, LIGHTGRAY); + CORO_END_REPEAT() + CORO_BEGIN_REPEAT(0.1) + DrawText("Now", c->x_pos, c->y_pos, 20, BLACK); + CORO_END_REPEAT() + CORO_END_REPEAT() + CORO_BEGIN_REPEAT(1.0) + DrawText("1s later", c->x_pos, c->y_pos, 20, LIGHTGRAY); + CORO_END_REPEAT() + CORO_BEGIN_REPEAT(1.0) + DrawText("2s later", c->x_pos, c->y_pos, 20, LIGHTGRAY); + CORO_END_REPEAT() + CORO_BEGIN_REPEAT(0.5) + DrawText("Bye", c->x_pos, c->y_pos, 20, LIGHTGRAY); + CORO_END_REPEAT() +CORO_END() + +int main(void) { + size_t n_coros = 0; + DrawCoro coros[64]; + size_t n_free_coros = 0; + size_t free_coros[64]; + + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "Simple coroutine example"); + + SetTargetFPS(60); + + while (!WindowShouldClose()) { + printf("Coros size: %zu, Free list size: %zu \r", n_coros, n_free_coros); + + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { + size_t coro; + if (n_free_coros > 0) { + coro = free_coros[--n_free_coros]; + } else { + if (n_coros+1 > 64) { + fprintf(stderr, "All coroutines in use!\n"); + abort(); + } + coro = n_coros++; + } + coros[coro] = (DrawCoro){0}; + coros[coro].alive = true; + coros[coro].x_pos = GetMouseX(); + coros[coro].y_pos = GetMouseY(); + } + + BeginDrawing(); + ClearBackground(RAYWHITE); + for (size_t i = 0; i < n_coros; i++) { + if (!coros[i].alive) + continue; + if (coros[i].coro.done) { + coros[i].alive = false; + if (n_free_coros+1 > 64) { + fprintf(stderr, "Coroutine free list overflow!\n"); + abort(); + } + free_coros[n_free_coros++] = i; + } else { + draw(&coros[i]); + coros[i].coro.time += GetFrameTime(); + } + } + EndDrawing(); + } + + CloseWindow(); + + return 0; +}