{"id":15052210,"url":"https://github.com/colleagueriley/opengl-context-creation","last_synced_at":"2025-04-10T03:00:25.217Z","repository":{"id":252297242,"uuid":"839992956","full_name":"ColleagueRiley/OpenGL-Context-Creation","owner":"ColleagueRiley","description":"A tutorial that explains how to create an OpenGL context for X11, WinAPI and Cocoa. ","archived":false,"fork":false,"pushed_at":"2024-09-12T15:11:45.000Z","size":92,"stargazers_count":9,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T04:24:19.154Z","etag":null,"topics":["article","cocoa","glx","opengl","rgfw","tutorial","wgl","winapi","x11"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ColleagueRiley.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-08T18:34:18.000Z","updated_at":"2025-03-12T00:43:53.000Z","dependencies_parsed_at":"2024-08-08T22:17:49.979Z","dependency_job_id":"e05d2de9-0c12-488a-b5d0-13c8575acfcd","html_url":"https://github.com/ColleagueRiley/OpenGL-Context-Creation","commit_stats":null,"previous_names":["colleagueriley/opengl-context-creation"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ColleagueRiley%2FOpenGL-Context-Creation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ColleagueRiley%2FOpenGL-Context-Creation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ColleagueRiley%2FOpenGL-Context-Creation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ColleagueRiley%2FOpenGL-Context-Creation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ColleagueRiley","download_url":"https://codeload.github.com/ColleagueRiley/OpenGL-Context-Creation/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248148217,"owners_count":21055547,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["article","cocoa","glx","opengl","rgfw","tutorial","wgl","winapi","x11"],"created_at":"2024-09-24T21:37:15.732Z","updated_at":"2025-04-10T03:00:25.099Z","avatar_url":"https://github.com/ColleagueRiley.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RGFW Under the Hood: OpenGL context creation\n\n## Introduction\nIn my experience working on RGFW, one of the most annoying parts of working with low-level APIs is creating an OpenGL context.\nThis is not because it's hard, but because there are many not-so-obvious steps that you must do correctly to create your OpenGL context. \nThis tutorial explains how to create an OpenGL context for Windows, MacOS and Linux so that way others don't have to struggle with figuring it out. \n\nNOTE: MacOS code will be written with a Cocoa C Wrapper in mind (see the RGFW.h or Silicon.h)\n\n## Overview\nA quick overview of the steps required\n1) Load OpenGL context creation functions (if you need to)\n2) Create an OpenGL pixel format (or Visual on X11) using an attribute list \n3) Create your OpenGL context using an attribute array to set the OpenGL version  \n4) Free the OpenGL context\n\nOn MacOS steps 2 and 3 are one step.\\\nEGL will not be included in this article because the setup is far easier. \n\n## Step 1 (Load OpenGL context creation functions)\n\nFirst RGFW needs to load these functions\n\nX11 (GLX): [`glXCreateContextAttribsARB`](https://registry.khronos.org/OpenGL/extensions/ARB/GLX_ARB_create_context.txt) for creating an accelerated OpenGL context and optionally [`glXSwapIntervalEXT`](https://registry.khronos.org/OpenGL/extensions/EXT/EXT_swap_control.txt) for changing the swap interval.\n\nWinAPI (WGL):  [`wglCreateContextAttribsARB`](https://registry.khronos.org/OpenGL/extensions/ARB/WGL_ARB_create_context.txt) for creating an OpenGL context, for choosing a pixel format [`wglChoosePixelFormatARB`](https://registry.khronos.org/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt) for OpenGL and optionally [`wglSwapIntervalEXT`](https://registry.khronos.org/OpenGL/extensions/EXT/WGL_EXT_swap_control.txt) for changing the swap interval.  \n\nIt needs to load these functions because they're extension functions provided by the hardware vendor. By default, [`wglCreateContext`](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-wglcreatecontext) or [`glXCreateContext`](https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glXCreateContext.xml) will create an OpenGL ~1.0 context that probably uses software rendering. \n\nTo load the extension functions RGFW has to start by defining them.\n\n### X11 (GLX):\n```c\ntypedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);\nstatic glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;\n\t\n//(optional)\ntypedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval);\nPFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL;\n```\n\n### Windows (WGL)\n```c\ntypedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);\nPFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;\n\ntypedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);\nstatic PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;\n\n//\t(optional)\ntypedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);\nstatic PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;\n```\n\nOnce the functions are defined, RGFW loads the functions with these API calls:\n\n```c\n/* X11 (GLX): */ glXGetProcAddress aad glXGetProcAddressARB\n/* Windows (WGL): */ wglGetProcAddress\n```\n\nFor example using GLX,\n\n```c\nglXCreateContextAttribsARB = glXGetProcAddressARB((GLubyte*) \"glXCreateContextAttribsARB\");\n\n// (optional)\nglXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((GLubyte*) \"glXSwapIntervalEXT\");\n```\n\nWGL is a little bit more complicated because it needs to start by creating a dummy context.\\\nThis dummy context is used by WGL to load the functions.\n\n### WGL dummy \n\nFirst, RGFW has to create a dummy window and device context, RGFW also uses this dummy window to get the height offset for the title bar. \n\n```c\nHWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, x, y, w, h, 0, 0, inh, 0);\n\nHDC dummy_dc = GetDC(dummyWin);\n```\n\nAfter that, RGFW creates a dummy pixel format for the dummy window and dummy OpenGL context.\n\n```c\nu32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;\n\nPIXELFORMATDESCRIPTOR pfd = {\n    sizeof(pfd),\n    1, /* version */\n    pfd_flags,\n    PFD_TYPE_RGBA, /* ipixel type */\n    24, /* color bits */\n    0, 0, 0, 0, 0, 0,\n    8, /* alpha bits */\n    0, 0, 0, 0, 0, 0,\n    32, /* depth bits */\n    8, /* stencil bits */ \n    0,\n    PFD_MAIN_PLANE, /* Layer type */\n    0, 0, 0, 0\n};\n\nint pixel_format = ChoosePixelFormat(dummy_dc, \u0026pfd);\nSetPixelFormat(dummy_dc, pixel_format, \u0026pfd);\n```\n\nNow RGFW can create a dummy context and set it to be the current context.\\\nThis context will be using the default OpenGL ~1.0 context, OpenGL.\n\n```c\nHGLRC dummy_context = wglCreateContext(dummy_dc);\nwglMakeCurrent(dummy_dc, dummy_context);\n```\n\nNow RGFW can load the functions and delete the dummy\n\n```c\nwglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress(\"wglCreateContextAttribsARB\");\nwglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress(\"wglChoosePixelFormatARB\");\n\n// (optional)\nwglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress(\"wglSwapIntervalEXT\");\n\nwglMakeCurrent(dummy_dc, 0);\nwglDeleteContext(dummy_context);\nReleaseDC(dummyWin, dummy_dc);\nDestroyWindow(dummyWin);\n```\n\n\n## Step 2 (Create an OpenGL pixel format (or Visual on X11) using an attribute list)\n\nRGFW needs to create a pixel format/visual so the window knows how to draw the data it gets and so OpenGL knows the draw format we want. \n\n### Step 2.1: creating the attribute list\n\nTo create an OpenGL pixel format using an attribute list, RGFW has a function that creates an attribute list, `RGFW_initFormatAttribs` for the pixel format.\n\nThis function uses macros based on the target OS's API, supporting all of the APIs with a single function. \n\nFor this tutorial, I'll be separating an array for each OS rather than using one big array. \n\n```c\nlinux:\n    static u32 attribs[] = {\n\t    GLX_X_VISUAL_TYPE,      GLX_TRUE_COLOR,\n\t    GLX_DEPTH_SIZE,         24,                            \n\t    GLX_X_RENDERABLE,       1,\n\t    GLX_RED_SIZE,           8,\n\t    GLX_GREEN_SIZE,         8,\n\t    GLX_BLUE_SIZE,          8,\n\t    GLX_ALPHA_SIZE,         8,\n\t    GLX_RENDER_TYPE,        GLX_RGBA_BIT,\n\t    GLX_DRAWABLE_TYPE,      GLX_WINDOW_BIT,\n\t\n\t    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n    };\n\nwindows:\n\n    // windows makes you define the macros yourself (or you can use wglext.h (which may or may not be installed on your system)\n\t// https://registry.khronos.org/OpenGL/api/GL/wglext.h\n\t// so I'll be using the hardcoded instead \n    static u32 attribs[] = {\n\t    0x2003, // WGL_ACCELERATION_ARB\n\t    0x2027, // WGL_FULL_ACCELERATION_ARB\n\t    0x201b, 8, // WGL_ALPHA_BITS_ARB\n\t    0x2022, 24, // WGL_DEPTH_BITS_ARB\n\t    0x2001, 1, // WGL_DRAW_TO_WINDOW_ARB\n\t    0x2015, 8, // WGL_RED_BITS_ARB\n\t    0x2017, 8, // WGL_GREEN_BITS_ARB\n\t    0x2019, 8, // WGL_BLUE_BITS_ARB\n\t    0x2013, 0x202B, // WGL_PIXEL_TYPE_ARB,  WGL_TYPE_RGBA_ARB\n\t    0x2010,\t\t1, // WGL_SUPPORT_OPENGL_ARB\n\t    0x2014,\t 32, // WGL_COLOR_BITS_ARB\n\t    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n    };\n\n// MacOS is using C, not objective C so the headers required aren't included\n// That means you'd have to define them yourself (you can find the defines in the MacOS documentation) \nmacos:\n\tstatic u32 attribs[] = {\n\t\t11      , 8, // alpha size\n\t\t24      , 24, // depth size\n\t\t72, // NSOpenGLPFANoRecovery\n\t\t8, 24, // NSOpenGLPFAColorSize\n\t\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n\t};\n```\n\nYou may notice the extra 0s at the bottom of the array, that's for optional arguments. \n\nTo fill in the optional arguments, first RGFW sets the index to the first 0.\n\n```c\nsize_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;\n```\n\nRGFW uses a macro to fill in optional arguments \n\n```c\n#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \\\n\tif (attVal) { \\\n\t\tattribs[index] = attrib;\\\n\t\tattribs[index + 1] = attVal;\\\n\t\tindex += 2;\\\n\t}\n```\n\nRGFW defines variables that can be changed by the user to fill in these arguments. \n\n```c\ni32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;\n```\n\nI'm going to split the arguments up between each platform.\n\n```c\nlinux:\n    if (RGFW_DOUBLE_BUFFER)\n        RGFW_GL_ADD_ATTRIB(GLX_DOUBLEBUFFER, 1);\n    RGFW_GL_ADD_ATTRIB(GLX_STENCIL_SIZE, RGFW_STENCIL);\n    RGFW_GL_ADD_ATTRIB(GLX_STEREO, RGFW_STEREO);\n    RGFW_GL_ADD_ATTRIB(GLX_AUX_BUFFERS, RGFW_AUX_BUFFERS);\n    // samples are handled by GLX later\n\nwindows:\n    if (RGFW_DOUBLE_BUFFER)\n        RGFW_GL_ADD_ATTRIB(0x2011, 1);  // double buffer\n    RGFW_GL_ADD_ATTRIB(0x2023, RGFW_STENCIL);\n    RGFW_GL_ADD_ATTRIB(0x2012, RGFW_STEREO);\n    RGFW_GL_ADD_ATTRIB(0x2024, RGFW_AUX_BUFFERS);\n    RGFW_GL_ADD_ATTRIB(0x2042, RGFW_SAMPLES);\n\nmacOS:\n    if (RGFW_DOUBLE_BUFFER)\n        RGFW_GL_ADD_ATTRIB(5, 1); // double buffer\n    RGFW_GL_ADD_ATTRIB(13, RGFW_STENCIL);\n    RGFW_GL_ADD_ATTRIB(6, RGFW_STEREO);\n    RGFW_GL_ADD_ATTRIB(7, RGFW_AUX_BUFFERS);\n    RGFW_GL_ADD_ATTRIB(55, RGFW_SAMPLES);\n\n    /* this is here because macOS has a specific way to handle using software rendering */\n\n    if (useSoftware) {\n        RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID);\n    } else {\n        attribs[index] = 73;\n        index += 1;\n    }\n```\n\nOn macOS RGFW also sets the version here. \n```c\nattribs[index] = 99;\nattribs[index + 1] = 0x1000;\n\nif (RGFW_majorVersion \u003e= 4 || RGFW_majorVersion \u003e= 3) {\n    attribs[index + 1] = (u32) ((RGFW_majorVersion \u003e= 4) ? 0x4100 : 0x3200);\n}\n```\n\nMake sure the final two arguments are set to 0, this is how OpenGL/WGL/GLX/NSOpenGL knows to stop reading.\n\n```c\nRGFW_GL_ADD_ATTRIB(0, 0);\n```\n\n\n### Step 2.2 creating the pixel format \n\nNow that the list is created, it can be used to create the pixel format.\n\n#### GLX\nGLX Handles this by creating an array of `GLXFBConfig` based on the attributes.\n\n```c\ni32 fbcount;\nGLXFBConfig* fbc = glXChooseFBConfig((Display*) display, DefaultScreen(display), (i32*) attribs, \u0026fbcount);\n\ni32 best_fbc = -1;\n\nif (fbcount == 0) {\n    printf(\"Failed to find any valid GLX visual configs\\n\");\n    return NULL;\n}\n```\n\nThen it uses the generated array to find the closest matching FBConfig object. (This is where RGFW_SAMPLES comes in)\n\n```c\nu32 i;\nfor (i = 0; i \u003c (u32)fbcount; i++) {\n    XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, fbc[I]);\n\tif (vi == NULL)\n        continue;\n                \n    XFree(vi);\n\n    i32 samp_buf, samples;\n    glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLE_BUFFERS, \u0026samp_buf);\n    glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLES, \u0026samples);\n    \n    if ((best_fbc \u003c 0 || samp_buf) \u0026\u0026 (samples == RGFW_SAMPLES || best_fbc == -1)) {\n        best_fbc = i;\n    }\n}\n\nif (best_fbc == -1) {\n    printf(\"Failed to get a valid GLX visual\\n\");\n    return NULL;\n}\n\nGLXFBConfig bestFbc = fbc[best_fbc];\n```\n\nOnce it finds the closest matching object, it gets the X11 visual (or pixel format) from the array and frees the array.\n\n```c\n/* Get a visual */\nXVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, bestFbc);\n\nXFree(fbc);\n```\n\nNow this Visual can be used to create a window and/or colormap.\n\n```c\nXSetWindowAttributes swa;\nColormap cmap;\n\nswa.colormap = cmap = XCreateColormap((Display*) display, DefaultRootWindow(display), vi-\u003evisual, AllocNone);\n\nswa.background_pixmap = None;\nswa.border_pixel = 0;\nswa.event_mask = event_mask;\n\nswa.background_pixel = 0;\n\nWindow window = XCreateWindow((Display*) display, DefaultRootWindow((Display*) display), x, y, w, h,\n\t\t\t\t0, vi-\u003edepth, InputOutput, vi-\u003evisual,\n\t\t\t\tCWColormap | CWBorderPixel | CWBackPixel | CWEventMask, \u0026swa);\n```\n\n#### WGL\n\nRGFW needs some WGL defines for creating the context:\n\n```c\n// Again, these can be found in wglext.h https://registry.khronos.org/OpenGL/api/GL/wglext.h\n#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001\n#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002\n#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091\n#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092\n#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126\n```\n\nFor WGL, RGFW has to first create a win32 pixel format. \n\n```c\nPIXELFORMATDESCRIPTOR pfd2 = (PIXELFORMATDESCRIPTOR){ sizeof(pfd2), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n```\n\nThen RGFW can create a WGL pixel format based on the attribs array.\n\n```c\nint pixel_format2;\nUINT num_formats;\nwglChoosePixelFormatARB(hdc, attribs, 0, 1, \u0026pixel_format2, \u0026num_formats);\nif (!num_formats) {\n    printf(\"Failed to create a pixel format for WGL.\\n\");\n}\n```\n\nNow RGFW can merge the two as one big format and set it as the format for your window.\n\n```c\nDescribePixelFormat(hdc, pixel_format2, sizeof(pfd2), \u0026pfd2);\nif (!SetPixelFormat(hdc, pixel_format2, \u0026pfd2)) {\n    printf(\"Failed to set the WGL pixel format.\\n\");\n}\n```\n\n\n## NSOpenGL\n\nFor MacOS, RGFW just has to create the pixel format and then init a view based on the format for OpenGL.\n\n```c\nvoid* format = NSOpenGLPixelFormat_initWithAttributes(attribs);\n\n/* the pixel format can be passed directly to opengl context creation to create a context \n    this is because the format also includes information about the opengl version (which may be a bad thing) */\nview = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {w, h}}, format);\nobjc_msgSend_void(view, sel_registerName(\"prepareOpenGL\"));\nctx = objc_msgSend_id(view, sel_registerName(\"openGLContext\"))\nobjc_msgSend_void(ctx, sel_registerName(\"makeCurrentContext\"));\n```\n\nIf you're following along for MacOS, you can skip step 3.\n\n\n## Step 3 (Create your OpenGL context using an attribute array to set the OpenGL version)\n\nNOTE: RGFW defines this enum and these variables so the user can control the OpenGL version:\n\n```c\ntypedef u8 RGFW_GL_profile; enum { RGFW_GL_CORE = 0,  RGFW_GL_COMPATIBILITY  };\ni32 RGFW_majorVersion = 0, RGFW_minorVersion = 0;\nb8 RGFW_profile = RGFW_GL_CORE;\n```\n\n### glx \nNow it's time to create the attribute array for the GL context creation and load the OpenGL version you want:\n\n```c\ni32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };\ncontext_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;\nif (RGFW_profile == RGFW_GL_CORE) \n    context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;\nelse \n    context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;\n\nif (RGFW_majorVersion || RGFW_minorVersion) {\n    context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;\n    context_attribs[3] = RGFW_majorVersion;\n    context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;\n    context_attribs[5] = RGFW_minorVersion;\n}\n```\n\nFinally, the context can be created using the context_attribs array:\n\n```c\nctx = glXCreateContextAttribsARB((Display*) display, bestFbc, NULL, True, context_attribs);\nglXMakeCurrent(display, window, ctx); // make the window the current so it can be rendered to in the same thread\n```\n\n### WGL\n\nFirst WGL needs to create an attribs array for setting the OpenGL Version\n\nIt also uses a helper macro called SET_ATTRIB\n\n```c\n#define SET_ATTRIB(a, v) { \\\n    assert(((size_t) index + 1) \u003c sizeof(context_attribs) / sizeof(context_attribs[0])); \\\n    context_attribs[index++] = a; \\\n    context_attribs[index++] = v; \\\n}\n\n/* create opengl/WGL context for the specified version */ \nu32 index = 0;\ni32 context_attribs[40];\n\nif (RGFW_profile == RGFW_GL_CORE) {\n    SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);\n}\nelse {\n    SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);\n}\n\nif (RGFW_majorVersion || RGFW_minorVersion) {\n    SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);\n    SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);\n}\n\nSET_ATTRIB(0, 0);\n```\n\nNow the context can be created:\n\n```c\nctx = (HGLRC)wglCreateContextAttribsARB(hdc, NULL, context_attribs);\nwglMakeCurrent(hdc, ctx); // make the window the current so it can be rendered to in the same thread\n```\n\n\n## Step 4 (Free the OpenGL context)\nNow that RGFW has created its OpenGL context, it has to free the context when it's done using it.\n\nThis is the easy part.\n\n\n```c\n// Linux (GLX):\n    glXDestroyContext((Display*) display, ctx);\n\n// windows (WGL):\n    wglDeleteContext((HGLRC) ctx);\n\n// macOS (NSOpenGL):\n    // I think macOS NSOpenGL stuff is freed automatically when everything else is freed \n```\n\n\n## Full code examples\n\n### X11\n```c\n// compile with gcc x11.c -lX11 -lGL\n\n#include \u003cX11/Xlib.h\u003e\n#include \u003cGL/glx.h\u003e\n\n#include \u003cstdio.h\u003e\n#include \u003cstdint.h\u003e\n\ntypedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);\ntypedef void (*PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval);\n\n#define GL_ADD_ATTRIB(attrib, attVal) \\\n\tif (attVal) { \\\n\t\tattribs[index] = attrib;\\\n\t\tattribs[index + 1] = attVal;\\\n\t\tindex += 2;\\\n\t}\n\ntypedef uint8_t GL_profile; enum  { GL_CORE = 0,  GL_COMPATIBILITY  };\nint32_t majorVersion = 0, minorVersion = 0;\nBool profile = GL_CORE;\n\nint32_t STENCIL = 0, SAMPLES = 0, STEREO = GL_FALSE, AUX_BUFFERS = 0, DOUBLE_BUFFER = 1;\n\nint main(void) {\n    typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval);\n    PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL;\n\n    glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((GLubyte*) \"glXCreateContextAttribsARB\");\n    glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) \"glXSwapIntervalEXT\");\n\n    static uint32_t attribs[] = {\n\t\tGLX_X_VISUAL_TYPE,      GLX_TRUE_COLOR,\n\t\tGLX_DEPTH_SIZE,         24,                            \n\t\tGLX_X_RENDERABLE,       1,\n\t\tGLX_RED_SIZE,           8,\n\t\tGLX_GREEN_SIZE,         8,\n\t\tGLX_BLUE_SIZE,          8,\n\t\tGLX_ALPHA_SIZE,         8,\n\t\tGLX_RENDER_TYPE,        GLX_RGBA_BIT,\n\t\tGLX_DRAWABLE_TYPE,      GLX_WINDOW_BIT,\n\n\t\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n    };\n    \n    size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;\n\n    if (DOUBLE_BUFFER)\n        GL_ADD_ATTRIB(GLX_DOUBLEBUFFER, 1);\n    GL_ADD_ATTRIB(GLX_STENCIL_SIZE, STENCIL);\n    GL_ADD_ATTRIB(GLX_STEREO, STEREO);\n    GL_ADD_ATTRIB(GLX_AUX_BUFFERS, AUX_BUFFERS);\n\n    Display *display;\n    XEvent event;\n \n    display = XOpenDisplay(NULL);\n    if (display == NULL) {\n        fprintf(stderr, \"Cannot open display\\n\");\n        return -1;\n    }\n \n    int s = DefaultScreen(display);\n\n    int32_t fbcount;\n    GLXFBConfig* fbc = glXChooseFBConfig((Display*) display, DefaultScreen(display), (int32_t*) attribs, \u0026fbcount);\n\n    int32_t best_fbc = -1;\n\n    if (fbcount == 0) {\n        printf(\"Failed to find any valid GLX visual configs\\n\");\n        return -1;\n    }\n  \n    uint32_t i;\n    for (i = 0; i \u003c (uint32_t)fbcount; i++) {\n        XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, fbc[i]);\n        if (vi == NULL)\n            continue;\n                    \n        XFree(vi);\n\n        int32_t samp_buf, samples;\n        glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLE_BUFFERS, \u0026samp_buf);\n        glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLES, \u0026samples);\n        \n        if ((best_fbc \u003c 0 || samp_buf) \u0026\u0026 (samples == SAMPLES || best_fbc == -1)) {\n            best_fbc = i;\n        }\n    }\n\n    if (best_fbc == -1) {\n        printf(\"Failed to get a valid GLX visual\\n\");\n        return -1;\n    }\n\n    GLXFBConfig bestFbc = fbc[best_fbc];\n    \n    XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, bestFbc);\n    \n    XFree(fbc);\n\n    XSetWindowAttributes swa;\n    Colormap cmap;\n\n    swa.colormap = cmap = XCreateColormap((Display*) display, DefaultRootWindow(display), vi-\u003evisual, AllocNone);\n\n    swa.background_pixmap = None;\n    swa.border_pixel = 0;\n    swa.event_mask = CWColormap | CWBorderPixel | CWBackPixel | CWEventMask;\n    \n    swa.background_pixel = 0;\n\n    Window window = XCreateWindow((Display*) display, DefaultRootWindow((Display*) display), 400, 400, 200, 200,\n\t\t\t\t\t\t0, vi-\u003edepth, InputOutput, vi-\u003evisual,\n\t\t\t\t\t\tCWColormap | CWBorderPixel | CWBackPixel | CWEventMask, \u0026swa);\n    \n    XSelectInput(display, window, ExposureMask | KeyPressMask);\n    \n    int32_t context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };\n    context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;\n    if (profile == GL_CORE) \n        context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;\n    else \n        context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;\n    \n    if (majorVersion || minorVersion) {\n        context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;\n        context_attribs[3] = majorVersion;\n        context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;\n        context_attribs[5] = minorVersion;\n    }\n    \n    GLXContext ctx = glXCreateContextAttribsARB((Display*) display, bestFbc, NULL, True, context_attribs);\n    glXMakeCurrent(display, window, ctx);\n\n    XMapWindow(display, window);\n    \n    for (;;) {\n        XNextEvent(display, \u0026event);\n\n        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT);\n        \n        glXSwapBuffers(display, window);\n    }\n    \n    glXDestroyContext((Display*) display, ctx);\n    XCloseDisplay(display);\n    \n    return 0;\n }\n```\n\n### Windows\n```c\n\n// compile with gcc winapi.c -lopengl32 -lgdi32\n#include \u003cwindows.h\u003e\n#include \u003cGL/gl.h\u003e\n\n#include \u003cstdio.h\u003e\n#include \u003cstdint.h\u003e\n#include \u003cassert.h\u003e\n\ntypedef uint8_t     u8;\ntypedef int8_t      i8;\ntypedef uint16_t   u16;\ntypedef int16_t    i16;\ntypedef uint32_t   u32;\ntypedef int32_t    i32;\ntypedef uint64_t   u64;\ntypedef int64_t    i64;\ntypedef u8 b8;\n\n#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001\n#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002\n#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091\n#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092\n#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126\n\nint main() {\n\ttypedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);\n\tPFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;\n\t\n    typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);\n\tstatic PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;\n\n    typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);\n    static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;\n\n    WNDCLASS wc = {0};\n    wc.lpfnWndProc   = DefWindowProc; // Default window procedure\n    wc.hInstance     = GetModuleHandle(NULL);\n    wc.lpszClassName = \"SampleWindowClass\";\n\n    RegisterClass(\u0026wc);\n    \n    HWND dummyWin = CreateWindowA(wc.lpszClassName, \"Sample Window\", 0, 200, 200, 300, 300, 0, 0, wc.hInstance, 0);\n    HDC dummy_dc = GetDC(dummyWin);\n\n    u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;\n\n    PIXELFORMATDESCRIPTOR pfd = {\n        sizeof(pfd),\n        1, /* version */\n        pfd_flags,\n        PFD_TYPE_RGBA, /* ipixel type */\n        24, /* color bits */\n        0, 0, 0, 0, 0, 0,\n        8, /* alpha bits */\n        0, 0, 0, 0, 0, 0,\n        32, /* depth bits */\n        8, /* stencil bits */ \n        0,\n        PFD_MAIN_PLANE, /* Layer type */\n        0, 0, 0, 0\n    };\n\n    int pixel_format = ChoosePixelFormat(dummy_dc, \u0026pfd);\n    SetPixelFormat(dummy_dc, pixel_format, \u0026pfd);\n\n    HGLRC dummy_context = wglCreateContext(dummy_dc);\n    wglMakeCurrent(dummy_dc, dummy_context);\n\n    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress(\"wglCreateContextAttribsARB\");\n    wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress(\"wglChoosePixelFormatARB\");\n\n    wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress(\"wglSwapIntervalEXT\");\n\n    wglMakeCurrent(dummy_dc, 0);\n    wglDeleteContext(dummy_context);\n    ReleaseDC(dummyWin, dummy_dc);\n    DestroyWindow(dummyWin);\n\n\n    // windows makes you define the macros yourself, so I'll be using the hardcoded instead \n    static u32 attribs[] = {\n\t\t0x2003, // WGL_ACCELERATION_ARB\n\t\t0x2027, // WGL_FULL_ACCELERATION_ARB\n\t\t0x201b, 8, // WGL_ALPHA_BITS_ARB\n\t\t0x2022, 24, // WGL_DEPTH_BITS_ARB\n\t\t0x2001, 1, // WGL_DRAW_TO_WINDOW_ARB\n\t\t0x2015, 8, // WGL_RED_BITS_ARB\n\t\t0x2017, 8, // WGL_GREEN_BITS_ARB\n\t\t0x2019, 8, // WGL_BLUE_BITS_ARB\n\t\t0x2013, 0x202B, // WGL_PIXEL_TYPE_ARB,  WGL_TYPE_RGBA_ARB\n\t\t0x2010,\t\t1, // WGL_SUPPORT_OPENGL_ARB\n\t\t0x2014,\t 32, // WGL_COLOR_BITS_ARB\n\t\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n\t};\n\n    size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;\n    #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \\\n\t\tif (attVal) { \\\n\t\t\tattribs[index] = attrib;\\\n\t\t\tattribs[index + 1] = attVal;\\\n\t\t\tindex += 2;\\\n\t\t}\n\n    i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;\n\n    if (RGFW_DOUBLE_BUFFER)\n        RGFW_GL_ADD_ATTRIB(0x2011, 1);  // double buffer\n    RGFW_GL_ADD_ATTRIB(0x2023, RGFW_STENCIL);\n    RGFW_GL_ADD_ATTRIB(0x2012, RGFW_STEREO);\n    RGFW_GL_ADD_ATTRIB(0x2024, RGFW_AUX_BUFFERS);\n    RGFW_GL_ADD_ATTRIB(0x2042, RGFW_SAMPLES);\n    RGFW_GL_ADD_ATTRIB(0, 0);\n\n    typedef u8 RGFW_GL_profile; enum { RGFW_GL_CORE = 0,  RGFW_GL_COMPATIBILITY  };\n\ti32 RGFW_majorVersion = 0, RGFW_minorVersion = 0;\n\tb8 RGFW_profile = RGFW_GL_CORE;\n\n    HWND hwnd = CreateWindowA(wc.lpszClassName, \"Sample Window\",\n                                0,\n                                400, 400, 300, 300,\n                                NULL, NULL, wc.hInstance, NULL);\n\n    HDC hdc = GetDC(hwnd);\n\n\n    PIXELFORMATDESCRIPTOR pfd2 = (PIXELFORMATDESCRIPTOR){ sizeof(pfd2), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n    int pixel_format2;\n    UINT num_formats;\n    wglChoosePixelFormatARB(hdc, attribs, 0, 1, \u0026pixel_format2, \u0026num_formats);\n    if (!num_formats) {\n        printf(\"Failed to create a pixel format for WGL.\\n\");\n    }\n\n    DescribePixelFormat(hdc, pixel_format2, sizeof(pfd2), \u0026pfd2);\n    if (!SetPixelFormat(hdc, pixel_format2, \u0026pfd2)) {\n        printf(\"Failed to set the WGL pixel format.\\n\");\n    }\n\n    #define SET_ATTRIB(a, v) { \\\n        assert(((size_t) index + 1) \u003c sizeof(context_attribs) / sizeof(context_attribs[0])); \\\n        context_attribs[index++] = a; \\\n        context_attribs[index++] = v; \\\n    }\n\n    /* create opengl/WGL context for the specified version */ \n    index = 0;\n    i32 context_attribs[40];\n\n    if (RGFW_profile == RGFW_GL_CORE) {\n        SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);\n    }\n    else {\n        SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);\n    }\n\n    if (RGFW_majorVersion || RGFW_minorVersion) {\n        SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);\n        SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);\n    }\n\n    SET_ATTRIB(0, 0);\n\n    HGLRC ctx = (HGLRC)wglCreateContextAttribsARB(hdc, NULL, context_attribs);\n    wglMakeCurrent(hdc, ctx);\n\n    ShowWindow(hwnd, SW_SHOW);\n    UpdateWindow(hwnd);\n\n    MSG msg;\n\n    BOOL running = TRUE;\n    while (running) {\n        if (PeekMessageA(\u0026msg, hwnd, 0u, 0u, PM_REMOVE)) {\n\t\t\tswitch (msg.message) {\n                case WM_CLOSE:\n                case WM_QUIT:\n                    running = FALSE;\n                    break;\n            }\n            TranslateMessage(\u0026msg);\n            DispatchMessage(\u0026msg);\n        }\n\n        running = IsWindow(hwnd);\n    \n\n        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT);\n        \n        SwapBuffers(hdc);\n    }\n    \n    DeleteDC(hdc);\n    DestroyWindow(hwnd);\n    return 0;\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcolleagueriley%2Fopengl-context-creation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcolleagueriley%2Fopengl-context-creation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcolleagueriley%2Fopengl-context-creation/lists"}