aboutsummaryrefslogtreecommitdiff
path: root/teensy
diff options
context:
space:
mode:
authoriamcheeseman <[email protected]>2026-05-08 19:30:44 -0400
committeriamcheeseman <[email protected]>2026-05-08 19:30:44 -0400
commit5625a8626fe303748b205c80f87035593cf2f561 (patch)
tree5e5a5adb6f1265358ae21ec47cd384362345df5a /teensy
Initial commit
Diffstat (limited to 'teensy')
-rw-r--r--teensy/common.c30
-rw-r--r--teensy/common.h14
-rw-r--r--teensy/context.c51
-rw-r--r--teensy/context.h10
-rw-r--r--teensy/dyn_arr.c44
-rw-r--r--teensy/dyn_arr.h33
-rw-r--r--teensy/log.c33
-rw-r--r--teensy/log.h47
-rw-r--r--teensy/mem.c171
-rw-r--r--teensy/mem.h24
-rw-r--r--teensy/platform.h12
-rw-r--r--teensy/renderer.c227
-rw-r--r--teensy/renderer.h10
-rw-r--r--teensy/teensy.h108
14 files changed, 814 insertions, 0 deletions
diff --git a/teensy/common.c b/teensy/common.c
new file mode 100644
index 0000000..4da4004
--- /dev/null
+++ b/teensy/common.c
@@ -0,0 +1,30 @@
+#include "common.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+char *read_file(const char *path, size_t *size_out)
+{
+ FILE *file = fopen(path, "r");
+ if (!file)
+ ty_log_fatal(TY_ERR_IO, "could not open file '%s'", path);
+
+ fseek(file, 0L, SEEK_END);
+ size_t size = ftell(file);
+ rewind(file);
+
+ char *dat = ty_alloc(sizeof(char) * (size + 1));
+
+ size_t bytes_read = fread(dat, sizeof(char), size, file);
+
+ if (bytes_read < size) {
+ ty_log_fatal(TY_ERR_IO, "could not read file '%s'", path);
+ }
+
+ fclose(file);
+
+ if (size_out)
+ *size_out = size;
+
+ return dat;
+}
diff --git a/teensy/common.h b/teensy/common.h
new file mode 100644
index 0000000..6e3e66f
--- /dev/null
+++ b/teensy/common.h
@@ -0,0 +1,14 @@
+#ifndef COMMON_H_
+#define COMMON_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "mem.h"
+
+#define TY_PI 3.14
+#define TY_DEG2RAD (180 / PI)
+
+#endif // COMMON_H_
diff --git a/teensy/context.c b/teensy/context.c
new file mode 100644
index 0000000..7095fe3
--- /dev/null
+++ b/teensy/context.c
@@ -0,0 +1,51 @@
+#include "context.h"
+#include "renderer.h"
+
+#include <math.h>
+
+#include "platform.h"
+
+struct ty_ctx ctx;
+extern struct ty_renderer r;
+
+void ty_init(struct ty_creation_hints creation_hints)
+{
+ ty_init_mem();
+
+ ctx.creation_hints = creation_hints;
+ ctx.ticrate = 1.0 / creation_hints.ticrate;
+ ctx.prev_tic_ts = 0;
+
+ ty_platform_init(&ctx);
+ ty_init_renderer();
+}
+
+void ty_deinit(void)
+{
+ ty_platform_deinit();
+ ty_deinit_renderer();
+ ty_deinit_mem();
+}
+
+bool ty_is_game_running(void)
+{
+ return !ty_platform_os_wants_quit();
+}
+
+double ty_get_time(void)
+{
+ return ty_platform_get_time();
+}
+
+int ty_tick(void)
+{
+ double current_time = ty_get_time();
+ double time_since_tic = current_time - ctx.prev_tic_ts;
+
+ int tics = time_since_tic / ctx.ticrate;
+ if (tics > 0) {
+ ctx.prev_tic_ts = current_time;
+ ty_platform_frame(r.screen);
+ }
+ return tics;
+}
diff --git a/teensy/context.h b/teensy/context.h
new file mode 100644
index 0000000..0bdc5f9
--- /dev/null
+++ b/teensy/context.h
@@ -0,0 +1,10 @@
+#ifndef CONTEXT_H_
+#define CONTEXT_H_
+
+#include "common.h"
+#include "renderer.h"
+#include "teensy.h"
+
+extern struct ty_ctx ctx;
+
+#endif // CONTEXT_H_
diff --git a/teensy/dyn_arr.c b/teensy/dyn_arr.c
new file mode 100644
index 0000000..38e3b31
--- /dev/null
+++ b/teensy/dyn_arr.c
@@ -0,0 +1,44 @@
+#include "dyn_arr.h"
+
+#include <stdio.h>
+#include <assert.h>
+
+void *_ty_list_create(size_t type_size, int init_cap)
+{
+ init_cap = init_cap < TY_LIST_MIN_CAP
+ ? TY_LIST_MIN_CAP
+ : init_cap;
+
+ int *arr = (int*)ty_alloc(type_size * init_cap + sizeof(int) * 2);
+
+ arr[0] = 0;
+ arr[1] = init_cap;
+ return (void*)(arr + 2);
+}
+
+void *_ty_list_append_slot(size_t type_size, void **arr)
+{
+ int *cap = ty_list_cap_ptr(*arr);
+ int *len = ty_list_len_ptr(*arr);
+
+ (*len)++;
+
+ if (*len > *cap) {
+ *cap *= TY_LIST_GROW_RATE;
+
+ void* base = ty_list_base(*arr);
+ base = ty_realloc(base, (type_size * *cap) + sizeof(int) * 2);
+ assert(base != NULL); // Just to handle the case
+ *arr = (void*)((int*)base + 2);
+
+ len = ty_list_len_ptr(*arr);
+ }
+
+ return (char*)*arr + ((*len - 1) * type_size);
+}
+
+void ty_list_free(void *arr)
+{
+ void *base = ty_list_base(arr);
+ ty_free(base);
+}
diff --git a/teensy/dyn_arr.h b/teensy/dyn_arr.h
new file mode 100644
index 0000000..7a45d4e
--- /dev/null
+++ b/teensy/dyn_arr.h
@@ -0,0 +1,33 @@
+#ifndef DYN_ARR_H_
+#define DYN_ARR_H_
+
+#include "common.h"
+
+#define TY_LIST_MIN_CAP 8
+#define TY_LIST_GROW_RATE 2
+
+#define ty_list_create(T, init_capacity) \
+ ((T*)_ty_list_create(sizeof(T), init_capacity))
+
+#define ty_list_len_ptr(arr) ((int*)arr - 2)
+#define ty_list_len(arr) (*ty_list_len_ptr(arr))
+
+#define ty_list_cap_ptr(arr) ((int*)arr - 1)
+#define ty_list_cap(arr) (*ty_list_cap_ptr(arr))
+
+#define ty_list_base(arr) ((void*)ty_list_len_ptr(arr))
+
+#define ty_list_append_slot(T, arr) \
+ ((T*)_ty_list_append_slot(sizeof(T), (void**)arr))
+
+#define ty_list_append(T, arr, elem) \
+ (*ty_list_append_slot(T, arr) = elem)
+
+#define ty_list_clear(arr) \
+ (*ty_list_len_ptr(arr) = 0)
+
+void *_ty_list_create(size_t type_size, int init_cap);
+void *_ty_list_append_slot(size_t type_size, void **arr);
+void ty_list_free(void *arr);
+
+#endif // DYN_ARR_H_
diff --git a/teensy/log.c b/teensy/log.c
new file mode 100644
index 0000000..fbd96a2
--- /dev/null
+++ b/teensy/log.c
@@ -0,0 +1,33 @@
+#include "log.h"
+
+#include <stdarg.h>
+
+#include "common.h"
+
+void ty_log_msg(
+ FILE *file,
+ const char *src_name,
+ int src_line,
+ const char *type,
+ const char *fmt,
+ ...
+)
+{
+ fprintf(file, "[%s] (%s:%d) ", type, src_name, src_line);
+
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(file, fmt, args);
+ va_end(args);
+
+ putc('\n', file);
+}
+
+void _ty_log_plain(FILE *file, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(file, fmt, args);
+ va_end(args);
+ putc('\n', file);
+}
diff --git a/teensy/log.h b/teensy/log.h
new file mode 100644
index 0000000..cebf39b
--- /dev/null
+++ b/teensy/log.h
@@ -0,0 +1,47 @@
+#ifndef LOG_H_
+#define LOG_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+// Don't include common.h, because this should be included there
+
+#define ty_log_info(fmt, ...) \
+ ty_log_msg(stdout, __FILE__, __LINE__, "info", fmt, ## __VA_ARGS__)
+#define ty_log_warn(fmt, ...) \
+ ty_log_msg(stderr, __FILE__, __LINE__, "warn", fmt, ## __VA_ARGS__)
+#define ty_log_err(fmt, ...) \
+ ty_log_msg(stderr, __FILE__, __LINE__, "error", fmt, ## __VA_ARGS__)
+#define ty_log_fatal(ec, fmt, ...) \
+ (ty_log_msg(stderr, __FILE__, __LINE__, "fatal", fmt, ## __VA_ARGS__), \
+ exit(ec))
+#define ty_olog(fmt, ...) \
+ _log_plain(stdout, fmt, ## __VA_ARGS__)
+#define ty_elog(fmt, ...) \
+ _log_plain(stderr, fmt, ## __VA_ARGS__)
+
+// To be passed as the exit code to log_fatal()
+
+// General errors
+#define TY_ERR_MEM 0x01
+#define TY_ERR_IO 0x02
+#define TY_ERR_MEM_LEAK 0x03
+#define TY_PLATFORM_ERR 0x04
+
+// Rendering errors
+#define TY_ERR_GLFW_INIT 0x30
+#define TY_ERR_RENDERER_NOT_INIT 0x31
+#define TY_ERR_RENDER_OOB 0x32 // Render out-of-bounds
+
+void ty_log_msg(
+ FILE *file,
+ const char *src_name,
+ int src_line,
+ const char *type,
+ const char *fmt,
+ ...
+);
+
+void _ty_log_plain(FILE *file, const char *fmt, ...);
+
+#endif // LOG_H_
diff --git a/teensy/mem.c b/teensy/mem.c
new file mode 100644
index 0000000..2c938a6
--- /dev/null
+++ b/teensy/mem.c
@@ -0,0 +1,171 @@
+#include "mem.h"
+
+#include "common.h"
+#include "dyn_arr.h"
+
+// Array to track temp allocations.
+void **temp_allocations;
+
+#ifdef TEENSY_DEBUG
+
+#include <execinfo.h>
+
+#define BACKTRACE_SIZE 16
+
+struct alloc {
+ void *ptr;
+ size_t size;
+ int backtrace_len;
+ char **backtrace;
+};
+
+int allocations_cap;
+int allocations_len;
+struct alloc *allocations;
+
+#endif // TODO TEENSY_DEBUG
+
+void ty_init_mem(void)
+{
+#ifdef TEENSY_DEBUG
+ allocations_len = 0;
+ allocations_cap = 8;
+ allocations = malloc(sizeof(struct alloc) * 8);
+ if (!allocations)
+ ty_log_fatal(TY_ERR_MEM, "(%s) (track) ran out of memory", __func__);
+#endif // TEENSY_DEBUG
+
+ temp_allocations = ty_list_create(void*, 0);
+}
+
+void ty_deinit_mem(void)
+{
+ ty_list_free(temp_allocations);
+#ifdef TEENSY_DEBUG
+ fprintf(stderr, "%d allocations leaked\n", allocations_len);
+ for (int i = 0; i < allocations_len; i++) {
+ struct alloc *alloc = &allocations[i];
+ fprintf(
+ stderr,
+ "leaked %zu bytes at %p\n",
+ alloc->size,
+ alloc->ptr
+ );
+ for (int j = 0; j < alloc->backtrace_len; j++) {
+ fprintf(stderr, "\t%s\n", alloc->backtrace[j]);
+ }
+ putc('\n', stderr);
+
+ free(alloc->backtrace);
+ }
+ free(allocations);
+
+ if (allocations_len > 0)
+ exit(TY_ERR_MEM_LEAK);
+#endif // TEENSY_DEBUG
+}
+
+void ty_free_temp_allocs(void)
+{
+ for (int i = 0; i < ty_list_len(temp_allocations); i++) {
+ void *temp = temp_allocations[i];
+ ty_free(temp);
+ }
+ ty_list_clear(temp_allocations);
+}
+
+void *ty_alloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (!ptr)
+ ty_log_fatal(TY_ERR_MEM, "(%s) ran out of memory", __func__);
+
+#ifdef TEENSY_DEBUG
+ // Log allocation
+ struct alloc alloc;
+ void *bt[BACKTRACE_SIZE];
+ int bt_len = backtrace(bt, BACKTRACE_SIZE);
+ char **symbols = backtrace_symbols(bt, bt_len);
+
+ alloc.ptr = ptr;
+ alloc.size = size;
+ alloc.backtrace_len = bt_len;
+ alloc.backtrace = symbols;
+
+ if (allocations_len + 1 > allocations_cap) {
+ allocations_cap *= 2;
+ allocations = realloc(
+ allocations,
+ sizeof(struct alloc) * allocations_cap
+ );
+ if (!allocations)
+ ty_log_fatal(
+ TY_ERR_MEM,
+ "(%s) (track) ran out of memory",
+ __func__
+ );
+ }
+
+ allocations[allocations_len++] = alloc;
+#endif // TEENSY_DEBUG
+
+ return ptr;
+}
+
+void *ty_realloc(void *ptr, size_t new_size)
+{
+ if (new_size == 0) {
+ ty_free(ptr);
+ return NULL;
+ }
+
+ void *new_ptr = realloc(ptr, new_size);
+ if (!new_ptr)
+ ty_log_fatal(TY_ERR_MEM, "(%s) ran out of memory", __func__);
+
+#ifdef TEENSY_DEBUG
+ for (int i = 0; i < allocations_len; i++) {
+ struct alloc *alloc = &allocations[i];
+ if (alloc->ptr == ptr) {
+ alloc->ptr = new_ptr;
+ alloc->size = new_size;
+ break;
+ }
+ }
+#endif // TEENSY_DEBUG
+
+ return new_ptr;
+}
+
+void *ty_talloc(size_t size)
+{
+ void *ptr = ty_alloc(size);
+ ty_list_append(void*, &temp_allocations, ptr);
+ return ptr;
+}
+
+void ty_free(void *ptr)
+{
+ free(ptr);
+
+#ifdef TEENSY_DEBUG
+ for (int i = 0; i < allocations_len; i++) {
+ struct alloc *alloc = &allocations[i];
+ if (alloc->ptr == ptr) {
+ free(alloc->backtrace);
+ *alloc = allocations[allocations_len - 1];
+ allocations_len--;
+ break;
+ }
+ }
+#endif // TEENSY_DEBUG
+}
+
+int ty_alloc_count(void)
+{
+#ifdef TEENSY_DEBUG
+ return allocations_len;
+#else
+ return -1;
+#endif
+}
diff --git a/teensy/mem.h b/teensy/mem.h
new file mode 100644
index 0000000..61f3ee2
--- /dev/null
+++ b/teensy/mem.h
@@ -0,0 +1,24 @@
+#ifndef MEM_H_
+#define MEM_H_
+
+#include <stdlib.h>
+
+// NOTE: Overrides GLFW allocator
+void ty_init_mem(void);
+void ty_deinit_mem(void);
+void ty_free_temp_allocs(void);
+// Temp allocation. Freed at the end of every frame. Do NOT realloc.
+void *ty_talloc(size_t size);
+
+// These mem_* functions handle the case of a bad allocation. No need to check
+// for NULL after allocating with these. In debug builds, they will also track
+// allocations and report any memory still in use when the program exits.
+
+void *ty_alloc(size_t size);
+void *ty_realloc(void *ptr, size_t new_size);
+void ty_free(void *ptr);
+
+// Returns -1 in release builds.
+int ty_alloc_count(void);
+
+#endif // MEM_H_
diff --git a/teensy/platform.h b/teensy/platform.h
new file mode 100644
index 0000000..df3c5f8
--- /dev/null
+++ b/teensy/platform.h
@@ -0,0 +1,12 @@
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+// This platform functions are to be implemented by a platform library.
+
+void ty_platform_init(struct ty_ctx *ctx);
+void ty_platform_deinit(void);
+void ty_platform_frame(struct ty_image img);
+bool ty_platform_os_wants_quit(void);
+double ty_platform_get_time(void);
+
+#endif // PLATFORM_H_
diff --git a/teensy/renderer.c b/teensy/renderer.c
new file mode 100644
index 0000000..9e5f824
--- /dev/null
+++ b/teensy/renderer.c
@@ -0,0 +1,227 @@
+#include "renderer.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "context.h"
+#include "dyn_arr.h"
+
+struct ty_renderer r;
+
+static
+void renderer_init_check()
+{
+#ifdef TEENSY_DEBUG
+ if (
+ r.screen.data == NULL ||
+ r.screen.width * r.screen.height == 0
+ ) {
+ ty_log_fatal(
+ TY_ERR_RENDERER_NOT_INIT,
+ "renderer is not yet intialized"
+ );
+ }
+#endif
+}
+
+static
+void image_bounds_check(struct ty_image img, struct ty_vec2i pos)
+{
+#ifdef TEENSY_DEBUG
+ if (
+ pos.x < 0 ||
+ pos.x >= img.width ||
+ pos.y < 0 ||
+ pos.y >= img.height
+ ) {
+ ty_log_fatal(
+ TY_ERR_RENDER_OOB,
+ "rendered at (%d, %d), when bounds are "
+ "(0, 0) to (%d, %d)",
+ pos.x, pos.y,
+ img.width, img.height
+ );
+ }
+#endif
+}
+
+void ty_init_renderer(void)
+{
+ r.screen = ty_create_image(
+ ctx.creation_hints.win_width,
+ ctx.creation_hints.win_height,
+ NULL
+ );
+}
+
+void ty_deinit_renderer(void)
+{
+ renderer_init_check();
+
+ ty_free_image(r.screen);
+}
+
+struct ty_image ty_create_image(int w, int h, const struct ty_color* data)
+{
+ struct ty_image img;
+
+ img.width = w;
+ img.height = h;
+
+ size_t size = sizeof(struct ty_color) * w * h;
+ img.data = ty_alloc(size);
+ if (data)
+ memcpy(img.data, data, size);
+ else
+ memset(img.data, 0, size);
+
+ return img;
+}
+
+void ty_free_image(struct ty_image img)
+{
+ ty_free(img.data);
+}
+
+struct ty_color ty_img_get_pixel(struct ty_image img, struct ty_vec2i pos)
+{
+ image_bounds_check(img, pos);
+ return img.data[pos.y * img.width + pos.x];
+}
+
+void ty_img_set_pixel(
+ struct ty_image img,
+ struct ty_vec2i pos,
+ struct ty_color color
+) {
+ image_bounds_check(img, pos);
+ img.data[pos.y * img.width + pos.x] = color;
+}
+
+void ty_draw_clear(struct ty_color col)
+{
+ renderer_init_check();
+ for (int i = 0; i < r.screen.width * r.screen.height; i++) {
+ r.screen.data[i] = col;
+ }
+}
+
+void ty_draw_image(struct ty_image img, struct ty_vec2i pos)
+{
+ int x1 = fmin(fmax(floor(pos.x), 0), r.screen.width);
+ int y1 = fmin(fmax(floor(pos.y), 0), r.screen.height);
+
+ int x2 = fmin(fmax(floor(pos.x + img.width), 0), r.screen.width);
+ int y2 = fmin(fmax(floor(pos.y + img.height), 0), r.screen.height);
+
+ for (int dx = x1; dx < x2; dx++) {
+ for (int dy = y1; dy < y2; dy++) {
+ struct ty_vec2i tex_coord = ty_vec2i(dx - x1, dy - y1);
+ ty_img_set_pixel(
+ r.screen,
+ (struct ty_vec2i){dx, dy},
+ ty_img_get_pixel(img, tex_coord)
+ );
+ }
+ }
+}
+
+void ty_draw_image_ex(
+ struct ty_image img,
+ struct ty_recti src,
+ struct ty_recti dst
+) {
+ int x1 = fmin(fmax(floor(dst.x), 0), r.screen.width);
+ int y1 = fmin(fmax(floor(dst.y), 0), r.screen.height);
+ int x2 = fmin(fmax(floor(dst.x + dst.w), 0), r.screen.width);
+ int y2 = fmin(fmax(floor(dst.y + dst.h), 0), r.screen.height);
+
+ for (int dx = x1; dx < x2; dx++) {
+ for (int dy = y1; dy < y2; dy++) {
+ int tex_x = (dx - x1) / (dst.w / src.w) + src.x;
+ int tex_y = (dy - y1) / (dst.h / src.h) + src.y;
+
+ ty_img_set_pixel(
+ r.screen,
+ (struct ty_vec2i){dx, dy},
+ ty_img_get_pixel(img, ty_vec2i(tex_x, tex_y))
+ );
+ }
+ }
+}
+
+void ty_draw_rect(struct ty_recti rect, struct ty_color color)
+{
+ int x1 = fmin(fmax(floor(rect.x), 0), r.screen.width);
+ int y1 = fmin(fmax(floor(rect.y), 0), r.screen.height);
+
+ int x2 = fmin(fmax(floor(rect.x + rect.w), 0), r.screen.width);
+ int y2 = fmin(fmax(floor(rect.y + rect.h), 0), r.screen.height);
+
+ for (int dx = x1; dx < x2; dx++) {
+ for (int dy = y1; dy < y2; dy++) {
+ ty_img_set_pixel(
+ r.screen,
+ (struct ty_vec2i){dx, dy},
+ color
+ );
+ }
+ }
+}
+
+void ty_draw_line(
+ struct ty_vec2i start,
+ struct ty_vec2i end,
+ struct ty_color color
+) {
+ int dx = end.x - start.x;
+ int dy = end.y - start.y;
+
+ if (abs(dx) > abs(dy)) {
+ if (start.x > end.x) {
+ int tmp = start.x;
+ start.x = end.x;
+ end.x = tmp;
+
+ tmp = start.y;
+ start.y = end.y;
+ end.y = tmp;
+ }
+
+ for (int x = start.x; x < end.x; x++) {
+ int y = start.y + dy * (x - start.x) / dx;
+
+ ty_img_set_pixel(
+ r.screen,
+ (struct ty_vec2i){x, y},
+ color
+ );
+ }
+ } else {
+ if (start.y > end.y) {
+ int tmp = start.x;
+ start.x = end.x;
+ end.x = tmp;
+
+ tmp = start.y;
+ start.y = end.y;
+ end.y = tmp;
+ }
+
+ for (int y = start.y; y < end.y; y++) {
+ int x = start.x + dx * (y - start.y) / dy;
+
+ ty_img_set_pixel(
+ r.screen,
+ ty_vec2i(x, y),
+ color
+ );
+ }
+ }
+}
+
+void ty_draw_end(void)
+{
+ renderer_init_check();
+}
diff --git a/teensy/renderer.h b/teensy/renderer.h
new file mode 100644
index 0000000..70eac72
--- /dev/null
+++ b/teensy/renderer.h
@@ -0,0 +1,10 @@
+#ifndef RENDERER_H_
+#define RENDERER_H_
+
+#include "common.h"
+#include "teensy.h"
+
+void ty_init_renderer(void);
+void ty_deinit_renderer(void);
+
+#endif // RENDERER_H_
diff --git a/teensy/teensy.h b/teensy/teensy.h
new file mode 100644
index 0000000..c8b8e9d
--- /dev/null
+++ b/teensy/teensy.h
@@ -0,0 +1,108 @@
+#ifndef TEENSY_H_
+#define TEENSY_H_
+
+#include "common.h"
+#include "context.h"
+
+#define ty_vec2(x, y) ((struct ty_vec2){x, y})
+#define ty_vec2i(x, y) ((struct ty_vec2i){x, y})
+#define ty_color(R, G, B) ((struct ty_color){R, G, B})
+
+#define TY_COLOR_BLACK ty_color(0, 0, 0 )
+#define TY_COLOR_RED ty_color(255, 0, 0 )
+#define TY_COLOR_GREEN ty_color(0, 255, 0 )
+#define TY_COLOR_YELLOW ty_color(255, 255, 0 )
+#define TY_COLOR_BLUE ty_color(0, 0, 255)
+#define TY_COLOR_MAGENTA ty_color(255, 0, 255)
+#define TY_COLOR_CYAN ty_color(0, 255, 255)
+#define TY_COLOR_WHITE ty_color(255, 255, 255)
+
+struct ty_creation_hints {
+ int win_width;
+ int win_height;
+ const char *win_title;
+ int ticrate;
+};
+
+struct ty_ctx {
+ struct ty_creation_hints creation_hints;
+ double ticrate;
+ double prev_tic_ts;
+};
+
+struct ty_color {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+struct ty_vec2 {
+ float x;
+ float y;
+};
+
+struct ty_vec2i {
+ int x;
+ int y;
+};
+
+struct ty_rect {
+ float x;
+ float y;
+ float w;
+ float h;
+};
+
+struct ty_recti {
+ int x;
+ int y;
+ int w;
+ int h;
+};
+
+struct ty_image {
+ struct ty_color *data;
+ int width;
+ int height;
+};
+
+struct ty_renderer {
+ struct ty_image screen;
+};
+
+void ty_init(struct ty_creation_hints creation_hints);
+void ty_deinit(void);
+
+// Whether or not the main loop should continue executing.
+bool ty_is_game_running(void);
+// Gets the real time that the game has been running.
+double ty_get_time(void);
+// Handle events, tick game logic, etc. Returns the amount of times you should
+// tic.
+int ty_tick(void);
+
+struct ty_image ty_create_image(int w, int h, const struct ty_color* data);
+void ty_free_image(struct ty_image img);
+struct ty_color ty_img_get_pixel(struct ty_image img, struct ty_vec2i pos);
+void ty_img_set_pixel(
+ struct ty_image img,
+ struct ty_vec2i pos,
+ struct ty_color color
+);
+
+void ty_draw_clear(struct ty_color col);
+void ty_draw_image(struct ty_image img, struct ty_vec2i pos);
+void ty_draw_image_ex(
+ struct ty_image img,
+ struct ty_recti src,
+ struct ty_recti dst
+);
+void ty_draw_rect(struct ty_recti rect, struct ty_color color);
+void ty_draw_line(
+ struct ty_vec2i start,
+ struct ty_vec2i end,
+ struct ty_color color
+);
+void ty_draw_end(void);
+
+#endif // TEENSY_H_