# 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 fgPlatformProcessWork ( 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;
+ }
- if( FETCH_WCB( *window, Reshape ) )
- INVOKE_WCB( *window, Reshape, ( width, height ) );
- else
+ if (notify || forceNotify)
{
- fgSetWindow( window );
- glViewport( 0, 0, width, height );
+ SFG_Window *saved_window = fgStructure.CurrentWindow;
+
+ 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 );
}
+}
- /*
- * 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;
+void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify)
+{
+ GLboolean notify = GL_FALSE;
- if( window->IsMenu )
- fgSetWindow( current_window );
+ 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;
- }
+ fgSetWindow( window );
+ INVOKE_WCB( *window, Display, ( ) );
- freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
+ fgSetWindow( current_window );
+}
- window->State.Redisplay = GL_FALSE;
+void fghRedrawWindowAndChildren ( SFG_Window *window )
+{
+ SFG_Window* child;
- freeglut_return_if_fail( window->State.Visible );
+ fghRedrawWindow(window);
- fgSetWindow( 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 )
+ fgPlatformProcessWork ( 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 );
}
/*
static void fghcbCheckJoystickPolls( SFG_Window *window,
SFG_Enumerator *enumerator )
{
- fg_time_t checkTime = fgElapsedTime( );
-
- if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
- checkTime )
+ 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 */
+ checkTime= fgElapsedTime( );
+
+ if( window->State.JoystickLastPoll + window->State.JoystickPollRate <=
+ checkTime )
+ {
#if !defined(_WIN32_WCE)
- fgJoystickPollWindow( window );
+ fgJoystickPollWindow( window );
#endif /* !defined(_WIN32_WCE) */
- window->State.JoystickLastPoll = checkTime;
+ window->State.JoystickLastPoll = checkTime;
+ }
}
fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
/*
* 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.
*/
static void fghCheckJoystickPolls( void )
{
SFG_Timer *timer = fgState.Timers.First;
if( timer->TriggerTime > checkTime )
+ /* Timers are sorted by triggerTime */
break;
fgListRemove( &fgState.Timers, &timer->Node );
/* 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,
- * and on Windows we (currently) do not have access to a 64-bit timestamp,
- * which means internal time will still get in trouble when running the
+ * which means you will still get in trouble when running the
* application for more than 49.7 days.
- * This value wraps every 49.7 days, but integer overflows cancel
- * when subtracting an initial start time, unless the total time exceeds
- * 32-bit, where the GLUT API return value is also overflowed.
*/
fg_time_t fgSystemTime(void)
{
va_end( ap );
} else {
-
+#ifdef FREEGLUT_PRINT_ERRORS
va_start( ap, fmt );
fprintf( stderr, "freeglut ");
fprintf( stderr, "\n" );
va_end( ap );
+#endif
if ( fgState.Initialised )
fgDeinitialize ();
va_end( ap );
} else {
-
+#ifdef FREEGLUT_PRINT_WARNINGS
va_start( ap, fmt );
fprintf( stderr, "freeglut ");
fprintf( stderr, "\n" );
va_end( ap );
+#endif
}
}
/*
- * Indicates whether Joystick events are being used by ANY window.
+ * Indicates whether work is pending for ANY window.
*
* The current mechanism is to walk all of the windows and ask if
- * there is a joystick callback. We have a short-circuit early
- * return if we find any joystick handler registered.
- *
- * 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.
- *
+ * work is pending. We have a short-circuit early return if we find any.
*/
-static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
+static void fghHavePendingWorkCallback( SFG_Window* w, SFG_Enumerator* e)
{
- if( FETCH_WCB( *w, Joystick ) )
+ if( w->State.WorkMask )
{
e->found = GL_TRUE;
e->data = w;
+ return;
}
- fgEnumSubWindows( w, fghCheckJoystickCallback, e );
+ fgEnumSubWindows( w, fghHavePendingWorkCallback, e );
}
-static int fghHaveJoystick( void )
+static int fghHavePendingWork (void)
{
SFG_Enumerator enumerator;
enumerator.found = GL_FALSE;
enumerator.data = NULL;
- fgEnumWindows( fghCheckJoystickCallback, &enumerator );
+ fgEnumWindows( fghHavePendingWorkCallback, &enumerator );
return !!enumerator.data;
}
-static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
-{
- if( w->State.Redisplay && w->State.Visible )
- {
- e->found = GL_TRUE;
- e->data = w;
- }
- fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
-}
-static int fghHavePendingRedisplays (void)
-{
- SFG_Enumerator enumerator;
- enumerator.found = GL_FALSE;
- enumerator.data = NULL;
- fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
- return !!enumerator.data;
-}
/*
* Returns the number of GLUT ticks (milliseconds) till the next timer event.
*/
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
{
fg_time_t msec;
- if( fgState.IdleCallback || fghHavePendingRedisplays( ) )
+ if( fghHavePendingWork( ) )
return;
msec = fghNextTimer( );
- /* XXX Use GLUT timers for joysticks... */
+ /* XXX Should use GLUT timers for joysticks... */
/* XXX Dumb; forces granularity to .01sec */
- if( fghHaveJoystick( ) && ( msec > 10 ) )
+ if( fgState.NumActiveJoysticks>0 && ( msec > 10 ) )
msec = 10;
fgPlatformSleepForEvents ( msec );
}
+/* Step through the work list */
+void fgPlatformProcessWork(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);
+ }
+ }
+
+ if (workMask & GLUT_DISPLAY_WORK)
+ {
+ if( window->State.Visible )
+ fghRedrawWindow ( window );
+ }
+}
+
+
/* -- INTERFACE FUNCTIONS -------------------------------------------------- */
/*
*/
void FGAPIENTRY glutMainLoopEvent( void )
{
+ /* Process input */
fgPlatformProcessSingleEvent ();
if( fgState.Timers.First )
fghCheckTimers( );
- fghCheckJoystickPolls( );
- fghDisplayAll( );
+ if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */
+ fghCheckJoystickPolls( );
+
+ /* Perform work on the window (position, reshape, display, etc) */
+ fghProcessWork( );
fgCloseWindows( );
}
FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
+ if (!fgStructure.Windows.First)
+ fgError(" ERROR: glutMainLoop called with no windows created.");
+
fgPlatformMainLoopPreliminaryWork ();
fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
fgSetWindow( window );
fgState.IdleCallback( );
}
-
- fghSleepForEvents( );
+ else
+ fghSleepForEvents( );
}
}