summaryrefslogtreecommitdiffstats
path: root/src/detection/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/opengl')
-rw-r--r--src/detection/opengl/opengl.h17
-rw-r--r--src/detection/opengl/opengl_apple.c66
-rw-r--r--src/detection/opengl/opengl_haiku.cpp38
-rw-r--r--src/detection/opengl/opengl_linux.c200
-rw-r--r--src/detection/opengl/opengl_shared.c214
-rw-r--r--src/detection/opengl/opengl_windows.c120
6 files changed, 655 insertions, 0 deletions
diff --git a/src/detection/opengl/opengl.h b/src/detection/opengl/opengl.h
new file mode 100644
index 0000000..f7ff9d5
--- /dev/null
+++ b/src/detection/opengl/opengl.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "fastfetch.h"
+#include "modules/opengl/option.h"
+
+typedef struct FFOpenGLResult {
+ FFstrbuf version;
+ FFstrbuf renderer;
+ FFstrbuf vendor;
+ FFstrbuf slv;
+ FFstrbuf library;
+} FFOpenGLResult;
+
+#define FF_OPENGL_BUFFER_WIDTH 1
+#define FF_OPENGL_BUFFER_HEIGHT 1
+
+const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result);
diff --git a/src/detection/opengl/opengl_apple.c b/src/detection/opengl/opengl_apple.c
new file mode 100644
index 0000000..9128593
--- /dev/null
+++ b/src/detection/opengl/opengl_apple.c
@@ -0,0 +1,66 @@
+
+#include "fastfetch.h"
+#include "opengl.h"
+
+#define GL_SILENCE_DEPRECATION
+#include <OpenGL/gl.h>
+#include <OpenGL/OpenGL.h> // This brings in CGL, not GL
+
+void ffOpenGLHandleResult(FFOpenGLResult* result, __typeof__(&glGetString) ffglGetString);
+
+static const char* cglHandleContext(FFOpenGLResult* result, CGLContextObj context) {
+ if (CGLSetCurrentContext(context) != kCGLNoError) {
+ return "CGLSetCurrentContext() failed";
+ }
+
+ ffOpenGLHandleResult(result, &glGetString);
+
+ GLint major, minor;
+ CGLGetVersion(&major, &minor);
+ ffStrbufSetF(&result->library, "CGL %d.%d", major, minor);
+
+ return NULL;
+}
+
+static const char* cglHandlePixelFormat(FFOpenGLResult* result, CGLPixelFormatObj pixelFormat) {
+ CGLContextObj context;
+
+ if (CGLCreateContext(pixelFormat, NULL, &context) != kCGLNoError) {
+ return "CGLCreateContext() failed";
+ }
+
+ const char* error = cglHandleContext(result, context);
+ CGLDestroyContext(context);
+ return error;
+}
+
+const char* cglDetectOpenGL(FFOpenGLResult* result) {
+ CGLPixelFormatObj pixelFormat;
+ CGLPixelFormatAttribute attrs[] = {
+ kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, kCGLPFAAccelerated, 0
+ };
+
+ GLint num;
+ if (CGLChoosePixelFormat(attrs, &pixelFormat, &num) != kCGLNoError) {
+ return "CGLChoosePixelFormat() failed";
+ }
+
+ const char* error = cglHandlePixelFormat(result, pixelFormat);
+ CGLDestroyPixelFormat(pixelFormat);
+ return error;
+}
+
+const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) {
+ if (options->library == FF_OPENGL_LIBRARY_AUTO) {
+ return cglDetectOpenGL(result);
+ } else if (options->library == FF_OPENGL_LIBRARY_EGL) {
+#if __has_include(<EGL/egl.h>)
+ const char* ffOpenGLDetectByEGL(FFOpenGLResult * result);
+ return ffOpenGLDetectByEGL(result);
+#else
+ return "fastfetch was compiled without egl support";
+#endif
+ } else {
+ return "Unsupported OpenGL library";
+ }
+}
diff --git a/src/detection/opengl/opengl_haiku.cpp b/src/detection/opengl/opengl_haiku.cpp
new file mode 100644
index 0000000..64a21a0
--- /dev/null
+++ b/src/detection/opengl/opengl_haiku.cpp
@@ -0,0 +1,38 @@
+#include <OpenGLKit.h>
+
+extern "C" {
+#include "opengl.h"
+#include "common/io.h"
+#if FF_HAVE_EGL
+const char* ffOpenGLDetectByEGL(FFOpenGLResult* result);
+#endif
+void ffOpenGLHandleResult(FFOpenGLResult* result, __typeof__(&glGetString) ffglGetString);
+}
+
+static const char* oglDetectOpenGL(FFOpenGLResult* result) {
+ BApplication app("application/x-vnd.fastfetch-cli-fastfetch");
+ FF_SUPPRESS_IO();
+
+ BGLView glView(BRect(), "ff_ogl_view", B_FOLLOW_NONE, B_WILL_DRAW, BGL_RGB);
+ auto ffglGetString = (decltype(&glGetString)) glView.GetGLProcAddress("glGetString");
+ if (!ffglGetString) {
+ return "glView.GetGLProcAddress() failed";
+ }
+ ffOpenGLHandleResult(result, ffglGetString);
+ ffStrbufSetStatic(&result->library, "OpenGLKit");
+ return NULL;
+}
+
+const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) {
+ if (options->library == FF_OPENGL_LIBRARY_AUTO) {
+ return oglDetectOpenGL(result);
+ } else if (options->library == FF_OPENGL_LIBRARY_EGL) {
+#if FF_HAVE_EGL
+ return ffOpenGLDetectByEGL(result);
+#else
+ return "fastfetch was compiled without egl support";
+#endif
+ } else {
+ return "Unsupported OpenGL library";
+ }
+}
diff --git a/src/detection/opengl/opengl_linux.c b/src/detection/opengl/opengl_linux.c
new file mode 100644
index 0000000..5f88b7e
--- /dev/null
+++ b/src/detection/opengl/opengl_linux.c
@@ -0,0 +1,200 @@
+#include "fastfetch.h"
+#include "opengl.h"
+#include "common/io.h"
+
+#include <string.h>
+
+#if __ANDROID__ && !defined(FF_HAVE_EGL)
+ // On Android, installing OpenGL headers is enough (mesa-dev)
+ #if __has_include(<EGL/egl.h>)
+ #define FF_HAVE_EGL 1
+ #endif
+#endif
+
+#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX)
+ #define FF_HAVE_GL 1
+
+ #include "common/library.h"
+
+ #include <GL/gl.h>
+
+void ffOpenGLHandleResult(FFOpenGLResult* result, __typeof__(&glGetString) ffglGetString);
+
+#endif // FF_HAVE_GL
+
+#ifdef FF_HAVE_GLX
+ #include <GL/glx.h>
+
+typedef struct GLXData {
+ FF_LIBRARY_SYMBOL(glGetString)
+ FF_LIBRARY_SYMBOL(glXGetProcAddress)
+ FF_LIBRARY_SYMBOL(glXQueryVersion)
+ FF_LIBRARY_SYMBOL(XOpenDisplay)
+ FF_LIBRARY_SYMBOL(glXChooseVisual)
+ FF_LIBRARY_SYMBOL(XCreatePixmap)
+ FF_LIBRARY_SYMBOL(glXCreateGLXPixmap)
+ FF_LIBRARY_SYMBOL(glXCreateContext)
+ FF_LIBRARY_SYMBOL(glXMakeCurrent)
+ FF_LIBRARY_SYMBOL(glXDestroyContext)
+ FF_LIBRARY_SYMBOL(glXDestroyGLXPixmap)
+ FF_LIBRARY_SYMBOL(XFreePixmap)
+ FF_LIBRARY_SYMBOL(XCloseDisplay)
+ FF_LIBRARY_SYMBOL(XFree)
+
+ Display* display;
+ XVisualInfo* visualInfo;
+ Pixmap pixmap;
+ GLXPixmap glxPixmap;
+ GLXContext context;
+} GLXData;
+
+static const char* glxHandleContext(FFOpenGLResult* result, GLXData* data) {
+ if (data->ffglXMakeCurrent(data->display, data->glxPixmap, data->context) != True) {
+ return "glXMakeCurrent returned False";
+ }
+ ffOpenGLHandleResult(result, data->ffglGetString);
+
+ int major, minor;
+ if (data->ffglXQueryVersion(data->display, &major, &minor)) {
+ ffStrbufSetF(&result->library, "GLX %d.%d", major, minor);
+ } else {
+ ffStrbufSetStatic(&result->library, "GLX");
+ }
+
+ return NULL;
+}
+
+static const char* glxHandleGLXPixmap(FFOpenGLResult* result, GLXData* data) {
+ data->context = data->ffglXCreateContext(data->display, data->visualInfo, NULL, True);
+ if (data->context == NULL) {
+ return "glXCreateContext returned NULL";
+ }
+
+ const char* error = glxHandleContext(result, data);
+ data->ffglXDestroyContext(data->display, data->context);
+ return error;
+}
+
+static const char* glxHandlePixmap(FFOpenGLResult* result, GLXData* data) {
+ data->glxPixmap = data->ffglXCreateGLXPixmap(data->display, data->visualInfo, data->pixmap);
+ if (data->glxPixmap == None) {
+ return "glXCreateGLXPixmap returned None";
+ }
+
+ const char* error = glxHandleGLXPixmap(result, data);
+ data->ffglXDestroyGLXPixmap(data->display, data->glxPixmap);
+ return error;
+}
+
+static const char* glxHandleVisualInfo(FFOpenGLResult* result, GLXData* data) {
+ data->pixmap = data->ffXCreatePixmap(data->display, DefaultRootWindow(data->display), FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT, (unsigned int) data->visualInfo->depth);
+ if (data->pixmap == None) {
+ return "XCreatePixmap returned None";
+ }
+
+ const char* error = glxHandlePixmap(result, data);
+ data->ffXFreePixmap(data->display, data->pixmap);
+ return error;
+}
+
+static const char* glxHandleDisplay(FFOpenGLResult* result, GLXData* data) {
+ data->visualInfo = data->ffglXChooseVisual(data->display, DefaultScreen(data->display), (int[]) { None });
+ if (data->visualInfo == NULL) {
+ return "glXChooseVisual returned NULL";
+ }
+
+ const char* error = glxHandleVisualInfo(result, data);
+ data->ffXFree(data->visualInfo);
+ return error;
+}
+
+static const char* glxHandleData(FFOpenGLResult* result, GLXData* data) {
+ data->ffglGetString = (__typeof__(data->ffglGetString)) data->ffglXGetProcAddress((const GLubyte*) "glGetString");
+ if (data->ffglGetString == NULL) {
+ return "glXGetProcAddress(glGetString) returned NULL";
+ }
+
+ data->display = data->ffXOpenDisplay(NULL);
+ if (data->display == NULL) {
+ return "XOpenDisplay returned NULL";
+ }
+
+ const char* error = glxHandleDisplay(result, data);
+ data->ffXCloseDisplay(data->display);
+ return error;
+}
+
+static const char* detectByGlx(FFOpenGLResult* result) {
+ GLXData data;
+
+ FF_LIBRARY_LOAD_MESSAGE(glx,
+ #if !__OpenBSD__ && !__NetBSD__
+ "libGLX"
+ #else
+ "libGL"
+ #endif
+ FF_LIBRARY_EXTENSION,
+ 1);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXGetProcAddress);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXQueryVersion);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XOpenDisplay);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXChooseVisual);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCreatePixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateGLXPixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXMakeCurrent);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyGLXPixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFreePixmap);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCloseDisplay);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFree);
+
+ FF_SUPPRESS_IO();
+
+ return glxHandleData(result, &data);
+}
+
+#endif // FF_HAVE_GLX
+
+const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) {
+#if FF_HAVE_GL
+
+ if (options->library == FF_OPENGL_LIBRARY_GLX) {
+ #ifdef FF_HAVE_GLX
+ return detectByGlx(result);
+ #else
+ return "fastfetch was compiled without glx support";
+ #endif
+ }
+
+ if (options->library == FF_OPENGL_LIBRARY_EGL) {
+ #ifdef FF_HAVE_EGL
+ const char* ffOpenGLDetectByEGL(FFOpenGLResult * result);
+ return ffOpenGLDetectByEGL(result);
+ #else
+ return "fastfetch was compiled without egl support";
+ #endif
+ }
+
+ const char* error = ""; // not NULL dummy value
+
+ #ifdef FF_HAVE_EGL
+ const char* ffOpenGLDetectByEGL(FFOpenGLResult * result);
+ error = ffOpenGLDetectByEGL(result);
+ #endif
+
+ #ifdef FF_HAVE_GLX
+ if (error != NULL) {
+ error = detectByGlx(result);
+ }
+ #endif
+
+ return error;
+
+#else
+
+ FF_UNUSED(options, result);
+ return "Fastfetch was built without gl support.";
+
+#endif // FF_HAVE_GL
+}
diff --git a/src/detection/opengl/opengl_shared.c b/src/detection/opengl/opengl_shared.c
new file mode 100644
index 0000000..307e71d
--- /dev/null
+++ b/src/detection/opengl/opengl_shared.c
@@ -0,0 +1,214 @@
+#include "opengl.h"
+#include "common/debug.h"
+#include "common/library.h"
+
+#if __has_include(<GL/gl.h>)
+ #include <GL/gl.h>
+#elif __APPLE__
+ #define GL_SILENCE_DEPRECATION 1
+ #include <OpenGL/gl.h>
+#else
+ #define FF_HAVE_NO_GL 1
+#endif
+
+#ifndef FF_HAVE_NO_GL
+
+ #ifndef GL_SHADING_LANGUAGE_VERSION // For WGL
+ #define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+ #endif
+
+void ffOpenGLHandleResult(FFOpenGLResult* result, __typeof__(&glGetString) ffglGetString) {
+ ffStrbufAppendS(&result->version, (const char*) ffglGetString(GL_VERSION));
+ ffStrbufAppendS(&result->renderer, (const char*) ffglGetString(GL_RENDERER));
+ ffStrbufAppendS(&result->vendor, (const char*) ffglGetString(GL_VENDOR));
+ ffStrbufAppendS(&result->slv, (const char*) ffglGetString(GL_SHADING_LANGUAGE_VERSION));
+}
+
+ #if defined(FF_HAVE_EGL) || __has_include(<EGL/egl.h>)
+ #include "common/io.h"
+
+ #define EGL_EGL_PROTOTYPES 1
+ #define EGL_EGLEXT_PROTOTYPES 1
+ #include <EGL/egl.h>
+ #include <EGL/eglext.h>
+
+typedef struct EGLData {
+ FF_LIBRARY_SYMBOL(glGetString)
+ FF_LIBRARY_SYMBOL(eglGetProcAddress)
+ FF_LIBRARY_SYMBOL(eglGetDisplay)
+ FF_LIBRARY_SYMBOL(eglQueryString)
+ FF_LIBRARY_SYMBOL(eglInitialize)
+ FF_LIBRARY_SYMBOL(eglBindAPI)
+ FF_LIBRARY_SYMBOL(eglGetConfigs)
+ FF_LIBRARY_SYMBOL(eglCreatePbufferSurface)
+ FF_LIBRARY_SYMBOL(eglCreateContext)
+ FF_LIBRARY_SYMBOL(eglMakeCurrent)
+ FF_LIBRARY_SYMBOL(eglDestroyContext)
+ FF_LIBRARY_SYMBOL(eglDestroySurface)
+ FF_LIBRARY_SYMBOL(eglTerminate)
+
+ EGLDisplay display;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLContext context;
+} EGLData;
+
+static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data) {
+ FF_DEBUG("Making EGL context current");
+ if (data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE) {
+ FF_DEBUG("eglMakeCurrent() returned EGL_FALSE");
+ return "eglMakeCurrent returned EGL_FALSE";
+ }
+
+ ffOpenGLHandleResult(result, data->ffglGetString);
+ ffStrbufSetF(&result->library, "EGL %s", data->ffeglQueryString(data->display, EGL_VERSION));
+ FF_DEBUG("OpenGL via EGL detected: version='%s', renderer='%s', vendor='%s', slv='%s', library='%s'",
+ result->version.chars,
+ result->renderer.chars,
+ result->vendor.chars,
+ result->slv.chars,
+ result->library.chars);
+ return NULL;
+}
+
+static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data, bool gles) {
+ FF_DEBUG("Creating EGL context (preferred API=%s, client version=%d)", gles ? "OpenGL ES" : "OpenGL", gles ? 2 : 1);
+ data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]) { EGL_CONTEXT_CLIENT_VERSION, gles ? 2 : 1, // Try GLES 2.0+ first
+ EGL_NONE });
+ if (data->context == EGL_NO_CONTEXT && gles) // Some ANGLE builds support GLES 1.1 only
+ {
+ FF_DEBUG("EGL context creation with GLES 2.x failed, retrying with default attributes (GLES 1.1 fallback)");
+ data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]) { EGL_NONE });
+ }
+ if (data->context == EGL_NO_CONTEXT) {
+ FF_DEBUG("eglCreateContext() returned EGL_NO_CONTEXT");
+ return "eglCreateContext returned EGL_NO_CONTEXT";
+ }
+
+ FF_DEBUG("EGL context created successfully");
+
+ const char* error = eglHandleContext(result, data);
+ FF_DEBUG("eglHandleContext() returns: %s", error ?: "success");
+
+ FF_DEBUG("Releasing current EGL context");
+ data->ffeglMakeCurrent(data->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ FF_DEBUG("Destroying EGL context");
+ data->ffeglDestroyContext(data->display, data->context);
+ return error;
+}
+
+static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data) {
+ // try use OpenGL API. If failed, use the default API (usually OpenGL ES)
+ bool gles = !data->ffeglBindAPI(EGL_OPENGL_API);
+ FF_DEBUG("eglBindAPI(EGL_OPENGL_API) %s, effective API=%s",
+ gles ? "failed" : "succeeded",
+ gles ? "default (usually OpenGL ES)" : "OpenGL");
+
+ EGLint eglConfigCount;
+ data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount);
+ FF_DEBUG("eglGetConfigs() returned %d config(s)", eglConfigCount);
+
+ if (eglConfigCount == 0) {
+ FF_DEBUG("No EGL config is available");
+ return "eglGetConfigs returned 0 configs";
+ }
+
+ FF_DEBUG("Creating EGL pbuffer surface (%dx%d)", FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT);
+ data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]) { EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH, EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT, EGL_NONE });
+
+ if (data->surface == EGL_NO_SURFACE) {
+ FF_DEBUG("eglCreatePbufferSurface() returned EGL_NO_SURFACE");
+ return "eglCreatePbufferSurface returned EGL_NO_SURFACE";
+ }
+
+ FF_DEBUG("EGL pbuffer surface created successfully");
+
+ const char* error = eglHandleSurface(result, data, gles);
+ FF_DEBUG("eglHandleSurface() returns: %s", error ?: "success");
+
+ FF_DEBUG("Destroying EGL surface");
+ data->ffeglDestroySurface(data->display, data->surface);
+ return error;
+}
+
+static const char* eglHandleData(FFOpenGLResult* result, EGLData* data) {
+ FF_DEBUG("Resolving glGetString via eglGetProcAddress()");
+ data->ffglGetString = (__typeof__(&glGetString)) data->ffeglGetProcAddress("glGetString");
+ if (!data->ffglGetString) {
+ FF_DEBUG("eglGetProcAddress('glGetString') returned NULL");
+ return "eglGetProcAddress(glGetString) returned NULL";
+ }
+
+ #if EGL_VERSION_1_5
+ PFNEGLGETPLATFORMDISPLAYEXTPROC ffeglGetPlatformDisplay = (PFNEGLGETPLATFORMDISPLAYEXTPROC) data->ffeglGetProcAddress("eglGetPlatformDisplay");
+ if (ffeglGetPlatformDisplay) {
+ FF_DEBUG("Trying eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA)");
+ data->display = ffeglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, NULL, NULL);
+ FF_DEBUG("eglGetPlatformDisplay() %s", data->display == EGL_NO_DISPLAY ? "failed" : "succeeded");
+ } else {
+ FF_DEBUG("eglGetPlatformDisplay is unavailable, falling back to eglGetDisplay");
+ }
+
+ if (!ffeglGetPlatformDisplay || data->display == EGL_NO_DISPLAY)
+ #endif
+
+ {
+ FF_DEBUG("Trying eglGetDisplay(EGL_DEFAULT_DISPLAY)");
+ data->display = data->ffeglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (data->display == EGL_NO_DISPLAY) {
+ FF_DEBUG("eglGetDisplay() returned EGL_NO_DISPLAY");
+ return "eglGetDisplay returned EGL_NO_DISPLAY";
+ }
+
+ FF_DEBUG("eglGetDisplay() succeeded");
+ }
+
+ EGLint major, minor;
+ if (data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE) {
+ FF_DEBUG("eglInitialize() returned EGL_FALSE");
+ return "eglInitialize returned EGL_FALSE";
+ }
+
+ FF_DEBUG("EGL initialized successfully: %d.%d", major, minor);
+
+ const char* error = eglHandleDisplay(result, data);
+ FF_DEBUG("eglHandleDisplay() returns: %s", error ?: "success");
+
+ FF_DEBUG("Terminating EGL display connection");
+ data->ffeglTerminate(data->display);
+ return error;
+}
+
+const char* ffOpenGLDetectByEGL(FFOpenGLResult* result) {
+ FF_DEBUG("Starting OpenGL detection via EGL");
+ EGLData eglData;
+
+ FF_LIBRARY_LOAD_MESSAGE(egl, "libEGL" FF_LIBRARY_EXTENSION, 1);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetProcAddress);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetDisplay);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglQueryString);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglInitialize);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglBindAPI);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetConfigs);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreatePbufferSurface);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreateContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglMakeCurrent);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroyContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate);
+
+ FF_DEBUG("Loaded EGL library and required symbols");
+
+ FF_SUPPRESS_IO();
+ FF_DEBUG("Suppressed stdout/stderr during EGL probing");
+
+ const char* error = eglHandleData(result, &eglData);
+ FF_DEBUG("OpenGL detection via EGL returns: %s", error ?: "success");
+
+ return error;
+}
+
+ #endif // FF_HAVE_EGL
+
+#endif // FF_HAVE_NO_GL
diff --git a/src/detection/opengl/opengl_windows.c b/src/detection/opengl/opengl_windows.c
new file mode 100644
index 0000000..01bd986
--- /dev/null
+++ b/src/detection/opengl/opengl_windows.c
@@ -0,0 +1,120 @@
+#include "opengl.h"
+#include "common/library.h"
+#include "common/printing.h"
+#include "common/windows/nt.h"
+
+#include <windows.h>
+#include <GL/gl.h>
+
+typedef struct WGLData {
+ FF_LIBRARY_SYMBOL(glGetString)
+ FF_LIBRARY_SYMBOL(wglMakeCurrent)
+ FF_LIBRARY_SYMBOL(wglCreateContext)
+ FF_LIBRARY_SYMBOL(wglDeleteContext)
+} WGLData;
+
+void ffOpenGLHandleResult(FFOpenGLResult* result, __typeof__(&glGetString) ffglGetString);
+
+static const char* wglHandleContext(WGLData* wglData, FFOpenGLResult* result, HDC hdc, HGLRC context) {
+ if (wglData->ffwglMakeCurrent(hdc, context) == FALSE) {
+ return "wglMakeCurrent() failed";
+ }
+ ffOpenGLHandleResult(result, wglData->ffglGetString);
+ ffStrbufSetStatic(&result->library, "WGL 1.0");
+ if (wglData->ffwglMakeCurrent(NULL, NULL) == FALSE) {
+ return "wglMakeCurrent(NULL, NULL) failed";
+ }
+ return NULL;
+}
+
+static const char* wglHandlePixelFormat(WGLData* wglData, FFOpenGLResult* result, HWND hWnd) {
+ HDC hdc = GetDC(hWnd);
+
+ if (hdc == NULL) {
+ return "GetDC() failed";
+ }
+
+ PIXELFORMATDESCRIPTOR pfd = {
+ .nSize = sizeof(PIXELFORMATDESCRIPTOR),
+ .nVersion = 1,
+ .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+ .iPixelType = PFD_TYPE_RGBA,
+ .cColorBits = 32,
+ .cDepthBits = 24,
+ .iLayerType = PFD_MAIN_PLANE
+ };
+ int pixelFormat = ChoosePixelFormat(hdc, &pfd);
+ if (pixelFormat == 0) {
+ ReleaseDC(hWnd, hdc);
+ return "ChoosePixelFormat() failed";
+ }
+
+ if (SetPixelFormat(hdc, pixelFormat, &pfd) == FALSE) {
+ ReleaseDC(hWnd, hdc);
+ return "SetPixelFormat() failed";
+ }
+
+ HGLRC context = wglData->ffwglCreateContext(hdc);
+ if (context == NULL) {
+ ReleaseDC(hWnd, hdc);
+ return "wglCreateContext() failed";
+ }
+
+ const char* error = wglHandleContext(wglData, result, hdc, context);
+ wglData->ffwglDeleteContext(context);
+
+ ReleaseDC(hWnd, hdc);
+
+ return error;
+}
+
+static const char* wglDetectOpenGL(FFOpenGLResult* result) {
+ FF_LIBRARY_LOAD_MESSAGE(opengl32, "opengl32" FF_LIBRARY_EXTENSION, 1);
+
+ WGLData data = {};
+
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opengl32, data, wglMakeCurrent);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opengl32, data, wglCreateContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opengl32, data, wglDeleteContext);
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opengl32, data, glGetString);
+
+ HINSTANCE hInstance = ffGetPeb()->ImageBaseAddress;
+
+ WNDCLASSW wc = {
+ .lpfnWndProc = DefWindowProcW,
+ .hInstance = hInstance,
+ .hbrBackground = (HBRUSH) COLOR_BACKGROUND,
+ .lpszClassName = L"ogl_version_check",
+ .style = CS_OWNDC,
+ };
+ if (!RegisterClassW(&wc)) {
+ return "RegisterClassW() failed";
+ }
+
+ HWND hWnd = CreateWindowW(wc.lpszClassName, L"ogl_version_check", 0, 0, 0, FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT, NULL, NULL, hInstance, NULL);
+ if (!hWnd) {
+ return "CreateWindowW() failed";
+ }
+
+ const char* error = wglHandlePixelFormat(&data, result, hWnd);
+
+ DestroyWindow(hWnd);
+ UnregisterClassW(wc.lpszClassName, hInstance);
+
+ return error;
+}
+
+const char* ffDetectOpenGL(FFOpenGLOptions* options, FFOpenGLResult* result) {
+ if (options->library == FF_OPENGL_LIBRARY_AUTO) {
+ return wglDetectOpenGL(result);
+ } else if (options->library == FF_OPENGL_LIBRARY_EGL) {
+#if __has_include(<EGL/egl.h>)
+ const char* ffOpenGLDetectByEGL(FFOpenGLResult * result);
+ return ffOpenGLDetectByEGL(result);
+#else
+ return "fastfetch was compiled without egl support";
+#endif
+ } else {
+ return "Unsupported OpenGL library";
+ }
+}