summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--makefile66
-rwxr-xr-xrun_tests.sh38
-rw-r--r--src/input.c35
-rw-r--r--src/input.h11
-rw-r--r--src/main.c67
-rw-r--r--src/util/alloc.h13
-rw-r--r--src/util/vec.c98
-rw-r--r--src/util/vec.h38
-rw-r--r--test/test_vec.c31
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;
+}