master ohmu/fastfetch-openbsd / src / detection / opengl / opengl_linux.c
ohmu  ·  2026-10-15
  1#include "fastfetch.h"
  2#include "opengl.h"
  3#include "common/debug.h"
  4#include "common/io.h"
  5
  6#include <string.h>
  7
  8#if __ANDROID__ && !defined(FF_HAVE_EGL)
  9    // On Android the EGL headers ship with the NDK, but the desktop GL ones only come from mesa-dev,
 10    // which a plain NDK build does not have. The block below includes <GL/gl.h>, so that is the
 11    // header to probe: probing <EGL/egl.h> turns this on for every NDK build and then fails to
 12    // compile. Termux installs mesa-dev, so it still gets the EGL path.
 13    #if __has_include(<EGL/egl.h>) && __has_include(<GL/gl.h>)
 14        #define FF_HAVE_EGL 1
 15    #endif
 16#endif
 17
 18#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX)
 19    #define FF_HAVE_GL 1
 20
 21    #include "common/library.h"
 22
 23    #include <GL/gl.h>
 24
 25void ffOpenGLHandleResult(FFOpenGLResult* result, typeof(&glGetString) ffglGetString);
 26
 27#endif // FF_HAVE_GL
 28
 29#ifdef FF_HAVE_GLX
 30    #include <GL/glx.h>
 31
 32typedef struct GLXData {
 33    FF_LIBRARY_SYMBOL(glGetString)
 34    FF_LIBRARY_SYMBOL(glXGetProcAddress)
 35    FF_LIBRARY_SYMBOL(glXQueryVersion)
 36    FF_LIBRARY_SYMBOL(XOpenDisplay)
 37    FF_LIBRARY_SYMBOL(glXChooseVisual)
 38    FF_LIBRARY_SYMBOL(XCreatePixmap)
 39    FF_LIBRARY_SYMBOL(glXCreateGLXPixmap)
 40    FF_LIBRARY_SYMBOL(glXCreateContext)
 41    FF_LIBRARY_SYMBOL(glXMakeCurrent)
 42    FF_LIBRARY_SYMBOL(glXDestroyContext)
 43    FF_LIBRARY_SYMBOL(glXDestroyGLXPixmap)
 44    FF_LIBRARY_SYMBOL(XFreePixmap)
 45    FF_LIBRARY_SYMBOL(XCloseDisplay)
 46    FF_LIBRARY_SYMBOL(XFree)
 47
 48    Display* display;
 49    XVisualInfo* visualInfo;
 50    Pixmap pixmap;
 51    GLXPixmap glxPixmap;
 52    GLXContext context;
 53} GLXData;
 54
 55static const char* glxHandleContext(FFOpenGLResult* result, GLXData* data) {
 56    if (data->ffglXMakeCurrent(data->display, data->glxPixmap, data->context) != True) {
 57        FF_DEBUG("glXMakeCurrent() returned False");
 58        return "glXMakeCurrent returned False";
 59    }
 60    ffOpenGLHandleResult(result, data->ffglGetString);
 61
 62    int major, minor;
 63    if (data->ffglXQueryVersion(data->display, &major, &minor)) {
 64        ffStrbufSetF(&result->library, "GLX %d.%d", major, minor);
 65    } else {
 66        ffStrbufSetStatic(&result->library, "GLX");
 67    }
 68
 69    return nullptr;
 70}
 71
 72static const char* glxHandleGLXPixmap(FFOpenGLResult* result, GLXData* data) {
 73    data->context = data->ffglXCreateContext(data->display, data->visualInfo, nullptr, True);
 74    if (data->context == nullptr) {
 75        FF_DEBUG("glXCreateContext() returned nullptr");
 76        return "glXCreateContext returned nullptr";
 77    }
 78
 79    const char* error = glxHandleContext(result, data);
 80    data->ffglXDestroyContext(data->display, data->context);
 81    return error;
 82}
 83
 84static const char* glxHandlePixmap(FFOpenGLResult* result, GLXData* data) {
 85    data->glxPixmap = data->ffglXCreateGLXPixmap(data->display, data->visualInfo, data->pixmap);
 86    if (data->glxPixmap == None) {
 87        FF_DEBUG("glXCreateGLXPixmap() returned None");
 88        return "glXCreateGLXPixmap returned None";
 89    }
 90
 91    const char* error = glxHandleGLXPixmap(result, data);
 92    data->ffglXDestroyGLXPixmap(data->display, data->glxPixmap);
 93    return error;
 94}
 95
 96static const char* glxHandleVisualInfo(FFOpenGLResult* result, GLXData* data) {
 97    data->pixmap = data->ffXCreatePixmap(data->display, DefaultRootWindow(data->display), FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT, (unsigned int) data->visualInfo->depth);
 98    if (data->pixmap == None) {
 99        FF_DEBUG("XCreatePixmap() returned None");
100        return "XCreatePixmap returned None";
101    }
102
103    const char* error = glxHandlePixmap(result, data);
104    data->ffXFreePixmap(data->display, data->pixmap);
105    return error;
106}
107
108static const char* glxHandleDisplay(FFOpenGLResult* result, GLXData* data) {
109    data->visualInfo = data->ffglXChooseVisual(data->display, DefaultScreen(data->display), (int[]) { None });
110    if (data->visualInfo == nullptr) {
111        FF_DEBUG("glXChooseVisual() returned nullptr");
112        return "glXChooseVisual returned nullptr";
113    }
114
115    const char* error = glxHandleVisualInfo(result, data);
116    data->ffXFree(data->visualInfo);
117    return error;
118}
119
120static const char* glxHandleData(FFOpenGLResult* result, GLXData* data) {
121    data->ffglGetString = (typeof(data->ffglGetString)) data->ffglXGetProcAddress((const GLubyte*) "glGetString");
122    if (data->ffglGetString == nullptr) {
123        FF_DEBUG("glXGetProcAddress(glGetString) returned nullptr");
124        return "glXGetProcAddress(glGetString) returned nullptr";
125    }
126
127    data->display = data->ffXOpenDisplay(nullptr);
128    if (data->display == nullptr) {
129        FF_DEBUG("XOpenDisplay() returned nullptr");
130        return "XOpenDisplay returned nullptr";
131    }
132
133    const char* error = glxHandleDisplay(result, data);
134    data->ffXCloseDisplay(data->display);
135    return error;
136}
137
138static const char* detectByGlx(FFOpenGLResult* result) {
139    GLXData data;
140
141    FF_LIBRARY_LOAD_MESSAGE(glx,
142    #if !__OpenBSD__ && !__NetBSD__
143        "libGLX"
144    #else
145        "libGL"
146    #endif
147        FF_LIBRARY_EXTENSION,
148        1);
149    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXGetProcAddress);
150    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXQueryVersion);
151    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XOpenDisplay);
152    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXChooseVisual);
153    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCreatePixmap);
154    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateGLXPixmap);
155    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateContext);
156    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXMakeCurrent);
157    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyContext);
158    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyGLXPixmap);
159    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFreePixmap);
160    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCloseDisplay);
161    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFree);
162
163    FF_SUPPRESS_IO();
164
165    return glxHandleData(result, &data);
166}
167
168#endif // FF_HAVE_GLX
169
170const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) {
171#if FF_HAVE_GL
172
173    if (options->library == FF_OPENGL_LIBRARY_GLX) {
174    #ifdef FF_HAVE_GLX
175        return detectByGlx(result);
176    #else
177        return "fastfetch was compiled without glx support";
178    #endif
179    }
180
181    if (options->library == FF_OPENGL_LIBRARY_EGL) {
182    #ifdef FF_HAVE_EGL
183        const char* ffOpenGLDetectByEGL(FFOpenGLResult * result);
184        return ffOpenGLDetectByEGL(result);
185    #else
186        return "fastfetch was compiled without egl support";
187    #endif
188    }
189
190    const char* error = ""; // not nullptr dummy value
191
192    #ifdef FF_HAVE_EGL
193    const char* ffOpenGLDetectByEGL(FFOpenGLResult * result);
194    error = ffOpenGLDetectByEGL(result);
195    #endif
196
197    #ifdef FF_HAVE_GLX
198    if (error != nullptr) {
199        error = detectByGlx(result);
200    }
201    #endif
202
203    return error;
204
205#else
206
207    FF_UNUSED(options, result);
208    return "Fastfetch was built without gl support.";
209
210#endif // FF_HAVE_GL
211}