Changed EXPAND_WCB so it works with MSVC and GCC
[freeglut] / src / fg_callbacks.c
1 /*
2  * fg_callbacks.c
3  *
4  * The callbacks setting methods.
5  *
6  * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
7  * Written by Pawel W. Olszta, <olszta@sourceforge.net>
8  * Creation date: Fri Dec 3 1999
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #include <GL/freeglut.h>
29 #include "fg_internal.h"
30
31 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
32
33
34 /*
35  * Global callbacks.
36  */
37 /* Sets the global idle callback */
38 void FGAPIENTRY glutIdleFuncUcall( FGCBIdleUC callback, FGCBUserData userData )
39 {
40     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutIdleFuncUcall" );
41     fgState.IdleCallback = callback;
42     fgState.IdleCallbackData = userData;
43 }
44
45 static void glutIdleFuncCallback( FGCBUserData userData )
46 {
47     FGCBIdle callback = (FGCBIdle)userData;
48     callback();
49 }
50
51 void FGAPIENTRY glutIdleFunc( FGCBIdle callback )
52 {
53     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutIdleFunc" );
54     if( callback )
55         glutIdleFuncUcall( glutIdleFuncCallback, (FGCBUserData)callback );
56     else
57         glutIdleFuncUcall( NULL, NULL );
58 }
59
60 /* Creates a timer and sets its callback */
61 void FGAPIENTRY glutTimerFuncUcall( unsigned int timeOut, FGCBTimerUC callback, int timerID, FGCBUserData userData )
62 {
63     SFG_Timer *timer, *node;
64
65     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutTimerFuncUcall" );
66
67     if( (timer = fgState.FreeTimers.Last) )
68     {
69         fgListRemove( &fgState.FreeTimers, &timer->Node );
70     }
71     else
72     {
73         if( ! (timer = malloc(sizeof(SFG_Timer))) )
74             fgError( "Fatal error: "
75                      "Memory allocation failure in glutTimerFunc()" );
76     }
77
78     timer->Callback     = callback;
79     timer->CallbackData = userData;
80     timer->ID           = timerID;
81     timer->TriggerTime  = fgElapsedTime() + timeOut;
82
83     /* Insert such that timers are sorted by end-time */
84     for( node = fgState.Timers.First; node; node = node->Node.Next )
85     {
86         if( node->TriggerTime > timer->TriggerTime )
87             break;
88     }
89
90     fgListInsert( &fgState.Timers, &node->Node, &timer->Node );
91 }
92
93 static void glutTimerFuncCallback( int ID, FGCBUserData userData )
94 {
95     FGCBTimer callback = (FGCBTimer)userData;
96     callback( ID );
97 }
98
99 void FGAPIENTRY glutTimerFunc( unsigned int timeOut, FGCBTimer callback, int timerID )
100 {
101     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutTimerFunc" );
102     if( callback )
103         glutTimerFuncUcall( timeOut, glutTimerFuncCallback, timerID, (FGCBUserData)callback );
104     else
105         glutTimerFuncUcall( timeOut, NULL, timerID, NULL );
106 }
107
108 /* Deprecated version of glutMenuStatusFunc callback setting method */
109 void FGAPIENTRY glutMenuStateFunc( FGCBMenuState callback )
110 {
111     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuStateFunc" );
112     fgState.MenuStateCallback = callback;
113 }
114
115 /* Sets the global menu status callback for the current window */
116 void FGAPIENTRY glutMenuStatusFuncUcall( FGCBMenuStatusUC callback, FGCBUserData userData )
117 {
118     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuStatusFuncUcall" );
119     fgState.MenuStatusCallback = callback;
120     fgState.MenuStatusCallbackData = userData;
121 }
122
123 static void glutMenuStatusFuncCallback( int menuState, int mouseX, int mouseY, FGCBUserData userData )
124 {
125     FGCBMenuStatus callback = (FGCBMenuStatus)userData;
126     callback( menuState, mouseX, mouseY );
127 }
128
129 void FGAPIENTRY glutMenuStatusFunc( FGCBMenuStatus callback )
130 {
131     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuStatusFunc" );
132     if( callback )
133         glutMenuStatusFuncUcall( glutMenuStatusFuncCallback, (FGCBUserData)callback );
134     else
135         glutMenuStatusFuncUcall( NULL, NULL );
136 }
137
138 /*
139  * Menu specific callbacks.
140  */
141 /* Callback upon menu destruction */
142 void FGAPIENTRY glutMenuDestroyFuncUcall( FGCBDestroyUC callback, FGCBUserData userData )
143 {
144     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuDestroyFuncUcall" );
145     if( fgStructure.CurrentMenu )
146     {
147         fgStructure.CurrentMenu->Destroy = callback;
148         fgStructure.CurrentMenu->DestroyData = userData;
149     }
150 }
151
152 static void glutMenuDestroyFuncCallback( FGCBUserData userData )
153 {
154     FGCBDestroy callback = (FGCBDestroy)userData;
155     callback();
156 }
157
158 void FGAPIENTRY glutMenuDestroyFunc( FGCBDestroy callback )
159 {
160     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuDestroyFunc" );
161     if( callback )
162         glutMenuDestroyFuncUcall( glutMenuDestroyFuncCallback, (FGCBUserData)callback );
163     else
164         glutMenuDestroyFuncUcall( NULL, NULL );
165 }
166
167
168 /*
169  * All of the window-specific callbacks setting methods can be generalized to this:
170  */
171 #define SET_CALLBACK(a)                                         \
172 do                                                              \
173 {                                                               \
174     if( fgStructure.CurrentWindow == NULL )                     \
175         return;                                                 \
176     SET_WCB( ( *( fgStructure.CurrentWindow ) ), a, callback, userData ); \
177 } while( 0 )
178 /*
179  * Types need to be defined for callbacks. It's not ideal, but it works for this.
180  */
181 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG0(a,b)                    \
182 static void glut##a##FuncCallback( FGCBUserData userData )      \
183 {                                                               \
184     FGCB##b callback = (FGCB##b)userData;                       \
185     callback();                                                 \
186 }
187 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG1(a,b)                    \
188 static void glut##a##FuncCallback( int arg1val, FGCBUserData userData ) \
189 {                                                               \
190     FGCB##b callback = (FGCB##b)userData;                       \
191     callback( arg1val );                                        \
192 }
193 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG2(a,b)                    \
194 static void glut##a##FuncCallback( int arg1val, int arg2val, FGCBUserData userData ) \
195 {                                                               \
196     FGCB##b callback = (FGCB##b)userData;                       \
197     callback( arg1val, arg2val );                               \
198 }
199 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG3_USER(a,b,arg1,arg2,arg3) \
200 static void glut##a##FuncCallback( arg1 arg1val, arg2 arg2val, arg3 arg3val, FGCBUserData userData ) \
201 {                                                               \
202     FGCB##b callback = (FGCB##b)userData;                       \
203     callback( arg1val, arg2val, arg3val );                      \
204 }
205 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG3(a,b) IMPLEMENT_CALLBACK_FUNC_CB_ARG3_USER(a,b,int,int,int)
206 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG4(a,b)                    \
207 static void glut##a##FuncCallback( int arg1val, int arg2val, int arg3val, int arg4val, FGCBUserData userData ) \
208 {                                                               \
209     FGCB##b callback = (FGCB##b)userData;                       \
210     callback( arg1val, arg2val, arg3val, arg4val );             \
211 }
212 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG5(a,b)                    \
213 static void glut##a##FuncCallback( int arg1val, int arg2val, int arg3val, int arg4val, int arg5val, FGCBUserData userData ) \
214 {                                                               \
215     FGCB##b callback = (FGCB##b)userData;                       \
216     callback( arg1val, arg2val, arg3val, arg4val, arg5val );    \
217 }
218 /*
219  * And almost every time the callback setter function can be implemented like this:
220  */
221 #define IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,b)                 \
222 void FGAPIENTRY glut##a##FuncUcall( FGCB##b##UC callback, FGCBUserData userData ) \
223 {                                                               \
224     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glut"#a"FuncUcall" );   \
225     SET_CALLBACK( b );                                          \
226 }                                                               \
227 void FGAPIENTRY glut##a##Func( FGCB##b callback )               \
228 {                                                               \
229     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glut"#a"Func" );        \
230     if( callback )                                              \
231         glut##a##FuncUcall( glut##a##FuncCallback, (FGCBUserData)callback ); \
232     else                                                        \
233         glut##a##FuncUcall( NULL, NULL ); \
234 }
235 /*
236  * Combine _glut and _cb macros:
237  */
238 #define IMPLEMENT_CALLBACK_FUNC_ARG0(a)                         \
239         IMPLEMENT_CALLBACK_FUNC_CB_ARG0(a,a)                    \
240         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
241
242 #define IMPLEMENT_CALLBACK_FUNC_ARG0_2NAME(a,b)                 \
243         IMPLEMENT_CALLBACK_FUNC_CB_ARG0(a,b)                    \
244         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,b)
245
246 #define IMPLEMENT_CALLBACK_FUNC_ARG1(a)                         \
247         IMPLEMENT_CALLBACK_FUNC_CB_ARG1(a,a)                    \
248         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
249
250 #define IMPLEMENT_CALLBACK_FUNC_ARG2(a)                         \
251         IMPLEMENT_CALLBACK_FUNC_CB_ARG2(a,a)                    \
252         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
253
254 #define IMPLEMENT_CALLBACK_FUNC_ARG2_2NAME(a,b)                 \
255         IMPLEMENT_CALLBACK_FUNC_CB_ARG2(a,b)                    \
256         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,b)
257
258 #define IMPLEMENT_CALLBACK_FUNC_ARG3(a)                         \
259         IMPLEMENT_CALLBACK_FUNC_CB_ARG3(a,a)                    \
260         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
261
262 #define IMPLEMENT_CALLBACK_FUNC_ARG3_USER(a,arg1,arg2,arg3)     \
263         IMPLEMENT_CALLBACK_FUNC_CB_ARG3_USER(a,a,arg1,arg2,arg3)\
264         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
265
266 #define IMPLEMENT_CALLBACK_FUNC_ARG4(a)                         \
267         IMPLEMENT_CALLBACK_FUNC_CB_ARG4(a,a)                    \
268         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
269
270 #define IMPLEMENT_CALLBACK_FUNC_ARG5(a)                         \
271         IMPLEMENT_CALLBACK_FUNC_CB_ARG5(a,a)                    \
272         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
273
274 /* Implement all these callback setter functions... */
275 IMPLEMENT_CALLBACK_FUNC_ARG2(Position)
276 IMPLEMENT_CALLBACK_FUNC_ARG3_USER(Keyboard,unsigned char,int,int)
277 IMPLEMENT_CALLBACK_FUNC_ARG3_USER(KeyboardUp,unsigned char,int,int)
278 IMPLEMENT_CALLBACK_FUNC_ARG3(Special)
279 IMPLEMENT_CALLBACK_FUNC_ARG3(SpecialUp)
280 IMPLEMENT_CALLBACK_FUNC_ARG4(Mouse)
281 IMPLEMENT_CALLBACK_FUNC_ARG4(MouseWheel)
282 IMPLEMENT_CALLBACK_FUNC_ARG2(Motion)
283 IMPLEMENT_CALLBACK_FUNC_ARG2_2NAME(PassiveMotion,Passive)
284 IMPLEMENT_CALLBACK_FUNC_ARG1(Entry)
285 /* glutWMCloseFunc is an alias for glutCloseFunc; both set the window's Destroy callback */
286 IMPLEMENT_CALLBACK_FUNC_ARG0_2NAME(Close,Destroy)
287 IMPLEMENT_CALLBACK_FUNC_ARG0_2NAME(WMClose,Destroy)
288 IMPLEMENT_CALLBACK_FUNC_ARG0(OverlayDisplay)
289 IMPLEMENT_CALLBACK_FUNC_ARG1(WindowStatus)
290 IMPLEMENT_CALLBACK_FUNC_ARG2(ButtonBox)
291 IMPLEMENT_CALLBACK_FUNC_ARG2(Dials)
292 IMPLEMENT_CALLBACK_FUNC_ARG2(TabletMotion)
293 IMPLEMENT_CALLBACK_FUNC_ARG4(TabletButton)
294 IMPLEMENT_CALLBACK_FUNC_ARG2(MultiEntry)
295 IMPLEMENT_CALLBACK_FUNC_ARG5(MultiButton)
296 IMPLEMENT_CALLBACK_FUNC_ARG3(MultiMotion)
297 IMPLEMENT_CALLBACK_FUNC_ARG3(MultiPassive)
298 IMPLEMENT_CALLBACK_FUNC_ARG0(InitContext)
299 IMPLEMENT_CALLBACK_FUNC_ARG1(AppStatus)
300
301 /*
302  * Sets the Display callback for the current window
303  */
304 void FGAPIENTRY glutDisplayFuncUcall( FGCBDisplayUC callback, FGCBUserData userData )
305 {
306     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDisplayFuncUcall" );
307     if( !callback )
308         fgError( "Fatal error in program.  NULL display callback not "
309                  "permitted in GLUT 3.0+ or freeglut 2.0.1+" );
310     SET_CALLBACK( Display );
311 }
312
313 static void glutDisplayFuncCallback( FGCBUserData userData )
314 {
315     FGCBDisplay callback = (FGCBDisplay)userData;
316     callback();
317 }
318
319 void FGAPIENTRY glutDisplayFunc( FGCBDisplay callback )
320 {
321     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDisplayFunc" );
322     if( callback )
323         glutDisplayFuncUcall( glutDisplayFuncCallback, (FGCBUserData)callback );
324     else
325         glutDisplayFuncUcall( NULL, NULL );
326 }
327
328 void fghDefaultReshape( int width, int height, FGCBUserData userData )
329 {
330     glViewport( 0, 0, width, height );
331 }
332
333 void FGAPIENTRY glutReshapeFuncUcall( FGCBReshapeUC callback, FGCBUserData userData )
334 {
335     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutReshapeFuncUcall" );
336     
337     if( !callback )
338     {
339         callback = fghDefaultReshape;
340         userData = NULL;
341     }
342
343     SET_CALLBACK( Reshape );
344 }
345
346 static void glutReshapeFuncCallback( int width, int height, FGCBUserData userData )
347 {
348     FGCBReshape callback = (FGCBReshape)userData;
349     callback( width, height );
350 }
351
352 void FGAPIENTRY glutReshapeFunc( FGCBReshape callback )
353 {
354     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutReshapeFunc" );
355     if( callback )
356         glutReshapeFuncUcall( glutReshapeFuncCallback, (FGCBUserData)callback );
357     else
358         glutReshapeFuncUcall( NULL, NULL );
359 }
360
361 /*
362  * Sets the Visibility callback for the current window.
363  * NB: the Visibility func is deprecated in favor of the WindowStatus func,
364  * which provides more detail. The visibility func callback is implemented
365  * as a translation step from the windowStatus func. When the user sets the
366  * windowStatus func, any visibility func is overwritten.
367  * DEVELOPER NOTE: in the library, only invoke the window status func, this
368  * gets automatically translated to the visibility func if thats what the
369  * user has set.
370  * window status is kind of anemic on win32 as there are no window messages
371  * to notify us that the window is covered by other windows or not.
372  * Should one want to query this, see
373  * http://stackoverflow.com/questions/5445889/get-which-process-window-is-actually-visible-in-c-sharp
374  * for an implementation outline (but it would be polling based, not push based).
375  */
376 static void fghVisibility( int status, FGCBUserData userData )
377 {
378     int vis_status;
379
380     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Visibility Callback" );
381     freeglut_return_if_fail( fgStructure.CurrentWindow );
382
383     /* Translate window status func states to visibility states */
384     if( ( GLUT_HIDDEN == status )  || ( GLUT_FULLY_COVERED == status ) )
385         vis_status = GLUT_NOT_VISIBLE;
386     else    /* GLUT_FULLY_RETAINED, GLUT_PARTIALLY_RETAINED */
387         vis_status = GLUT_VISIBLE;
388
389     INVOKE_WCB( *( fgStructure.CurrentWindow ), Visibility, ( vis_status ) );
390 }
391
392 void FGAPIENTRY glutVisibilityFuncUcall( FGCBVisibilityUC callback, FGCBUserData userData )
393 {
394     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutVisibilityFuncUcall" );
395
396     if ( !callback )
397     {
398         userData = NULL;
399     }
400
401     SET_CALLBACK( Visibility );
402
403     if( callback )
404         glutWindowStatusFuncUcall( fghVisibility, NULL );
405     else
406         glutWindowStatusFuncUcall( NULL, NULL );
407 }
408
409 static void glutVisibilityFuncCallback( int visibility, FGCBUserData userData )
410 {
411     FGCBVisibility callback = (FGCBVisibility)userData;
412     callback( visibility );
413 }
414
415 void FGAPIENTRY glutVisibilityFunc( FGCBVisibility callback )
416 {
417     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutVisibilityFunc" );
418     if( callback )
419         glutVisibilityFuncUcall( glutVisibilityFuncCallback, (FGCBUserData)callback );
420     else
421         glutVisibilityFuncUcall( NULL, NULL );
422 }
423
424 /*
425  * Sets the joystick callback and polling rate for the current window
426  */
427 void FGAPIENTRY glutJoystickFuncUcall( FGCBJoystickUC callback, int pollInterval, FGCBUserData userData )
428 {
429     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickFuncUcall" );
430     fgInitialiseJoysticks ();
431
432     if ( (
433            fgStructure.CurrentWindow->State.JoystickPollRate <= 0 ||        /* Joystick callback was disabled */
434            !FETCH_WCB(*fgStructure.CurrentWindow,Joystick)
435          ) &&
436          ( 
437            callback && ( pollInterval > 0 )                                 /* but is now enabled */
438          ) )
439         ++fgState.NumActiveJoysticks;
440     else if ( ( 
441                 fgStructure.CurrentWindow->State.JoystickPollRate > 0 &&    /* Joystick callback was enabled */
442                 FETCH_WCB(*fgStructure.CurrentWindow,Joystick)
443               ) &&  
444               ( 
445                 !callback || ( pollInterval <= 0 )                          /* but is now disabled */
446               ) )
447         --fgState.NumActiveJoysticks;
448
449     SET_CALLBACK( Joystick );
450     fgStructure.CurrentWindow->State.JoystickPollRate = pollInterval;
451
452     /* set last poll time such that joystick will be polled asap */
453     fgStructure.CurrentWindow->State.JoystickLastPoll = fgElapsedTime();
454     if (fgStructure.CurrentWindow->State.JoystickLastPoll < pollInterval)
455         fgStructure.CurrentWindow->State.JoystickLastPoll = 0;
456     else
457         fgStructure.CurrentWindow->State.JoystickLastPoll -= pollInterval;
458 }
459
460 static void glutJoystickFuncCallback( unsigned int buttons, int axis0, int axis1, int axis2, FGCBUserData userData )
461 {
462     FGCBJoystick callback = (FGCBJoystick)userData;
463     callback( buttons, axis0, axis1, axis2 );
464 }
465
466 void FGAPIENTRY glutJoystickFunc( FGCBJoystick callback, int pollInterval )
467 {
468     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickFunc" );
469     if( callback )
470         glutJoystickFuncUcall( glutJoystickFuncCallback, pollInterval, (FGCBUserData)callback );
471     else
472         glutJoystickFuncUcall( NULL, pollInterval, NULL );
473 }
474
475 /*
476  * Sets the spaceball motion callback for the current window
477  */
478 void FGAPIENTRY glutSpaceballMotionFuncUcall( FGCBSpaceMotionUC callback, FGCBUserData userData )
479 {
480     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFuncUcall" );
481     fgInitialiseSpaceball();
482
483     SET_CALLBACK( SpaceMotion );
484 }
485
486 static void glutSpaceballMotionFuncCallback( int x, int y, int z, FGCBUserData userData )
487 {
488     FGCBSpaceMotion callback = (FGCBSpaceMotion)userData;
489     callback( x, y, z );
490 }
491
492 void FGAPIENTRY glutSpaceballMotionFunc( FGCBSpaceMotion callback )
493 {
494     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFunc" );
495     if( callback )
496         glutSpaceballMotionFuncUcall( glutSpaceballMotionFuncCallback, (FGCBUserData)callback );
497     else
498         glutSpaceballMotionFuncUcall( NULL, NULL );
499 }
500
501 /*
502  * Sets the spaceball rotate callback for the current window
503  */
504 void FGAPIENTRY glutSpaceballRotateFuncUcall( FGCBSpaceRotationUC callback, FGCBUserData userData )
505 {
506     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFuncUcall" );
507     fgInitialiseSpaceball();
508
509     SET_CALLBACK( SpaceRotation );
510 }
511
512 static void glutSpaceballRotateFuncCallback( int x, int y, int z, FGCBUserData userData )
513 {
514     FGCBSpaceRotation callback = (FGCBSpaceRotation)userData;
515     callback( x, y, z );
516 }
517
518 void FGAPIENTRY glutSpaceballRotateFunc( FGCBSpaceRotation callback )
519 {
520     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFunc" );
521     if( callback )
522         glutSpaceballRotateFuncUcall( glutSpaceballRotateFuncCallback, (FGCBUserData)callback );
523     else
524         glutSpaceballRotateFuncUcall( NULL, NULL );
525 }
526
527 /*
528  * Sets the spaceball button callback for the current window
529  */
530 void FGAPIENTRY glutSpaceballButtonFuncUcall( FGCBSpaceButtonUC callback, FGCBUserData userData )
531 {
532     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFuncUcall" );
533     fgInitialiseSpaceball();
534
535     SET_CALLBACK( SpaceButton );
536 }
537
538 static void glutSpaceballButtonFuncCallback( int button, int buttonState, FGCBUserData userData )
539 {
540     FGCBSpaceButton callback = (FGCBSpaceButton)userData;
541     callback( button, buttonState );
542 }
543
544 void FGAPIENTRY glutSpaceballButtonFunc( FGCBSpaceButton callback )
545 {
546     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFunc" );
547     if( callback )
548         glutSpaceballButtonFuncUcall( glutSpaceballButtonFuncCallback, (FGCBUserData)callback );
549     else
550         glutSpaceballButtonFuncUcall( NULL, NULL );
551 }
552
553 /*** END OF FILE ***/