5f94b65816d9c0451c9ab9f6d0efe0805ae819f9
[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 /*
140  * Menu specific callbacks.
141  */
142 /* Callback upon menu destruction */
143 void FGAPIENTRY glutMenuDestroyFuncUcall( FGCBDestroyUC callback, FGCBUserData userData )
144 {
145     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuDestroyFuncUcall" );
146     if( fgStructure.CurrentMenu )
147     {
148         fgStructure.CurrentMenu->Destroy = callback;
149         fgStructure.CurrentMenu->DestroyData = userData;
150     }
151 }
152
153 static void glutMenuDestroyFuncCallback( FGCBUserData userData )
154 {
155     FGCBDestroy callback = (FGCBDestroy)userData;
156     callback();
157 }
158
159 void FGAPIENTRY glutMenuDestroyFunc( FGCBDestroy callback )
160 {
161     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuDestroyFunc" );
162     if( callback )
163         glutMenuDestroyFuncUcall( glutMenuDestroyFuncCallback, (FGCBUserData)callback );
164     else
165         glutMenuDestroyFuncUcall( NULL, NULL );
166 }
167
168
169 /*
170  * All of the window-specific callbacks setting methods can be generalized to this:
171  */
172 #define SET_CALLBACK(a)                                         \
173 do                                                              \
174 {                                                               \
175     if( fgStructure.CurrentWindow == NULL )                     \
176         return;                                                 \
177     SET_WCB( ( *( fgStructure.CurrentWindow ) ), a, callback, userData ); \
178 } while( 0 )
179 /*
180  * Types need to be defined for callbacks. It's not ideal, but it works for this.
181  */
182 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG0(a,b)                    \
183 static void glut##a##FuncCallback( FGCBUserData userData )             \
184 {                                                               \
185     FGCB##b callback = (FGCB##b)userData;                       \
186     callback();                                                 \
187 }
188 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG1(a,b)                    \
189 static void glut##a##FuncCallback( int arg1val, FGCBUserData userData ) \
190 {                                                               \
191     FGCB##b callback = (FGCB##b)userData;                       \
192     callback( arg1val );                                        \
193 }
194 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG2(a,b)                    \
195 static void glut##a##FuncCallback( int arg1val, int arg2val, FGCBUserData userData ) \
196 {                                                               \
197     FGCB##b callback = (FGCB##b)userData;                       \
198     callback( arg1val, arg2val );                               \
199 }
200 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG3_USER(a,b,arg1,arg2,arg3) \
201 static void glut##a##FuncCallback( arg1 arg1val, arg2 arg2val, arg3 arg3val, FGCBUserData userData ) \
202 {                                                               \
203     FGCB##b callback = (FGCB##b)userData;                       \
204     callback( arg1val, arg2val, arg3val );                      \
205 }
206 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG3(a,b) IMPLEMENT_CALLBACK_FUNC_CB_ARG3_USER(a,b,int,int,int)
207 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG4(a,b)                    \
208 static void glut##a##FuncCallback( int arg1val, int arg2val, int arg3val, int arg4val, FGCBUserData userData ) \
209 {                                                               \
210     FGCB##b callback = (FGCB##b)userData;                       \
211     callback( arg1val, arg2val, arg3val, arg4val );             \
212 }
213 #define IMPLEMENT_CALLBACK_FUNC_CB_ARG5(a,b)                    \
214 static void glut##a##FuncCallback( int arg1val, int arg2val, int arg3val, int arg4val, int arg5val, FGCBUserData userData ) \
215 {                                                               \
216     FGCB##b callback = (FGCB##b)userData;                       \
217     callback( arg1val, arg2val, arg3val, arg4val, arg5val );    \
218 }
219 /*
220  * And almost every time the callback setter function can be implemented like this:
221  */
222 #define IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,b)                 \
223 void FGAPIENTRY glut##a##FuncUcall( FGCB##b##UC callback, FGCBUserData userData ) \
224 {                                                               \
225     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glut"#a"FuncUcall" );   \
226     SET_CALLBACK( b );                                          \
227 }                                                               \
228 void FGAPIENTRY glut##a##Func( FGCB##b callback )               \
229 {                                                               \
230     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glut"#a"Func" );        \
231     if( callback )                                              \
232         glut##a##FuncUcall( glut##a##FuncCallback, (FGCBUserData)callback ); \
233     else                                                        \
234         glut##a##FuncUcall( NULL, NULL ); \
235 }
236 /*
237  * Combine _glut and _cb macros:
238  */
239 #define IMPLEMENT_CALLBACK_FUNC_ARG0(a)                         \
240         IMPLEMENT_CALLBACK_FUNC_CB_ARG0(a,a)                    \
241         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
242
243 #define IMPLEMENT_CALLBACK_FUNC_ARG0_2NAME(a,b)                 \
244         IMPLEMENT_CALLBACK_FUNC_CB_ARG0(a,b)                    \
245         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,b)
246
247 #define IMPLEMENT_CALLBACK_FUNC_ARG1(a)                         \
248         IMPLEMENT_CALLBACK_FUNC_CB_ARG1(a,a)                    \
249         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
250
251 #define IMPLEMENT_CALLBACK_FUNC_ARG2(a)                         \
252         IMPLEMENT_CALLBACK_FUNC_CB_ARG2(a,a)                    \
253         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
254
255 #define IMPLEMENT_CALLBACK_FUNC_ARG2_2NAME(a,b)                 \
256         IMPLEMENT_CALLBACK_FUNC_CB_ARG2(a,b)                    \
257         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,b)
258
259 #define IMPLEMENT_CALLBACK_FUNC_ARG3(a)                         \
260         IMPLEMENT_CALLBACK_FUNC_CB_ARG3(a,a)                    \
261         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
262
263 #define IMPLEMENT_CALLBACK_FUNC_ARG3_USER(a,arg1,arg2,arg3)     \
264         IMPLEMENT_CALLBACK_FUNC_CB_ARG3_USER(a,a,arg1,arg2,arg3)\
265         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
266
267 #define IMPLEMENT_CALLBACK_FUNC_ARG4(a)                         \
268         IMPLEMENT_CALLBACK_FUNC_CB_ARG4(a,a)                    \
269         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
270
271 #define IMPLEMENT_CALLBACK_FUNC_ARG5(a)                         \
272         IMPLEMENT_CALLBACK_FUNC_CB_ARG5(a,a)                    \
273         IMPLEMENT_CALLBACK_FUNC_2NAME_GLUT(a,a)
274
275 /* Implement all these callback setter functions... */
276 IMPLEMENT_CALLBACK_FUNC_ARG2(Position)
277 IMPLEMENT_CALLBACK_FUNC_ARG3_USER(Keyboard,unsigned char,int,int)
278 IMPLEMENT_CALLBACK_FUNC_ARG3_USER(KeyboardUp,unsigned char,int,int)
279 IMPLEMENT_CALLBACK_FUNC_ARG3(Special)
280 IMPLEMENT_CALLBACK_FUNC_ARG3(SpecialUp)
281 IMPLEMENT_CALLBACK_FUNC_ARG4(Mouse)
282 IMPLEMENT_CALLBACK_FUNC_ARG4(MouseWheel)
283 IMPLEMENT_CALLBACK_FUNC_ARG2(Motion)
284 IMPLEMENT_CALLBACK_FUNC_ARG2_2NAME(PassiveMotion,Passive)
285 IMPLEMENT_CALLBACK_FUNC_ARG1(Entry)
286 /* glutWMCloseFunc is an alias for glutCloseFunc; both set the window's Destroy callback */
287 IMPLEMENT_CALLBACK_FUNC_ARG0_2NAME(Close,Destroy)
288 IMPLEMENT_CALLBACK_FUNC_ARG0_2NAME(WMClose,Destroy)
289 IMPLEMENT_CALLBACK_FUNC_ARG0(OverlayDisplay)
290 IMPLEMENT_CALLBACK_FUNC_ARG1(WindowStatus)
291 IMPLEMENT_CALLBACK_FUNC_ARG2(ButtonBox)
292 IMPLEMENT_CALLBACK_FUNC_ARG2(Dials)
293 IMPLEMENT_CALLBACK_FUNC_ARG2(TabletMotion)
294 IMPLEMENT_CALLBACK_FUNC_ARG4(TabletButton)
295 IMPLEMENT_CALLBACK_FUNC_ARG2(MultiEntry)
296 IMPLEMENT_CALLBACK_FUNC_ARG5(MultiButton)
297 IMPLEMENT_CALLBACK_FUNC_ARG3(MultiMotion)
298 IMPLEMENT_CALLBACK_FUNC_ARG3(MultiPassive)
299 IMPLEMENT_CALLBACK_FUNC_ARG0(InitContext)
300 IMPLEMENT_CALLBACK_FUNC_ARG1(AppStatus)
301
302 /*
303  * Sets the Display callback for the current window
304  */
305 void FGAPIENTRY glutDisplayFuncUcall( FGCBDisplayUC callback, FGCBUserData userData )
306 {
307     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDisplayFuncUcall" );
308     if( !callback )
309         fgError( "Fatal error in program.  NULL display callback not "
310                  "permitted in GLUT 3.0+ or freeglut 2.0.1+" );
311     SET_CALLBACK( Display );
312 }
313
314 static void glutDisplayFuncCallback( FGCBUserData userData )
315 {
316     FGCBDisplay callback = (FGCBDisplay)userData;
317     callback();
318 }
319
320 void FGAPIENTRY glutDisplayFunc( FGCBDisplay callback )
321 {
322     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDisplayFunc" );
323     if( callback )
324         glutDisplayFuncUcall( glutDisplayFuncCallback, (FGCBUserData)callback );
325     else
326         glutDisplayFuncUcall( NULL, NULL );
327 }
328
329 void fghDefaultReshape( int width, int height, FGCBUserData userData )
330 {
331     glViewport( 0, 0, width, height );
332 }
333
334 void FGAPIENTRY glutReshapeFuncUcall( FGCBReshapeUC callback, FGCBUserData userData )
335 {
336     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutReshapeFuncUcall" );
337     
338     if( !callback )
339     {
340         callback = fghDefaultReshape;
341         userData = NULL;
342     }
343
344     SET_CALLBACK( Reshape );
345 }
346
347 static void glutReshapeFuncCallback( int width, int height, FGCBUserData userData )
348 {
349     FGCBReshape callback = (FGCBReshape)userData;
350     callback( width, height );
351 }
352
353 void FGAPIENTRY glutReshapeFunc( FGCBReshape callback )
354 {
355     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutReshapeFunc" );
356     if( callback )
357         glutReshapeFuncUcall( glutReshapeFuncCallback, (FGCBUserData)callback );
358     else
359         glutReshapeFuncUcall( NULL, NULL );
360 }
361
362 /*
363  * Sets the Visibility callback for the current window.
364  * NB: the Visibility func is deprecated in favor of the WindowStatus func,
365  * which provides more detail. The visibility func callback is implemented
366  * as a translation step from the windowStatus func. When the user sets the
367  * windowStatus func, any visibility func is overwritten.
368  * DEVELOPER NOTE: in the library, only invoke the window status func, this
369  * gets automatically translated to the visibility func if thats what the
370  * user has set.
371  * window status is kind of anemic on win32 as there are no window messages
372  * to notify us that the window is covered by other windows or not.
373  * Should one want to query this, see
374  * http://stackoverflow.com/questions/5445889/get-which-process-window-is-actually-visible-in-c-sharp
375  * for an implementation outline (but it would be polling based, not push based).
376  */
377 static void fghVisibility( int status, FGCBUserData userData )
378 {
379     int vis_status;
380
381     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Visibility Callback" );
382     freeglut_return_if_fail( fgStructure.CurrentWindow );
383
384     /* Translate window status func states to visibility states */
385     if( ( GLUT_HIDDEN == status )  || ( GLUT_FULLY_COVERED == status ) )
386         vis_status = GLUT_NOT_VISIBLE;
387     else    /* GLUT_FULLY_RETAINED, GLUT_PARTIALLY_RETAINED */
388         vis_status = GLUT_VISIBLE;
389
390     INVOKE_WCB( *( fgStructure.CurrentWindow ), Visibility, ( vis_status ) );
391 }
392
393 void FGAPIENTRY glutVisibilityFuncUcall( FGCBVisibilityUC callback, FGCBUserData userData )
394 {
395     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutVisibilityFuncUcall" );
396
397     if ( !callback )
398     {
399         userData = NULL;
400     }
401
402     SET_CALLBACK( Visibility );
403
404     if( callback )
405         glutWindowStatusFuncUcall( fghVisibility, NULL );
406     else
407         glutWindowStatusFuncUcall( NULL, NULL );
408 }
409
410 static void glutVisibilityFuncCallback( int visibility, FGCBUserData userData )
411 {
412     FGCBVisibility callback = (FGCBVisibility)userData;
413     callback( visibility );
414 }
415
416 void FGAPIENTRY glutVisibilityFunc( FGCBVisibility callback )
417 {
418     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutVisibilityFunc" );
419     if( callback )
420         glutVisibilityFuncUcall( glutVisibilityFuncCallback, (FGCBUserData)callback );
421     else
422         glutVisibilityFuncUcall( NULL, NULL );
423 }
424
425 /*
426  * Sets the joystick callback and polling rate for the current window
427  */
428 void FGAPIENTRY glutJoystickFuncUcall( FGCBJoystickUC callback, int pollInterval, FGCBUserData userData )
429 {
430     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickFuncUcall" );
431     fgInitialiseJoysticks ();
432
433     if ( (
434            fgStructure.CurrentWindow->State.JoystickPollRate <= 0 ||        /* Joystick callback was disabled */
435            !FETCH_WCB(*fgStructure.CurrentWindow,Joystick)
436          ) &&
437          ( 
438            callback && ( pollInterval > 0 )                                 /* but is now enabled */
439          ) )
440         ++fgState.NumActiveJoysticks;
441     else if ( ( 
442                 fgStructure.CurrentWindow->State.JoystickPollRate > 0 &&    /* Joystick callback was enabled */
443                 FETCH_WCB(*fgStructure.CurrentWindow,Joystick)
444               ) &&  
445               ( 
446                 !callback || ( pollInterval <= 0 )                          /* but is now disabled */
447               ) )
448         --fgState.NumActiveJoysticks;
449
450     SET_CALLBACK( Joystick );
451     fgStructure.CurrentWindow->State.JoystickPollRate = pollInterval;
452
453     /* set last poll time such that joystick will be polled asap */
454     fgStructure.CurrentWindow->State.JoystickLastPoll = fgElapsedTime();
455     if (fgStructure.CurrentWindow->State.JoystickLastPoll < pollInterval)
456         fgStructure.CurrentWindow->State.JoystickLastPoll = 0;
457     else
458         fgStructure.CurrentWindow->State.JoystickLastPoll -= pollInterval;
459 }
460
461 static void glutJoystickFuncCallback( unsigned int buttons, int axis0, int axis1, int axis2, FGCBUserData userData )
462 {
463     FGCBJoystick callback = (FGCBJoystick)userData;
464     callback( buttons, axis0, axis1, axis2 );
465 }
466
467 void FGAPIENTRY glutJoystickFunc( FGCBJoystick callback, int pollInterval )
468 {
469     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickFunc" );
470     if( callback )
471         glutJoystickFuncUcall( glutJoystickFuncCallback, pollInterval, (FGCBUserData)callback );
472     else
473         glutJoystickFuncUcall( NULL, pollInterval, NULL );
474 }
475
476 /*
477  * Sets the spaceball motion callback for the current window
478  */
479 void FGAPIENTRY glutSpaceballMotionFuncUcall( FGCBSpaceMotionUC callback, FGCBUserData userData )
480 {
481     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFuncUcall" );
482     fgInitialiseSpaceball();
483
484     SET_CALLBACK( SpaceMotion );
485 }
486
487 static void glutSpaceballMotionFuncCallback( int x, int y, int z, FGCBUserData userData )
488 {
489     FGCBSpaceMotion callback = (FGCBSpaceMotion)userData;
490     callback( x, y, z );
491 }
492
493 void FGAPIENTRY glutSpaceballMotionFunc( FGCBSpaceMotion callback )
494 {
495     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFunc" );
496     if( callback )
497         glutSpaceballMotionFuncUcall( glutSpaceballMotionFuncCallback, (FGCBUserData)callback );
498     else
499         glutSpaceballMotionFuncUcall( NULL, NULL );
500 }
501
502 /*
503  * Sets the spaceball rotate callback for the current window
504  */
505 void FGAPIENTRY glutSpaceballRotateFuncUcall( FGCBSpaceRotationUC callback, FGCBUserData userData )
506 {
507     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFuncUcall" );
508     fgInitialiseSpaceball();
509
510     SET_CALLBACK( SpaceRotation );
511 }
512
513 static void glutSpaceballRotateFuncCallback( int x, int y, int z, FGCBUserData userData )
514 {
515     FGCBSpaceRotation callback = (FGCBSpaceRotation)userData;
516     callback( x, y, z );
517 }
518
519 void FGAPIENTRY glutSpaceballRotateFunc( FGCBSpaceRotation callback )
520 {
521     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFunc" );
522     if( callback )
523         glutSpaceballRotateFuncUcall( glutSpaceballRotateFuncCallback, (FGCBUserData)callback );
524     else
525         glutSpaceballRotateFuncUcall( NULL, NULL );
526 }
527
528 /*
529  * Sets the spaceball button callback for the current window
530  */
531 void FGAPIENTRY glutSpaceballButtonFuncUcall( FGCBSpaceButtonUC callback, FGCBUserData userData )
532 {
533     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFuncUcall" );
534     fgInitialiseSpaceball();
535
536     SET_CALLBACK( SpaceButton );
537 }
538
539 static void glutSpaceballButtonFuncCallback( int button, int buttonState, FGCBUserData userData )
540 {
541     FGCBSpaceButton callback = (FGCBSpaceButton)userData;
542     callback( button, buttonState );
543 }
544
545 void FGAPIENTRY glutSpaceballButtonFunc( FGCBSpaceButton callback )
546 {
547     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFunc" );
548     if( callback )
549         glutSpaceballButtonFuncUcall( glutSpaceballButtonFuncCallback, (FGCBUserData)callback );
550     else
551         glutSpaceballButtonFuncUcall( NULL, NULL );
552 }
553
554 /*** END OF FILE ***/