├── README.md ├── winapi.c └── x11.c /README.md: -------------------------------------------------------------------------------- 1 | # RGFW Under the Hood: OpenGL context creation 2 | 3 | ## Introduction 4 | In my experience working on RGFW, one of the most annoying parts of working with low-level APIs is creating an OpenGL context. 5 | This 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. 6 | This 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. 7 | 8 | NOTE: MacOS code will be written with a Cocoa C Wrapper in mind (see the RGFW.h or Silicon.h) 9 | 10 | ## Overview 11 | A quick overview of the steps required 12 | 1) Load OpenGL context creation functions (if you need to) 13 | 2) Create an OpenGL pixel format (or Visual on X11) using an attribute list 14 | 3) Create your OpenGL context using an attribute array to set the OpenGL version 15 | 4) Free the OpenGL context 16 | 17 | On MacOS steps 2 and 3 are one step.\ 18 | EGL will not be included in this article because the setup is far easier. 19 | 20 | ## Step 1 (Load OpenGL context creation functions) 21 | 22 | First RGFW needs to load these functions 23 | 24 | X11 (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. 25 | 26 | WinAPI (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. 27 | 28 | It 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. 29 | 30 | To load the extension functions RGFW has to start by defining them. 31 | 32 | ### X11 (GLX): 33 | ```c 34 | typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 35 | static glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; 36 | 37 | //(optional) 38 | typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval); 39 | PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL; 40 | ``` 41 | 42 | ### Windows (WGL) 43 | ```c 44 | typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); 45 | PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; 46 | 47 | typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); 48 | static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; 49 | 50 | // (optional) 51 | typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); 52 | static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; 53 | ``` 54 | 55 | Once the functions are defined, RGFW loads the functions with these API calls: 56 | 57 | ```c 58 | /* X11 (GLX): */ glXGetProcAddress aad glXGetProcAddressARB 59 | /* Windows (WGL): */ wglGetProcAddress 60 | ``` 61 | 62 | For example using GLX, 63 | 64 | ```c 65 | glXCreateContextAttribsARB = glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); 66 | 67 | // (optional) 68 | glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"); 69 | ``` 70 | 71 | WGL is a little bit more complicated because it needs to start by creating a dummy context.\ 72 | This dummy context is used by WGL to load the functions. 73 | 74 | ### WGL dummy 75 | 76 | First, 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. 77 | 78 | ```c 79 | HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, x, y, w, h, 0, 0, inh, 0); 80 | 81 | HDC dummy_dc = GetDC(dummyWin); 82 | ``` 83 | 84 | After that, RGFW creates a dummy pixel format for the dummy window and dummy OpenGL context. 85 | 86 | ```c 87 | u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 88 | 89 | PIXELFORMATDESCRIPTOR pfd = { 90 | sizeof(pfd), 91 | 1, /* version */ 92 | pfd_flags, 93 | PFD_TYPE_RGBA, /* ipixel type */ 94 | 24, /* color bits */ 95 | 0, 0, 0, 0, 0, 0, 96 | 8, /* alpha bits */ 97 | 0, 0, 0, 0, 0, 0, 98 | 32, /* depth bits */ 99 | 8, /* stencil bits */ 100 | 0, 101 | PFD_MAIN_PLANE, /* Layer type */ 102 | 0, 0, 0, 0 103 | }; 104 | 105 | int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); 106 | SetPixelFormat(dummy_dc, pixel_format, &pfd); 107 | ``` 108 | 109 | Now RGFW can create a dummy context and set it to be the current context.\ 110 | This context will be using the default OpenGL ~1.0 context, OpenGL. 111 | 112 | ```c 113 | HGLRC dummy_context = wglCreateContext(dummy_dc); 114 | wglMakeCurrent(dummy_dc, dummy_context); 115 | ``` 116 | 117 | Now RGFW can load the functions and delete the dummy 118 | 119 | ```c 120 | wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB"); 121 | wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB"); 122 | 123 | // (optional) 124 | wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); 125 | 126 | wglMakeCurrent(dummy_dc, 0); 127 | wglDeleteContext(dummy_context); 128 | ReleaseDC(dummyWin, dummy_dc); 129 | DestroyWindow(dummyWin); 130 | ``` 131 | 132 | 133 | ## Step 2 (Create an OpenGL pixel format (or Visual on X11) using an attribute list) 134 | 135 | RGFW 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. 136 | 137 | ### Step 2.1: creating the attribute list 138 | 139 | To create an OpenGL pixel format using an attribute list, RGFW has a function that creates an attribute list, `RGFW_initFormatAttribs` for the pixel format. 140 | 141 | This function uses macros based on the target OS's API, supporting all of the APIs with a single function. 142 | 143 | For this tutorial, I'll be separating an array for each OS rather than using one big array. 144 | 145 | ```c 146 | linux: 147 | static u32 attribs[] = { 148 | GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, 149 | GLX_DEPTH_SIZE, 24, 150 | GLX_X_RENDERABLE, 1, 151 | GLX_RED_SIZE, 8, 152 | GLX_GREEN_SIZE, 8, 153 | GLX_BLUE_SIZE, 8, 154 | GLX_ALPHA_SIZE, 8, 155 | GLX_RENDER_TYPE, GLX_RGBA_BIT, 156 | GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 157 | 158 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 159 | }; 160 | 161 | windows: 162 | 163 | // windows makes you define the macros yourself (or you can use wglext.h (which may or may not be installed on your system) 164 | // https://registry.khronos.org/OpenGL/api/GL/wglext.h 165 | // so I'll be using the hardcoded instead 166 | static u32 attribs[] = { 167 | 0x2003, // WGL_ACCELERATION_ARB 168 | 0x2027, // WGL_FULL_ACCELERATION_ARB 169 | 0x201b, 8, // WGL_ALPHA_BITS_ARB 170 | 0x2022, 24, // WGL_DEPTH_BITS_ARB 171 | 0x2001, 1, // WGL_DRAW_TO_WINDOW_ARB 172 | 0x2015, 8, // WGL_RED_BITS_ARB 173 | 0x2017, 8, // WGL_GREEN_BITS_ARB 174 | 0x2019, 8, // WGL_BLUE_BITS_ARB 175 | 0x2013, 0x202B, // WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB 176 | 0x2010, 1, // WGL_SUPPORT_OPENGL_ARB 177 | 0x2014, 32, // WGL_COLOR_BITS_ARB 178 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 179 | }; 180 | 181 | // MacOS is using C, not objective C so the headers required aren't included 182 | // That means you'd have to define them yourself (you can find the defines in the MacOS documentation) 183 | macos: 184 | static u32 attribs[] = { 185 | 11 , 8, // alpha size 186 | 24 , 24, // depth size 187 | 72, // NSOpenGLPFANoRecovery 188 | 8, 24, // NSOpenGLPFAColorSize 189 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 190 | }; 191 | ``` 192 | 193 | You may notice the extra 0s at the bottom of the array, that's for optional arguments. 194 | 195 | To fill in the optional arguments, first RGFW sets the index to the first 0. 196 | 197 | ```c 198 | size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; 199 | ``` 200 | 201 | RGFW uses a macro to fill in optional arguments 202 | 203 | ```c 204 | #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ 205 | if (attVal) { \ 206 | attribs[index] = attrib;\ 207 | attribs[index + 1] = attVal;\ 208 | index += 2;\ 209 | } 210 | ``` 211 | 212 | RGFW defines variables that can be changed by the user to fill in these arguments. 213 | 214 | ```c 215 | i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; 216 | ``` 217 | 218 | I'm going to split the arguments up between each platform. 219 | 220 | ```c 221 | linux: 222 | if (RGFW_DOUBLE_BUFFER) 223 | RGFW_GL_ADD_ATTRIB(GLX_DOUBLEBUFFER, 1); 224 | RGFW_GL_ADD_ATTRIB(GLX_STENCIL_SIZE, RGFW_STENCIL); 225 | RGFW_GL_ADD_ATTRIB(GLX_STEREO, RGFW_STEREO); 226 | RGFW_GL_ADD_ATTRIB(GLX_AUX_BUFFERS, RGFW_AUX_BUFFERS); 227 | // samples are handled by GLX later 228 | 229 | windows: 230 | if (RGFW_DOUBLE_BUFFER) 231 | RGFW_GL_ADD_ATTRIB(0x2011, 1); // double buffer 232 | RGFW_GL_ADD_ATTRIB(0x2023, RGFW_STENCIL); 233 | RGFW_GL_ADD_ATTRIB(0x2012, RGFW_STEREO); 234 | RGFW_GL_ADD_ATTRIB(0x2024, RGFW_AUX_BUFFERS); 235 | RGFW_GL_ADD_ATTRIB(0x2042, RGFW_SAMPLES); 236 | 237 | macOS: 238 | if (RGFW_DOUBLE_BUFFER) 239 | RGFW_GL_ADD_ATTRIB(5, 1); // double buffer 240 | RGFW_GL_ADD_ATTRIB(13, RGFW_STENCIL); 241 | RGFW_GL_ADD_ATTRIB(6, RGFW_STEREO); 242 | RGFW_GL_ADD_ATTRIB(7, RGFW_AUX_BUFFERS); 243 | RGFW_GL_ADD_ATTRIB(55, RGFW_SAMPLES); 244 | 245 | /* this is here because macOS has a specific way to handle using software rendering */ 246 | 247 | if (useSoftware) { 248 | RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID); 249 | } else { 250 | attribs[index] = 73; 251 | index += 1; 252 | } 253 | ``` 254 | 255 | On macOS RGFW also sets the version here. 256 | ```c 257 | attribs[index] = 99; 258 | attribs[index + 1] = 0x1000; 259 | 260 | if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) { 261 | attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200); 262 | } 263 | ``` 264 | 265 | Make sure the final two arguments are set to 0, this is how OpenGL/WGL/GLX/NSOpenGL knows to stop reading. 266 | 267 | ```c 268 | RGFW_GL_ADD_ATTRIB(0, 0); 269 | ``` 270 | 271 | 272 | ### Step 2.2 creating the pixel format 273 | 274 | Now that the list is created, it can be used to create the pixel format. 275 | 276 | #### GLX 277 | GLX Handles this by creating an array of `GLXFBConfig` based on the attributes. 278 | 279 | ```c 280 | i32 fbcount; 281 | GLXFBConfig* fbc = glXChooseFBConfig((Display*) display, DefaultScreen(display), (i32*) attribs, &fbcount); 282 | 283 | i32 best_fbc = -1; 284 | 285 | if (fbcount == 0) { 286 | printf("Failed to find any valid GLX visual configs\n"); 287 | return NULL; 288 | } 289 | ``` 290 | 291 | Then it uses the generated array to find the closest matching FBConfig object. (This is where RGFW_SAMPLES comes in) 292 | 293 | ```c 294 | u32 i; 295 | for (i = 0; i < (u32)fbcount; i++) { 296 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, fbc[I]); 297 | if (vi == NULL) 298 | continue; 299 | 300 | XFree(vi); 301 | 302 | i32 samp_buf, samples; 303 | glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); 304 | glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLES, &samples); 305 | 306 | if ((best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) { 307 | best_fbc = i; 308 | } 309 | } 310 | 311 | if (best_fbc == -1) { 312 | printf("Failed to get a valid GLX visual\n"); 313 | return NULL; 314 | } 315 | 316 | GLXFBConfig bestFbc = fbc[best_fbc]; 317 | ``` 318 | 319 | Once it finds the closest matching object, it gets the X11 visual (or pixel format) from the array and frees the array. 320 | 321 | ```c 322 | /* Get a visual */ 323 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, bestFbc); 324 | 325 | XFree(fbc); 326 | ``` 327 | 328 | Now this Visual can be used to create a window and/or colormap. 329 | 330 | ```c 331 | XSetWindowAttributes swa; 332 | Colormap cmap; 333 | 334 | swa.colormap = cmap = XCreateColormap((Display*) display, DefaultRootWindow(display), vi->visual, AllocNone); 335 | 336 | swa.background_pixmap = None; 337 | swa.border_pixel = 0; 338 | swa.event_mask = event_mask; 339 | 340 | swa.background_pixel = 0; 341 | 342 | Window window = XCreateWindow((Display*) display, DefaultRootWindow((Display*) display), x, y, w, h, 343 | 0, vi->depth, InputOutput, vi->visual, 344 | CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); 345 | ``` 346 | 347 | #### WGL 348 | 349 | RGFW needs some WGL defines for creating the context: 350 | 351 | ```c 352 | // Again, these can be found in wglext.h https://registry.khronos.org/OpenGL/api/GL/wglext.h 353 | #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 354 | #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 355 | #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 356 | #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 357 | #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 358 | ``` 359 | 360 | For WGL, RGFW has to first create a win32 pixel format. 361 | 362 | ```c 363 | 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}; 364 | ``` 365 | 366 | Then RGFW can create a WGL pixel format based on the attribs array. 367 | 368 | ```c 369 | int pixel_format2; 370 | UINT num_formats; 371 | wglChoosePixelFormatARB(hdc, attribs, 0, 1, &pixel_format2, &num_formats); 372 | if (!num_formats) { 373 | printf("Failed to create a pixel format for WGL.\n"); 374 | } 375 | ``` 376 | 377 | Now RGFW can merge the two as one big format and set it as the format for your window. 378 | 379 | ```c 380 | DescribePixelFormat(hdc, pixel_format2, sizeof(pfd2), &pfd2); 381 | if (!SetPixelFormat(hdc, pixel_format2, &pfd2)) { 382 | printf("Failed to set the WGL pixel format.\n"); 383 | } 384 | ``` 385 | 386 | 387 | ## NSOpenGL 388 | 389 | For MacOS, RGFW just has to create the pixel format and then init a view based on the format for OpenGL. 390 | 391 | ```c 392 | void* format = NSOpenGLPixelFormat_initWithAttributes(attribs); 393 | 394 | /* the pixel format can be passed directly to opengl context creation to create a context 395 | this is because the format also includes information about the opengl version (which may be a bad thing) */ 396 | view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {w, h}}, format); 397 | objc_msgSend_void(view, sel_registerName("prepareOpenGL")); 398 | ctx = objc_msgSend_id(view, sel_registerName("openGLContext")) 399 | objc_msgSend_void(ctx, sel_registerName("makeCurrentContext")); 400 | ``` 401 | 402 | If you're following along for MacOS, you can skip step 3. 403 | 404 | 405 | ## Step 3 (Create your OpenGL context using an attribute array to set the OpenGL version) 406 | 407 | NOTE: RGFW defines this enum and these variables so the user can control the OpenGL version: 408 | 409 | ```c 410 | typedef u8 RGFW_GL_profile; enum { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY }; 411 | i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; 412 | b8 RGFW_profile = RGFW_GL_CORE; 413 | ``` 414 | 415 | ### glx 416 | Now it's time to create the attribute array for the GL context creation and load the OpenGL version you want: 417 | 418 | ```c 419 | i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; 420 | context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; 421 | if (RGFW_profile == RGFW_GL_CORE) 422 | context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; 423 | else 424 | context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 425 | 426 | if (RGFW_majorVersion || RGFW_minorVersion) { 427 | context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; 428 | context_attribs[3] = RGFW_majorVersion; 429 | context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; 430 | context_attribs[5] = RGFW_minorVersion; 431 | } 432 | ``` 433 | 434 | Finally, the context can be created using the context_attribs array: 435 | 436 | ```c 437 | ctx = glXCreateContextAttribsARB((Display*) display, bestFbc, NULL, True, context_attribs); 438 | glXMakeCurrent(display, window, ctx); // make the window the current so it can be rendered to in the same thread 439 | ``` 440 | 441 | ### WGL 442 | 443 | First WGL needs to create an attribs array for setting the OpenGL Version 444 | 445 | It also uses a helper macro called SET_ATTRIB 446 | 447 | ```c 448 | #define SET_ATTRIB(a, v) { \ 449 | assert(((size_t) index + 1) < sizeof(context_attribs) / sizeof(context_attribs[0])); \ 450 | context_attribs[index++] = a; \ 451 | context_attribs[index++] = v; \ 452 | } 453 | 454 | /* create opengl/WGL context for the specified version */ 455 | u32 index = 0; 456 | i32 context_attribs[40]; 457 | 458 | if (RGFW_profile == RGFW_GL_CORE) { 459 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); 460 | } 461 | else { 462 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); 463 | } 464 | 465 | if (RGFW_majorVersion || RGFW_minorVersion) { 466 | SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); 467 | SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); 468 | } 469 | 470 | SET_ATTRIB(0, 0); 471 | ``` 472 | 473 | Now the context can be created: 474 | 475 | ```c 476 | ctx = (HGLRC)wglCreateContextAttribsARB(hdc, NULL, context_attribs); 477 | wglMakeCurrent(hdc, ctx); // make the window the current so it can be rendered to in the same thread 478 | ``` 479 | 480 | 481 | ## Step 4 (Free the OpenGL context) 482 | Now that RGFW has created its OpenGL context, it has to free the context when it's done using it. 483 | 484 | This is the easy part. 485 | 486 | 487 | ```c 488 | // Linux (GLX): 489 | glXDestroyContext((Display*) display, ctx); 490 | 491 | // windows (WGL): 492 | wglDeleteContext((HGLRC) ctx); 493 | 494 | // macOS (NSOpenGL): 495 | // I think macOS NSOpenGL stuff is freed automatically when everything else is freed 496 | ``` 497 | 498 | 499 | ## Full code examples 500 | 501 | ### X11 502 | ```c 503 | // compile with gcc x11.c -lX11 -lGL 504 | 505 | #include 506 | #include 507 | 508 | #include 509 | #include 510 | 511 | typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 512 | typedef void (*PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval); 513 | 514 | #define GL_ADD_ATTRIB(attrib, attVal) \ 515 | if (attVal) { \ 516 | attribs[index] = attrib;\ 517 | attribs[index + 1] = attVal;\ 518 | index += 2;\ 519 | } 520 | 521 | typedef uint8_t GL_profile; enum { GL_CORE = 0, GL_COMPATIBILITY }; 522 | int32_t majorVersion = 0, minorVersion = 0; 523 | Bool profile = GL_CORE; 524 | 525 | int32_t STENCIL = 0, SAMPLES = 0, STEREO = GL_FALSE, AUX_BUFFERS = 0, DOUBLE_BUFFER = 1; 526 | 527 | int main(void) { 528 | typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval); 529 | PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL; 530 | 531 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); 532 | glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"); 533 | 534 | static uint32_t attribs[] = { 535 | GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, 536 | GLX_DEPTH_SIZE, 24, 537 | GLX_X_RENDERABLE, 1, 538 | GLX_RED_SIZE, 8, 539 | GLX_GREEN_SIZE, 8, 540 | GLX_BLUE_SIZE, 8, 541 | GLX_ALPHA_SIZE, 8, 542 | GLX_RENDER_TYPE, GLX_RGBA_BIT, 543 | GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 544 | 545 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 546 | }; 547 | 548 | size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; 549 | 550 | if (DOUBLE_BUFFER) 551 | GL_ADD_ATTRIB(GLX_DOUBLEBUFFER, 1); 552 | GL_ADD_ATTRIB(GLX_STENCIL_SIZE, STENCIL); 553 | GL_ADD_ATTRIB(GLX_STEREO, STEREO); 554 | GL_ADD_ATTRIB(GLX_AUX_BUFFERS, AUX_BUFFERS); 555 | 556 | Display *display; 557 | XEvent event; 558 | 559 | display = XOpenDisplay(NULL); 560 | if (display == NULL) { 561 | fprintf(stderr, "Cannot open display\n"); 562 | return -1; 563 | } 564 | 565 | int s = DefaultScreen(display); 566 | 567 | int32_t fbcount; 568 | GLXFBConfig* fbc = glXChooseFBConfig((Display*) display, DefaultScreen(display), (int32_t*) attribs, &fbcount); 569 | 570 | int32_t best_fbc = -1; 571 | 572 | if (fbcount == 0) { 573 | printf("Failed to find any valid GLX visual configs\n"); 574 | return -1; 575 | } 576 | 577 | uint32_t i; 578 | for (i = 0; i < (uint32_t)fbcount; i++) { 579 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, fbc[i]); 580 | if (vi == NULL) 581 | continue; 582 | 583 | XFree(vi); 584 | 585 | int32_t samp_buf, samples; 586 | glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); 587 | glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLES, &samples); 588 | 589 | if ((best_fbc < 0 || samp_buf) && (samples == SAMPLES || best_fbc == -1)) { 590 | best_fbc = i; 591 | } 592 | } 593 | 594 | if (best_fbc == -1) { 595 | printf("Failed to get a valid GLX visual\n"); 596 | return -1; 597 | } 598 | 599 | GLXFBConfig bestFbc = fbc[best_fbc]; 600 | 601 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, bestFbc); 602 | 603 | XFree(fbc); 604 | 605 | XSetWindowAttributes swa; 606 | Colormap cmap; 607 | 608 | swa.colormap = cmap = XCreateColormap((Display*) display, DefaultRootWindow(display), vi->visual, AllocNone); 609 | 610 | swa.background_pixmap = None; 611 | swa.border_pixel = 0; 612 | swa.event_mask = CWColormap | CWBorderPixel | CWBackPixel | CWEventMask; 613 | 614 | swa.background_pixel = 0; 615 | 616 | Window window = XCreateWindow((Display*) display, DefaultRootWindow((Display*) display), 400, 400, 200, 200, 617 | 0, vi->depth, InputOutput, vi->visual, 618 | CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); 619 | 620 | XSelectInput(display, window, ExposureMask | KeyPressMask); 621 | 622 | int32_t context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; 623 | context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; 624 | if (profile == GL_CORE) 625 | context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; 626 | else 627 | context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 628 | 629 | if (majorVersion || minorVersion) { 630 | context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; 631 | context_attribs[3] = majorVersion; 632 | context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; 633 | context_attribs[5] = minorVersion; 634 | } 635 | 636 | GLXContext ctx = glXCreateContextAttribsARB((Display*) display, bestFbc, NULL, True, context_attribs); 637 | glXMakeCurrent(display, window, ctx); 638 | 639 | XMapWindow(display, window); 640 | 641 | for (;;) { 642 | XNextEvent(display, &event); 643 | 644 | glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 645 | glClear(GL_COLOR_BUFFER_BIT); 646 | 647 | glXSwapBuffers(display, window); 648 | } 649 | 650 | glXDestroyContext((Display*) display, ctx); 651 | XCloseDisplay(display); 652 | 653 | return 0; 654 | } 655 | ``` 656 | 657 | ### Windows 658 | ```c 659 | 660 | // compile with gcc winapi.c -lopengl32 -lgdi32 661 | #include 662 | #include 663 | 664 | #include 665 | #include 666 | #include 667 | 668 | typedef uint8_t u8; 669 | typedef int8_t i8; 670 | typedef uint16_t u16; 671 | typedef int16_t i16; 672 | typedef uint32_t u32; 673 | typedef int32_t i32; 674 | typedef uint64_t u64; 675 | typedef int64_t i64; 676 | typedef u8 b8; 677 | 678 | #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 679 | #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 680 | #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 681 | #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 682 | #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 683 | 684 | int main() { 685 | typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); 686 | PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; 687 | 688 | typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); 689 | static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; 690 | 691 | typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); 692 | static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; 693 | 694 | WNDCLASS wc = {0}; 695 | wc.lpfnWndProc = DefWindowProc; // Default window procedure 696 | wc.hInstance = GetModuleHandle(NULL); 697 | wc.lpszClassName = "SampleWindowClass"; 698 | 699 | RegisterClass(&wc); 700 | 701 | HWND dummyWin = CreateWindowA(wc.lpszClassName, "Sample Window", 0, 200, 200, 300, 300, 0, 0, wc.hInstance, 0); 702 | HDC dummy_dc = GetDC(dummyWin); 703 | 704 | u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 705 | 706 | PIXELFORMATDESCRIPTOR pfd = { 707 | sizeof(pfd), 708 | 1, /* version */ 709 | pfd_flags, 710 | PFD_TYPE_RGBA, /* ipixel type */ 711 | 24, /* color bits */ 712 | 0, 0, 0, 0, 0, 0, 713 | 8, /* alpha bits */ 714 | 0, 0, 0, 0, 0, 0, 715 | 32, /* depth bits */ 716 | 8, /* stencil bits */ 717 | 0, 718 | PFD_MAIN_PLANE, /* Layer type */ 719 | 0, 0, 0, 0 720 | }; 721 | 722 | int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); 723 | SetPixelFormat(dummy_dc, pixel_format, &pfd); 724 | 725 | HGLRC dummy_context = wglCreateContext(dummy_dc); 726 | wglMakeCurrent(dummy_dc, dummy_context); 727 | 728 | wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB"); 729 | wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB"); 730 | 731 | wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); 732 | 733 | wglMakeCurrent(dummy_dc, 0); 734 | wglDeleteContext(dummy_context); 735 | ReleaseDC(dummyWin, dummy_dc); 736 | DestroyWindow(dummyWin); 737 | 738 | 739 | // windows makes you define the macros yourself, so I'll be using the hardcoded instead 740 | static u32 attribs[] = { 741 | 0x2003, // WGL_ACCELERATION_ARB 742 | 0x2027, // WGL_FULL_ACCELERATION_ARB 743 | 0x201b, 8, // WGL_ALPHA_BITS_ARB 744 | 0x2022, 24, // WGL_DEPTH_BITS_ARB 745 | 0x2001, 1, // WGL_DRAW_TO_WINDOW_ARB 746 | 0x2015, 8, // WGL_RED_BITS_ARB 747 | 0x2017, 8, // WGL_GREEN_BITS_ARB 748 | 0x2019, 8, // WGL_BLUE_BITS_ARB 749 | 0x2013, 0x202B, // WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB 750 | 0x2010, 1, // WGL_SUPPORT_OPENGL_ARB 751 | 0x2014, 32, // WGL_COLOR_BITS_ARB 752 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 753 | }; 754 | 755 | size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; 756 | #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ 757 | if (attVal) { \ 758 | attribs[index] = attrib;\ 759 | attribs[index + 1] = attVal;\ 760 | index += 2;\ 761 | } 762 | 763 | i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; 764 | 765 | if (RGFW_DOUBLE_BUFFER) 766 | RGFW_GL_ADD_ATTRIB(0x2011, 1); // double buffer 767 | RGFW_GL_ADD_ATTRIB(0x2023, RGFW_STENCIL); 768 | RGFW_GL_ADD_ATTRIB(0x2012, RGFW_STEREO); 769 | RGFW_GL_ADD_ATTRIB(0x2024, RGFW_AUX_BUFFERS); 770 | RGFW_GL_ADD_ATTRIB(0x2042, RGFW_SAMPLES); 771 | RGFW_GL_ADD_ATTRIB(0, 0); 772 | 773 | typedef u8 RGFW_GL_profile; enum { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY }; 774 | i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; 775 | b8 RGFW_profile = RGFW_GL_CORE; 776 | 777 | HWND hwnd = CreateWindowA(wc.lpszClassName, "Sample Window", 778 | 0, 779 | 400, 400, 300, 300, 780 | NULL, NULL, wc.hInstance, NULL); 781 | 782 | HDC hdc = GetDC(hwnd); 783 | 784 | 785 | 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}; 786 | int pixel_format2; 787 | UINT num_formats; 788 | wglChoosePixelFormatARB(hdc, attribs, 0, 1, &pixel_format2, &num_formats); 789 | if (!num_formats) { 790 | printf("Failed to create a pixel format for WGL.\n"); 791 | } 792 | 793 | DescribePixelFormat(hdc, pixel_format2, sizeof(pfd2), &pfd2); 794 | if (!SetPixelFormat(hdc, pixel_format2, &pfd2)) { 795 | printf("Failed to set the WGL pixel format.\n"); 796 | } 797 | 798 | #define SET_ATTRIB(a, v) { \ 799 | assert(((size_t) index + 1) < sizeof(context_attribs) / sizeof(context_attribs[0])); \ 800 | context_attribs[index++] = a; \ 801 | context_attribs[index++] = v; \ 802 | } 803 | 804 | /* create opengl/WGL context for the specified version */ 805 | index = 0; 806 | i32 context_attribs[40]; 807 | 808 | if (RGFW_profile == RGFW_GL_CORE) { 809 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); 810 | } 811 | else { 812 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); 813 | } 814 | 815 | if (RGFW_majorVersion || RGFW_minorVersion) { 816 | SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); 817 | SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); 818 | } 819 | 820 | SET_ATTRIB(0, 0); 821 | 822 | HGLRC ctx = (HGLRC)wglCreateContextAttribsARB(hdc, NULL, context_attribs); 823 | wglMakeCurrent(hdc, ctx); 824 | 825 | ShowWindow(hwnd, SW_SHOW); 826 | UpdateWindow(hwnd); 827 | 828 | MSG msg; 829 | 830 | BOOL running = TRUE; 831 | while (running) { 832 | if (PeekMessageA(&msg, hwnd, 0u, 0u, PM_REMOVE)) { 833 | switch (msg.message) { 834 | case WM_CLOSE: 835 | case WM_QUIT: 836 | running = FALSE; 837 | break; 838 | } 839 | TranslateMessage(&msg); 840 | DispatchMessage(&msg); 841 | } 842 | 843 | running = IsWindow(hwnd); 844 | 845 | 846 | glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 847 | glClear(GL_COLOR_BUFFER_BIT); 848 | 849 | SwapBuffers(hdc); 850 | } 851 | 852 | DeleteDC(hdc); 853 | DestroyWindow(hwnd); 854 | return 0; 855 | } 856 | ``` 857 | -------------------------------------------------------------------------------- /winapi.c: -------------------------------------------------------------------------------- 1 | // compile with gcc winapi.c -lopengl32 -lgdi32 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | typedef uint8_t u8; 11 | typedef int8_t i8; 12 | typedef uint16_t u16; 13 | typedef int16_t i16; 14 | typedef uint32_t u32; 15 | typedef int32_t i32; 16 | typedef uint64_t u64; 17 | typedef int64_t i64; 18 | typedef u8 b8; 19 | 20 | #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 21 | #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 22 | #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 23 | #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 24 | #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 25 | 26 | int main() { 27 | typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); 28 | PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; 29 | 30 | typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); 31 | static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; 32 | 33 | typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); 34 | static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; 35 | 36 | WNDCLASS wc = {0}; 37 | wc.lpfnWndProc = DefWindowProc; // Default window procedure 38 | wc.hInstance = GetModuleHandle(NULL); 39 | wc.lpszClassName = "SampleWindowClass"; 40 | 41 | RegisterClass(&wc); 42 | 43 | HWND dummyWin = CreateWindowA(wc.lpszClassName, "Sample Window", 0, 200, 200, 300, 300, 0, 0, wc.hInstance, 0); 44 | HDC dummy_dc = GetDC(dummyWin); 45 | 46 | u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 47 | 48 | PIXELFORMATDESCRIPTOR pfd = { 49 | sizeof(pfd), 50 | 1, /* version */ 51 | pfd_flags, 52 | PFD_TYPE_RGBA, /* ipixel type */ 53 | 24, /* color bits */ 54 | 0, 0, 0, 0, 0, 0, 55 | 8, /* alpha bits */ 56 | 0, 0, 0, 0, 0, 0, 57 | 32, /* depth bits */ 58 | 8, /* stencil bits */ 59 | 0, 60 | PFD_MAIN_PLANE, /* Layer type */ 61 | 0, 0, 0, 0 62 | }; 63 | 64 | int pixel_format = ChoosePixelFormat(dummy_dc, &pfd); 65 | SetPixelFormat(dummy_dc, pixel_format, &pfd); 66 | 67 | HGLRC dummy_context = wglCreateContext(dummy_dc); 68 | wglMakeCurrent(dummy_dc, dummy_context); 69 | 70 | wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB"); 71 | wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB"); 72 | 73 | wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); 74 | 75 | wglMakeCurrent(dummy_dc, 0); 76 | wglDeleteContext(dummy_context); 77 | ReleaseDC(dummyWin, dummy_dc); 78 | DestroyWindow(dummyWin); 79 | 80 | 81 | // windows makes you define the macros yourself, so I'll be using the hardcoded instead 82 | static u32 attribs[] = { 83 | 0x2003, // WGL_ACCELERATION_ARB 84 | 0x2027, // WGL_FULL_ACCELERATION_ARB 85 | 0x201b, 8, // WGL_ALPHA_BITS_ARB 86 | 0x2022, 24, // WGL_DEPTH_BITS_ARB 87 | 0x2001, 1, // WGL_DRAW_TO_WINDOW_ARB 88 | 0x2015, 8, // WGL_RED_BITS_ARB 89 | 0x2017, 8, // WGL_GREEN_BITS_ARB 90 | 0x2019, 8, // WGL_BLUE_BITS_ARB 91 | 0x2013, 0x202B, // WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB 92 | 0x2010, 1, // WGL_SUPPORT_OPENGL_ARB 93 | 0x2014, 32, // WGL_COLOR_BITS_ARB 94 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 95 | }; 96 | 97 | size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; 98 | #define RGFW_GL_ADD_ATTRIB(attrib, attVal) \ 99 | if (attVal) { \ 100 | attribs[index] = attrib;\ 101 | attribs[index + 1] = attVal;\ 102 | index += 2;\ 103 | } 104 | 105 | i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = GL_FALSE, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1; 106 | 107 | if (RGFW_DOUBLE_BUFFER) 108 | RGFW_GL_ADD_ATTRIB(0x2011, 1); // double buffer 109 | RGFW_GL_ADD_ATTRIB(0x2023, RGFW_STENCIL); 110 | RGFW_GL_ADD_ATTRIB(0x2012, RGFW_STEREO); 111 | RGFW_GL_ADD_ATTRIB(0x2024, RGFW_AUX_BUFFERS); 112 | RGFW_GL_ADD_ATTRIB(0x2042, RGFW_SAMPLES); 113 | RGFW_GL_ADD_ATTRIB(0, 0); 114 | 115 | typedef u8 RGFW_GL_profile; enum { RGFW_GL_CORE = 0, RGFW_GL_COMPATIBILITY }; 116 | i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0; 117 | b8 RGFW_profile = RGFW_GL_CORE; 118 | 119 | HWND hwnd = CreateWindowA(wc.lpszClassName, "Sample Window", 120 | 0, 121 | 400, 400, 300, 300, 122 | NULL, NULL, wc.hInstance, NULL); 123 | 124 | HDC hdc = GetDC(hwnd); 125 | 126 | 127 | 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}; 128 | int pixel_format2; 129 | UINT num_formats; 130 | wglChoosePixelFormatARB(hdc, attribs, 0, 1, &pixel_format2, &num_formats); 131 | if (!num_formats) { 132 | printf("Failed to create a pixel format for WGL.\n"); 133 | } 134 | 135 | DescribePixelFormat(hdc, pixel_format2, sizeof(pfd2), &pfd2); 136 | if (!SetPixelFormat(hdc, pixel_format2, &pfd2)) { 137 | printf("Failed to set the WGL pixel format.\n"); 138 | } 139 | 140 | #define SET_ATTRIB(a, v) { \ 141 | assert(((size_t) index + 1) < sizeof(context_attribs) / sizeof(context_attribs[0])); \ 142 | context_attribs[index++] = a; \ 143 | context_attribs[index++] = v; \ 144 | } 145 | 146 | /* create opengl/WGL context for the specified version */ 147 | index = 0; 148 | i32 context_attribs[40]; 149 | 150 | if (RGFW_profile == RGFW_GL_CORE) { 151 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB); 152 | } 153 | else { 154 | SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); 155 | } 156 | 157 | if (RGFW_majorVersion || RGFW_minorVersion) { 158 | SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion); 159 | SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion); 160 | } 161 | 162 | SET_ATTRIB(0, 0); 163 | 164 | HGLRC ctx = (HGLRC)wglCreateContextAttribsARB(hdc, NULL, context_attribs); 165 | wglMakeCurrent(hdc, ctx); 166 | 167 | ShowWindow(hwnd, SW_SHOW); 168 | UpdateWindow(hwnd); 169 | 170 | MSG msg; 171 | 172 | BOOL running = TRUE; 173 | while (running) { 174 | if (PeekMessageA(&msg, hwnd, 0u, 0u, PM_REMOVE)) { 175 | switch (msg.message) { 176 | case WM_CLOSE: 177 | case WM_QUIT: 178 | running = FALSE; 179 | break; 180 | } 181 | TranslateMessage(&msg); 182 | DispatchMessage(&msg); 183 | } 184 | 185 | running = IsWindow(hwnd); 186 | 187 | 188 | glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 189 | glClear(GL_COLOR_BUFFER_BIT); 190 | 191 | SwapBuffers(hdc); 192 | } 193 | 194 | DeleteDC(hdc); 195 | DestroyWindow(hwnd); 196 | return 0; 197 | } 198 | -------------------------------------------------------------------------------- /x11.c: -------------------------------------------------------------------------------- 1 | // compile with gcc x11.c -lX11 -lGL 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 10 | typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval); 11 | 12 | #define GL_ADD_ATTRIB(attrib, attVal) \ 13 | if (attVal) { \ 14 | attribs[index] = attrib;\ 15 | attribs[index + 1] = attVal;\ 16 | index += 2;\ 17 | } 18 | 19 | typedef uint8_t GL_profile; enum { GL_CORE = 0, GL_COMPATIBILITY }; 20 | int32_t majorVersion = 0, minorVersion = 0; 21 | Bool profile = GL_CORE; 22 | 23 | int32_t STENCIL = 0, SAMPLES = 0, STEREO = GL_FALSE, AUX_BUFFERS = 0, DOUBLE_BUFFER = 1; 24 | 25 | int main(void) { 26 | typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval); 27 | PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = NULL; 28 | 29 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); 30 | glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"); 31 | 32 | if (glXCreateContextAttribsARB == NULL) 33 | return -1; 34 | 35 | static uint32_t attribs[] = { 36 | GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, 37 | GLX_DEPTH_SIZE, 24, 38 | GLX_X_RENDERABLE, 1, 39 | GLX_RED_SIZE, 8, 40 | GLX_GREEN_SIZE, 8, 41 | GLX_BLUE_SIZE, 8, 42 | GLX_ALPHA_SIZE, 8, 43 | GLX_RENDER_TYPE, GLX_RGBA_BIT, 44 | GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 45 | 46 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 47 | }; 48 | 49 | size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13; 50 | 51 | if (DOUBLE_BUFFER) 52 | GL_ADD_ATTRIB(GLX_DOUBLEBUFFER, 1); 53 | GL_ADD_ATTRIB(GLX_STENCIL_SIZE, STENCIL); 54 | GL_ADD_ATTRIB(GLX_STEREO, STEREO); 55 | GL_ADD_ATTRIB(GLX_AUX_BUFFERS, AUX_BUFFERS); 56 | 57 | Display *display; 58 | XEvent event; 59 | 60 | display = XOpenDisplay(NULL); 61 | if (display == NULL) { 62 | fprintf(stderr, "Cannot open display\n"); 63 | return -1; 64 | } 65 | 66 | int s = DefaultScreen(display); 67 | 68 | int32_t fbcount; 69 | GLXFBConfig* fbc = glXChooseFBConfig((Display*) display, DefaultScreen(display), (int32_t*) attribs, &fbcount); 70 | 71 | int32_t best_fbc = -1; 72 | 73 | if (fbcount == 0) { 74 | printf("Failed to find any valid GLX visual configs\n"); 75 | return -1; 76 | } 77 | 78 | uint32_t i; 79 | for (i = 0; i < (uint32_t)fbcount; i++) { 80 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, fbc[i]); 81 | if (vi == NULL) 82 | continue; 83 | 84 | XFree(vi); 85 | 86 | int32_t samp_buf, samples; 87 | glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); 88 | glXGetFBConfigAttrib((Display*) display, fbc[i], GLX_SAMPLES, &samples); 89 | 90 | if ((best_fbc < 0 || samp_buf) && (samples == SAMPLES || best_fbc == -1)) { 91 | best_fbc = i; 92 | } 93 | } 94 | 95 | if (best_fbc == -1) { 96 | printf("Failed to get a valid GLX visual\n"); 97 | return -1; 98 | } 99 | 100 | GLXFBConfig bestFbc = fbc[best_fbc]; 101 | 102 | XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) display, bestFbc); 103 | 104 | XFree(fbc); 105 | 106 | XSetWindowAttributes swa; 107 | Colormap cmap; 108 | 109 | swa.colormap = cmap = XCreateColormap((Display*) display, 110 | DefaultRootWindow(display), 111 | vi->visual, AllocNone); 112 | 113 | swa.background_pixmap = None; 114 | swa.border_pixel = 0; 115 | swa.event_mask = CWColormap | CWBorderPixel | CWBackPixel | CWEventMask; 116 | 117 | swa.background_pixel = 0; 118 | 119 | Window window = XCreateWindow((Display*) display, DefaultRootWindow((Display*) display), 400, 400, 200, 200, 120 | 0, vi->depth, InputOutput, vi->visual, 121 | CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); 122 | 123 | XSelectInput(display, window, ExposureMask | KeyPressMask); 124 | 125 | int32_t context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; 126 | context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; 127 | if (profile == GL_CORE) 128 | context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; 129 | else 130 | context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 131 | 132 | if (majorVersion || minorVersion) { 133 | context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; 134 | context_attribs[3] = majorVersion; 135 | context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; 136 | context_attribs[5] = minorVersion; 137 | } 138 | 139 | GLXContext ctx = glXCreateContextAttribsARB((Display*) display, bestFbc, NULL, True, context_attribs); 140 | glXMakeCurrent(display, window, ctx); 141 | 142 | XMapWindow(display, window); 143 | 144 | for (;;) { 145 | XNextEvent(display, &event); 146 | 147 | glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 148 | glClear(GL_COLOR_BUFFER_BIT); 149 | 150 | glXSwapBuffers(display, window); 151 | } 152 | 153 | glXDestroyContext((Display*) display, ctx); 154 | XCloseDisplay(display); 155 | 156 | return 0; 157 | } 158 | --------------------------------------------------------------------------------