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}