master ohmu/fastfetch-openbsd / src / detection / opengl / opengl_shared.c
ohmu  ·  2026-10-15
  1#include "opengl.h"
  2#include "common/debug.h"
  3#include "common/library.h"
  4#include "common/io.h"
  5
  6#if __has_include(<GL/gl.h>)
  7    #include <GL/gl.h>
  8#elif __APPLE__
  9    #define GL_SILENCE_DEPRECATION 1
 10    #include <OpenGL/gl.h>
 11#else
 12    #define FF_HAVE_NO_GL 1
 13#endif
 14
 15#ifndef FF_HAVE_NO_GL
 16
 17    #ifndef GL_SHADING_LANGUAGE_VERSION // For WGL
 18        #define GL_SHADING_LANGUAGE_VERSION 0x8B8C
 19    #endif
 20
 21void ffOpenGLHandleResult(FFOpenGLResult* result, typeof(&glGetString) ffglGetString) {
 22    ffStrbufAppendS(&result->version, (const char*) ffglGetString(GL_VERSION));
 23    ffStrbufAppendS(&result->renderer, (const char*) ffglGetString(GL_RENDERER));
 24    ffStrbufAppendS(&result->vendor, (const char*) ffglGetString(GL_VENDOR));
 25    ffStrbufAppendS(&result->slv, (const char*) ffglGetString(GL_SHADING_LANGUAGE_VERSION));
 26}
 27
 28    #if defined(FF_HAVE_EGL) || __has_include(<EGL/egl.h>)
 29        #include "common/io.h"
 30
 31        #define EGL_EGL_PROTOTYPES 1
 32        #define EGL_EGLEXT_PROTOTYPES 1
 33        #include <EGL/egl.h>
 34        #include <EGL/eglext.h>
 35
 36typedef struct EGLData {
 37    FF_LIBRARY_SYMBOL(glGetString)
 38    FF_LIBRARY_SYMBOL(eglQueryString)
 39    FF_LIBRARY_SYMBOL(eglInitialize)
 40    FF_LIBRARY_SYMBOL(eglBindAPI)
 41    FF_LIBRARY_SYMBOL(eglGetConfigs)
 42    FF_LIBRARY_SYMBOL(eglCreatePbufferSurface)
 43    FF_LIBRARY_SYMBOL(eglCreateContext)
 44    FF_LIBRARY_SYMBOL(eglMakeCurrent)
 45    FF_LIBRARY_SYMBOL(eglDestroyContext)
 46    FF_LIBRARY_SYMBOL(eglDestroySurface)
 47    FF_LIBRARY_SYMBOL(eglTerminate)
 48
 49    EGLDisplay display;
 50    EGLConfig config;
 51    EGLSurface surface;
 52    EGLContext context;
 53} EGLData;
 54
 55static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data) {
 56    FF_DEBUG("Making EGL context current");
 57    if (data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE) {
 58        FF_DEBUG("eglMakeCurrent() returned EGL_FALSE");
 59        return "eglMakeCurrent returned EGL_FALSE";
 60    }
 61
 62    ffOpenGLHandleResult(result, data->ffglGetString);
 63    ffStrbufSetF(&result->library, "EGL %s", data->ffeglQueryString(data->display, EGL_VERSION));
 64    FF_DEBUG("OpenGL via EGL detected: version='%s', renderer='%s', vendor='%s', slv='%s', library='%s'",
 65        result->version.chars,
 66        result->renderer.chars,
 67        result->vendor.chars,
 68        result->slv.chars,
 69        result->library.chars);
 70    return nullptr;
 71}
 72
 73static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data, bool gles) {
 74    FF_DEBUG("Creating EGL context (preferred API=%s, client version=%d)", gles ? "OpenGL ES" : "OpenGL", gles ? 2 : 1);
 75    data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]) { EGL_CONTEXT_CLIENT_VERSION, gles ? 2 : 1, // Try GLES 2.0+ first
 76                                                                                              EGL_NONE });
 77    if (data->context == EGL_NO_CONTEXT && gles) // Some ANGLE builds support GLES 1.1 only
 78    {
 79        FF_DEBUG("EGL context creation with GLES 2.x failed, retrying with default attributes (GLES 1.1 fallback)");
 80        data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]) { EGL_NONE });
 81    }
 82    if (data->context == EGL_NO_CONTEXT) {
 83        FF_DEBUG("eglCreateContext() returned EGL_NO_CONTEXT");
 84        return "eglCreateContext returned EGL_NO_CONTEXT";
 85    }
 86
 87    FF_DEBUG("EGL context created successfully");
 88
 89    const char* error = eglHandleContext(result, data);
 90    FF_DEBUG("eglHandleContext() returns: %s", error ?: "success");
 91
 92    FF_DEBUG("Releasing current EGL context");
 93    data->ffeglMakeCurrent(data->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 94
 95    FF_DEBUG("Destroying EGL context");
 96    data->ffeglDestroyContext(data->display, data->context);
 97    return error;
 98}
 99
