Commit: 74599cfcd30e741feae2c71e80eeb396c5bc8ae1
Author: Randy Palamar
Date: Sat, 22 Jun 2024 09:55:08 -0600
start doing the thing
Diffstat:
A | LICENSE | | | 13 | +++++++++++++ |
A | build.sh | | | 15 | +++++++++++++++ |
A | main.c | | | 114 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | os_unix.c | | | 23 | +++++++++++++++++++++++ |
A | util.c | | | 16 | ++++++++++++++++ |
A | util.h | | | 55 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | vtgl.c | | | 48 | ++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 284 insertions(+), 0 deletions(-)
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,13 @@
+© 2024 Randy Palamar <randy@rnpnr.xyz>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/build.sh b/build.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+cflags="-march=native -ggdb -O0 -Wall"
+cflags="$cflags $(pkg-config --cflags glfw3 gl freetype2)"
+ldflags=""
+ldflags="$ldflags -lfreetype $(pkg-config --static --libs glfw3 gl)"
+
+# Hot Reloading/Debugging
+cflags="$cflags -D_DEBUG"
+
+libcflags="$cflags -fPIC"
+libldflags="$ldflags -shared"
+
+cc $libcflags vtgl.c -o vtgl.so $libldflags
+cc $cflags -o vtgl main.c $ldflags
diff --git a/main.c b/main.c
@@ -0,0 +1,114 @@
+/* See LICENSE for copyright details */
+#define GL_GLEXT_PROTOTYPES 1
+#include <GL/glcorearb.h>
+#include <GL/glext.h>
+#include <GLFW/glfw3.h>
+
+#include "util.h"
+
+#include "os_unix.c"
+
+#ifndef _DEBUG
+static void do_debug(void) { }
+#else
+#include <dlfcn.h>
+#include <time.h>
+
+static char *libname = "./vtgl.so";
+static void *libhandle;
+
+typedef void do_terminal_fn(Term *, Arena);
+static do_terminal_fn *do_terminal;
+
+typedef void init_callbacks_fn(GLCtx *);
+static init_callbacks_fn *init_callbacks;
+
+static void
+load_library(const char *lib)
+{
+ /* NOTE: glibc sucks and will crash if this is NULL */
+ if (libhandle)
+ dlclose(libhandle);
+ libhandle = dlopen(lib, RTLD_NOW|RTLD_LOCAL);
+ if (!libhandle)
+ fprintf(stderr, "do_debug: dlopen: %s\n", dlerror());
+ do_terminal = dlsym(libhandle, "do_terminal");
+ if (!do_terminal)
+ fprintf(stderr, "do_debug: dlsym: %s\n", dlerror());
+ init_callbacks = dlsym(libhandle, "init_callbacks");
+ if (!init_callbacks)
+ fprintf(stderr, "do_debug: dlsym: %s\n", dlerror());
+}
+
+static void
+do_debug(GLCtx *gl)
+{
+ static os_file_stats updated;
+ os_file_stats test = os_get_file_stats(libname);
+
+ if (os_filetime_is_newer(test.timestamp, updated.timestamp)) {
+ updated = test;
+ /* NOTE: sucks but seems to be easiest reliable way to make sure lib is written */
+ struct timespec sleep_time = { .tv_nsec = 100e6 };
+ nanosleep(&sleep_time, &sleep_time);
+ load_library(libname);
+ init_callbacks(gl);
+ }
+}
+
+#endif /* _DEBUG */
+
+static void
+error_callback(int code, const char *desc)
+{
+ fprintf(stderr, "GLFW Error (0x%04X): %s\n", code, desc);
+}
+
+static void
+init_window(Term *t)
+{
+ if (!glfwInit())
+ die("Failed to init GLFW\n");
+ glfwSetErrorCallback(error_callback);
+
+ GLFWmonitor *mon = glfwGetPrimaryMonitor();
+ if (!mon) {
+ glfwTerminate();
+ die("Failed to get GLFW monitor\n");
+ }
+
+ glfwGetMonitorWorkarea(mon, NULL, NULL, &t->gl.window_size.w, &t->gl.window_size.h);
+
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ t->gl.window = glfwCreateWindow(t->gl.window_size.w, t->gl.window_size.h, "vtgl", NULL, NULL);
+ if (!t->gl.window) {
+ glfwTerminate();
+ die("Failed to spawn GLFW window\n");
+ }
+ glfwMakeContextCurrent(t->gl.window);
+ glfwSetWindowUserPointer(t->gl.window, t);
+
+ /* TODO: swap interval is not needed because we will sleep on waiting for terminal input */
+ glfwSwapInterval(1);
+}
+
+i32
+main(void)
+{
+ Arena memory = {0};
+ Term term = {0};
+
+ init_window(&term);
+
+ while (!glfwWindowShouldClose(term.gl.window)) {
+ do_debug(&term.gl);
+
+ do_terminal(&term, memory);
+ }
+
+ return 0;
+}
diff --git a/os_unix.c b/os_unix.c
@@ -0,0 +1,23 @@
+/* See LICENSE for copyright details */
+#include <fcntl.h>
+#include <sys/stat.h>
+
+typedef struct timespec os_filetime;
+typedef struct {
+ size filesize;
+ os_filetime timestamp;
+} os_file_stats;
+
+static os_file_stats
+os_get_file_stats(const char *name)
+{
+ struct stat sb = {0};
+ stat(name, &sb);
+ return (os_file_stats){.timestamp = sb.st_mtim, .filesize = sb.st_size};
+}
+
+static b32
+os_filetime_is_newer(os_filetime a, os_filetime b)
+{
+ return (a.tv_sec - b.tv_sec) + (a.tv_nsec - b.tv_nsec);
+}
diff --git a/util.c b/util.c
@@ -0,0 +1,16 @@
+/* See LICENSE for copyright details */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void __attribute__((noreturn))
+die(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
diff --git a/util.h b/util.h
@@ -0,0 +1,55 @@
+/* See LICENSE for copyright details */
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef _DEBUG
+#define ASSERT(c) do { if (!(c)) asm("int3; nop"); } while(0)
+#define DEBUG_EXPORT
+#else
+#define ASSERT(c)
+#define DEBUG_EXPORT static
+#endif
+
+typedef float f32;
+typedef double f64;
+typedef uint8_t u8;
+typedef int32_t i32;
+typedef uint32_t u32;
+typedef uint32_t b32;
+typedef ptrdiff_t size;
+
+typedef union {
+ struct { i32 x, y; };
+ struct { i32 w, h; };
+ i32 E[2];
+} iv2;
+
+typedef union {
+ struct { f32 x, y, z, w; };
+ struct { f32 r, g, b, a; };
+ f32 E[4];
+} v4;
+
+typedef union {
+ /* NOTE: this assumes little endian */
+ struct { u8 a, b, g, r; };
+ u32 rgba;
+} Colour;
+
+typedef struct { u8 *beg, *end; } Arena;
+
+typedef struct {
+ GLFWwindow *window;
+ iv2 window_size;
+} GLCtx;
+
+typedef struct {
+ GLCtx gl;
+} Term;
+
+#include "util.c"
+
+#endif /* _UTIL_H_ */
diff --git a/vtgl.c b/vtgl.c
@@ -0,0 +1,48 @@
+/* See LICENSE for copyright details */
+#define GL_GLEXT_PROTOTYPES 1
+#include <GL/glcorearb.h>
+#include <GL/glext.h>
+#include <GLFW/glfw3.h>
+
+#include "util.h"
+
+static v4
+normalized_colour(Colour c)
+{
+ return (v4){.r = c.r / 255.0f, .g = c.g / 255.0f, .b = c.b / 255.0f, .a = c.a / 255.0f};
+}
+
+static void
+clear_colour(void)
+{
+ v4 cc = normalized_colour((Colour){.r = 20, .g = 20, .b = 20, .a = 255});
+ glClearColor(cc.r, cc.g, cc.b, cc.a);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+static void
+key_callback(GLFWwindow *w, i32 key, i32 sc, i32 act, i32 mods)
+{
+ if (key == GLFW_KEY_ESCAPE && act == GLFW_PRESS)
+ glfwSetWindowShouldClose(w, GL_TRUE);
+}
+
+DEBUG_EXPORT void
+init_callbacks(GLCtx *gl)
+{
+ //glfwSetCharCallback(gl->window, char_callback);
+ //glfwSetFramebufferSizeCallback(gl->window, fb_callback);
+ glfwSetKeyCallback(gl->window, key_callback);
+ //glfwSetWindowRefreshCallback(gl->window, refresh_callback);
+ //glfwSetScrollCallback(gl->window, scroll_callback);
+}
+
+DEBUG_EXPORT void
+do_terminal(Term *t, Arena a)
+{
+ glfwPollEvents();
+
+ clear_colour();
+
+ glfwSwapBuffers(t->gl.window);
+}