Finished implementing all user-data callbacks (GCC-only for now).
[freeglut] / src / fg_main.c
index d2108e7..45f13f5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * freeglut_main.c
+ * fg_main.c
  *
  * The windows message processing methods.
  *
 
 #include <GL/freeglut.h>
 #include "fg_internal.h"
-#ifdef HAVE_ERRNO_H
-#    include <errno.h>
-#endif
+#include <errno.h>
 #include <stdarg.h>
-#ifdef  HAVE_VFPRINTF
-#    define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
-#elif defined(HAVE__DOPRNT)
-#    define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
-#else
-#    define VFPRINTF(s,f,a)
-#endif
 
 /*
  * Try to get the maximum value allowed for ints, falling back to the minimum
 #    define MIN(a,b) (((a)<(b)) ? (a) : (b))
 #endif
 
-extern void fgPlatformProcessWork   ( SFG_Window *window );
+extern void fgProcessWork   ( SFG_Window *window );
 extern fg_time_t fgPlatformSystemTime ( void );
 extern void fgPlatformSleepForEvents( fg_time_t msec );
 extern void fgPlatformProcessSingleEvent ( void );
 extern void fgPlatformMainLoopPreliminaryWork ( void );
 
-
+extern void fgPlatformInitWork(SFG_Window* window);
+extern void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask);
+extern void fgPlatformVisibilityWork(SFG_Window* window);
 
 
 /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */
@@ -92,7 +85,7 @@ void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean for
          * window.
          * DN: Hmm.. the above sounds like a concern only in single buffered mode...
          */
-        glutPostRedisplay( );
+        window->State.WorkMask |= GLUT_DISPLAY_WORK;
         if( window->IsMenu )
             fgSetWindow( saved_window );
     }
@@ -161,7 +154,7 @@ static void fghcbProcessWork( SFG_Window *window,
                               SFG_Enumerator *enumerator )
 {
     if( window->State.WorkMask )
-               fgPlatformProcessWork ( window );
+        fgProcessWork ( window );
 
     fgEnumSubWindows( window, fghcbProcessWork, enumerator );
 }
@@ -179,33 +172,6 @@ static void fghProcessWork( void )
     fgEnumWindows( fghcbProcessWork, &enumerator );
 }
 
-
-static void fghcbDisplayWindow( SFG_Window *window,
-                                SFG_Enumerator *enumerator )
-{
-    if( window->State.Redisplay &&
-        window->State.Visible )
-    {
-        window->State.Redisplay = GL_FALSE;
-               fghRedrawWindow ( window );
-    }
-
-    fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
-}
-
-/*
- * Make all windows perform a display call
- */
-static void fghDisplayAll( void )
-{
-    SFG_Enumerator enumerator;
-
-    enumerator.found = GL_FALSE;
-    enumerator.data  =  NULL;
-
-    fgEnumWindows( fghcbDisplayWindow, &enumerator );
-}
-
 /*
  * Window enumerator callback to check for the joystick polling code
  */
@@ -261,13 +227,13 @@ static void fghCheckTimers( void )
         SFG_Timer *timer = fgState.Timers.First;
 
         if( timer->TriggerTime > checkTime )
-            /* XXX: are timers always sorted by triggerTime? If not, this and fghNextTimer are wrong */
+            /* Timers are sorted by triggerTime */
             break;
 
         fgListRemove( &fgState.Timers, &timer->Node );
         fgListAppend( &fgState.FreeTimers, &timer->Node );
 
-        timer->Callback( timer->ID );
+        timer->Callback( timer->ID, timer->CallbackData );
     }
 }
 
@@ -303,7 +269,7 @@ void fgError( const char *fmt, ... )
         va_start( ap, fmt );
 
         /* call user set error handler here */
-        fgState.ErrorFunc(fmt, ap);
+        fgState.ErrorFunc(fmt, ap, fgState.ErrorFuncData);
 
         va_end( ap );
 
@@ -314,7 +280,7 @@ void fgError( const char *fmt, ... )
         fprintf( stderr, "freeglut ");
         if( fgState.ProgramName )
             fprintf( stderr, "(%s): ", fgState.ProgramName );
-        VFPRINTF( stderr, fmt, ap );
+        vfprintf( stderr, fmt, ap );
         fprintf( stderr, "\n" );
 
         va_end( ap );
@@ -336,7 +302,7 @@ void fgWarning( const char *fmt, ... )
         va_start( ap, fmt );
 
         /* call user set warning handler here */
-        fgState.WarningFunc(fmt, ap);
+        fgState.WarningFunc(fmt, ap, fgState.WarningFuncData);
 
         va_end( ap );
 
@@ -347,7 +313,7 @@ void fgWarning( const char *fmt, ... )
         fprintf( stderr, "freeglut ");
         if( fgState.ProgramName )
             fprintf( stderr, "(%s): ", fgState.ProgramName );
-        VFPRINTF( stderr, fmt, ap );
+        vfprintf( stderr, fmt, ap );
         fprintf( stderr, "\n" );
 
         va_end( ap );
@@ -357,29 +323,28 @@ void fgWarning( const char *fmt, ... )
 
 
 /*
- * Indicates whether a redisplay is pending for ANY window.
+ * Indicates whether work is pending for ANY window.
  *
  * The current mechanism is to walk all of the windows and ask if
- * a redisplay is pending. We have a short-circuit early
- * return if we find any.
+ * work is pending. We have a short-circuit early return if we find any.
  */
