Before I go gung-ho on this tangent, I'd like some feedback.
Basically, my beef with GLUT is that the idle callback function is global and cannot be assigned on a per-window basis. I think that I have worked around this in such a way that should be stable and behave expectedly.
#ifndef BOOTSTRAP_HPP
#define BOOTSTRAP_HPP
static_assert(__clang__ && __cplusplus >= 199711L, "Your compiler is not supported.");
#include <map>
#include "Platform.hpp" // Contains platform-specific junk
namespace detail
{
/**
* The ultimate base class for all bootstrap implementations.
*
* Internally used, this class allows for heterogenus storage of bootstrap implementations in STL containers
* while at the same time providing a simple means by which to tell whether glew has been initialized or not
*
*/
struct AbstractBootstrap
{
protected:
/// Has glew been initialized yet?
staticthread_localbool glew_initialized;
public:
/**
* Implicitly invoked by GLUT
*
* @see (https://www.opengl.org/resources/libraries/glut/spec3/node63.html)
*
*/
virtualvoid idle() = 0;
/**
* Implicitly invoked by GLUT
*
* @see (http://www.opengl.org/resources/libraries/glut/spec3/node46.html)
*
*/
virtualvoid render() = 0;
/**
* Default virtual destructor
*
*/
virtual ~AbstractBootstrap() = default;
};
thread_localbool AbstractBootstrap::glew_initialized = false;
/// A pointer to the currently active bootstrap
thread_local AbstractBootstrap* current = nullptr;
/// A mapping of all valid bootstraps
thread_local std::map<int, AbstractBootstrap*> bootstraps;
/**
* Registers the provided bootstrap
*
* @tparam N the ID of the bootstrap to register
*
* @param bootstrap the bootstrap to register
*
*/
template<int N>
void register_this(AbstractBootstrap* bootstrap) noexcept
{
bootstraps.insert(std::make_pair(N, bootstrap));
}
/**
* Unregisters 'this' bootstrap
*
* @tparam N the ID of the bootstrap to unregister
*
*/
template<int N>
void unregister_this() noexcept
{
bootstraps.erase(N);
}
/**
* A delegate function that is compatible with GLUT that acts as a bridge between the C and C++ APIs
*
* @tparam N the ID of the bootstrap that this delegate represents
*
*/
template<int N>
void glutIdleFuncDelegate()
{
bootstraps[N]->idle();
}
/**
* A delegate function that is compatible with GLUT that acts as a bridge between the C and C++ APIs
*
* @tparam N the ID of the bootstrap that this delegate represents
*
*/
template<int N>
void glutDisplayFuncDelegate()
{
/// HACK! Switch out the idle function delegate if the active window has changed
if(bootstraps[N] != current)
{
/// Invoke new idle function one time
glutIdleFuncDelegate<N>();
/// Set subsequent idle function invocations
glutIdleFunc(glutIdleFuncDelegate<N>);
current = bootstraps[N];
}
bootstraps[N]->render();
}
}
/**
* The default bootstrap implementation. A bootstrap encapsulates the underlying C-API that provides native GUI
* resources.
*
* TODO: Docs @ options ( + defaults )
*
* Derived classes are expected to override the appropriate virtual methods.
*
* @tparam N the static ID of this bootstrap
*
*/
template<int N>
class Bootstrap : public detail::AbstractBootstrap
{
public:
Bootstrap(int windowWidth, int windowHeight)
{
glutInitWindowSize(windowWidth, windowHeight);
glutInitWindowPosition(100, 100);
glutCreateWindow((std::string("Bootstrap Test (") + std::to_string(N) + ")").c_str());
if(!glew_initialized)
{
glewInit();
glew_initialized = true;
}
glutDisplayFunc(detail::glutDisplayFuncDelegate<N>);
glutIdleFunc(detail::glutIdleFuncDelegate<N>);
detail::register_this<N>(this);
}
virtualvoid idle() {}
virtualvoid render() {}
virtual ~Bootstrap()
{
detail::unregister_this<N>();
}
};
/// Quick test
/// Implicitly initialize 'glew' and provide per-window idle callbacks
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
Bootstrap<0> bootstrap(300, 300);
Bootstrap<1> bootstrap1(300, 300);
glutMainLoop();
return 0;
}
#endif