apoorvaj.io
Creating a modern OpenGL context
25 September 2017

Creating a modern OpenGL context is an exercise in frustration if you don’t know where to look. This is a minimal code snippet that may help those in need.

Currently, this is Linux-only. If you have comments, suggestions, corrections or questions, feel free to get in touch with me on Twitter or email (links in the footer).

/* gcc this.c -lGL -lX11 */

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/glx.h>
#include <X11/Xlib.h>

/*
    License: Public domain

    Contents
    --------
    - Create_display_and_window
    - Create_the_modern_OpenGL_context
    - Show_the_window
    - Application_loop
*/

typedef GLXContext (*glXCreateContextAttribsARBProc)
    (Display*, GLXFBConfig, GLXContext, Bool, const int*);

int main()
{
    Display* disp = 0;
    Window win = 0;

    /* Create_display_and_window
       -------------------------
       Skip if you already have a display and window */
    disp = XOpenDisplay(0);
    win = XCreateSimpleWindow(disp, DefaultRootWindow(disp),
                              10, 10,   /* x, y */
                              800, 600, /* width, height */
                              0, 0,     /* border_width, border */
                              0);       /* background */

    /* Create_the_modern_OpenGL_context
       -------------------------------- */
    static int visual_attribs[] = {
        GLX_RENDER_TYPE, GLX_RGBA_BIT,
        GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
        GLX_DOUBLEBUFFER, true,
        GLX_RED_SIZE, 1,
        GLX_GREEN_SIZE, 1,
        GLX_BLUE_SIZE, 1,
        None
    };

    int num_fbc = 0;
    GLXFBConfig *fbc = glXChooseFBConfig(disp,
                                         DefaultScreen(disp),
                                         visual_attribs, &num_fbc);
    if (!fbc) {
        printf("glXChooseFBConfig() failed\n");
        exit(1);
    }

    /* If we were on Windows (i.e. WGL), we would need to create an old
       dummy OpenGL context here, before calling GetProcAddress(). This is
       unnecessary on Linux (GLX).

       For details, refer to the spec
       (https://www.khronos.org/registry/OpenGL/extensions/ARB/GLX_ARB_get_proc_address.txt)
       which says:
           > Are function pointers context-independent? Yes. The pointer to an
           > extension function can be used with any context [...]

       This is in direct contrast to WGL's wglGetProcAddress. */

    glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
    glXCreateContextAttribsARB =
        (glXCreateContextAttribsARBProc)
        glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");

    /* If we were on Winows, we would destroy the dummy context here. Again,
       this is unnecessary on Linux.*/

    if (!glXCreateContextAttribsARB) {
        printf("glXCreateContextAttribsARB() not found\n");
        exit(1);
    }

    /* Set desired minimum OpenGL version */
    static int context_attribs[] = {
        GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
        GLX_CONTEXT_MINOR_VERSION_ARB, 2,
        None
    };
    /* Create modern OpenGL context */
    GLXContext ctx = glXCreateContextAttribsARB(disp, fbc[0], NULL, true,
                                                context_attribs);
    if (!ctx) {
        printf("Failed to create OpenGL context. Exiting.\n");
        exit(1);
    }

    /* Show_the_window
       --------------- */
    XMapWindow(disp, win);
    glXMakeCurrent(disp, win, ctx);

    int major = 0, minor = 0;
    glGetIntegerv(GL_MAJOR_VERSION, &major);
    glGetIntegerv(GL_MINOR_VERSION, &minor);
    printf("OpenGL context created.\nVersion %d.%d\nVendor %s\nRenderer %s\n",
           major, minor,
           glGetString(GL_VENDOR),
           glGetString(GL_RENDERER));

    /* Application_loop
       ---------------- */
    while(1) {}

    return 0;
}

Acknowledgements

Thanks to Julian Kirsch for pointing out that GLX—unlike WGL—doesn’t need a dummy context. This makes the code a tad simpler.