GLUT - Non-shared Idle Function?

Hello all, I’m looking into making an object oriented toolkit and have ran into a bit of a snag with glut’s idle function callback. The problem is that I can’t seem to attach a dedicated idle function callback on a per-window basis. Is there any way to make glut accept idle functions on a per-window basis? Thanks for any help.

Below is what I have mocked together so far (It’s a bit long, but cannot be further compressed for a SSCCE, sorry):
(Note: The code is in C++11, only Clang is supported, though GCC might work)


#ifndef BOOTSTRAP_HPP
#define BOOTSTRAP_HPP

#include <iostream>
#include <map>

#include "Platform.hpp"

namespace detail
{
    struct VirtualBootstrap
    {
      protected:
        static thread_local bool glew_initialized;
      public:
        virtual void idle() = 0;
        virtual void tick() = 0;
        virtual void render() = 0;
        virtual ~VirtualBootstrap() = default;
    };
    
    thread_local bool VirtualBootstrap::glew_initialized = false;
    
    thread_local std::map<int, VirtualBootstrap*> bootstraps;
    
    template<int N>
    void register_this(VirtualBootstrap* bootstrap) noexcept
    {
        bootstraps.insert(std::make_pair(N, bootstrap));
    }
    
    template<int N>
    void unregister_this() noexcept
    {
        bootstraps.erase(N);
    }
    
    template<int N>
    void glutDisplayFuncDelegate()
    {
        std::cout << N << " rendering" << std::endl;
        bootstraps[N]->render();
    }
    
    template<int N>
    void glutIdleFuncDelegate()
    {
        std::cout << N << " idling" << std::endl;
        bootstraps[N]->idle();
    }
}

template<int N>
class Bootstrap : public detail::VirtualBootstrap
{
  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);
    }
     
    virtual ~Bootstrap()
    {
        detail::unregister_this<N>();
    }
};

template<int N>
class TestBootstrap : public Bootstrap<N>
{
  public:
    TestBootstrap(int w, int h) : Bootstrap<N>(w, h) {}
    virtual void idle() { /** update some uniforms... */ glutPostRedisplay();}
    virtual void tick() {}
    virtual void render() { /** Render something impressive */}
};

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
    TestBootstrap<0> bootstrap0(300, 300);
    TestBootstrap<1> bootstrap1(300, 300);
    glutMainLoop();
}

#endif

Do’H!

I think I just found a quick and dirty work-around…but would appreciate any peer reviews that might be offered!

#ifndef BOOTSTRAP_HPP
#define BOOTSTRAP_HPP

#include <map>

#include "Platform.hpp"

namespace detail
{
    struct VirtualBootstrap
    {
      protected:
        static thread_local bool glew_initialized;
      public:
        virtual void idle() = 0;
        virtual void tick() = 0;
        virtual void render() = 0;
        virtual ~VirtualBootstrap() = default;
    };
    
    thread_local bool VirtualBootstrap::glew_initialized = false;
    
    thread_local VirtualBootstrap* current = nullptr;
    
    thread_local std::map<int, VirtualBootstrap*> bootstraps;
    
    template<int N>
    void register_this(VirtualBootstrap* bootstrap) noexcept
    {
        bootstraps.insert(std::make_pair(N, bootstrap));
    }
    
    template<int N>
    void unregister_this() noexcept
    {
        bootstraps.erase(N);
    }
    
    template<int N>
    void glutIdleFuncDelegate()
    {
        bootstraps[N]->idle();
    }
    
    template<int N>
    void glutDisplayFuncDelegate()
    {
        if(bootstraps[N] != current)
        {
            current = bootstraps[N];
            
            /// Invoke idle function one time
            glutIdleFuncDelegate<N>();
            
            /// Set subsequent idle function invocations
            glutIdleFunc(glutIdleFuncDelegate<N>);
        }
        bootstraps[N]->render();
    }
}

template<int N>
class Bootstrap : public detail::VirtualBootstrap
{
  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);
    }
     
    virtual ~Bootstrap()
    {
        detail::unregister_this<N>();
    }
};

#endif