opengl_x11

Playing with OpenGL
git clone git://bsandro.tech/opengl_x11
Log | Files | Refs | README | LICENSE

commit 1fafa50a4833e1c448f3773c37d44466bdad90f0
parent 3cde30ce082208b0cd8f485d13209fb1e6c3ea9a
Author: bsandro <email@bsandro.tech>
Date:   Wed, 26 Nov 2025 01:47:54 +0200

First geometry shader

Diffstat:
Aobj2d.c | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 215 insertions(+), 0 deletions(-)

diff --git a/obj2d.c b/obj2d.c @@ -0,0 +1,215 @@ +/* 2d object from single vertex */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <math.h> +#include <X11/X.h> +#include <X11/Xlib.h> +#define GL_GLEXT_PROTOTYPES 1 +#include <GL/gl.h> +#include <GL/glx.h> +#include <GL/glu.h> + +#define LEN(x) (sizeof(x)/sizeof(x[0])) + +#define GL_CHECK(x) \ +x; \ +{ \ + GLenum glError = glGetError(); \ + if (glError!=GL_NO_ERROR) { \ + printf("glGetError() = %i (%#.8x) at %s:%i\n", glError, glError, __FILE__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ +} + +static const double current_ts() { + struct timespec ts; + timespec_get(&ts, TIME_UTC); + return (double)ts.tv_sec + (double)ts.tv_nsec/1e9; +} + +static double t_started; + +//@todo move shaders to separate files +static const GLchar *vertexSrc = R"( +#version 420 +layout(location=0) in vec2 position; +void main() { + gl_Position = vec4(position, 0.0, 1.0); +} +)"; + +static const GLchar *geometrySrc = R"( +#version 420 +layout(points) in; +layout(line_strip,max_vertices=6) out; +void main() { + vec4 p = gl_in[0].gl_Position; + gl_Position = p+vec4(-0.1, -0.1, 0.0, 0.0); + EmitVertex(); + gl_Position = p+vec4(0.1, -0.1, 0.0, 0.0); + EmitVertex(); + gl_Position = p+vec4(0.1, 0.1, 0.0, 0.0); + EmitVertex(); + gl_Position = p+vec4(-0.1, 0.1, 0.0, 0.0); + EmitVertex(); + gl_Position = p+vec4(-0.1, -0.1, 0.0, 0.0); + EmitVertex(); + + EndPrimitive(); +} +)"; + +static const GLchar *fragmentSrc = R"( +#version 420 +uniform double iTime; +out vec4 outColor; +void main() { + vec3 color; + color.r = 0.2; + color.g = 0.6; + color.b = 0.1; + outColor = vec4(color, 1.0); +} +)"; + +static GLuint mkShader(GLenum type, const GLchar *src) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + GLint ok = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + if (!ok) { + static char buf[512] = {0}; + glGetShaderInfoLog(shader, sizeof(buf)-1, NULL, buf); + fprintf(stderr, "Shader %d compile error: %s\n", (int)type, buf); + } + return shader; +} + +static GLuint linkProgram(GLuint vertex, GLuint geometry, GLuint fragment) { + GLuint shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vertex); + glAttachShader(shaderProgram, geometry); + glAttachShader(shaderProgram, fragment); + glBindAttribLocation(shaderProgram, 0, "position"); + glLinkProgram(shaderProgram); + return shaderProgram; +} + +void __attribute__((constructor)) start() { + printf("start()\n"); + t_started = current_ts(); +} + +void __attribute((destructor)) end() { + printf("end()\n"); +} + +int main(int argc, char *argv[]) { + Display *dpy = XOpenDisplay(NULL); + XSetWindowAttributes swa; + Window win; + XWindowAttributes gwa; + GLint attr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; + + if (dpy == NULL) { + printf("\n\tcannot connect to X server\n\n"); + exit(0); + } + + Window root = DefaultRootWindow(dpy); + XVisualInfo *vi = glXChooseVisual(dpy, 0, attr); + + if (vi == NULL) { + printf("\n\tno appropriate visual found\n\n"); + exit(0); + } else { + printf("\n\tvisual %p selected\n", (void *)vi->visualid); /* %p creates hexadecimal output like in glxinfo */ + } + + Colormap cmap = XCreateColormap(dpy, root, vi->visual, AllocNone); + swa.colormap = cmap; + swa.event_mask = ExposureMask | KeyPressMask; + win = XCreateWindow(dpy, root, 0, 0, 500, 500, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa); + XMapWindow(dpy, win); + XStoreName(dpy, win, "OPENGL"); + GLXContext ctx = glXCreateContext(dpy, vi, NULL, GL_TRUE); + glXMakeCurrent(dpy, win, ctx); + + // USEFUL DEBUG INFO + //@todo validate ASTC support + /*GLint extn; + GL_CHECK(glGetIntegerv(GL_NUM_EXTENSIONS, &extn)); + printf("%d GL Extensions:\n", extn); + for (GLint i=0; i<extn; ++i) { + printf("\t%s\n", glGetStringi(GL_EXTENSIONS, i)); + }*/ + + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // x,y + static const GLfloat vertices[] = { + -0.5, -0.5, + 0.5, -0.5, + 0.5, 0.5 + }; + + GLuint vbo; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + GLuint vertex = mkShader(GL_VERTEX_SHADER, vertexSrc); + GLuint geometry = mkShader(GL_GEOMETRY_SHADER, geometrySrc); + GLuint fragment = mkShader(GL_FRAGMENT_SHADER, fragmentSrc); + GLuint shaderPrg = linkProgram(vertex, geometry, fragment); + + GLint prgStatus; + glGetProgramiv(shaderPrg, GL_LINK_STATUS, &prgStatus); + printf("shader link status: %s\n", prgStatus == GL_TRUE ? "ok" : "error"); + + GLint position = glGetAttribLocation(shaderPrg, "position"); + glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (void *)0); + glEnableVertexAttribArray(position); + + while (1) { + while (XPending(dpy)) { + XEvent xev; + XNextEvent(dpy, &xev); + if (xev.type == Expose) { + XGetWindowAttributes(dpy, win, &gwa); + glViewport(0, 0, gwa.width, gwa.height); + } else if (xev.type == KeyPress) { + printf("btn:%3d\n", xev.xbutton.button); + if (xev.xbutton.button==24||xev.xbutton.button==9) { + glXMakeCurrent(dpy, None, NULL); + glXDestroyContext(dpy, ctx); + XDestroyWindow(dpy, win); + XCloseDisplay(dpy); + exit(0); + } + } else if (xev.type == ConfigureNotify) { + XConfigureEvent *ce = (XConfigureEvent *)&xev; + glViewport(0, 0, ce->width, ce->height); + } + } + + glClearColor(0.1f, 0.1f, 0.15f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + GLfloat iTime = glGetUniformLocation(shaderPrg, "iTime"); + glUniform1d(iTime, current_ts()-t_started); + //printf("%f\n", fabsf(sinf(current_ts()-t_started))); + + glUseProgram(shaderPrg); + glDrawArrays(GL_POINTS, 0, LEN(vertices)); + + glXSwapBuffers(dpy, win); + usleep(1000*7); // 144hz + } +}