some whitespace cleanup
[freeglut] / src / fg_main.c
index 2bc8435..ff01110 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 fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
-extern void fgPlatformDisplayWindow ( 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 ---------------------------------------------------- */
 
-static void fghReshapeWindow ( SFG_Window *window, int width, int height )
+void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify)
 {
-    SFG_Window *current_window = fgStructure.CurrentWindow;
+    GLboolean notify = GL_FALSE;
 
-    freeglut_return_if_fail( window != NULL );
+    if( width  != window->State.Width ||
+        height != window->State.Height )
+    {
+        window->State.Width = width;
+        window->State.Height = height;
 
-       fgPlatformReshapeWindow ( window, width, height );
+        notify = GL_TRUE;
+    }
 
-    /*
-     * Force a window redraw.  In Windows at least this is only a partial
-     * solution:  if the window is increasing in size in either dimension,
-     * the already-drawn part does not get drawn again and things look funny.
-     * But without this we get this bad behaviour whenever we resize the
-     * window.
-     * DN: Hmm.. the above sounds like a concern only in single buffered mode...
-     */
-    window->State.Redisplay = GL_TRUE;
+    if (notify || forceNotify)
+    {
+        SFG_Window *saved_window = fgStructure.CurrentWindow;
 
-    if( window->IsMenu )
-        fgSetWindow( current_window );
+        INVOKE_WCB( *window, Reshape, ( width, height ) );
+
+        /*
+         * Force a window redraw.  In Windows at least this is only a partial
+         * solution:  if the window is increasing in size in either dimension,
+         * the already-drawn part does not get drawn again and things look funny.
+         * But without this we get this bad behaviour whenever we resize the
+         * window.
+         * DN: Hmm.. the above sounds like a concern only in single buffered mode...
+         */
+        window->State.WorkMask |= GLUT_DISPLAY_WORK;
+        if( window->IsMenu )
+            fgSetWindow( saved_window );
+    }
+}
+
+void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify)
+{
+    GLboolean notify = GL_FALSE;
+
+    if( x  != window->State.Xpos ||
+        y != window->State.Ypos )
+    {
+        window->State.Xpos = x;
+        window->State.Ypos = y;
+
+        notify = GL_TRUE;
+    }
+
+    if (notify || forceNotify)
+    {
+        SFG_Window *saved_window = fgStructure.CurrentWindow;
+        INVOKE_WCB( *window, Position, ( x, y ) );
+        fgSetWindow( saved_window );
+    }
 }
 
 /*
  * Calls a window's redraw method. This is used when
- * a redraw is forced by the incoming window messages.
+ * a redraw is forced by the incoming window messages,
+ * or if a redisplay is otherwise pending.
+ * this is lean and mean without checks as it is
+ * currently only called from fghcbDisplayWindow which
+ * only calls this if the window is visible and needs
+ * a redisplay.
+ * Note that the fgSetWindow call on Windows makes the
+ * right device context current on windows, allowing
+ * direct drawing without BeginPaint/EndPaint in the
+ * WM_PAINT handler.
  */
 void fghRedrawWindow ( SFG_Window *window )
 {
     SFG_Window *current_window = fgStructure.CurrentWindow;
 
-    freeglut_return_if_fail( window );
-
-    if( window->State.NeedToInitContext ) {
-        INVOKE_WCB( *window, InitContext, ());
-        window->State.NeedToInitContext = GL_FALSE;
-    }
-
-    freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
+    fgSetWindow( window );
+    INVOKE_WCB( *window, Display, ( ) );
 
-    window->State.Redisplay = GL_FALSE;
+    fgSetWindow( current_window );
+}
 
-    freeglut_return_if_fail( window->State.Visible );
+void fghRedrawWindowAndChildren ( SFG_Window *window )
+{
+    SFG_Window* child;
 
-    fgSetWindow( window );
+    fghRedrawWindow(window);
 
-    if( window->State.NeedToResize )
+    for( child = ( SFG_Window * )window->Children.First;
+         child;
+         child = ( SFG_Window * )child->Node.Next )
     {
-        /* Set need to resize to false before calling fghReshapeWindow, otherwise
-           in the case the user's reshape callback calls glutReshapeWindow,
-           his request would get canceled after fghReshapeWindow gets called.
-         */
-        window->State.NeedToResize = GL_FALSE;
-
-        fghReshapeWindow(
-            window,
-            window->State.Width,
-            window->State.Height
-        );
+        fghRedrawWindowAndChildren(child);
     }
-
-    INVOKE_WCB( *window, Display, ( ) );
-
-    fgSetWindow( current_window );
 }
 
 
