Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/freeglut/src/fg_main.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 2 * fg_main.c | |
| 3 * | |
| 4 * The windows message processing 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 #include <errno.h> | |
| 31 #include <stdarg.h> | |
| 32 | |
| 33 /* | |
| 34 * Try to get the maximum value allowed for ints, falling back to the minimum | |
| 35 * guaranteed by ISO C99 if there is no suitable header. | |
| 36 */ | |
| 37 #ifdef HAVE_LIMITS_H | |
| 38 # include <limits.h> | |
| 39 #endif | |
| 40 #ifndef INT_MAX | |
| 41 # define INT_MAX 32767 | |
| 42 #endif | |
| 43 | |
| 44 #ifndef MIN | |
| 45 # define MIN(a,b) (((a)<(b)) ? (a) : (b)) | |
| 46 #endif | |
| 47 | |
| 48 extern void fgProcessWork ( SFG_Window *window ); | |
| 49 extern fg_time_t fgPlatformSystemTime ( void ); | |
| 50 extern void fgPlatformSleepForEvents( fg_time_t msec ); | |
| 51 extern void fgPlatformProcessSingleEvent ( void ); | |
| 52 extern void fgPlatformMainLoopPreliminaryWork ( void ); | |
| 53 | |
| 54 extern void fgPlatformInitWork(SFG_Window* window); | |
| 55 extern void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask); | |
| 56 extern void fgPlatformVisibilityWork(SFG_Window* window); | |
| 57 | |
| 58 | |
| 59 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ | |
| 60 | |
| 61 void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify) | |
| 62 { | |
| 63 GLboolean notify = GL_FALSE; | |
| 64 | |
| 65 if( width != window->State.Width || | |
| 66 height != window->State.Height ) | |
| 67 { | |
| 68 window->State.Width = width; | |
| 69 window->State.Height = height; | |
| 70 | |
| 71 notify = GL_TRUE; | |
| 72 } | |
| 73 | |
| 74 if (notify || forceNotify) | |
| 75 { | |
| 76 SFG_Window *saved_window = fgStructure.CurrentWindow; | |
| 77 | |
| 78 INVOKE_WCB( *window, Reshape, ( width, height ) ); | |
| 79 | |
| 80 /* | |
| 81 * Force a window redraw. In Windows at least this is only a partial | |
| 82 * solution: if the window is increasing in size in either dimension, | |
| 83 * the already-drawn part does not get drawn again and things look funny. | |
| 84 * But without this we get this bad behaviour whenever we resize the | |
| 85 * window. | |
| 86 * DN: Hmm.. the above sounds like a concern only in single buffered mode... | |
| 87 */ | |
| 88 window->State.WorkMask |= GLUT_DISPLAY_WORK; | |
| 89 if( window->IsMenu ) | |
| 90 fgSetWindow( saved_window ); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify) | |
| 95 { | |
| 96 GLboolean notify = GL_FALSE; | |
| 97 | |
| 98 if( x != window->State.Xpos || | |
| 99 y != window->State.Ypos ) | |
| 100 { | |
| 101 window->State.Xpos = x; | |
| 102 window->State.Ypos = y; | |
| 103 | |
| 104 notify = GL_TRUE; | |
| 105 } | |
| 106 | |
| 107 if (notify || forceNotify) | |
| 108 { | |
| 109 SFG_Window *saved_window = fgStructure.CurrentWindow; | |
| 110 INVOKE_WCB( *window, Position, ( x, y ) ); | |
| 111 fgSetWindow( saved_window ); | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 /* | |
| 116 * Calls a window's redraw method. This is used when | |
| 117 * a redraw is forced by the incoming window messages, | |
| 118 * or if a redisplay is otherwise pending. | |
| 119 * this is lean and mean without checks as it is | |
| 120 * currently only called from fghcbDisplayWindow which | |
| 121 * only calls this if the window is visible and needs | |
| 122 * a redisplay. | |
| 123 * Note that the fgSetWindow call on Windows makes the | |
| 124 * right device context current on windows, allowing | |
| 125 * direct drawing without BeginPaint/EndPaint in the | |
| 126 * WM_PAINT handler. | |
| 127 */ | |
| 128 void fghRedrawWindow ( SFG_Window *window ) | |
| 129 { | |
| 130 SFG_Window *current_window = fgStructure.CurrentWindow; | |
| 131 | |
| 132 fgSetWindow( window ); | |
| 133 INVOKE_WCB( *window, Display, ( ) ); | |
| 134 | |
| 135 fgSetWindow( current_window ); | |
| 136 } | |
| 137 | |
| 138 void fghRedrawWindowAndChildren ( SFG_Window *window ) | |
| 139 { | |
| 140 SFG_Window* child; | |
| 141 | |
| 142 fghRedrawWindow(window); | |
| 143 | |
| 144 for( child = ( SFG_Window * )window->Children.First; | |
| 145 child; | |
| 146 child = ( SFG_Window * )child->Node.Next ) | |
| 147 { | |
| 148 fghRedrawWindowAndChildren(child); | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 | |
| 153 static void fghcbProcessWork( SFG_Window *window, | |
| 154 SFG_Enumerator *enumerator ) | |
| 155 { | |
| 156 if( window->State.WorkMask ) | |
| 157 fgProcessWork ( window ); | |
| 158 | |
| 159 fgEnumSubWindows( window, fghcbProcessWork, enumerator ); | |
| 160 } | |
| 161 | |
| 162 /* | |
| 163 * Make all windows process their work list | |
| 164 */ | |
| 165 static void fghProcessWork( void ) | |
| 166 { | |
| 167 SFG_Enumerator enumerator; | |
| 168 | |
| 169 enumerator.found = GL_FALSE; | |
| 170 enumerator.data = NULL; | |
| 171 | |
| 172 fgEnumWindows( fghcbProcessWork, &enumerator ); | |
| 173 } | |
| 174 | |
| 175 /* | |
| 176 * Window enumerator callback to check for the joystick polling code | |
| 177 */ | |
| 178 static void fghcbCheckJoystickPolls( SFG_Window *window, | |
| 179 SFG_Enumerator *enumerator ) | |
| 180 { | |
| 181 fg_time_t checkTime; | |
| 182 | |
| 183 if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick )) | |
| 184 { | |
| 185 /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */ | |
| 186 checkTime= fgElapsedTime( ); | |
| 187 | |
| 188 if( window->State.JoystickLastPoll + window->State.JoystickPollRate <= | |
| 189 checkTime ) | |
| 190 { | |
| 191 #if !defined(_WIN32_WCE) | |
| 192 fgJoystickPollWindow( window ); | |
| 193 #endif /* !defined(_WIN32_WCE) */ | |
| 194 window->State.JoystickLastPoll = checkTime; | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator ); | |
| 199 } | |
| 200 | |
| 201 /* | |
| 202 * Check all windows for joystick polling | |
| 203 * | |
| 204 * The real way to do this is to make use of the glutTimer() API | |
| 205 * to more cleanly re-implement the joystick API. Then, this code | |
| 206 * and all other "joystick timer" code can be yanked. | |
| 207 */ | |
| 208 static void fghCheckJoystickPolls( void ) | |
| 209 { | |
| 210 SFG_Enumerator enumerator; | |
| 211 | |
| 212 enumerator.found = GL_FALSE; | |
| 213 enumerator.data = NULL; | |
| 214 | |
| 215 fgEnumWindows( fghcbCheckJoystickPolls, &enumerator ); | |
| 216 } | |
| 217 | |
| 218 /* | |
| 219 * Check the global timers | |
| 220 */ | |
| 221 static void fghCheckTimers( void ) | |
| 222 { | |
| 223 fg_time_t checkTime = fgElapsedTime( ); | |
| 224 | |
| 225 while( fgState.Timers.First ) | |
| 226 { | |
| 227 SFG_Timer *timer = fgState.Timers.First; | |
| 228 | |
| 229 if( timer->TriggerTime > checkTime ) | |
| 230 /* Timers are sorted by triggerTime */ | |
| 231 break; | |
| 232 | |
| 233 fgListRemove( &fgState.Timers, &timer->Node ); | |
| 234 fgListAppend( &fgState.FreeTimers, &timer->Node ); | |
| 235 | |
| 236 timer->Callback( timer->ID ); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 | |
| 241 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer. | |
| 242 * This doesn't overflow in any reasonable time, so no need to worry about | |
| 243 * that. The GLUT API return value will however overflow after 49.7 days, | |
| 244 * which means you will still get in trouble when running the | |
| 245 * application for more than 49.7 days. | |
| 246 */ | |
| 247 fg_time_t fgSystemTime(void) | |
| 248 { | |
| 249 return fgPlatformSystemTime(); | |
| 250 } | |
| 251 | |
| 252 /* | |
| 253 * Elapsed Time | |
| 254 */ | |
| 255 fg_time_t fgElapsedTime( void ) | |
| 256 { | |
| 257 return fgSystemTime() - fgState.Time; | |
| 258 } | |
| 259 | |
| 260 /* | |
| 261 * Error Messages. | |
| 262 */ | |
| 263 void fgError( const char *fmt, ... ) | |
| 264 { | |
| 265 va_list ap; | |
| 266 | |
| 267 if (fgState.ErrorFunc) { | |
| 268 | |
| 269 va_start( ap, fmt ); | |
| 270 | |
| 271 /* call user set error handler here */ | |
| 272 fgState.ErrorFunc(fmt, ap); | |
| 273 | |
| 274 va_end( ap ); | |
| 275 | |
| 276 } else { | |
| 277 #ifdef FREEGLUT_PRINT_ERRORS | |
| 278 va_start( ap, fmt ); | |
| 279 | |
| 280 fprintf( stderr, "freeglut "); | |
| 281 if( fgState.ProgramName ) | |
| 282 fprintf( stderr, "(%s): ", fgState.ProgramName ); | |
| 283 vfprintf( stderr, fmt, ap ); | |
| 284 fprintf( stderr, "\n" ); | |
| 285 | |
| 286 va_end( ap ); | |
| 287 #endif | |
| 288 | |
| 289 if ( fgState.Initialised ) | |
| 290 fgDeinitialize (); | |
| 291 | |
| 292 exit( 1 ); | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 void fgWarning( const char *fmt, ... ) | |
| 297 { | |
| 298 va_list ap; | |
| 299 | |
| 300 if (fgState.WarningFunc) { | |
| 301 | |
| 302 va_start( ap, fmt ); | |
| 303 | |
| 304 /* call user set warning handler here */ | |
| 305 fgState.WarningFunc(fmt, ap); | |
| 306 | |
| 307 va_end( ap ); | |
| 308 | |
| 309 } else { | |
| 310 #ifdef FREEGLUT_PRINT_WARNINGS | |
| 311 va_start( ap, fmt ); | |
| 312 | |
| 313 fprintf( stderr, "freeglut "); | |
| 314 if( fgState.ProgramName ) | |
| 315 fprintf( stderr, "(%s): ", fgState.ProgramName ); | |
| 316 vfprintf( stderr, fmt, ap ); | |
| 317 fprintf( stderr, "\n" ); | |
| 318 | |
| 319 va_end( ap ); | |
| 320 #endif | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 | |
| 325 /* | |
| 326 * Indicates whether work is pending for ANY window. | |
| 327 * | |
| 328 * The current mechanism is to walk all of the windows and ask if | |
| 329 * work is pending. We have a short-circuit early return if we find any. | |
| 330 */ | |
| 331 static void fghHavePendingWorkCallback( SFG_Window* w, SFG_Enumerator* e) | |
| 332 { | |
| 333 if( w->State.WorkMask ) | |
| 334 { | |
| 335 e->found = GL_TRUE; | |
| 336 e->data = w; | |
| 337 return; | |
| 338 } | |
| 339 fgEnumSubWindows( w, fghHavePendingWorkCallback, e ); | |
| 340 } | |
| 341 static int fghHavePendingWork (void) | |
| 342 { | |
| 343 SFG_Enumerator enumerator; | |
| 344 | |
| 345 enumerator.found = GL_FALSE; | |
| 346 enumerator.data = NULL; | |
| 347 fgEnumWindows( fghHavePendingWorkCallback, &enumerator ); | |
| 348 return !!enumerator.data; | |
| 349 } | |
| 350 | |
| 351 /* | |
| 352 * Returns the number of GLUT ticks (milliseconds) till the next timer event. | |
| 353 */ | |
| 354 static fg_time_t fghNextTimer( void ) | |
| 355 { | |
| 356 fg_time_t currentTime; | |
| 357 SFG_Timer *timer = fgState.Timers.First; /* timers are sorted by trigger time, so only have to check the first */ | |
| 358 | |
| 359 if( !timer ) | |
| 360 return INT_MAX; | |
| 361 | |
| 362 currentTime = fgElapsedTime(); | |
| 363 if( timer->TriggerTime < currentTime ) | |
| 364 return 0; | |
| 365 else | |
| 366 return timer->TriggerTime - currentTime; | |
| 367 } | |
| 368 | |
| 369 static void fghSleepForEvents( void ) | |
| 370 { | |
| 371 fg_time_t msec; | |
| 372 | |
| 373 if( fghHavePendingWork( ) ) | |
| 374 return; | |
| 375 | |
| 376 msec = fghNextTimer( ); | |
| 377 /* XXX Should use GLUT timers for joysticks... */ | |
| 378 /* XXX Dumb; forces granularity to .01sec */ | |
| 379 if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) ) | |
| 380 msec = 10; | |
| 381 | |
| 382 fgPlatformSleepForEvents ( msec ); | |
| 383 } | |
| 384 | |
| 385 | |
| 386 /* Step through the work list */ | |
| 387 void fgProcessWork(SFG_Window *window) | |
| 388 { | |
| 389 unsigned int workMask = window->State.WorkMask; | |
| 390 /* Now clear it so that any callback generated by the actions below can set work again */ | |
| 391 window->State.WorkMask = 0; | |
| 392 | |
| 393 if (workMask&~GLUT_DISPLAY_WORK) /* Display work is the common case, skip all the below at once */ | |
| 394 { | |
| 395 if (workMask & GLUT_INIT_WORK) | |
| 396 { | |
| 397 /* This is before the first display callback: if needed for the platform, | |
| 398 * call a few callbacks to inform user of window size, position, etc | |
| 399 */ | |
| 400 fgPlatformInitWork(window); | |
| 401 | |
| 402 /* Call init context callback */ | |
| 403 INVOKE_WCB( *window, InitContext, ()); | |
| 404 | |
| 405 /* Lastly, check if we have a display callback, error out if not | |
| 406 * This is the right place to do it, as the redisplay will be | |
| 407 * next right after we exit this function, so there is no more | |
| 408 * opportunity for the user to register a callback for this window. | |
| 409 */ | |
| 410 if (!FETCH_WCB(*window, Display)) | |
| 411 fgError ( "ERROR: No display callback registered for window %d\n", window->ID ); | |
| 412 } | |
| 413 | |
| 414 /* On windows we can position, resize and change z order at the same time */ | |
| 415 if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK)) | |
| 416 { | |
| 417 fgPlatformPosResZordWork(window,workMask); | |
| 418 } | |
| 419 | |
| 420 if (workMask & GLUT_VISIBILITY_WORK) | |
| 421 { | |
| 422 fgPlatformVisibilityWork(window); | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 /* check window state's workmask as well as some of the above callbacks might have generated redisplay requests. We can deal with those right now instead of wait for the next mainloop iteration. */ | |
| 427 if (workMask & GLUT_DISPLAY_WORK || window->State.WorkMask & GLUT_DISPLAY_WORK) | |
| 428 { | |
| 429 if( window->State.Visible ) | |
| 430 { | |
| 431 /* Strip out display work from the work list */ | |
| 432 /* NB: do this before the display callback is called as user might call postredisplay in his display callback */ | |
| 433 window->State.WorkMask &= ~GLUT_DISPLAY_WORK; | |
| 434 | |
| 435 fghRedrawWindow ( window ); | |
| 436 } | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 | |
| 441 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ | |
| 442 | |
| 443 /* | |
| 444 * Executes a single iteration in the freeglut processing loop. | |
| 445 */ | |
| 446 void FGAPIENTRY glutMainLoopEvent( void ) | |
| 447 { | |
| 448 /* Process input */ | |
| 449 fgPlatformProcessSingleEvent (); | |
| 450 | |
| 451 if( fgState.Timers.First ) | |
| 452 fghCheckTimers( ); | |
| 453 if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */ | |
| 454 fghCheckJoystickPolls( ); | |
| 455 | |
| 456 /* Perform work on the window (position, reshape, display, etc) */ | |
| 457 fghProcessWork( ); | |
| 458 | |
| 459 /* Check OpenGL error state if requested. | |
| 460 * Don't call if no more open windows (can happen if user closes window from | |
| 461 * title bar), would lead to infinite error loop in glutReportErrors | |
| 462 */ | |
| 463 if (fgState.GLDebugSwitch && fgStructure.CurrentWindow) | |
| 464 glutReportErrors( ); | |
| 465 | |
| 466 fgCloseWindows( ); | |
| 467 } | |
| 468 | |
| 469 /* | |
| 470 * Enters the freeglut processing loop. | |
| 471 * Stays until the "ExecState" changes to "GLUT_EXEC_STATE_STOP". | |
| 472 */ | |
| 473 void FGAPIENTRY glutMainLoop( void ) | |
| 474 { | |
| 475 int action; | |
| 476 | |
| 477 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" ); | |
| 478 | |
| 479 if (!fgStructure.Windows.First) | |
| 480 fgError(" ERROR: glutMainLoop called with no windows created."); | |
| 481 | |
| 482 fgPlatformMainLoopPreliminaryWork (); | |
| 483 | |
| 484 fgState.ExecState = GLUT_EXEC_STATE_RUNNING ; | |
| 485 while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING ) | |
| 486 { | |
| 487 SFG_Window *window; | |
| 488 | |
| 489 glutMainLoopEvent( ); | |
| 490 /* | |
| 491 * Step through the list of windows, seeing if there are any | |
| 492 * that are not menus | |
| 493 */ | |
| 494 for( window = ( SFG_Window * )fgStructure.Windows.First; | |
| 495 window; | |
| 496 window = ( SFG_Window * )window->Node.Next ) | |
| 497 if ( ! ( window->IsMenu ) ) | |
| 498 break; | |
| 499 | |
| 500 if( ! window ) | |
| 501 fgState.ExecState = GLUT_EXEC_STATE_STOP; | |
| 502 else | |
| 503 { | |
| 504 if( fgState.IdleCallback ) | |
| 505 { | |
| 506 if( fgStructure.CurrentWindow && | |
| 507 fgStructure.CurrentWindow->IsMenu ) | |
| 508 /* fail safe */ | |
| 509 fgSetWindow( window ); | |
| 510 fgState.IdleCallback( ); | |
| 511 } | |
| 512 else | |
| 513 fghSleepForEvents( ); | |
| 514 } | |
| 515 } | |
| 516 | |
| 517 /* | |
| 518 * When this loop terminates, destroy the display, state and structure | |
| 519 * of a freeglut session, so that another glutInit() call can happen | |
| 520 * | |
| 521 * Save the "ActionOnWindowClose" because "fgDeinitialize" resets it. | |
| 522 */ | |
| 523 action = fgState.ActionOnWindowClose; | |
| 524 fgDeinitialize( ); | |
| 525 if( action == GLUT_ACTION_EXIT ) | |
| 526 exit( 0 ); | |
| 527 } | |
| 528 | |
| 529 /* | |
| 530 * Leaves the freeglut processing loop. | |
| 531 */ | |
| 532 void FGAPIENTRY glutLeaveMainLoop( void ) | |
| 533 { | |
| 534 FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" ); | |
| 535 fgState.ExecState = GLUT_EXEC_STATE_STOP ; | |
| 536 } | |
| 537 | |
| 538 | |
| 539 | |
| 540 /*** END OF FILE ***/ |