-static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
+static void fghHavePendingWorkCallback( SFG_Window* w, SFG_Enumerator* e)
 {
-    if( w->State.Redisplay && w->State.Visible )
+    if( w->State.WorkMask )
     {
         e->found = GL_TRUE;
         e->data = w;
         return;
     }
-    fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
+    fgEnumSubWindows( w, fghHavePendingWorkCallback, e );
 }
-static int fghHavePendingRedisplays (void)
+static int fghHavePendingWork (void)
 {
     SFG_Enumerator enumerator;
 
     enumerator.found = GL_FALSE;
     enumerator.data = NULL;
-    fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
+    fgEnumWindows( fghHavePendingWorkCallback, &enumerator );
     return !!enumerator.data;
 }
 
@@ -388,12 +353,13 @@ static int fghHavePendingRedisplays (void)
  */
 static fg_time_t fghNextTimer( void )
 {
-    fg_time_t currentTime = fgElapsedTime();
-    SFG_Timer *timer = fgState.Timers.First;
+    fg_time_t currentTime;
+    SFG_Timer *timer = fgState.Timers.First;    /* timers are sorted by trigger time, so only have to check the first */
 
     if( !timer )
         return INT_MAX;
 
+    currentTime = fgElapsedTime();
     if( timer->TriggerTime < currentTime )
         return 0;
     else
@@ -404,16 +370,71 @@ static void fghSleepForEvents( void )
 {
     fg_time_t msec;
 
-    if( fghHavePendingRedisplays( ) )
+    if( fghHavePendingWork( ) )
         return;
 
     msec = fghNextTimer( );
     /* XXX Should use GLUT timers for joysticks... */
     /* XXX Dumb; forces granularity to .01sec */
-    if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )     
+    if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )
         msec = 10;
 
-       fgPlatformSleepForEvents ( msec );
+    fgPlatformSleepForEvents ( msec );
+}
+
+
+/* Step through the work list */
+void fgProcessWork(SFG_Window *window)
+{
+    unsigned int workMask = window->State.WorkMask;
+    /* Now clear it so that any callback generated by the actions below can set work again */
+    window->State.WorkMask = 0;
+
+    if (workMask&~GLUT_DISPLAY_WORK)    /* Display work is the common case, skip all the below at once */
+    {
+        if (workMask & GLUT_INIT_WORK)
+        {
+            /* This is before the first display callback: if needed for the platform,
+             * call a few callbacks to inform user of window size, position, etc
+             */
+            fgPlatformInitWork(window);
+
+            /* Call init context callback */
+            INVOKE_WCB( *window, InitContext, ());
+
+            /* Lastly, check if we have a display callback, error out if not
+             * This is the right place to do it, as the redisplay will be
+             * next right after we exit this function, so there is no more
+             * opportunity for the user to register a callback for this window.
+             */
+            if (!FETCH_WCB(*window, Display))
+                fgError ( "ERROR:  No display callback registered for window %d\n", window->ID );
+        }
+
+        /* On windows we can position, resize and change z order at the same time */
+        if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK))
+        {
+            fgPlatformPosResZordWork(window,workMask);
+        }
+
+        if (workMask & GLUT_VISIBILITY_WORK)
+        {
+            fgPlatformVisibilityWork(window);
+        }
+    }
+
+    /* 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. */
+    if (workMask & GLUT_DISPLAY_WORK || window->State.WorkMask & GLUT_DISPLAY_WORK)
+    {
+        if( window->State.Visible )
+        {
+            /* Strip out display work from the work list */
+            /* NB: do this before the display callback is called as user might call postredisplay in his display callback */
+            window->State.WorkMask &= ~GLUT_DISPLAY_WORK;
+
+            fghRedrawWindow ( window );
+        }
+    }
 }
 
 
@@ -432,11 +453,15 @@ void FGAPIENTRY glutMainLoopEvent( void )
     if (fgState.NumActiveJoysticks>0)   /* If zero, don't poll joysticks */
         fghCheckJoystickPolls( );
 
-    /* Perform work on the window (position, reshape, etc) */
+    /* Perform work on the window (position, reshape, display, etc) */
     fghProcessWork( );
 
-    /* Display */
-    fghDisplayAll( );
+    /* Check OpenGL error state if requested.
+     * Don't call if no more open windows (can happen if user closes window from
+     * title bar), would lead to infinite error loop in glutReportErrors
+     */
+    if (fgState.GLDebugSwitch && fgStructure.CurrentWindow)
+        glutReportErrors( );
 
     fgCloseWindows( );
 }
@@ -454,14 +479,16 @@ void FGAPIENTRY glutMainLoop( void )
     if (!fgStructure.Windows.First)
         fgError(" ERROR:  glutMainLoop called with no windows created.");
 
-       fgPlatformMainLoopPreliminaryWork ();
+    fgPlatformMainLoopPreliminaryWork ();
 
     fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
-    while( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
+    for(;;)
     {
         SFG_Window *window;
 
         glutMainLoopEvent( );
+        if( fgState.ExecState != GLUT_EXEC_STATE_RUNNING )
+            break;
         /*
          * Step through the list of windows, seeing if there are any
          * that are not menus
@@ -482,7 +509,7 @@ void FGAPIENTRY glutMainLoop( void )
                     fgStructure.CurrentWindow->IsMenu )
                     /* fail safe */
                     fgSetWindow( window );
-                fgState.IdleCallback( );
+                fgState.IdleCallback( fgState.IdleCallbackData );
             }
             else
                 fghSleepForEvents( );