#include #include "types.h" #include "sys.h" #include #include #include "gldefs.h" #include "context.h" #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); struct xcontext { Display *dpy; Window win; i32 w; i32 h; i32 stop; f64 last_time; f64 time; f64 dt; }; static GLXFBConfig find_best_fbconfig(Display *dpy, i32 *attribs) { i32 fbcount; GLXFBConfig *fbconfigs = glXChooseFBConfig(dpy, DefaultScreen(dpy), attribs, &fbcount); if (!fbconfigs) die("failed to retrieve a framebuffer configs"); i32 best = 0, best_samples = 0; for (i32 i = 0; i < fbcount; i++) { XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, fbconfigs[i]); if (!vi) continue; i32 buffers, samples; glXGetFBConfigAttrib(dpy, fbconfigs[i], GLX_SAMPLE_BUFFERS, &buffers); glXGetFBConfigAttrib(dpy, fbconfigs[i], GLX_SAMPLES, &samples); if (buffers && samples > best_samples) { best = i; best_samples = samples; } XFree(vi); } GLXFBConfig fbconfig = fbconfigs[best]; XFree(fbconfigs); return fbconfig; } static GLXContext create_gl_context(struct xcontext xctx, GLXFBConfig fbconfig) { const char *extensions = glXQueryExtensionsString(xctx.dpy, DefaultScreen(xctx.dpy)); glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB("glXCreateContextAttribsARB"); if (!strstr(extensions, "GLX_ARB_create_context") || !glXCreateContextAttribsARB) die("glXCreateContextAttribsARB not found"); i32 context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 3, None }; GLXContext ctx = glXCreateContextAttribsARB(xctx.dpy, fbconfig, 0, 1, context_attribs); XFree(fbconfig); if (!ctx) die("failed to create gl 3.3 context "); return ctx; } static f64 get_time(void) { struct timeval tv; gettimeofday(&tv, 0); return (f64)tv.tv_sec + tv.tv_usec / 1000000.0; } #define check_gl_function(f) if (!#f) die("failed to load #f"); static void check_gl_functions(void) { check_gl_function(gl_create_shader); if (!gl_create_shader) die("failed to load glCreateShader"); if (!gl_clear_color) die("failed to load glClearColor"); if (!gl_clear) die("failed to load glClear"); if (!gl_viewport) die("failed to load glViewport"); if (!gl_gen_vertex_arrays) die("failed to load glGenVertexArrays"); if (!gl_delete_vertex_arrays) die("failed to load glDeleteVertexArrays"); if (!gl_bind_vertex_array) die("failed to load glBindVertexArray"); if (!gl_enable_vertex_attrib_array) die("failed to load glEnableVertexAttribArray"); if (!gl_disable_vertex_attrib_array) die("failed to load glDisableVertexAttribArray"); if (!gl_vertex_attrib_pointer) die("failed to load glVertexAttribPointer"); if (!gl_gen_buffers) die("failed to load glGenBuffers"); if (!gl_delete_buffers) die("failed to load glDeleteBuffers"); if (!gl_bind_buffer) die("failed to load glBindBuffer"); if (!gl_buffer_data) die("failed to load glBufferData"); if (!gl_create_shader) die("failed to load glCreateShader"); if (!gl_delete_shader) die("failed to load glDeleteShader"); if (!gl_shader_source) die("failed to load glShaderSource"); if (!gl_compile_shader) die("failed to load glCompileShader"); if (!gl_get_shader_iv) die("failed to load glGetShaderiv"); if (!gl_get_shader_info_log) die("failed to load glGetShaderInfoLog"); if (!gl_create_program) die("failed to load glCreateProgram"); if (!gl_attach_shader) die("failed to load glAttachShader"); if (!gl_link_program) die("failed to load glLinkProgram"); if (!gl_use_program) die("failed to load glUseProgram"); if (!gl_get_uniform_location) die("failed to load glGetUniformLocation"); if (!gl_uniform_matrix4fv) die("failed to load glUniformMatrix4fv"); if (!gl_get_program_iv) die("failed to load glGetProgramiv"); if (!gl_get_program_info_log) die("failed to load glGetProgramInfoLog"); if (!gl_draw_arrays) die("failed to load glDrawArrays"); } void load_gl_functions(void) { gl_clear_color = (void (*)(f32, f32, f32, f32))glXGetProcAddress("glClearColor"); gl_clear = (void (*)(u32))glXGetProcAddress("glClear"); gl_viewport = (void (*)(i32, i32, i32, i32))glXGetProcAddress("glViewport"); gl_gen_vertex_arrays = (void (*)(i32, u32 *))glXGetProcAddress("glGenVertexArrays"); gl_delete_vertex_arrays = (void (*)(i32, const u32 *))glXGetProcAddress("glDeleteVertexArrays"); gl_bind_vertex_array = (void (*)(u32))glXGetProcAddress("glBindVertexArray"); gl_enable_vertex_attrib_array = (void (*)(u32))glXGetProcAddress("glEnableVertexAttribArray"); gl_disable_vertex_attrib_array = (void (*)(u32))glXGetProcAddress("glDisableVertexAttribArray"); gl_vertex_attrib_pointer = (void (*)(u32, i32, u32, u8, i32, const void *))glXGetProcAddress("glVertexAttribPointer"); gl_gen_buffers = (void (*)(i32, u32 *))glXGetProcAddress("glGenBuffers"); gl_delete_buffers = (void (*)(i32, const u32 *))glXGetProcAddress("glDeleteBuffers"); gl_bind_buffer = (void (*)(u32, u32))glXGetProcAddress("glBindBuffer"); gl_buffer_data = (void (*)(u32, i64, const void *, u32))glXGetProcAddress("glBufferData"); gl_create_shader = (u32 (*)(u32))glXGetProcAddress("glCreateShader"); gl_delete_shader = (void (*)(u32))glXGetProcAddress("glDeleteShader"); gl_shader_source = (void (*)(u32, i32, const char *const *, const i32 *))glXGetProcAddress("glShaderSource"); gl_compile_shader = (void (*)(u32))glXGetProcAddress("glCompileShader"); gl_get_shader_iv = (void (*)(u32, u32, i32 *))glXGetProcAddress("glGetShaderiv"); gl_get_shader_info_log = (void (*)(u32, i32, i32 *, char *))glXGetProcAddress("glGetShaderInfoLog"); gl_create_program = (u32 (*)(void))glXGetProcAddress("glCreateProgram"); gl_attach_shader = (void (*)(u32, u32))glXGetProcAddress("glAttachShader"); gl_link_program = (void (*)(u32))glXGetProcAddress("glLinkProgram"); gl_use_program = (void (*)(u32))glXGetProcAddress("glUseProgram"); gl_get_uniform_location = (i32 (*)(u32, const char *))glXGetProcAddress("glGetUniformLocation"); gl_uniform_matrix4fv = (void (*)(i32, i32, u8, const f32 *))glXGetProcAddress("glUniformMatrix4fv"); gl_get_program_iv = (void (*)(u32, u32, i32 *))glXGetProcAddress("glGetProgramiv"); gl_get_program_info_log = (void (*)(u32, i32, i32 *, char *))glXGetProcAddress("glGetProgramInfoLog"); gl_draw_arrays = (void (*)(u32, i32, i32))glXGetProcAddress("glDrawArrays"); check_gl_functions(); } static struct xcontext xinit(struct prge_context prge, const char *name) { struct xcontext xctx = {0}; Display *dpy = XOpenDisplay(0); if (!dpy) die("failed to open x dpy"); xctx.dpy = dpy; i32 major, minor; if (!glXQueryVersion(dpy, &major, &minor)) die("failed to get glx version"); if ((major < 1) || ((major == 1) && (minor < 3))) die("invalid glx version"); i32 attribs[] = { GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, 8, GLX_DOUBLEBUFFER, True, None }; GLXFBConfig fbconfig = find_best_fbconfig(dpy, attribs); XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, fbconfig); XSetWindowAttributes swattribs = {0}; swattribs.colormap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone); swattribs.event_mask = StructureNotifyMask | KeyPressMask; Window root = RootWindow(xctx.dpy, vi->screen); u32 flags = CWBorderPixel | CWColormap | CWEventMask; xctx.w = prge.width; xctx.h = prge.height; xctx.win = XCreateWindow(xctx.dpy, root, 0, 0, xctx.w, xctx.h, 0, vi->depth, InputOutput, vi->visual, flags, &swattribs); if (!xctx.win) die("failed to create window"); XFree(vi); XStoreName(xctx.dpy, xctx.win, name); XMapWindow(xctx.dpy, xctx.win); GLXContext glctx = create_gl_context(xctx, fbconfig); glXMakeCurrent(xctx.dpy, xctx.win, glctx); load_gl_functions(); i32 (*glXGetSwapInterval)(void) = 0; i32 (*glXSwapInterval)(u32) = 0; glXGetSwapInterval = (i32 (*)(void))glXGetProcAddressARB("glXGetSwapIntervalMESA"); glXSwapInterval = (i32 (*)(u32))glXGetProcAddressARB("glXSwapIntervalMESA"); if (glXGetSwapInterval && glXSwapInterval) { i32 swap_interval = glXGetSwapInterval(); info("swap interval %d", swap_interval); i32 swap = glXSwapInterval(0); swap_interval = glXGetSwapInterval(); info("swap %d interval %d", swap, swap_interval); } else { info("cannot get swap interval"); } xctx.last_time = get_time(); return xctx; } void handle_keyboard(struct xcontext *xctx, i32 code) { switch (code) { case XK_Escape: xctx->stop = 1; break; } } void handle_events(struct xcontext *xctx) { while (XPending(xctx->dpy)) { XEvent ev; XNextEvent(xctx->dpy, &ev); switch (ev.type) { case KeyPress: handle_keyboard(xctx, XLookupKeysym(&ev.xkey, 0)); break; case ConfigureNotify: info("%d %d", ev.xconfigure.width, ev.xconfigure.height); gl_viewport(0, 0, ev.xconfigure.width, ev.xconfigure.height); break; } } } static void update_time(struct xcontext *xctx) { f64 current_time = get_time(); xctx->dt = current_time - xctx->last_time; xctx->time += xctx->dt; xctx->last_time = current_time; } i32 main(void) { struct prge_context prge = init_prge(800, 600); struct xcontext xctx = xinit(prge, "loadgl"); f64 ms = 16.666; f64 last = get_time(); f64 dt = 0.0; while (!xctx.stop) { handle_events(&xctx); update_time(&xctx); info("%f", xctx.dt); gl_clear_color(0.16f, 0.16f, 0.16f, 1.0f); gl_clear(gl_color_buffer_bit); glXSwapBuffers(xctx.dpy, xctx.win); } return 0; }