Skip to content

Commit 00686f7

Browse files
committed
move to platform directory, use separate GladLoader class
1 parent a455942 commit 00686f7

11 files changed

Lines changed: 989 additions & 537 deletions

src/libprojectM/ProjectMCWrapper.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#include "ProjectMCWrapper.hpp"
22

3+
#include "Renderer/Platform/GladLoader.hpp"
4+
35
#include <projectM-4/projectM.h>
46

57
#include <Logging.hpp>
68

79
#include <Audio/AudioConstants.hpp>
8-
#include <Renderer/PlatformGLResolver.hpp>
10+
#include <Renderer/Platform/GLResolver.hpp>
911

1012
#include <projectM-4/parameters.h>
1113
#include <projectM-4/render_opengl.h>
@@ -76,13 +78,15 @@ projectm_handle projectm_create_with_opengl_load_proc(void* (*load_proc)(const c
7678
{
7779
try
7880
{
79-
// obtain shared resolver instance
80-
auto& glResolver = libprojectM::Renderer::Platform::GLResolver::Instance();
81+
// Init resolver to discover gl function pointers (guarded internally, valid to call multiple times)
82+
// Note: only the initial load_proc will be used, parameters on subsequent calls are ignored
83+
if (!libprojectM::Renderer::Platform::GLResolver::Instance().Initialize(load_proc, user_data))
84+
{
85+
return nullptr;
86+
}
8187

82-
// init resolver to discover gl function pointers and init GLAD
83-
// Initialize() is guarded internally, may be called multiple times
84-
auto success = glResolver.Initialize(load_proc, user_data);
85-
if (!success)
88+
// Check GL requirements and init GLAD (guarded internally, valid to call multiple times)
89+
if (!libprojectM::Renderer::Platform::GladLoader::Instance().LoadGlad())
8690
{
8791
return nullptr;
8892
}

src/libprojectM/Renderer/CMakeLists.txt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ add_library(Renderer OBJECT
3232
MilkdropNoise.cpp
3333
MilkdropNoise.hpp
3434
OpenGL.h
35-
PlatformGLContextCheck.cpp
36-
PlatformGLContextCheck.hpp
37-
PlatformGLResolver.cpp
38-
PlatformGLResolver.hpp
39-
PlatformLoader.cpp
40-
PlatformLoader.hpp
35+
Platform/DynamicLibrary.cpp
36+
Platform/DynamicLibrary.hpp
37+
Platform/GladLoader.cpp
38+
Platform/GladLoader.hpp
39+
Platform/GLProbe.cpp
40+
Platform/GLProbe.hpp
41+
Platform/GLResolver.cpp
42+
Platform/GLResolver.hpp
4143
Point.hpp
4244
PresetTransition.cpp
4345
PresetTransition.hpp

src/libprojectM/Renderer/PlatformLoader.cpp renamed to src/libprojectM/Renderer/Platform/DynamicLibrary.cpp

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11

2-
#include "PlatformLoader.hpp"
2+
#include "DynamicLibrary.hpp"
3+
4+
#include "Logging.hpp"
5+
6+
#include <cstdio>
37

48
#ifndef __EMSCRIPTEN__
59

@@ -31,7 +35,7 @@
3135
#endif
3236

3337
// -------------------------------------------------------------------------
34-
// Optional legacy DLL search fallback
38+
// Legacy DLL search fallback
3539
// -------------------------------------------------------------------------
3640
//
3741
// If the OS loader does not support LOAD_LIBRARY_SEARCH_* flags (ERROR_INVALID_PARAMETER),
@@ -42,10 +46,10 @@
4246
// (which can consult legacy search paths such as the process current working directory).
4347
// This is disabled by default for security hardening.
4448
//
45-
// Define PLATFORM_ALLOW_UNSAFE_DLL_SEARCH=1 to re-enable the legacy fallback.
46-
#ifndef PLATFORM_ALLOW_UNSAFE_DLL_SEARCH
49+
// Define GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH=1 to re-enable the legacy fallback.
50+
#ifndef GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH
4751

48-
#define PLATFORM_ALLOW_UNSAFE_DLL_SEARCH 0
52+
#define GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH 0
4953

5054
#endif
5155

@@ -89,9 +93,49 @@ auto TrimTrailingWhitespace(std::string& str) -> void
8993

9094
} // namespace
9195

96+
auto EnvFlagEnabled(const char* name, bool defaultValue) -> bool
97+
{
98+
if (name == nullptr)
99+
{
100+
return defaultValue;
101+
}
102+
const char* v = std::getenv(name);
103+
if (v == nullptr || v[0] == '\0')
104+
{
105+
return defaultValue;
106+
}
107+
108+
// Lowercase first char is enough for common values.
109+
const char c0 = (v[0] >= 'A' && v[0] <= 'Z') ? static_cast<char>(v[0] - 'A' + 'a') : v[0];
110+
if (c0 == '1' || c0 == 'y' || c0 == 't')
111+
{
112+
return true;
113+
}
114+
if (c0 == '0' || c0 == 'n' || c0 == 'f')
115+
{
116+
return false;
117+
}
118+
119+
// Accept "on"/"off".
120+
if ((c0 == 'o') && (v[1] != '\0'))
121+
{
122+
const char c1 = (v[1] >= 'A' && v[1] <= 'Z') ? static_cast<char>(v[1] - 'A' + 'a') : v[1];
123+
if (c1 == 'n')
124+
{
125+
return true;
126+
}
127+
if (c1 == 'f')
128+
{
129+
return false;
130+
}
131+
}
132+
133+
return defaultValue;
134+
}
135+
92136
#ifndef __EMSCRIPTEN__
93137

94-
DynamicLibrary::~DynamicLibrary()
138+
DynamicLibrary::~DynamicLibrary()
95139
{
96140
if (m_closeOnDestruct)
97141
{
@@ -142,14 +186,16 @@ auto TrimTrailingWhitespace(std::string& str) -> void
142186
continue;
143187
}
144188

189+
LOG_DEBUG(std::string("[DynamicLibrary] open attempt: ") + name);
190+
145191
#ifdef _WIN32
146192

147193
// DLL loading policy:
148194
// - Prefer LoadLibraryExA with LOAD_LIBRARY_SEARCH_* flags to avoid CWD/PATH hijacking.
149195
// - If the OS loader doesn't support the flags (ERROR_INVALID_PARAMETER), fall back to:
150196
// * application directory (explicit full path based on the running module)
151197
// * System32 (for known system DLLs like opengl32.dll)
152-
// * optionally, legacy LoadLibraryA(name) (disabled by default; see PLATFORM_ALLOW_UNSAFE_DLL_SEARCH).
198+
// * optionally, legacy LoadLibraryA(name) (disabled by default; see GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH).
153199
//
154200
// NOTE: We intentionally do not call SetDefaultDllDirectories() here. It changes
155201
// the process-wide DLL search behavior and can surprise a host application.
@@ -202,7 +248,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
202248
return ::LoadLibraryA(dllName);
203249
};
204250

205-
// Best-effort legacy fallback when LOAD_LIBRARY_SEARCH_* flags are unavailable.
251+
// legacy fallback when LOAD_LIBRARY_SEARCH_* flags are unavailable.
206252
// Prefer LOAD_WITH_ALTERED_SEARCH_PATH for absolute paths so dependent DLLs are
207253
// resolved relative to the loaded module directory.
208254
auto tryLoadExplicitPathFallback = [&](const char* dllPath) -> HMODULE {
@@ -285,7 +331,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
285331

286332
if (handle == nullptr && ::GetLastError() == ERROR_INVALID_PARAMETER)
287333
{
288-
// Flags unsupported: best-effort manual safe search.
334+
// Flags unsupported: manual safe search.
289335
// 1) Application directory
290336
if (handle == nullptr && !appFull.empty())
291337
{
@@ -307,7 +353,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
307353
// NOTE: LoadLibrary(name) without LOAD_LIBRARY_SEARCH_* flags can consult legacy
308354
// search paths (including the current working directory) depending on process
309355
// configuration. See Microsoft guidance on DLL search order hardening.
310-
if (handle == nullptr && PLATFORM_ALLOW_UNSAFE_DLL_SEARCH != 0)
356+
if (handle == nullptr && GLRESOLVER_ALLOW_UNSAFE_DLL_SEARCH != 0)
311357
{
312358
handle = tryLoad(name);
313359
}
@@ -326,11 +372,43 @@ auto TrimTrailingWhitespace(std::string& str) -> void
326372
::dlerror(); // clear any prior error
327373
m_handle = ::dlopen(name, RTLD_NOW | RTLD_LOCAL);
328374

375+
#if defined(__APPLE__)
376+
// Allow callers to specify an explicit search directory for bundled dylibs
377+
// (e.g., ANGLE's libEGL.dylib / libGLESv2.dylib inside a macOS app bundle).
378+
// This is an optional override for deployments where @rpath-based discovery is not sufficient.
379+
// It is intentionally scoped to macOS to avoid changing loader semantics elsewhere.
380+
if (m_handle == nullptr)
381+
{
382+
const char* const extraDir = std::getenv("GLRESOLVER_DYLIB_DIR");
383+
if (extraDir != nullptr && extraDir[0] != '\00')
384+
{
385+
std::string full(extraDir);
386+
if (!full.empty() && full.back() != '/')
387+
{
388+
full.push_back('/');
389+
}
390+
full += name;
391+
::dlerror();
392+
393+
m_handle = ::dlopen(full.c_str(), RTLD_NOW | RTLD_LOCAL);
394+
395+
if (m_handle != nullptr)
396+
{
397+
m_loadedName = full;
398+
LOG_DEBUG(std::string("[DynamicLibrary] opened: ") + m_loadedName);
399+
return true;
400+
}
401+
}
402+
}
403+
404+
#endif
405+
329406
#endif
330407

331408
if (m_handle != nullptr)
332409
{
333410
m_loadedName = name;
411+
LOG_DEBUG(std::string("[DynamicLibrary] opened: ") + m_loadedName);
334412
return true;
335413
}
336414

@@ -382,6 +460,10 @@ auto TrimTrailingWhitespace(std::string& str) -> void
382460

383461
} // for loop
384462

463+
if (!reason.empty())
464+
{
465+
LOG_DEBUG(std::string("[DynamicLibrary] open failed: ") + reason);
466+
}
385467
return false;
386468
}
387469

@@ -467,7 +549,7 @@ auto TrimTrailingWhitespace(std::string& str) -> void
467549
}
468550

469551
// If the host has already loaded EGL/GLES provider DLLs (e.g., ANGLE), probe those modules as well.
470-
// This is a best-effort enhancement for applications embedding this library where we may not have
552+
// This is a enhancement for applications embedding this library where we may not have
471553
// opened the provider libraries ourselves.
472554
{
473555
static constexpr std::array<const char*, 6> moduleNames =

src/libprojectM/Renderer/PlatformLoader.hpp renamed to src/libprojectM/Renderer/Platform/DynamicLibrary.hpp

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,6 @@
1111

1212
#include <windows.h>
1313

14-
// -------------------------------------------------------------------------
15-
// Optional legacy DLL search fallback
16-
// -------------------------------------------------------------------------
17-
//
18-
// If the OS loader does not support LOAD_LIBRARY_SEARCH_* flags (ERROR_INVALID_PARAMETER),
19-
// this loader tries to load from explicit safe locations (application directory and
20-
// System32 for known system DLLs).
21-
//
22-
// As a last resort, some applications may still want to fall back to LoadLibrary(name)
23-
// (which can consult legacy search paths such as the process current working directory).
24-
// This is disabled by default for security hardening.
25-
//
26-
// Define PLATFORM_ALLOW_UNSAFE_DLL_SEARCH=1 to re-enable the legacy fallback.
27-
#ifndef PLATFORM_ALLOW_UNSAFE_DLL_SEARCH
28-
29-
#define PLATFORM_ALLOW_UNSAFE_DLL_SEARCH 0
30-
31-
#endif
32-
3314
#else // #ifdef _WIN32
3415

3516
#include <dlfcn.h>
@@ -62,11 +43,11 @@
6243
// Optional loader diagnostics
6344
// -------------------------------------------------------------------------
6445
//
65-
// When enabled, the loader prints best-effort diagnostics for unusual ABI
46+
// When enabled, the loader prints diagnostics for unusual ABI
6647
// situations (e.g., platforms where data pointers and function pointers have
6748
// different representations/sizes). Disabled by default to avoid noisy output.
68-
#ifndef PLATFORM_LOADER_DIAGNOSTICS
69-
#define PLATFORM_LOADER_DIAGNOSTICS 0
49+
#ifndef GLRESOLVER_LOADER_DIAGNOSTICS
50+
#define GLRESOLVER_LOADER_DIAGNOSTICS 0
7051
#endif
7152

7253
namespace libprojectM {
@@ -86,7 +67,7 @@ using LibHandle = void*;
8667

8768
#endif
8869

89-
#if PLATFORM_LOADER_DIAGNOSTICS
70+
#if GLRESOLVER_LOADER_DIAGNOSTICS
9071

9172
inline auto ReportFnPtrSizeMismatch(const char* where, std::size_t fnSize, std::size_t ptrSize) -> void
9273
{
@@ -99,7 +80,7 @@ inline auto ReportFnPtrSizeMismatch(const char* where, std::size_t fnSize, std::
9980
#endif
10081

10182
/**
102-
* @brief Converts a symbol pointer (void*) into a function pointer type (best-effort).
83+
* @brief Converts a symbol pointer (void*) into a function pointer type .
10384
*
10485
* dlsym/GetProcAddress return untyped procedure addresses (void* on POSIX, FARPROC on Windows). Converting those to function
10586
* pointers via reinterpret_cast is not guaranteed to be portable in ISO C++.
@@ -129,7 +110,7 @@ auto SymbolToFunction(void* symbol) -> Fn
129110
if (sizeof(Fn) != sizeof(void*))
130111
{
131112

132-
#if PLATFORM_LOADER_DIAGNOSTICS
113+
#if GLRESOLVER_LOADER_DIAGNOSTICS
133114

134115
ReportFnPtrSizeMismatch("SymbolToFunction", sizeof(Fn), sizeof(void*));
135116

@@ -144,7 +125,7 @@ auto SymbolToFunction(void* symbol) -> Fn
144125
}
145126

146127
/**
147-
* @brief Converts a function pointer into a symbol pointer representation (best-effort).
128+
* @brief Converts a function pointer into a symbol pointer representation .
148129
*
149130
* The inverse of SymbolToFunction(). This is used at API boundaries where legacy
150131
* interfaces represent procedure addresses as void*.
@@ -168,7 +149,7 @@ auto FunctionToSymbol(Fn func) -> void*
168149
if (sizeof(Fn) != sizeof(void*))
169150
{
170151

171-
#if PLATFORM_LOADER_DIAGNOSTICS
152+
#if GLRESOLVER_LOADER_DIAGNOSTICS
172153

173154
ReportFnPtrSizeMismatch("FunctionToSymbol", sizeof(Fn), sizeof(void*));
174155

@@ -183,7 +164,7 @@ auto FunctionToSymbol(Fn func) -> void*
183164
}
184165

185166
/**
186-
* @brief Converts a function pointer into an integer representation (best-effort).
167+
* @brief Converts a function pointer into an integer representation .
187168
*
188169
* Useful for validating platform-specific sentinel values (e.g. Windows WGL).
189170
* Returns 0 if the conversion is not representable.
@@ -203,7 +184,7 @@ auto FunctionToInteger(Fn func) -> std::uintptr_t
203184
if (sizeof(Fn) != sizeof(void*))
204185
{
205186

206-
#if PLATFORM_LOADER_DIAGNOSTICS
187+
#if GLRESOLVER_LOADER_DIAGNOSTICS
207188

208189
ReportFnPtrSizeMismatch("FunctionToInteger", sizeof(Fn), sizeof(void*));
209190

@@ -222,10 +203,15 @@ auto FunctionToInteger(Fn func) -> std::uintptr_t
222203
return value;
223204
}
224205

206+
/**
207+
* @brief Parses a bool-ish env var. Truthy: 1, y, yes, t, true, on (case-insensitive). Falsy: 0, n, no, f, false, off.
208+
*/
209+
auto EnvFlagEnabled(const char* name, bool defaultValue) -> bool;
210+
225211
#ifdef _WIN32
226212

227213
/**
228-
* @brief Converts a Windows FARPROC into the generic symbol representation (void*) (best-effort).
214+
* @brief Converts a Windows FARPROC into the generic symbol representation (void*) .
229215
*
230216
* Windows GetProcAddress returns a function pointer type (FARPROC). To avoid relying on a direct
231217
* reinterpret_cast between function pointers and object pointers, this helper copies the bit pattern

0 commit comments

Comments
 (0)