100static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data) {
101    // try use OpenGL API. If failed, use the default API (usually OpenGL ES)
102    bool gles = !data->ffeglBindAPI(EGL_OPENGL_API);
103    FF_DEBUG("eglBindAPI(EGL_OPENGL_API) %s, effective API=%s",
104        gles ? "failed" : "succeeded",
105        gles ? "default (usually OpenGL ES)" : "OpenGL");
106
107    EGLint eglConfigCount;
108    data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount);
109    FF_DEBUG("eglGetConfigs() returned %d config(s)", eglConfigCount);
110
111    if (eglConfigCount == 0) {
112        FF_DEBUG("No EGL config is available");
113        return "eglGetConfigs returned 0 configs";
114    }
115
116    FF_DEBUG("Creating EGL pbuffer surface (%dx%d)", FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT);
117    data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]) { EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH, EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT, EGL_NONE });
118
119    if (data->surface == EGL_NO_SURFACE) {
120        FF_DEBUG("eglCreatePbufferSurface() returned EGL_NO_SURFACE");
121        return "eglCreatePbufferSurface returned EGL_NO_SURFACE";
122    }
123
124    FF_DEBUG("EGL pbuffer surface created successfully");
125
126    const char* error = eglHandleSurface(result, data, gles);
127    FF_DEBUG("eglHandleSurface() returns: %s", error ?: "success");
128
129    FF_DEBUG("Destroying EGL surface");
130    data->ffeglDestroySurface(data->display, data->surface);
131    return error;
132}
133
134static const char* eglHandlePreDisplay(FFOpenGLResult* result, EGLData* data) {
135    EGLint major, minor;
136    if (data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE) {
137        FF_DEBUG("eglInitialize() returned EGL_FALSE");
138        return "eglInitialize returned EGL_FALSE";
139    }
140
141    FF_DEBUG("EGL initialized successfully: %d.%d", major, minor);
142
143    const char* error = eglHandleDisplay(result, data);
144    FF_DEBUG("eglHandleDisplay() returns: %s", error ?: "success");
145
146    FF_DEBUG("Terminating EGL display connection");
147    data->ffeglTerminate(data->display);
148    return error;
149}
150
151const char* ffOpenGLDetectByEGL(FFOpenGLResult* result) {
152    FF_DEBUG("Starting OpenGL detection via EGL");
153    EGLData eglData;
154
155    FF_LIBRARY_LOAD_MESSAGE(egl, "libEGL" FF_LIBRARY_EXTENSION, 1);
156    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(egl, eglGetProcAddress);
157    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglQueryString);
158    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglInitialize);
159    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglBindAPI);
160    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetConfigs);
161    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreatePbufferSurface);
162    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreateContext);
163    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglMakeCurrent);
164    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroyContext);
165    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface);
166    FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate);
167
168    FF_DEBUG("Resolving glGetString via eglGetProcAddress()");
169    eglData.ffglGetString = (typeof(&glGetString)) ffeglGetProcAddress("glGetString");
170    if (!eglData.ffglGetString) {
171        FF_DEBUG("eglGetProcAddress('glGetString') returned nullptr");
172        return "eglGetProcAddress(glGetString) returned nullptr";
173    }
174
175    FF_DEBUG("Loaded EGL library and required symbols");
176
177    FF_SUPPRESS_IO();
178
179    EGLDisplay display = EGL_NO_DISPLAY;
180        #if EGL_VERSION_1_5
181    PFNEGLGETPLATFORMDISPLAYEXTPROC ffeglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC) ffeglGetProcAddress("eglGetPlatformDisplay");
182    if (!ffeglGetPlatformDisplay) {
183        ffeglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC) ffeglGetProcAddress("eglGetPlatformDisplayEXT");
184    }
185    if (ffeglGetPlatformDisplay) {
186        FF_DEBUG("Trying eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA)");
187        display = ffeglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, nullptr /*EGL_DEFAULT_DISPLAY*/, nullptr);
188        FF_DEBUG("eglGetPlatformDisplay() %s", display == EGL_NO_DISPLAY ? "failed" : "succeeded");
189    } else {
190        FF_DEBUG("eglGetPlatformDisplay is unavailable, falling back to eglGetDisplay");
191    }
192
193    if (display == EGL_NO_DISPLAY)
194        #endif
195
196    {
197        FF_LIBRARY_LOAD_SYMBOL_MESSAGE(egl, eglGetDisplay);
198
199        FF_DEBUG("Trying eglGetDisplay(EGL_DEFAULT_DISPLAY)");
200        display = ffeglGetDisplay(EGL_DEFAULT_DISPLAY);
201        if (display == EGL_NO_DISPLAY) {
202            FF_DEBUG("eglGetDisplay() returned EGL_NO_DISPLAY");
203            return "eglGetDisplay returned EGL_NO_DISPLAY";
204        }
205
206        FF_DEBUG("eglGetDisplay() succeeded");
207    }
208    eglData.display = display;
209
210    const char* error = eglHandlePreDisplay(result, &eglData);
211
212    FF_DEBUG("OpenGL detection via EGL returns: %s", error ?: "success");
213
214    return error;
215}
216
217    #endif // FF_HAVE_EGL
218
219#endif // FF_HAVE_NO_GL