Moved as many callback setters as possible to macro function generators
[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  * Global callbacks.
35  */
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 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG0(Idle)
46
47 /* Creates a timer and sets its callback */
48 void FGAPIENTRY glutTimerFuncUcall( unsigned int timeOut, FGCBTimerUC callback, int timerID, FGCBUserData userData )
49 {
50     SFG_Timer *timer, *node;
51
52     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutTimerFuncUcall" );
53
54     if( (timer = fgState.FreeTimers.Last) )
55     {
56         fgListRemove( &fgState.FreeTimers, &timer->Node );
57     }
58     else
59     {
60         if( ! (timer = malloc(sizeof(SFG_Timer))) )
61             fgError( "Fatal error: "
62                      "Memory allocation failure in glutTimerFunc()" );
63     }
64
65     timer->Callback     = callback;
66     timer->CallbackData = userData;
67     timer->ID           = timerID;
68     timer->TriggerTime  = fgElapsedTime() + timeOut;
69
70     /* Insert such that timers are sorted by end-time */
71     for( node = fgState.Timers.First; node; node = node->Node.Next )
72     {
73         if( node->TriggerTime > timer->TriggerTime )
74             break;
75     }
76
77     fgListInsert( &fgState.Timers, &node->Node, &timer->Node );
78 }
79
80 IMPLEMENT_CALLBACK_FUNC_CB_ARG1(Timer, Timer)
81
82 void FGAPIENTRY glutTimerFunc( unsigned int timeOut, FGCBTimer callback, int timerID )
83 {
84     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutTimerFunc" );
85     if( callback )
86         glutTimerFuncUcall( timeOut, fghTimerFuncCallback, timerID, (FGCBUserData)callback );
87     else
88         glutTimerFuncUcall( timeOut, NULL, timerID, NULL );
89 }
90
91 /* Deprecated version of glutMenuStatusFunc callback setting method */
92 void FGAPIENTRY glutMenuStateFunc( FGCBMenuState callback )
93 {
94     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuStateFunc" );
95     fgState.MenuStateCallback = callback;
96 }
97
98 /* Sets the global menu status callback for the current window */
99 void FGAPIENTRY glutMenuStatusFuncUcall( FGCBMenuStatusUC callback, FGCBUserData userData )
100 {
101     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuStatusFuncUcall" );
102     fgState.MenuStatusCallback = callback;
103     fgState.MenuStatusCallbackData = userData;
104 }
105
106 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG3(MenuStatus)
107
108 /*
109  * Menu specific callbacks.
110  */
111 /* Callback upon menu destruction */
112 void FGAPIENTRY glutMenuDestroyFuncUcall( FGCBDestroyUC callback, FGCBUserData userData )
113 {
114     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuDestroyFuncUcall" );
115     if( fgStructure.CurrentMenu )
116     {
117         fgStructure.CurrentMenu->Destroy = callback;
118         fgStructure.CurrentMenu->DestroyData = userData;
119     }
120 }
121
122 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG0_2NAME(MenuDestroy, Destroy)
123
124 /* Implement all these callback setter functions... */
125 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG2(Position)
126 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG3_USER(Keyboard,unsigned char,int,int)
127 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG3_USER(KeyboardUp,unsigned char,int,int)
128 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG3(Special)
129 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG3(SpecialUp)
130 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG4(Mouse)
131 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG4(MouseWheel)
132 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG2(Motion)
133 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG2_2NAME(PassiveMotion,Passive)
134 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG1(Entry)
135 /* glutWMCloseFunc is an alias for glutCloseFunc; both set the window's Destroy callback */
136 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG0_2NAME(Close,Destroy)
137 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG0_2NAME(WMClose,Destroy)
138 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG0(OverlayDisplay)
139 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG1(WindowStatus)
140 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG2(ButtonBox)
141 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG2(Dials)
142 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG2(TabletMotion)
143 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG4(TabletButton)
144 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG2(MultiEntry)
145 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG5(MultiButton)
146 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG3(MultiMotion)
147 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG3(MultiPassive)
148 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG0(InitContext)
149 IMPLEMENT_CURRENT_WINDOW_CALLBACK_FUNC_ARG1(AppStatus)
150
151 /*
152  * Sets the Display callback for the current window
153  */
154 void FGAPIENTRY glutDisplayFuncUcall( FGCBDisplayUC callback, FGCBUserData userData )
155 {
156     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDisplayFuncUcall" );
157     if( !callback )
158         fgError( "Fatal error in program.  NULL display callback not "
159                  "permitted in GLUT 3.0+ or freeglut 2.0.1+" );
160     SET_CURRENT_WINDOW_CALLBACK( Display );
161 }
162
163 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG0(Display)
164
165 void fghDefaultReshape( int width, int height, FGCBUserData userData )
166 {
167     glViewport( 0, 0, width, height );
168 }
169
170 void FGAPIENTRY glutReshapeFuncUcall( FGCBReshapeUC callback, FGCBUserData userData )
171 {
172     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutReshapeFuncUcall" );
173     
174     if( !callback )
175     {
176         callback = fghDefaultReshape;
177         userData = NULL;
178     }
179
180     SET_CURRENT_WINDOW_CALLBACK( Reshape );
181 }
182
183 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG2(Reshape)
184
185 /*
186  * Sets the Visibility callback for the current window.
187  * NB: the Visibility func is deprecated in favor of the WindowStatus func,
188  * which provides more detail. The visibility func callback is implemented
189  * as a translation step from the windowStatus func. When the user sets the
190  * windowStatus func, any visibility func is overwritten.
191  * DEVELOPER NOTE: in the library, only invoke the window status func, this
192  * gets automatically translated to the visibility func if thats what the
193  * user has set.
194  * window status is kind of anemic on win32 as there are no window messages
195  * to notify us that the window is covered by other windows or not.
196  * Should one want to query this, see
197  * http://stackoverflow.com/questions/5445889/get-which-process-window-is-actually-visible-in-c-sharp
198  * for an implementation outline (but it would be polling based, not push based).
199  */
200 static void fghVisibility( int status, FGCBUserData userData )
201 {
202     int vis_status;
203
204     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Visibility Callback" );
205     freeglut_return_if_fail( fgStructure.CurrentWindow );
206
207     /* Translate window status func states to visibility states */
208     if( ( status == GLUT_HIDDEN)  || ( status == GLUT_FULLY_COVERED) )
209         vis_status = GLUT_NOT_VISIBLE;
210     else    /* GLUT_FULLY_RETAINED, GLUT_PARTIALLY_RETAINED */
211         vis_status = GLUT_VISIBLE;
212
213     INVOKE_WCB( *( fgStructure.CurrentWindow ), Visibility, ( vis_status ) );
214 }
215
216 void FGAPIENTRY glutVisibilityFuncUcall( FGCBVisibilityUC callback, FGCBUserData userData )
217 {
218     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutVisibilityFuncUcall" );
219
220     if ( !callback )
221     {
222         userData = NULL;
223     }
224
225     SET_CURRENT_WINDOW_CALLBACK( Visibility );
226
227     if( callback )
228         glutWindowStatusFuncUcall( fghVisibility, NULL );
229     else
230         glutWindowStatusFuncUcall( NULL, NULL );
231 }
232
233 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG1(Visibility)
234
235 /*
236  * Sets the joystick callback and polling rate for the current window
237  */
238 void FGAPIENTRY glutJoystickFuncUcall( FGCBJoystickUC callback, int pollInterval, FGCBUserData userData )
239 {
240     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickFuncUcall" );
241     fgInitialiseJoysticks ();
242
243     if ( (
244            fgStructure.CurrentWindow->State.JoystickPollRate <= 0 ||        /* Joystick callback was disabled */
245            !FETCH_WCB(*fgStructure.CurrentWindow,Joystick)
246          ) &&
247          ( 
248            callback && ( pollInterval > 0 )                                 /* but is now enabled */
249          ) )
250         ++fgState.NumActiveJoysticks;
251     else if ( ( 
252                 fgStructure.CurrentWindow->State.JoystickPollRate > 0 &&    /* Joystick callback was enabled */
253                 FETCH_WCB(*fgStructure.CurrentWindow,Joystick)
254               ) &&  
255               ( 
256                 !callback || ( pollInterval <= 0 )                          /* but is now disabled */
257               ) )
258         --fgState.NumActiveJoysticks;
259
260     SET_CURRENT_WINDOW_CALLBACK( Joystick );
261     fgStructure.CurrentWindow->State.JoystickPollRate = pollInterval;
262
263     /* set last poll time such that joystick will be polled asap */
264     fgStructure.CurrentWindow->State.JoystickLastPoll = fgElapsedTime();
265     if (fgStructure.CurrentWindow->State.JoystickLastPoll < pollInterval)
266         fgStructure.CurrentWindow->State.JoystickLastPoll = 0;
267     else
268         fgStructure.CurrentWindow->State.JoystickLastPoll -= pollInterval;
269 }
270
271 static void fghJoystickFuncCallback( unsigned int buttons, int axis0, int axis1, int axis2, FGCBUserData userData )
272 {
273     FGCBJoystick callback = (FGCBJoystick)userData;
274     callback( buttons, axis0, axis1, axis2 );
275 }
276
277 void FGAPIENTRY glutJoystickFunc( FGCBJoystick callback, int pollInterval )
278 {
279     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickFunc" );
280     if( callback )
281         glutJoystickFuncUcall( fghJoystickFuncCallback, pollInterval, (FGCBUserData)callback );
282     else
283         glutJoystickFuncUcall( NULL, pollInterval, NULL );
284 }
285
286 /*
287  * Sets the spaceball motion callback for the current window
288  */
289 void FGAPIENTRY glutSpaceballMotionFuncUcall( FGCBSpaceMotionUC callback, FGCBUserData userData )
290 {
291     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFuncUcall" );
292     fgInitialiseSpaceball();
293
294     SET_CURRENT_WINDOW_CALLBACK( SpaceMotion );
295 }
296
297 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG3_2NAME(SpaceballMotion, SpaceMotion)
298
299 /*
300  * Sets the spaceball rotate callback for the current window
301  */
302 void FGAPIENTRY glutSpaceballRotateFuncUcall( FGCBSpaceRotationUC callback, FGCBUserData userData )
303 {
304     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFuncUcall" );
305     fgInitialiseSpaceball();
306
307     SET_CURRENT_WINDOW_CALLBACK( SpaceRotation );
308 }
309
310 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG3_2NAME(SpaceballRotate, SpaceRotation)
311
312 /*
313  * Sets the spaceball button callback for the current window
314  */
315 void FGAPIENTRY glutSpaceballButtonFuncUcall( FGCBSpaceButtonUC callback, FGCBUserData userData )
316 {
317     FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFuncUcall" );
318     fgInitialiseSpaceball();
319
320     SET_CURRENT_WINDOW_CALLBACK( SpaceButton );
321 }
322
323 IMPLEMENT_GLUT_CALLBACK_FUNC_ARG2_2NAME(SpaceballButton, SpaceButton)
324
325 /*** END OF FILE ***/