diff options
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | makefile | 66 | ||||
| -rwxr-xr-x | run_tests.sh | 38 | ||||
| -rw-r--r-- | src/input.c | 35 | ||||
| -rw-r--r-- | src/input.h | 11 | ||||
| -rw-r--r-- | src/main.c | 67 | ||||
| -rw-r--r-- | src/util/alloc.h | 13 | ||||
| -rw-r--r-- | src/util/vec.c | 98 | ||||
| -rw-r--r-- | src/util/vec.h | 38 | ||||
| -rw-r--r-- | test/test_vec.c | 31 |
10 files changed, 403 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..802da0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/ +test/bin +test/obj + +.clangd +**/compile_flags.txt diff --git a/makefile b/makefile new file mode 100644 index 0000000..76ee33e --- /dev/null +++ b/makefile @@ -0,0 +1,66 @@ + +CC := gcc +CFLAGS := -Wall -MMD -MP -std=c99 +LFLAGS := -lSDL3 -lGL + +# ============================================================================== + +EXEC := game + +BUILD := build +BIN := $(BUILD)/bin +OBJ := $(BUILD)/obj + +SRC := src + +TEST := test +TESTBIN := $(TEST)/bin +TESTOBJ := $(TEST)/obj + +# ============================================================================== + +SRCS := $(shell find $(SRC) -name '*.c') +OBJS := $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SRCS)) +DEPS := $(OBJS:.o=.d) + +LIBOBJS = $(filter-out $(OBJ)/main.o, $(OBJS)) + +TESTS := $(wildcard $(TEST)/*.c) +TESTOBJS := $(patsubst $(TEST)/%.c, $(TESTOBJ)/%.o, $(TESTS)) +TESTBINS := $(patsubst $(TEST)/%.c, $(TESTBIN)/%, $(TESTS)) +TESTDEPS := $(TESTOBJS:.o=.d) + +# ============================================================================== + +$(BIN)/$(EXEC): $(OBJS) + @mkdir -p $(BIN) + $(CC) -o $@ $^ $(LFLAGS) + +$(OBJ)/%.o: $(SRC)/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c -o $@ $< + +.PHONY: run +run: ${BIN}/${EXEC} + ./${BIN}/${EXEC} + +.PHONY: test +test: ${TESTBIN}/test_vec + ./run_tests.sh + +$(TESTBIN)/%: $(TESTOBJS) $(LIBOBJS) + @mkdir -p $(TESTBIN) + $(CC) -o $@ $^ $(LFLAGS) + +$(TESTOBJ)%.o: $(TEST)/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c -I$(SRC) -o $@ $< + +-include $(DEPS) + +.PHONY: clean +clean: + rm -rf ${BIN} + rm -rf ${OBJ} + rm -rf ${TESTBIN} + rm -rf ${TESTOBJ} diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..578392a --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +testdir=test/bin + +red='\e[0;31m' +yellow='\e[0;33m' +green='\e[0;32m' + +reset='\e[0m' + +lprintf() { + local fmt="$1" + local text="$2" + [[ -x "$text" ]] && return + while IFS= read -r line; do + printf "$fmt" "$line" + done <<< "$text" +} + +echo " ╓──────────╖" +echo "╔═╣ leo test ╠════════════════════════════════════" +echo "║ ╙──────────╜" + +for testbin in $testdir/*; do + if [[ -x "$testbin" ]]; then + echo -ne "╟─ running ${yellow}${testbin}${reset}:\t" + output=$(./$testbin 2>&1) # TODO save output and display at end + if [[ $? -eq 0 ]]; then + echo -e "${green}passed${reset}" + else + echo -e "${red}failed${reset}" + lprintf "║ %s\n" "$output" + fi + fi +done + +echo "║" +echo "╚═════════════════════════════════════════════════" diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..ab7768c --- /dev/null +++ b/src/input.c @@ -0,0 +1,35 @@ +#include "input.h" +#include <SDL3/SDL_events.h> + +struct LEO_InputManager { + bool quit; +}; + +static struct LEO_InputManager *LEO_InputManager_singleton() +{ + static struct LEO_InputManager manager; + static bool initialized = false; + + if (!initialized) { + manager.quit = false; + initialized = true; + } + + return &manager; +} + +void LEO_Input_update(const SDL_Event event) +{ + struct LEO_InputManager *manager = LEO_InputManager_singleton(); + + switch (event.type) { + case SDL_EVENT_QUIT: + manager->quit = true; + } +} + +bool LEO_Input_quit() +{ + struct LEO_InputManager *manager = LEO_InputManager_singleton(); + return manager->quit; +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..28b7cfc --- /dev/null +++ b/src/input.h @@ -0,0 +1,11 @@ +#ifndef LEO_INPUT_H +#define LEO_INPUT_H + +#include <stdbool.h> +#include <SDL3/SDL_events.h> + +void LEO_Input_update(const SDL_Event event); + +bool LEO_Input_quit(); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d57a434 --- /dev/null +++ b/src/main.c @@ -0,0 +1,67 @@ +#include "input.h" +#include <SDL3/SDL.h> +#include <SDL3/SDL_video.h> + +struct LEO_App { + SDL_Window *window; + SDL_GLContext gl_context; +}; + +int LEO_App_init(struct LEO_App *app) +{ + if (!SDL_InitSubSystem(SDL_INIT_VIDEO)) { + return 1; + } + + app->window = SDL_CreateWindow("title", 640, 480, SDL_WINDOW_OPENGL); + if (app->window == NULL) { + return 2; + } + + app->gl_context = SDL_GL_CreateContext(app->window); + if (app->gl_context == NULL) { + return 3; + } + + return 0; +} + +void LEO_App_run(struct LEO_App *app) +{ + SDL_Event event; + while (true) { + while (SDL_PollEvent(&event)) + LEO_Input_update(event); + + if (LEO_Input_quit()) + break; + + SDL_GL_SwapWindow(app->window); + + SDL_Delay(10); + } +} + +void LEO_App_free(struct LEO_App *app) +{ + if (app->gl_context != NULL) + SDL_GL_DestroyContext(app->gl_context); + + if (app->window != NULL) + SDL_DestroyWindow(app->window); + + SDL_Quit(); +} + +int main(int argc, char **argv) +{ + struct LEO_App app; + if (LEO_App_init(&app)) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "failed to init app: %s", SDL_GetError()); + } + + LEO_App_run(&app); + + LEO_App_free(&app); + return 0; +} diff --git a/src/util/alloc.h b/src/util/alloc.h new file mode 100644 index 0000000..17841e4 --- /dev/null +++ b/src/util/alloc.h @@ -0,0 +1,13 @@ +#ifndef LEO_ALLOC_H +#define LEO_ALLOC_H + +#include <stddef.h> + +struct LEO_Allocator { + void *(*alloc)(size_t size); + void *(*realloc)(void *ptr, size_t new_size); + void (*free)(void *ptr); + void *ctx; +}; + +#endif diff --git a/src/util/vec.c b/src/util/vec.c new file mode 100644 index 0000000..1a37bef --- /dev/null +++ b/src/util/vec.c @@ -0,0 +1,98 @@ +#include <stddef.h> +#include <string.h> + +#include "vec.h" +#include "alloc.h" + +static enum LEO_Vec_Error LEO_Vec_grow(struct LEO_Vec *vec) +{ + size_t new_capacity = vec->capacity + * LEO_Vec_GROWTH_FACTOR_NUMER + / LEO_Vec_GROWTH_FACTOR_DENOM; + + if (new_capacity <= vec->capacity) + return LEO_Vec_TOO_BIG; + + vec->capacity = new_capacity; + vec->data = vec->allocator.realloc(vec->data, + vec->capacity * vec->item_size); + + if (vec->data == NULL) + return LEO_Vec_ALLOCATION_FAILURE; + + return LEO_Vec_OK; +} + +enum LEO_Vec_Error LEO_Vec_init(struct LEO_Vec *vec, size_t item_size, + struct LEO_Allocator allocator) +{ + if (vec == NULL) + return LEO_Vec_NULL_VEC_POINTER; + if (item_size == 0) + return LEO_Vec_INVALID_ITEM_SIZE; + + vec->allocator = allocator; + vec->item_size = item_size; + vec->length = 0; + vec->capacity = LEO_Vec_DEFAULT_CAPACITY; + vec->data = vec->allocator.alloc(vec->capacity * vec->item_size); + + if (!vec->data) + return LEO_Vec_ALLOCATION_FAILURE; + + return LEO_Vec_OK; +} + +void LEO_Vec_free(struct LEO_Vec *vec) +{ + if (vec == NULL) + return; + + vec->allocator.free(vec->data); + vec->data = NULL; +} + +enum LEO_Vec_Error LEO_Vec_push(struct LEO_Vec *vec, const void *item) +{ + if (vec == NULL) + return LEO_Vec_NULL_VEC_POINTER; + if (item == NULL) + return LEO_Vec_NULL_ITEM_POINTER; + + if (vec->length >= vec->capacity) { + enum LEO_Vec_Error result = LEO_Vec_grow(vec); + if (result != LEO_Vec_OK) + return result; + } + + unsigned char *dest = (unsigned char *)vec->data + (vec->length++) * vec->item_size; + memcpy(dest, item, vec->item_size); + + return LEO_Vec_OK; +} + +enum LEO_Vec_Error LEO_Vec_pop(struct LEO_Vec *vec, void *item) +{ + if (vec == NULL) + return LEO_Vec_NULL_VEC_POINTER; + if (vec->length == 0) + return LEO_Vec_OUT_OF_BOUNDS; + + void *src = vec->data + (vec->length--) * vec->item_size; + memcpy(item, src, vec->item_size); + + return LEO_Vec_OK; +} + +enum LEO_Vec_Error LEO_Vec_get(struct LEO_Vec *vec, size_t i, void *item) +{ + if (vec == NULL) + return LEO_Vec_NULL_VEC_POINTER; + if (i >= vec->length) + return LEO_Vec_OUT_OF_BOUNDS; + + unsigned char *src = (unsigned char *) vec->data + i * vec->item_size; + memcpy(item, src, vec->item_size); + + return LEO_Vec_OK; +} diff --git a/src/util/vec.h b/src/util/vec.h new file mode 100644 index 0000000..2b86e17 --- /dev/null +++ b/src/util/vec.h @@ -0,0 +1,38 @@ +#ifndef LEO_VEC_H +#define LEO_VEC_H + +#include <stddef.h> + +#include "alloc.h" + +static const size_t LEO_Vec_GROWTH_FACTOR_NUMER = 3; +static const size_t LEO_Vec_GROWTH_FACTOR_DENOM = 2; +static const size_t LEO_Vec_DEFAULT_CAPACITY = 8; + +struct LEO_Vec { + struct LEO_Allocator allocator; + void *data; + size_t item_size; + size_t length; + size_t capacity; +}; + +enum LEO_Vec_Error { + LEO_Vec_OK = 0, + LEO_Vec_NULL_VEC_POINTER = 1, + LEO_Vec_NULL_ITEM_POINTER = 2, + LEO_Vec_INVALID_ITEM_SIZE = 3, + LEO_Vec_ALLOCATION_FAILURE = 4, + LEO_Vec_TOO_BIG = 5, + LEO_Vec_OUT_OF_BOUNDS = 6, +}; + +enum LEO_Vec_Error LEO_Vec_init (struct LEO_Vec *vec, size_t item_size, + struct LEO_Allocator allocator); + +void LEO_Vec_free (struct LEO_Vec *vec); +enum LEO_Vec_Error LEO_Vec_push (struct LEO_Vec *vec, const void *item); +enum LEO_Vec_Error LEO_Vec_pop (struct LEO_Vec *vec, void *item); +enum LEO_Vec_Error LEO_Vec_get (struct LEO_Vec *vec, size_t i, void *item); + +#endif diff --git a/test/test_vec.c b/test/test_vec.c new file mode 100644 index 0000000..a9788eb --- /dev/null +++ b/test/test_vec.c @@ -0,0 +1,31 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include "util/alloc.h" +#include "util/vec.h" + +int main(int argc, char **argv) { + + struct LEO_Allocator allocator = { + malloc, + realloc, + free, + NULL + }; + + struct LEO_Vec vec; + assert(LEO_Vec_init(&vec, sizeof(int), allocator) == LEO_Vec_OK); + + for (int i = 0; i < 100; i++) { + assert(LEO_Vec_push(&vec, &i) == LEO_Vec_OK); + } + + for (int i = 0; i < 100; i++) { + int item; + assert(LEO_Vec_get(&vec, i, &item) == LEO_Vec_OK); + assert(item == i); + } + + LEO_Vec_free(&vec); + return 0; +} |
