- moving to a new way of handling window changes (position, size, visibility)
[freeglut] / src / fg_main.c
index 414fa1c..82f708c 100644 (file)
@@ -54,8 +54,7 @@
 #    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 );
@@ -66,77 +65,118 @@ extern void fgPlatformMainLoopPreliminaryWork ( void );
 
 /* -- 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...
+         */
+        glutPostRedisplay( );
+        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( x  != window->State.Xpos ||
+        y != window->State.Ypos )
+    {
+        window->State.Xpos = x;
+        window->State.Ypos = y;
 
-    if( window->IsMenu )
-        fgSetWindow( current_window );
+        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 );
+    fgSetWindow( window );
+    INVOKE_WCB( *window, Display, ( ) );
 
-    if( window->State.NeedToInitContext ) {
-        INVOKE_WCB( *window, InitContext, ());
-        window->State.NeedToInitContext = GL_FALSE;
-    }
+    fgSetWindow( current_window );
+}
 
-    freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
+void fghRedrawWindowAndChildren ( SFG_Window *window )
+{
+    SFG_Window* child;
 
-    window->State.Redisplay = GL_FALSE;
+    fghRedrawWindow(window);
 
-    freeglut_return_if_fail( window->State.Visible );
+    for( child = ( SFG_Window * )window->Children.First;
+         child;
+         child = ( SFG_Window * )child->Node.Next )
+    {
+        fghRedrawWindowAndChildren(child);
+    }
+}
 
-    fgSetWindow( window );
 
-    if( window->State.NeedToResize )
-    {
-        /* 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;
+static void fghcbProcessWork( SFG_Window *window,
+                              SFG_Enumerator *enumerator )
+{
+    if( window->State.WorkMask )
+               fgPlatformProcessWork ( window );
 
-        fghReshapeWindow(
-            window,
-            window->State.Width,
-            window->State.Height
-        );
-    }
+    fgEnumSubWindows( window, fghcbProcessWork, enumerator );
+}
 
-    INVOKE_WCB( *window, Display, ( ) );
+/*
+ * Make all windows process their work list
+ */
+static void fghProcessWork( void )
+{
+    SFG_Enumerator enumerator;
 
-    fgSetWindow( current_window );
+    enumerator.found = GL_FALSE;
+    enumerator.data  =  NULL;
+
+    fgEnumWindows( fghcbProcessWork, &enumerator );
 }
 
 
@@ -147,7 +187,7 @@ static void fghcbDisplayWindow( SFG_Window *window,
         window->State.Visible )
     {
         window->State.Redisplay = GL_FALSE;
-               fgPlatformDisplayWindow ( window );
+               fghRedrawWindow ( window );
     }
 
     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
@@ -317,13 +357,11 @@ void fgWarning( const char *fmt, ... )
 
 
 /*
- * Indicates whether Joystick events are being used by ANY window.
+ * Indicates whether a redisplay 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.
- *
- *
+ * a redisplay is pending. We have a short-circuit early
+ * return if we find any.
  */
 static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
 {
@@ -331,6 +369,7 @@ static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
     {
         e->found = GL_TRUE;
         e->data = w;
+        return;
     }
     fgEnumSubWindows( w, fghHavePendingRedisplaysCallback, e );
 }
@@ -385,12 +424,18 @@ static void fghSleepForEvents( void )
  */
 void FGAPIENTRY glutMainLoopEvent( void )
 {
+    /* Process input */
        fgPlatformProcessSingleEvent ();
 
     if( fgState.Timers.First )
         fghCheckTimers( );
     if (fgState.NumActiveJoysticks>0)   /* If zero, don't poll joysticks */
         fghCheckJoystickPolls( );
+
+    /* Perform work on the window (position, reshape, etc) */
+    fghProcessWork( );
+
+    /* Display */
     fghDisplayAll( );
 
     fgCloseWindows( );