| 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 glutIdleFunc( FGCBIdle callback ) |
| 39 | { |
| 40 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutIdleFunc" ); |
| 41 | fgState.IdleCallback = callback; |
| 42 | } |
| 43 | |
| 44 | /* Creates a timer and sets its callback */ |
| 45 | void FGAPIENTRY glutTimerFunc( unsigned int timeOut, FGCBTimer callback, int timerID ) |
| 46 | { |
| 47 | SFG_Timer *timer, *node; |
| 48 | |
| 49 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutTimerFunc" ); |
| 50 | |
| 51 | if( (timer = fgState.FreeTimers.Last) ) |
| 52 | { |
| 53 | fgListRemove( &fgState.FreeTimers, &timer->Node ); |
| 54 | } |
| 55 | else |
| 56 | { |
| 57 | if( ! (timer = malloc(sizeof(SFG_Timer))) ) |
| 58 | fgError( "Fatal error: " |
| 59 | "Memory allocation failure in glutTimerFunc()" ); |
| 60 | } |
| 61 | |
| 62 | timer->Callback = callback; |
| 63 | timer->ID = timerID; |
| 64 | timer->TriggerTime = fgElapsedTime() + timeOut; |
| 65 | |
| 66 | /* Insert such that timers are sorted by end-time */ |
| 67 | for( node = fgState.Timers.First; node; node = node->Node.Next ) |
| 68 | { |
| 69 | if( node->TriggerTime > timer->TriggerTime ) |
| 70 | break; |
| 71 | } |
| 72 | |
| 73 | fgListInsert( &fgState.Timers, &node->Node, &timer->Node ); |
| 74 | } |
| 75 | |
| 76 | /* Deprecated version of glutMenuStatusFunc callback setting method */ |
| 77 | void FGAPIENTRY ( FGCBMenuState callback ) |
| 78 | { |
| 79 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuStateFunc" ); |
| 80 | fgState.MenuStateCallback = callback; |
| 81 | } |
| 82 | |
| 83 | /* Sets the global menu status callback for the current window */ |
| 84 | void FGAPIENTRY ( FGCBMenuStatus callback ) |
| 85 | { |
| 86 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuStatusFunc" ); |
| 87 | fgState.MenuStatusCallback = callback; |
| 88 | } |
| 89 | |
| 90 | |
| 91 | /* |
| 92 | * Menu specific callbacks. |
| 93 | */ |
| 94 | /* Callback upon menu destruction */ |
| 95 | void FGAPIENTRY ( FGCBDestroy callback ) |
| 96 | { |
| 97 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMenuDestroyFunc" ); |
| 98 | if( fgStructure.CurrentMenu ) |
| 99 | fgStructure.CurrentMenu->Destroy = callback; |
| 100 | } |
| 101 | |
| 102 | |
| 103 | /* |
| 104 | * All of the window-specific callbacks setting methods can be generalized to this: |
| 105 | */ |
| 106 | #define SET_CALLBACK(a) \ |
| 107 | do \ |
| 108 | { \ |
| 109 | if( fgStructure.CurrentWindow == NULL ) \ |
| 110 | return; \ |
| 111 | SET_WCB( ( *( fgStructure.CurrentWindow ) ), a, callback ); \ |
| 112 | } while( 0 ) |
| 113 | /* |
| 114 | * And almost every time the callback setter function can be implemented like this: |
| 115 | */ |
| 116 | #define IMPLEMENT_CALLBACK_FUNC_2NAME(a,b) \ |
| 117 | void FGAPIENTRY glut##a##Func( FGCB##b callback ) \ |
| 118 | { \ |
| 119 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glut"#a"Func" ); \ |
| 120 | SET_CALLBACK( b ); \ |
| 121 | } |
| 122 | #define IMPLEMENT_CALLBACK_FUNC(a) IMPLEMENT_CALLBACK_FUNC_2NAME(a,a) |
| 123 | |
| 124 | /* Implement all these callback setter functions... */ |
| 125 | IMPLEMENT_CALLBACK_FUNC(Position) |
| 126 | IMPLEMENT_CALLBACK_FUNC(Keyboard) |
| 127 | IMPLEMENT_CALLBACK_FUNC(KeyboardExt) |
| 128 | IMPLEMENT_CALLBACK_FUNC(KeyboardDown) |
| 129 | IMPLEMENT_CALLBACK_FUNC(KeyboardUp) |
| 130 | IMPLEMENT_CALLBACK_FUNC(Special) |
| 131 | IMPLEMENT_CALLBACK_FUNC(SpecialUp) |
| 132 | IMPLEMENT_CALLBACK_FUNC(Mouse) |
| 133 | IMPLEMENT_CALLBACK_FUNC(MouseWheel) |
| 134 | IMPLEMENT_CALLBACK_FUNC(Motion) |
| 135 | IMPLEMENT_CALLBACK_FUNC_2NAME(PassiveMotion,Passive) |
| 136 | IMPLEMENT_CALLBACK_FUNC(Entry) |
| 137 | /* glutWMCloseFunc is an alias for glutCloseFunc; both set the window's Destroy callback */ |
| 138 | IMPLEMENT_CALLBACK_FUNC_2NAME(Close,Destroy) |
| 139 | IMPLEMENT_CALLBACK_FUNC_2NAME(WMClose,Destroy) |
| 140 | IMPLEMENT_CALLBACK_FUNC(OverlayDisplay) |
| 141 | IMPLEMENT_CALLBACK_FUNC(WindowStatus) |
| 142 | IMPLEMENT_CALLBACK_FUNC(ButtonBox) |
| 143 | IMPLEMENT_CALLBACK_FUNC(Dials) |
| 144 | IMPLEMENT_CALLBACK_FUNC(TabletMotion) |
| 145 | IMPLEMENT_CALLBACK_FUNC(TabletButton) |
| 146 | IMPLEMENT_CALLBACK_FUNC(MultiEntry) |
| 147 | IMPLEMENT_CALLBACK_FUNC(MultiButton) |
| 148 | IMPLEMENT_CALLBACK_FUNC(MultiMotion) |
| 149 | IMPLEMENT_CALLBACK_FUNC(MultiPassive) |
| 150 | IMPLEMENT_CALLBACK_FUNC(InitContext) |
| 151 | IMPLEMENT_CALLBACK_FUNC(AppStatus) |
| 152 | |
| 153 | |
| 154 | |
| 155 | /* |
| 156 | * Sets the Display callback for the current window |
| 157 | */ |
| 158 | void FGAPIENTRY glutDisplayFunc( FGCBDisplay callback ) |
| 159 | { |
| 160 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutDisplayFunc" ); |
| 161 | if( !callback ) |
| 162 | fgError( "Fatal error in program. NULL display callback not " |
| 163 | "permitted in GLUT 3.0+ or freeglut 2.0.1+" ); |
| 164 | SET_CALLBACK( Display ); |
| 165 | } |
| 166 | |
| 167 | void fghDefaultReshape(int width, int height) |
| 168 | { |
| 169 | glViewport( 0, 0, width, height ); |
| 170 | } |
| 171 | |
| 172 | void FGAPIENTRY glutReshapeFunc( FGCBReshape callback ) |
| 173 | { |
| 174 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutReshapeFunc" ); |
| 175 | |
| 176 | if( !callback ) |
| 177 | callback = fghDefaultReshape; |
| 178 | |
| 179 | SET_CALLBACK( Reshape ); |
| 180 | } |
| 181 | |
| 182 | /* |
| 183 | * Sets the Visibility callback for the current window. |
| 184 | * NB: the Visibility func is deprecated in favor of the WindowStatus func, |
| 185 | * which provides more detail. The visibility func callback is implemented |
| 186 | * as a translation step from the windowStatus func. When the user sets the |
| 187 | * windowStatus func, any visibility func is overwritten. |
| 188 | * DEVELOPER NOTE: in the library, only invoke the window status func, this |
| 189 | * gets automatically translated to the visibility func if thats what the |
| 190 | * user has set. |
| 191 | * window status is kind of anemic on win32 as there are no window messages |
| 192 | * to notify us that the window is covered by other windows or not. |
| 193 | * Should one want to query this, see |
| 194 | * http://stackoverflow.com/questions/5445889/get-which-process-window-is-actually-visible-in-c-sharp |
| 195 | * for an implementation outline (but it would be polling based, not push based). |
| 196 | */ |
| 197 | static void fghVisibility( int status ) |
| 198 | { |
| 199 | int vis_status; |
| 200 | |
| 201 | FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Visibility Callback" ); |
| 202 | freeglut_return_if_fail( fgStructure.CurrentWindow ); |
| 203 | |
| 204 | /* Translate window status func states to visibility states */ |
| 205 | if( ( GLUT_HIDDEN == status ) || ( GLUT_FULLY_COVERED == status ) ) |
| 206 | vis_status = GLUT_NOT_VISIBLE; |
| 207 | else /* GLUT_FULLY_RETAINED, GLUT_PARTIALLY_RETAINED */ |
| 208 | vis_status = GLUT_VISIBLE; |
| 209 | |
| 210 | INVOKE_WCB( *( fgStructure.CurrentWindow ), Visibility, ( vis_status ) ); |
| 211 | } |
| 212 | |
| 213 | void FGAPIENTRY glutVisibilityFunc( FGCBVisibility callback ) |
| 214 | { |
| 215 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutVisibilityFunc" ); |
| 216 | SET_CALLBACK( Visibility ); |
| 217 | |
| 218 | if( callback ) |
| 219 | glutWindowStatusFunc( fghVisibility ); |
| 220 | else |
| 221 | glutWindowStatusFunc( NULL ); |
| 222 | } |
| 223 | |
| 224 | /* |
| 225 | * Sets the joystick callback and polling rate for the current window |
| 226 | */ |
| 227 | void FGAPIENTRY glutJoystickFunc( FGCBJoystick callback, int pollInterval ) |
| 228 | { |
| 229 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutJoystickFunc" ); |
| 230 | fgInitialiseJoysticks (); |
| 231 | |
| 232 | if ( ( |
| 233 | fgStructure.CurrentWindow->State.JoystickPollRate <= 0 || /* Joystick callback was disabled */ |
| 234 | !FETCH_WCB(*fgStructure.CurrentWindow,Joystick) |
| 235 | ) && |
| 236 | ( |
| 237 | callback && ( pollInterval > 0 ) /* but is now enabled */ |
| 238 | ) ) |
| 239 | ++fgState.NumActiveJoysticks; |
| 240 | else if ( ( |
| 241 | fgStructure.CurrentWindow->State.JoystickPollRate > 0 && /* Joystick callback was enabled */ |
| 242 | FETCH_WCB(*fgStructure.CurrentWindow,Joystick) |
| 243 | ) && |
| 244 | ( |
| 245 | !callback || ( pollInterval <= 0 ) /* but is now disabled */ |
| 246 | ) ) |
| 247 | --fgState.NumActiveJoysticks; |
| 248 | |
| 249 | SET_CALLBACK( Joystick ); |
| 250 | fgStructure.CurrentWindow->State.JoystickPollRate = pollInterval; |
| 251 | |
| 252 | /* set last poll time such that joystick will be polled asap */ |
| 253 | fgStructure.CurrentWindow->State.JoystickLastPoll = fgElapsedTime(); |
| 254 | if (fgStructure.CurrentWindow->State.JoystickLastPoll < pollInterval) |
| 255 | fgStructure.CurrentWindow->State.JoystickLastPoll = 0; |
| 256 | else |
| 257 | fgStructure.CurrentWindow->State.JoystickLastPoll -= pollInterval; |
| 258 | } |
| 259 | |
| 260 | |
| 261 | |
| 262 | /* |
| 263 | * Sets the spaceball motion callback for the current window |
| 264 | */ |
| 265 | void FGAPIENTRY glutSpaceballMotionFunc( FGCBSpaceMotion callback ) |
| 266 | { |
| 267 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballMotionFunc" ); |
| 268 | fgInitialiseSpaceball(); |
| 269 | |
| 270 | SET_CALLBACK( SpaceMotion ); |
| 271 | } |
| 272 | |
| 273 | /* |
| 274 | * Sets the spaceball rotate callback for the current window |
| 275 | */ |
| 276 | void FGAPIENTRY glutSpaceballRotateFunc( FGCBSpaceRotation callback ) |
| 277 | { |
| 278 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballRotateFunc" ); |
| 279 | fgInitialiseSpaceball(); |
| 280 | |
| 281 | SET_CALLBACK( SpaceRotation ); |
| 282 | } |
| 283 | |
| 284 | /* |
| 285 | * Sets the spaceball button callback for the current window |
| 286 | */ |
| 287 | void FGAPIENTRY glutSpaceballButtonFunc( FGCBSpaceButton callback ) |
| 288 | { |
| 289 | FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutSpaceballButtonFunc" ); |
| 290 | fgInitialiseSpaceball(); |
| 291 | |
| 292 | SET_CALLBACK( SpaceButton ); |
| 293 | } |
| 294 | |
| 295 | /*** END OF FILE ***/ |
| 296 | |