X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2Ffg_main.c;h=3b32f064aa470108a9b17ce9aa71368823d320bc;hb=c2de6d3474dc0c697fa0d5c9fd84988896a077ef;hp=b664fa3ebc15b7b6949c56b75ada0c7794dcb967;hpb=b23128a3f3779555ea461278346d628a9b49fad0;p=freeglut diff --git a/src/fg_main.c b/src/fg_main.c index b664fa3..3b32f06 100644 --- a/src/fg_main.c +++ b/src/fg_main.c @@ -54,123 +54,131 @@ # 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; - printf("fghRedrawWindow\n"); - freeglut_return_if_fail( window ); - printf("got window\n"); - - 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 ) ); - printf("got displayCB\n"); + fgSetWindow( current_window ); +} - window->State.Redisplay = GL_FALSE; +void fghRedrawWindowAndChildren ( SFG_Window *window ) +{ + SFG_Window* child; - freeglut_return_if_fail( window->State.Visible ); - printf("we're visible\n"); + 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); } - - printf("invoking displayCB\n"); - 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 ) { - printf("window %p, %i\n",window,window->State.Redisplay); - if( window->State.Redisplay && - window->State.Visible ) - { - window->State.Redisplay = GL_FALSE; - printf("redisplaying...\n"); - 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 ); } /* @@ -179,15 +187,21 @@ static void fghDisplayAll( void ) 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 ); @@ -195,6 +209,10 @@ 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. */ static void fghCheckJoystickPolls( void ) { @@ -218,6 +236,7 @@ static void fghCheckTimers( void ) SFG_Timer *timer = fgState.Timers.First; if( timer->TriggerTime > checkTime ) + /* Timers are sorted by triggerTime */ break; fgListRemove( &fgState.Timers, &timer->Node ); @@ -231,12 +250,8 @@ static void fghCheckTimers( void ) /* 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) { @@ -268,7 +283,7 @@ void fgError( const char *fmt, ... ) va_end( ap ); } else { - +#ifdef FREEGLUT_PRINT_ERRORS va_start( ap, fmt ); fprintf( stderr, "freeglut "); @@ -278,6 +293,7 @@ void fgError( const char *fmt, ... ) fprintf( stderr, "\n" ); va_end( ap ); +#endif if ( fgState.Initialised ) fgDeinitialize (); @@ -300,7 +316,7 @@ void fgWarning( const char *fmt, ... ) va_end( ap ); } else { - +#ifdef FREEGLUT_PRINT_WARNINGS va_start( ap, fmt ); fprintf( stderr, "freeglut "); @@ -310,69 +326,49 @@ void fgWarning( const char *fmt, ... ) 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 @@ -383,19 +379,70 @@ static void fghSleepForEvents( void ) { 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 ); + + /* Strip out display work that might have ended up on work list now as some of the above genereates callbacks */ + window->State.WorkMask &= ~GLUT_DISPLAY_WORK; + } +} + + /* -- INTERFACE FUNCTIONS -------------------------------------------------- */ /* @@ -403,12 +450,16 @@ static void fghSleepForEvents( void ) */ 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( ); } @@ -423,6 +474,9 @@ void FGAPIENTRY glutMainLoop( void ) 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 ; @@ -453,8 +507,8 @@ void FGAPIENTRY glutMainLoop( void ) fgSetWindow( window ); fgState.IdleCallback( ); } - - fghSleepForEvents( ); + else + fghSleepForEvents( ); } }