-static void fghcbDisplayWindow( SFG_Window *window,
-                                SFG_Enumerator *enumerator )
+static void fghcbProcessWork( SFG_Window *window,
+                              SFG_Enumerator *enumerator )
 {
-    if( window->State.Redisplay &&
-        window->State.Visible )
-    {
-        window->State.Redisplay = GL_FALSE;
-               fgPlatformDisplayWindow ( window );
-    }
+    if( window->State.WorkMask )
+        fgProcessWork ( window );
 
-    fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
+    fgEnumSubWindows( window, fghcbProcessWork, enumerator );
 }
 
 /*
- * Make all windows perform a display call
+ * Make all windows process their work list
  */
-static void fghDisplayAll( void )
+static void fghProcessWork( void )
 {
     SFG_Enumerator enumerator;
 
     enumerator.found = GL_FALSE;
     enumerator.data  =  NULL;
 
-    fgEnumWindows( fghcbDisplayWindow, &enumerator );
+    fgEnumWindows( fghcbProcessWork, &enumerator );
 }
 
 /*
@@ -165,7 +179,7 @@ static void fghcbCheckJoystickPolls( SFG_Window *window,
                                      SFG_Enumerator *enumerator )
 {
     fg_time_t checkTime;
-    
+
     if (window->State.JoystickPollRate > 0 && FETCH_WCB( *window, Joystick ))
     {
         /* This window has a joystick to be polled (if pollrate <= 0, user needs to poll manually with glutForceJoystickFunc */
@@ -186,7 +200,7 @@ static void fghcbCheckJoystickPolls( SFG_Window *window,
 
 /*
  * Check all windows for joystick polling
- * 
+ *
  * The real way to do this is to make use of the glutTimer() API
  * to more cleanly re-implement the joystick API.  Then, this code
  * and all other "joystick timer" code can be yanked.
@@ -213,28 +227,28 @@ 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 );
     }
 }
 
+
 /* Platform-dependent time in milliseconds, as an unsigned 64-bit integer.
  * This doesn't overflow in any reasonable time, so no need to worry about
  * that. The GLUT API return value will however overflow after 49.7 days,
  * which means you will still get in trouble when running the
  * application for more than 49.7 days.
- */  
+ */
 fg_time_t fgSystemTime(void)
 {
-       return fgPlatformSystemTime();
+    return fgPlatformSystemTime();
 }
-  
+
 /*
  * Elapsed Time
  */
@@ -255,18 +269,18 @@ 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 );
 
     } else {
-#if FREEGLUT_ERRORS
+#ifdef FREEGLUT_PRINT_ERRORS
         va_start( ap, 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 );
@@ -288,18 +302,18 @@ 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 );
 
     } else {
-#if FREEGLUT_WARNINGS
+#ifdef FREEGLUT_PRINT_WARNINGS
         va_start( ap, 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 );
@@ -309,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;
 }
 
@@ -340,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
@@ -356,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 );
+        }
+    }
 }
 
 
@@ -376,13 +445,23 @@ static void fghSleepForEvents( void )
  */
 void FGAPIENTRY glutMainLoopEvent( void )
 {
-       fgPlatformProcessSingleEvent ();
+    /* Process input */
+    fgPlatformProcessSingleEvent ();
 
     if( fgState.Timers.First )
         fghCheckTimers( );
     if (fgState.NumActiveJoysticks>0)   /* If zero, don't poll joysticks */
         fghCheckJoystickPolls( );
-    fghDisplayAll( );
+
+    /* Perform work on the window (position, reshape, display, etc) */
+    fghProcessWork( );
+
+    /* 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( );
 }
@@ -400,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
@@ -428,7 +509,7 @@ void FGAPIENTRY glutMainLoop( void )
                     fgStructure.CurrentWindow->IsMenu )
                     /* fail safe */
                     fgSetWindow( window );
-                fgState.IdleCallback( );
+                fgState.IdleCallback( fgState.IdleCallbackData );
             }
             else
                 fghSleepForEvents( );