summaryrefslogtreecommitdiff
path: root/src/platform_glfw.c
diff options
context:
space:
mode:
authoronelin <oscar@nelin.dk>2025-10-31 23:55:42 +0000
committeronelin <oscar@nelin.dk>2025-11-02 22:07:17 +0000
commitd38deeef3af2316a666f8fc0173940bd769b748e (patch)
tree6e30d4a9eea18daa5705c894f28cd99ff047e8f9 /src/platform_glfw.c
parent6c077751982ea2c7bd2d9262b01b9f8602f80dc8 (diff)
Flatten project structure
This will make it easier to break up the code into smaller chunks again later. One would think doing this seems fun to me at this point.
Diffstat (limited to 'src/platform_glfw.c')
-rw-r--r--src/platform_glfw.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/src/platform_glfw.c b/src/platform_glfw.c
new file mode 100644
index 0000000..211df47
--- /dev/null
+++ b/src/platform_glfw.c
@@ -0,0 +1,232 @@
+// Required for OpenGL rendering backend
+#define GLAD_GL_IMPLEMENTATION
+#include <glad/gl.h>
+#undef GLAD_GL_IMPLEMENTATION
+
+// TODO: import vulkan thingymajig once I get around to it.
+// TODO: move OpenGL initialization code at some point
+
+#undef GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+
+#include <daw/logging.h>
+
+#include <daw/platform.h>
+
+static void window_resize_callback(GLFWwindow *restrict window, int width, int height);
+static void framebuffer_resize_callback(GLFWwindow *restrict window, int width, int height);
+static void glfw_err_callback(int code, const char* description);
+static void render_init_opengl(Window *restrict w, const u32 flags);
+static void window_size(GLFWwindow *restrict w, ivec2 *restrict dst);
+
+
+Window* window_init_glfw(const char *restrict windowtitle, ivec2 windowsize, const u32 flags) {
+ Window* ret = NULL;
+ GLFWwindow* window = NULL;
+
+ glfwSetErrorCallback(&glfw_err_callback);
+
+ INFO_("initializing glfw...");
+ if (glfwInit() == GLFW_FALSE) {
+ const char *desc;
+ int code = glfwGetError(&desc);
+ ERROR("failed to initialize glfw [%d]: %s\n", code, *desc);
+ exit(EXIT_FAILURE);
+ } else
+ printf("ok\n");
+
+
+ INFO_("initializing window...");
+ //glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+ if (!(flags & DAW_WINDOW_RESIZEABLE)) {
+ glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
+ }
+
+ glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
+ glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
+
+ glfwWindowHint(GLFW_SAMPLES, 0); // Disable anti aliasing
+
+ // Use a modern opengl version
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
+
+ // Lean and mean
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+#ifdef __APPLE__
+ // To make MacOS happy; should not be needed
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+#endif
+
+ /* "On Wayland specifically, you need to swap the buffers
+ * once of a window for it to become visible." */
+ {
+ GLFWmonitor* mon = NULL;
+ if (flags & DAW_WINDOW_FULLSCREEN) mon = glfwGetPrimaryMonitor();
+
+ window = glfwCreateWindow(windowsize[0], windowsize[1], windowtitle, mon, NULL);
+ }
+
+ if (window == NULL) {
+ ERROR("Failed to create GLFW window!\n");
+ const char *desc;
+ int code = glfwGetError(&desc);
+ ERROR("failed to initialize glfw window [%d]: %s\n", code, desc);
+ exit(EXIT_FAILURE);
+ } else
+ printf("ok\n");
+
+ // Setup callbacks
+ glfwSetFramebufferSizeCallback(window, framebuffer_resize_callback);
+ glfwSetWindowSizeCallback(window, window_resize_callback);
+ glfwSetKeyCallback(window, (GLFWkeyfun)key_callback);
+
+ // Create the window datastructure
+ ret = (Window*)calloc(1, sizeof(Window));
+ ret->framework = WINDOW_FRAMEWORK_GLFW;
+ ret->renderer = WINDOW_RENDERER_NONE;
+ ret->window = window;
+ /* Last parameter is used for the renderer */
+ ret->context = NULL;
+
+ {
+ ivec2 wsize;
+ vec2 wscaling;
+ glfwGetWindowContentScale(window, &wscaling[0], &wscaling[1]);
+ glfwGetWindowSize(window, &wsize[0], &wsize[1]);
+
+ ret->windowsize[0] = (i32)((f32)wsize[0] * wscaling[0]);
+ ret->windowsize[1] = (i32)((f32)wsize[1] * wscaling[1]);
+
+ INFO("WINDOW CONTENT SCALING: %.2f x %.2f", wscaling[0], wscaling[1]);
+ INFO("WINDOW SIZE: %d x %d -> %d x %d", wsize[0], wsize[1], ret->windowsize[0], ret->windowsize[1]);
+ }
+
+ // TODO: set this to `ret` once all the garbage is moved to `struct Window`
+ glfwSetWindowUserPointer(window, (void*)ret);
+
+ render_init_opengl(ret, flags);
+
+ return ret;
+}
+
+void window_destroy_glfw(Window *restrict w) {
+ glfwDestroyWindow(w->window);
+ w->window = NULL;
+
+ // If we ever do multi-window support, we need to make sure this is the last
+ // window before terminating
+ glfwTerminate();
+
+ switch(w->renderer) {
+ case WINDOW_RENDERER_OPENGL:
+ // Missing unloader function in glad MX library
+ free(w->context);
+ w->context = NULL;
+ break;
+ default:
+ ERROR("Destroying unknown renderer type.");
+ }
+}
+
+void window_resize_glfw(Window *restrict window, int width, int height) {
+ window_resize_callback(window->window, width, height);
+ framebuffer_resize_callback(window->window, width, height);
+}
+
+bool window_should_close_glfw(Window *restrict window) {
+ return glfwWindowShouldClose(window->window);
+}
+
+void window_poll_glfw(void) {
+ glfwPollEvents();
+}
+
+// Helper function implementations
+static void window_resize_callback(GLFWwindow* window, int width, int height) {
+ (void)width; (void)height;
+ Window* w = glfwGetWindowUserPointer(window);
+ glfwSwapBuffers(window);
+ if (w != NULL) {
+ const GladGLContext* gl = w->context;
+ gl->Finish();
+ }
+}
+
+static void framebuffer_resize_callback(GLFWwindow* window, int width, int height) {
+ (void)width; (void)height;
+ Window* w = glfwGetWindowUserPointer(window);
+ if (w != NULL) {
+ const GladGLContext* gl = w->context;
+ //TODO: Move the camera to window->renderer
+ //Camera* c = w->cam;
+ gl->Viewport(0,0, width, height);
+ w->windowsize[0] = width;
+ w->windowsize[1] = height;
+
+ //r_reset_camera(c);
+ }
+}
+
+static void glfw_err_callback(int code, const char* description) {
+ ERROR("glfw [%d]: %s\n", code, description);
+ // Terminate?
+ exit(EXIT_FAILURE);
+}
+
+static void render_init_opengl(Window *restrict w, const u32 flags) {
+ if (w->renderer != WINDOW_RENDERER_NONE || w->context != NULL) {
+ ERROR("Window already initialized with a renderer!");
+ return;
+ }
+
+ // This is GLFW specific
+ glfwMakeContextCurrent(w->window);
+
+ GladGLContext* ctx = (GladGLContext*)malloc(sizeof(GladGLContext));
+ if (!ctx) {
+ ERROR("Failed to allocate memory for context");
+ }
+
+ int version = gladLoadGLContext(ctx, glfwGetProcAddress);
+ INFO("Loaded OpenGL %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
+
+ if (ctx == NULL) {
+ ERROR("Failed to create glad context");
+ exit(EXIT_FAILURE);
+ }
+
+ if (flags & DAW_WINDOW_VSYNC) {
+ glfwSwapInterval(1);
+ } else {
+ glfwSwapInterval(0);
+ }
+
+
+ ctx->Viewport(0, 0, w->windowsize[0], w->windowsize[1]);
+
+#ifdef _DEBUG
+ ctx->ClearColor((float)0x10 / 255.f, (float)0x0a / 255.f, (float)0x33 / 255.f, 0.f);
+#else
+ ctx->ClearColor(0x0, 0x0, 0x0, 0.f);
+#endif
+
+ // Make sure faces closest to the camera are drawn on-top of faces that are
+ // further away
+ ctx->Enable(GL_DEPTH_TEST);
+ ctx->DepthFunc(GL_LESS);
+
+ w->context = ctx;
+ w->renderer = WINDOW_RENDERER_OPENGL;
+}
+
+static void window_size(GLFWwindow *restrict w, ivec2 *restrict dst) {
+ ivec2 wsize;
+ vec2 wscaling;
+ glfwGetWindowContentScale(w, &wscaling[0], &wscaling[1]);
+ glfwGetWindowSize(w, &wsize[0], &wsize[1]);
+
+ *dst[0] = (i32)((f32)wsize[0] * wscaling[0]);
+ *dst[1] = (i32)((f32)wsize[1] * wscaling[1]);
+}