// gl_utils.c #include "gl_utils.h" #include #include #include #include void glfw_error_callback(int error, const char* description) { fprintf(stderr, "GLFW error %d: %s\n", error, description); } char* load_text_file(const char* path) { FILE* f = fopen(path, "rb"); if (!f) { fprintf(stderr, "Failed to open '%s': %s\n", path, strerror(errno)); return NULL; } if (fseek(f, 0, SEEK_END) != 0) { fprintf(stderr, "fseek failed on '%s'\n", path); fclose(f); return NULL; } long size = ftell(f); if (size < 0) { fprintf(stderr, "ftell failed on '%s'\n", path); fclose(f); return NULL; } rewind(f); char* buffer = malloc((size_t)size + 1); if (!buffer) { fprintf(stderr, "Out of memory reading '%s'\n", path); fclose(f); return NULL; } size_t read = fread(buffer, 1, (size_t)size, f); fclose(f); if (read != (size_t)size) { fprintf(stderr, "Short read on '%s'\n", path); free(buffer); return NULL; } buffer[size] = '\0'; return buffer; } GLuint compile_shader(GLenum type, const char* source, const char* debugName) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, NULL); glCompileShader(shader); GLint success = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { char log[1024]; glGetShaderInfoLog(shader, sizeof(log), NULL, log); fprintf(stderr, "Shader compile failed (%s):\n%s\n", debugName ? debugName : "unknown", log); glDeleteShader(shader); exit(EXIT_FAILURE); } return shader; } GLuint create_program_from_files(const char* vsPath, const char* fsPath) { char* vsSource = load_text_file(vsPath); if (!vsSource) { fprintf(stderr, "Could not load vertex shader from '%s'\n", vsPath); exit(EXIT_FAILURE); } char* fsSource = load_text_file(fsPath); if (!fsSource) { fprintf(stderr, "Could not load fragment shader from '%s'\n", fsPath); free(vsSource); exit(EXIT_FAILURE); } GLuint vs = compile_shader(GL_VERTEX_SHADER, vsSource, vsPath); GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fsSource, fsPath); free(vsSource); free(fsSource); GLuint prog = glCreateProgram(); glAttachShader(prog, vs); glAttachShader(prog, fs); glLinkProgram(prog); glDeleteShader(vs); glDeleteShader(fs); GLint linked = 0; glGetProgramiv(prog, GL_LINK_STATUS, &linked); if (!linked) { char log[1024]; glGetProgramInfoLog(prog, sizeof(log), NULL, log); fprintf(stderr, "Program link failed:\n%s\n", log); glDeleteProgram(prog); exit(EXIT_FAILURE); } return prog; } GLFWwindow* init_glfw_glad(const char* title, int width, int height) { glfwSetErrorCallback(glfw_error_callback); // Prefer Wayland if this GLFW build supports it if (glfwPlatformSupported(GLFW_PLATFORM_WAYLAND)) { glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_WAYLAND); fprintf(stderr, "[info] Requesting Wayland platform via glfwInitHint\n"); } else { fprintf(stderr, "[warn] Wayland platform not supported by this GLFW build, using default\n"); } if (!glfwInit()) { fprintf(stderr, "[fatal] glfwInit failed\n"); return NULL; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL); if (!window) { fprintf(stderr, "[fatal] glfwCreateWindow failed\n"); glfwTerminate(); return NULL; } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { fprintf(stderr, "[fatal] Failed to initialize GLAD\n"); glfwDestroyWindow(window); glfwTerminate(); return NULL; } return window; }