[Peer Review] - GLUT bypassing global idle function limitation

closed account (3hM2Nwbp)
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#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?
        static thread_local bool glew_initialized;
        
      public:
        
        /**
         * Implicitly invoked by GLUT
         * 
         * @see (https://www.opengl.org/resources/libraries/glut/spec3/node63.html)
         * 
         */
        virtual void idle() = 0;
        
        /**
         * Implicitly invoked by GLUT
         * 
         * @see (http://www.opengl.org/resources/libraries/glut/spec3/node46.html)
         * 
         */
        virtual void render() = 0;
        
        /**
         * Default virtual destructor
         * 
         */
        virtual ~AbstractBootstrap() = default;
    };
    
    thread_local bool 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);
    }
    
    virtual void idle() {}
    virtual void 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 
Topic archived. No new replies allowed.