Last of the hard TABs in the src/*.c files.
[freeglut] / src / freeglut_main.c
index 7984f58..5ddf375 100644 (file)
 #include "../include/GL/freeglut.h"
 #include "freeglut_internal.h"
 
+#include <limits.h>
+#if TARGET_HOST_UNIX_X11
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#elif TARGET_HOST_WIN32
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+
 /*
  * TODO BEFORE THE STABLE RELEASE:
  *
@@ -59,38 +78,13 @@ static void fghRedrawWindowByHandle
     ( HWND handle )
 #endif
 {
-    /*
-     * Find the window we have to redraw...
-     */
     SFG_Window* window = fgWindowByHandle( handle );
     freeglut_return_if_fail( window != NULL );
-
-    /*
-     * Check if there is a display callback hooked to it
-     */
     freeglut_return_if_fail( window->Callbacks.Display != NULL );
-
-    /*
-     * Return if the window is not visible
-     */
     freeglut_return_if_fail( window->State.Visible == TRUE );
 
-    /*
-     * Set the window as the current one.
-     */
     fgSetWindow( window );
-
-    /*
-     * Do not exagerate with the redisplaying
-     */
     window->State.Redisplay = FALSE;
-
-    /*
-     * Have the callback executed now. The buffers should
-     * be swapped by the glutSwapBuffers() execution inside
-     * the callback itself.
-     */
-
     window->Callbacks.Display();
 }
 
@@ -106,42 +100,28 @@ static void fghReshapeWindowByHandle
     ( HWND handle, int width, int height )
 #endif
 {
-    /*
-     * Find the window that received the reshape event
-     */
+  SFG_Window *current_window = fgStructure.Window ;
+
     SFG_Window* window = fgWindowByHandle( handle );
     freeglut_return_if_fail( window != NULL );
 
-    /*
-     * Remember about setting the current window...
-     */
     fgSetWindow( window );
-
-    /*
-     * Check if there is a reshape callback hooked
-     */
     if( window->Callbacks.Reshape != NULL )
-    {
-        /*
-         * OKi, have it called immediately
-         */
         window->Callbacks.Reshape( width, height );
-    }
     else
-    {
-        /*
-         * Otherwise just resize the viewport
-         */
         glViewport( 0, 0, 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.
+     * 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.
      */
     window->State.Redisplay = TRUE ;
+
+    if ( window->IsMenu )
+      fgSetWindow ( current_window ) ;
 }
 
 /*
@@ -150,36 +130,24 @@ static void fghReshapeWindowByHandle
 static void fghcbDisplayWindow( SFG_Window *window, SFG_Enumerator *enumerator )
 {
 #if TARGET_HOST_UNIX_X11
-    /*
-     * Check if there is an idle callback hooked
-     */
     if( (window->Callbacks.Display != NULL) &&
         (window->State.Redisplay == TRUE) &&
         (window->State.Visible == TRUE) )
     {
-        /*
-         * OKi, this is the case: have the window set as the current one
-         */
-        fgSetWindow( window );
+        SFG_Window *current_window = fgStructure.Window ;
 
-        /*
-         * Do not exagerate with the redisplaying
-         */
+        fgSetWindow( window );
         window->State.Redisplay = FALSE;
-
-        /*
-         * And execute the display callback immediately after
-         */
         window->Callbacks.Display();
+        fgSetWindow ( current_window ) ;
     }
 
 #elif TARGET_HOST_WIN32
 
-    /*
-     * Do we need to explicitly resize the window?
-     */
     if( window->State.NeedToResize )
     {
+        SFG_Window *current_window = fgStructure.Window ;
+
         fgSetWindow( window );
 
         fghReshapeWindowByHandle( 
@@ -188,35 +156,24 @@ static void fghcbDisplayWindow( SFG_Window *window, SFG_Enumerator *enumerator )
             glutGet( GLUT_WINDOW_HEIGHT )
         );
 
-        /*
-         * Never ever do that again:
-         */
         window->State.NeedToResize = FALSE;
+        fgSetWindow ( current_window ) ;
     }
 
-    /*
-     * This is done in a bit different way under Windows
-     */
     if( (window->Callbacks.Display != NULL) &&
         (window->State.Redisplay == TRUE) &&
         (window->State.Visible == TRUE) )
     {
-      /*
-       * Do not exagerate with the redisplaying
-       */
       window->State.Redisplay = FALSE;
 
       RedrawWindow( 
         window->Window.Handle, NULL, NULL, 
         RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW
-       );
+        );
     }
 
 #endif
 
-    /*
-     * Process this window's children (if any)
-     */
     fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
 }
 
@@ -227,15 +184,9 @@ static void fghDisplayAll( void )
 {
     SFG_Enumerator enumerator;
 
-    /*
-     * Uses a method very similiar for fgWindowByHandle...
-     */
     enumerator.found = FALSE;
     enumerator.data  =  NULL;
 
-    /*
-     * Start the enumeration now:
-     */
     fgEnumWindows( fghcbDisplayWindow, &enumerator );
 }
 
@@ -246,25 +197,12 @@ static void fghcbCheckJoystickPolls( SFG_Window *window, SFG_Enumerator *enumera
 {
     long int checkTime = fgElapsedTime();
 
-    /*
-     * Check if actually need to do the poll for the currently enumerated window:
-     */
-    if( window->State.JoystickLastPoll + window->State.JoystickPollRate >= checkTime )
+    if( window->State.JoystickLastPoll + window->State.JoystickPollRate <= checkTime )
     {
-        /*
-         * Yeah, that's it. Poll the joystick...
-         */
         fgJoystickPollWindow( window );
-
-        /*
-         * ...and reset the polling counters:
-         */
         window->State.JoystickLastPoll = checkTime;
     }
 
-    /*
-     * Process this window's children (if any)
-     */
     fgEnumSubWindows( window, fghcbCheckJoystickPolls, enumerator );
 }
 
@@ -275,15 +213,9 @@ static void fghCheckJoystickPolls( void )
 {
     SFG_Enumerator enumerator;
 
-    /*
-     * Uses a method very similiar for fgWindowByHandle...
-     */
     enumerator.found = FALSE;
     enumerator.data  =  NULL;
 
-    /*
-     * Start the enumeration now:
-     */
     fgEnumWindows( fghcbCheckJoystickPolls, &enumerator );
 }
 
@@ -301,19 +233,15 @@ static void fghCheckTimers( void )
     /*
      * For every timer that is waiting for triggering
      */
-    for( timer = fgState.Timers.First; timer; timer = next )
+    for( timer = (SFG_Timer *)fgState.Timers.First;
+         timer;
+         timer = (SFG_Timer *)next )
     {
-       next = timer->Node.Next;
+              next = (SFG_Timer *)timer->Node.Next;
 
-        /*
-         * Check for the timeout:
-         */
         if( timer->TriggerTime <= checkTime )
         {
-            /*
-             * Add the timer to the timed out timers list
-             */
-           fgListRemove( &fgState.Timers, &timer->Node );
+          fgListRemove( &fgState.Timers, &timer->Node );
             fgListAppend( &timedOut, &timer->Node );
         }
     }
@@ -322,11 +250,11 @@ static void fghCheckTimers( void )
      * Now feel free to execute all the hooked and timed out timer callbacks
      * And delete the timed out timers...
      */
-    while ( (timer = timedOut.First) )
+    while ( (timer = (SFG_Timer *)timedOut.First) )
     {
         if( timer->Callback != NULL )
             timer->Callback( timer->ID );
-       fgListRemove( &timedOut, &timer->Node );
+        fgListRemove( &timedOut, &timer->Node );
         free( timer );
     }
 }
@@ -338,17 +266,17 @@ static void fghCheckTimers( void )
 long fgElapsedTime( void )
 {
 #if TARGET_HOST_UNIX_X11
-       struct timeval now;
-       long elapsed;
-
-       gettimeofday( &now, NULL );
-
-       elapsed = (now.tv_usec - fgState.Time.Value.tv_usec) / 1000;
-       elapsed += (now.tv_sec - fgState.Time.Value.tv_sec) * 1000;
-
-       return( elapsed );
+    struct timeval now;
+    long elapsed;
+    
+    gettimeofday( &now, NULL );
+    
+    elapsed = (now.tv_usec - fgState.Time.Value.tv_usec) / 1000;
+    elapsed += (now.tv_sec - fgState.Time.Value.tv_sec) * 1000;
+    
+    return( elapsed );
 #elif TARGET_HOST_WIN32
-  return (timeGetTime() - fgState.Time.Value);
+    return (timeGetTime() - fgState.Time.Value);
 #endif
 }
 
@@ -361,7 +289,9 @@ void fgError( const char *fmt, ... )
 
     va_start( ap, fmt );
 
-    fprintf( stderr, "freeglut: ");
+    fprintf( stderr, "freeglut ");
+    if( fgState.ProgramName )
+        fprintf (stderr, "(%s): ", fgState.ProgramName);
     vfprintf( stderr, fmt, ap );
     fprintf( stderr, "\n" );
 
@@ -376,7 +306,9 @@ void fgWarning( const char *fmt, ... )
 
     va_start( ap, fmt );
 
-    fprintf( stderr, "freeglut: ");
+    fprintf( stderr, "freeglut ");
+    if( fgState.ProgramName )
+        fprintf( stderr, "(%s): ", fgState.ProgramName );
     vfprintf( stderr, fmt, ap );
     fprintf( stderr, "\n" );
 
@@ -384,66 +316,117 @@ void fgWarning( const char *fmt, ... )
 }
 
 /*
- * Clean up on exit
+ * Indicates whether Joystick events are being used by ANY window.
+ *
+ * The current mechanism is to walk all of the windows and ask if
+ * there is a joystick callback.  Certainly in some cases, maybe
+ * in all cases, the joystick is attached to the system and accessed
+ * from ONE point by GLUT/freeglut, so this is not the right way,
+ * in general, to do this.  However, the Joystick code is segregated
+ * in its own little world, so we can't access the information that
+ * we need in order to do that nicely.
+ *
+ * Some alternatives:
+ *  * Store Joystick data into freeglut global state.
+ *  * Provide NON-static functions or data from Joystick *.c file.
+ *
+ * Basically, the RIGHT way to do this requires knowing something
+ * about the Joystick.  Right now, the Joystick code is behind
+ * an opaque wall.
+ *
  */
-static void fgCleanUpGlutsMess( void ) 
+static void fgCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
 {
-  int i;
-
-  i = 0;
-
-  if ( fgStructure.Windows.First != NULL ) 
-  {
-    SFG_Window *win = fgStructure.Windows.First ;
-    glEnd();
-    glFinish();
-    glFlush();
-    while ( win != NULL )
+    if( w->Callbacks.Joystick )
     {
-      SFG_Window *temp_win = win->Node.Next ;
-      fgDestroyWindow ( win, FALSE ) ;
-      win = temp_win ;
+        e->found = TRUE;
+        e->data = w;
     }
-  }
-
-#if 0
-  /* these are pointers to external handles */
-
-  __glutWindowListSize    = 0;
-  __glutStaleWindowList   = NULL;
-  __glutWindowList        = NULL;
-  __glutCurrentWindow     = NULL;
-
-  /* make sure we no longer have a GL context */
-
-  if ( wglGetCurrentContext() != NULL ) 
-  {
-    wglDeleteContext( wglGetCurrentContext() );
-  }
-
-  hInstance = GetModuleHandle(NULL);
-  UnregisterClass( classname, hInstance );
-
-  /* clean up allocated timer memory */
+    fgEnumSubWindows( w, fgCheckJoystickCallback, e );
+}
+static int fgHaveJoystick( void )
+{
+    SFG_Enumerator enumerator;
+    enumerator.found = FALSE;
+    enumerator.data = NULL;
+    fgEnumWindows( fgCheckJoystickCallback, &enumerator );
+    return !!enumerator.data;
+}
+static void fgHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
+{
+    if( w->State.Redisplay )
+    {
+        e->found = TRUE;
+        e->data = w;
+    }
+    fgEnumSubWindows( w, fgHavePendingRedisplaysCallback, e );
+}        
+static int fgHavePendingRedisplays (void)
+{
+    SFG_Enumerator enumerator;
+    enumerator.found = FALSE;
+    enumerator.data = NULL;
+    fgEnumWindows( fgHavePendingRedisplaysCallback, &enumerator );
+    return !!enumerator.data;
+}
+/*
+ * Indicates whether there are any outstanding timers.
+ */
+static int fgHaveTimers( void )
+{
+    return !!fgState.Timers.First;
+}
+/*
+ * Returns the number of GLUT ticks (milliseconds) till the next timer event.
+ */
+static long fgNextTimer( void )
+{
+    long now = fgElapsedTime();
+    long ret = INT_MAX;
+    SFG_Timer *timer;
 
-  tList = __glutTimerList;
-  i = 0;
+    for( timer = (SFG_Timer *)fgState.Timers.First;
+         timer;
+         timer = (SFG_Timer *)timer->Node.Next )
+        ret = MIN( ret, MAX( 0, (timer->TriggerTime) - now ) );
 
-  while ( __glutTimerList ) 
-  {
-    i++;
-    tList = __glutTimerList;
+    return ret;
+}
+/*
+ * Does the magic required to relinquish the CPU until something interesting
+ * happens.
+ */
+static void fgSleepForEvents( void )
+{
+#if TARGET_HOST_UNIX_X11
+    fd_set fdset;
+    int err;
+    int socket;
+    struct timeval wait;
+    long msec;    
+    
+    if( fgState.IdleCallback ||
+        fgHavePendingRedisplays() )
+        return;
+    socket = ConnectionNumber( fgDisplay.Display );
+    FD_ZERO( &fdset );
+    FD_SET( socket, &fdset );
+    
+    msec = fgNextTimer();
+    if( fgHaveJoystick() )
+        msec = MIN( msec, 10 );
     
-    if ( __glutTimerList )
-      __glutTimerList = __glutTimerList->next;
+    wait.tv_sec = msec / 1000;
+    wait.tv_usec = (msec % 1000) * 1000;
+    err = select( socket+1, &fdset, NULL, NULL, &wait );
 
-    if ( tList )
-      free( tList );
-  }
+    if( -1 == err )
+        printf( "freeglut select() error: %d\n", errno );
+    
+#elif TARGET_HOST_WIN32
 #endif
 }
 
-
 /* -- INTERFACE FUNCTIONS -------------------------------------------------- */
 
 /*
@@ -459,36 +442,23 @@ void FGAPIENTRY glutMainLoopEvent( void )
   /*
    * This code was repeated constantly, so here it goes into a definition:
    */
-# define GETWINDOW(a) window = fgWindowByHandle( event.a.window );if( window == NULL ) break;
-# define GETMOUSE(a) window->State.MouseX = event.a.x; window->State.MouseY = event.a.y;
+# define GETWINDOW(a)                          \
+  window = fgWindowByHandle( event.a.window ); \
+  if( window == NULL )                         \
+    break;
+
+# define GETMOUSE(a)                           \
+  window->State.MouseX = event.a.x;            \
+  window->State.MouseY = event.a.y;
 
-  /*
-   * Make sure the display has been created etc.
-   */
   freeglut_assert_ready;
 
-  /*
-   * Do we have any event messages pending?
-   */
-  if( XPending( fgDisplay.Display ) )
+  while( XPending( fgDisplay.Display ) )
   {
-    /*
-     * Grab the next event to be processed...
-     */
     XNextEvent( fgDisplay.Display, &event );
-    window = fgWindowByHandle ( event.xany.window ) ;
 
-    /*
-     * Check the event's type
-     */
     switch( event.type )
     {
-    case CreateNotify:
-      /*
-       * The window creation confirmation
-       */
-      break;
-
     case DestroyNotify:
       /*
        * This is sent to confirm the XDestroyWindow call.
@@ -496,7 +466,6 @@ void FGAPIENTRY glutMainLoopEvent( void )
       /*
        * Call the window closure callback, remove from the structure, etc.
        */
-      fgStructure.Window = window ;
 /*      fgAddToWindowDestroyList ( window, FALSE ); */
 
       break;
@@ -507,7 +476,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
        */
       if( (Atom) event.xclient.data.l[ 0 ] == fgDisplay.DeleteWindow )
       {
-        fgStructure.Window = window ;
+        GETWINDOW( xclient ); 
 
         /*
          * Call the XWindows functions to close the window
@@ -532,9 +501,22 @@ void FGAPIENTRY glutMainLoopEvent( void )
        * We are too dumb to process partial exposes...
        */
       if( event.xexpose.count == 0 )
-          fghRedrawWindowByHandle( window->Window.Handle );
+          fghRedrawWindowByHandle( event.xexpose.window );
       break;
 
+      /*
+       * CreateNotify causes a configure-event so that sub-windows are
+       * handled compatibly with GLUT.
+       *
+       * NOTE that it is possible that you will more than one Reshape
+       * event for your top-level window, but something like this appears
+       * to be required for compatbility.
+       *
+       * GLUT presumably does this because it generally tries to treat
+       * sub-windows the same as windows.
+       *
+       */
+    case CreateNotify:
     case ConfigureNotify:
       /*
        * The window gets resized
@@ -559,6 +541,8 @@ void FGAPIENTRY glutMainLoopEvent( void )
         /*
          * The window's visiblity might have changed
          */
+        GETWINDOW( xvisibility ); 
+
         /*
          * Break now if no window status callback has been hooked to that window
          */
@@ -609,7 +593,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
         /*
          * Mouse is over one of our windows
          */
-        GETMOUSE( xcrossing );
+        GETWINDOW( xcrossing ); GETMOUSE( xcrossing );
 
         /*
          * Is there an entry callback hooked to the window?
@@ -617,6 +601,11 @@ void FGAPIENTRY glutMainLoopEvent( void )
         if( window->Callbacks.Entry != NULL )
         {
           /*
+           * Set the current window
+           */
+          fgSetWindow ( window ) ;
+
+          /*
            * Yeah. Notify the window about having the mouse cursor over
            */
           window->Callbacks.Entry( GLUT_ENTERED );
@@ -629,7 +618,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
         /*
          * Mouse is no longer over one of our windows
          */
-        GETMOUSE( xcrossing );
+        GETWINDOW( xcrossing ); GETMOUSE( xcrossing );
 
         /*
          * Is there an entry callback hooked to the window?
@@ -637,6 +626,11 @@ void FGAPIENTRY glutMainLoopEvent( void )
         if( window->Callbacks.Entry != NULL )
         {
           /*
+           * Set the current window
+           */
+          fgSetWindow ( window ) ;
+
+          /*
            * Yeah. Notify the window about having the mouse cursor over
            */
           window->Callbacks.Entry( GLUT_LEFT );
@@ -649,12 +643,31 @@ void FGAPIENTRY glutMainLoopEvent( void )
         /*
          * The mouse cursor was moved...
          */
-        GETMOUSE( xmotion );
+        GETWINDOW( xmotion ); GETMOUSE( xmotion );
 
         /*
-         * Set the current window
+         * Fallback if there's an active menu hooked to this window
          */
-        fgStructure.Window = window ;
+        if( window->ActiveMenu != NULL )
+        {
+            if ( window == window->ActiveMenu->ParentWindow )
+            {
+                window->ActiveMenu->Window->State.MouseX = event.xmotion.x_root - window->ActiveMenu->X ;
+                window->ActiveMenu->Window->State.MouseY = event.xmotion.y_root - window->ActiveMenu->Y ;
+            }
+
+            /*
+             * Let's make the window redraw as a result of the mouse motion.
+             */
+            window->ActiveMenu->Window->State.Redisplay = TRUE ;
+
+            /*
+             * Since the window is a menu, make the parent window current
+             */
+            fgSetWindow ( window->ActiveMenu->ParentWindow ) ;
+
+            break;
+        }
 
         /*
          * What kind of a movement was it?
@@ -670,6 +683,11 @@ void FGAPIENTRY glutMainLoopEvent( void )
           if( window->Callbacks.Motion != NULL )
           {
             /*
+             * Set the current window
+             */
+            fgSetWindow ( window ) ;
+
+            /*
              * Yup. Have it executed immediately
              */
             window->Callbacks.Motion( event.xmotion.x, event.xmotion.y );
@@ -683,6 +701,11 @@ void FGAPIENTRY glutMainLoopEvent( void )
           if( window->Callbacks.Passive != NULL )
           {
             /*
+             * Set the current window
+             */
+            fgSetWindow ( window ) ;
+
+            /*
              * That's right, and there is a passive callback, too.
              */
             window->Callbacks.Passive( event.xmotion.x, event.xmotion.y );
@@ -703,28 +726,17 @@ void FGAPIENTRY glutMainLoopEvent( void )
          * A mouse button has been pressed or released. Traditionally,
          * break if the window was found within the freeglut structures.
          */
-        GETMOUSE( xbutton );
-
-        /*
-         * GLUT API assumes that you can't have more than three mouse buttons, so:
-         */
-        switch( event.xbutton.button )
-        {
-        /*
-         * WARNING: this might be wrong, if we only have two mouse buttons,
-         *          Button2 might mean the right button, isn't that right?
-         */
-        case Button1:   button = GLUT_LEFT_BUTTON;   break;
-        case Button2:   button = GLUT_MIDDLE_BUTTON; break;
-        case Button3:   button = GLUT_RIGHT_BUTTON;  break;
-        default:        button = -1;                 break;
-        }
+        GETWINDOW( xbutton ); GETMOUSE( xbutton );
 
         /*
-         * Skip the unwanted mouse buttons...
-         */
-        if( button == -1 )
-          break;
+               * An X button (at least in XFree86) is numbered from 1.
+               * A GLUT button is numbered from 0.
+               * Old GLUT passed through buttons other than just the first
+               * three, though it only gave symbolic names and official
+               * support to the first three.
+               *
+               */
+              button = event.xbutton.button - 1;
 
         /*
          * Do not execute the application's mouse callback if a menu is hooked to this button.
@@ -739,39 +751,46 @@ void FGAPIENTRY glutMainLoopEvent( void )
          */
         if ( window->ActiveMenu != NULL )  /* Window has an active menu, it absorbs any mouse click */
         {
-          if ( fgCheckActiveMenu ( window, window->ActiveMenu ) == TRUE )  /* Inside the menu, invoke the callback and deactivate the menu*/
-          {
-            /* Save the current window and menu and set the current window to the window whose menu this is */
-            SFG_Window *save_window = fgStructure.Window ;
-            SFG_Menu *save_menu = fgStructure.Menu ;
-            fgSetWindow ( window ) ;
-            fgStructure.Menu = window->ActiveMenu ;
-
-            /* Execute the menu callback */
-            fgExecuteMenuCallback ( window->ActiveMenu ) ;
-            fgDeactivateMenu ( window ) ;
-
-            /* Restore the current window and menu */
-            fgSetWindow ( save_window ) ;
-            fgStructure.Menu = save_menu ;
-          }
-          else  /* Outside the menu, deactivate the menu if it's a downclick */
-          {
-            if ( pressed == TRUE ) fgDeactivateMenu ( window ) ;
-          }
-
-          /*
-           * Let's make the window redraw as a result of the mouse click and menu activity.
-           */
-          window->State.Redisplay = TRUE ;
+            if ( window == window->ActiveMenu->ParentWindow )
+            {
+                window->ActiveMenu->Window->State.MouseX = event.xbutton.x_root - window->ActiveMenu->X ;
+                window->ActiveMenu->Window->State.MouseY = event.xbutton.y_root - window->ActiveMenu->Y ;
+            }
+            
+            if ( fgCheckActiveMenu ( window->ActiveMenu->Window, window->ActiveMenu ) == TRUE )  /* Inside the menu, invoke the callback and deactivate the menu*/
+            {
+                /* Save the current window and menu and set the current window to the window whose menu this is */
+                SFG_Window *save_window = fgStructure.Window ;
+                SFG_Menu *save_menu = fgStructure.Menu ;
+                SFG_Window *parent_window = window->ActiveMenu->ParentWindow ;
+                fgSetWindow ( parent_window ) ;
+                fgStructure.Menu = window->ActiveMenu ;
+
+                /* Execute the menu callback */
+                fgExecuteMenuCallback ( window->ActiveMenu ) ;
+                fgDeactivateMenu ( parent_window ) ;
+
+                /* Restore the current window and menu */
+                fgSetWindow ( save_window ) ;
+                fgStructure.Menu = save_menu ;
+            }
+            else  /* Outside the menu, deactivate the menu if it's a downclick */
+                if ( pressed == TRUE )
+                    fgDeactivateMenu ( window->ActiveMenu->ParentWindow ) ;
 
-          break ;
+            /*
+             * Let's make the window redraw as a result of the mouse click and menu activity.
+             */
+            window->State.Redisplay = TRUE ;
+            
+            break ;
         }
 
         /*
          * No active menu, let's check whether we need to activate one.
          */
-        if ( ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) )
+        if (( 0 <= button ) && ( 2 >= button ) &&
+                  ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) )
         {
           /*
            * Let's make the window redraw as a result of the mouse click.
@@ -779,6 +798,11 @@ void FGAPIENTRY glutMainLoopEvent( void )
           window->State.Redisplay = TRUE ;
 
           /*
+           * Set the current window
+           */
+          fgSetWindow( window );
+
+          /*
            * Activate the appropriate menu structure...
            */
           fgActivateMenu( window, button );
@@ -787,19 +811,15 @@ void FGAPIENTRY glutMainLoopEvent( void )
         }
 
         /*
-         * Check if there is a mouse callback hooked to the window
+         * Check if there is a mouse or mouse wheel callback hooked to the
+         * window
          */
-        if( window->Callbacks.Mouse == NULL )
+        if ( ( window->Callbacks.Mouse == NULL ) &&
+             ( window->Callbacks.MouseWheel == NULL ) )
           break;
 
-        /*
-         * Set the current window
-         */
-        fgSetWindow( window );
+        fgSetWindow ( window );
 
-        /*
-         * Remember the current modifiers state
-         */
         modifiers = 0;
         if (event.xbutton.state & (ShiftMask|LockMask))
           modifiers |= GLUT_ACTIVE_SHIFT;
@@ -807,22 +827,61 @@ void FGAPIENTRY glutMainLoopEvent( void )
           modifiers |= GLUT_ACTIVE_CTRL;
         if (event.xbutton.state & Mod1Mask)
           modifiers |= GLUT_ACTIVE_ALT;
-        window->State.Modifiers = modifiers;
+        fgStructure.Window->State.Modifiers = modifiers;
 
         /*
-         * Finally execute the mouse callback
+         * Finally execute the mouse or mouse wheel callback
+         *
+         * XXX Use a symbolic constant, *not* "4"!
          */
-        window->Callbacks.Mouse(
-            button,
-            event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
-            event.xbutton.x,
-            event.xbutton.y
-        );
+        if ( button < 4 )
+        {
+          fgStructure.Window->Callbacks.Mouse(
+              button,
+              event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
+              event.xbutton.x,
+              event.xbutton.y
+          );
+        }
+        else
+        {
+          if ( window->Callbacks.MouseWheel )
+          {
+              /*
+               * Map 4 and 5 to wheel zero; EVEN to +1, ODD to -1
+               *  "  6 and 7 "    "   one; ...
+               *
+               * XXX This *should* be behind some variables/macros,
+               * XXX since the order and numbering isn't certain
+               * XXX See XFree86 configuration docs (even back in the
+               * XXX 3.x days, and especially with 4.x).
+               */
+            int wheel_number = (button - 4) / 2;
+            int direction = (button & 1)*2 - 1;
+
+            if( ButtonPress )
+                fgStructure.Window->Callbacks.MouseWheel(
+                    wheel_number,
+                    direction,
+                    event.xbutton.x,
+                    event.xbutton.y
+                );
+          }
+          else
+          {
+            fgStructure.Window->Callbacks.Mouse(
+                button,
+                event.type == ButtonPress ? GLUT_DOWN : GLUT_UP,
+                event.xbutton.x,
+                event.xbutton.y
+                );
+          }
+        }
 
         /*
          * Trash the modifiers state
          */
-        window->State.Modifiers = 0xffffffff;
+        fgStructure.Window->State.Modifiers = 0xffffffff;
       }
       break;
 
@@ -835,6 +894,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
         /*
          * A key has been pressed, find the window that had the focus:
          */
+        GETWINDOW( xkey );
         GETMOUSE( xkey );
 
         if( event.type == KeyPress )
@@ -864,11 +924,6 @@ void FGAPIENTRY glutMainLoopEvent( void )
           len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode), &keySym, &composeStatus );
 
           /*
-           * Get ready to calling the keyboard/special callbacks
-           */
-          fgSetWindow( window );
-
-          /*
            * GLUT API tells us to have two separate callbacks...
            */
           if( len > 0 )
@@ -879,6 +934,11 @@ void FGAPIENTRY glutMainLoopEvent( void )
             if( keyboard_cb != NULL )
             {
               /*
+               * Set the current window
+               */
+              fgSetWindow( window );
+
+              /*
                * Remember the current modifiers state
                */
               modifiers = 0;
@@ -952,6 +1012,8 @@ void FGAPIENTRY glutMainLoopEvent( void )
              */
             if( (special_cb != NULL) && (special != -1) )
             {
+              fgSetWindow( window );
+
               /*
                * Remember the current modifiers state
                */
@@ -977,77 +1039,26 @@ void FGAPIENTRY glutMainLoopEvent( void )
       break;
     }
   }
-  else
-  {
-    /*
-     * Have all the timers checked.
-     */
-    fghCheckTimers();
-
-    /*
-     * Poll the joystick and notify all windows that want to be notified...
-     */
-    fghCheckJoystickPolls();
-
-    /*
-     * No messages in the queue, which means we are idling...
-     */
-    if( fgState.IdleCallback != NULL )
-        fgState.IdleCallback();
-
-    /*
-     * Remember about displaying all the windows that have
-     * been marked for a redisplay (possibly in the idle call):
-     */
-    fghDisplayAll();
-  }
 
 #elif TARGET_HOST_WIN32
 
   MSG stMsg;
 
-  /*
-   * The windows processing is considerably smaller
-   */
-  if( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
+  while ( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
   {
-    /*
-     * Grab the message now, checking for WM_QUIT
-     */
     if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
       fgState.ExecState = GLUT_EXEC_STATE_STOP ;
 
-    /*
-     * Translate virtual-key messages and send them to the window...
-     */
     TranslateMessage( &stMsg );
     DispatchMessage( &stMsg );
   }
-  else
-  {
-    /*
-     * Have all the timers checked.
-     */
-    fghCheckTimers();
-
-    /*
-     * Poll the joystick and notify all windows that want to be notified...
-     */
-    fghCheckJoystickPolls();
+#endif
 
-    /*
-     * No messages in the queue, which means we are idling...
-     */
-    if( fgState.IdleCallback != NULL )
-      fgState.IdleCallback();
+  fghCheckTimers();
+  fghCheckJoystickPolls();
+  fghDisplayAll();
 
-    /*
-     * Remember about displaying all the windows that have
-     * been marked for a redisplay (possibly in the idle call):
-     */
-    fghDisplayAll();
-  }
-#endif
+  fgCloseWindows () ;
 }
 
 /*
@@ -1056,73 +1067,64 @@ void FGAPIENTRY glutMainLoopEvent( void )
 void FGAPIENTRY glutMainLoop( void )
 {
 #if TARGET_HOST_WIN32
-  SFG_Window *window = fgStructure.Windows.First ;
+  SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
 #endif
 
-  /*
-   * Make sure the display has been created etc.
-   */
   freeglut_assert_ready;
 
 #if TARGET_HOST_WIN32
   /*
-   * Processing before the main loop:  If there is a window which is open and which
-   * has a visibility callback, call it.  I know this is an ugly hack, but I'm not sure
-   * what else to do about it.  Ideally we should leave something uninitialized in the
-   * create window code and initialize it in the main loop, and have that initialization
-   * create a "WM_ACTIVATE" message.  Then we would put the visibility callback code in
-   * the "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
+   * Processing before the main loop:  If there is a window which is open and
+   * which has a visibility callback, call it.  I know this is an ugly hack,
+   * but I'm not sure what else to do about it.  Ideally we should leave
+   * something uninitialized in the create window code and initialize it in
+   * the main loop, and have that initialization create a "WM_ACTIVATE"
+   * message.  Then we would put the visibility callback code in the
+   * "case WM_ACTIVATE" block below.         - John Fay -- 10/24/02
    */
   while ( window != NULL )
   {
     if ( window->Callbacks.Visibility != NULL )
+    {
+      SFG_Window *current_window = fgStructure.Window ;
+
+      fgSetWindow( window );
       window->Callbacks.Visibility ( window->State.Visible ) ;
+      fgSetWindow( current_window );
+    }
 
-    window = window->Node.Next ;
+    window = (SFG_Window *)window->Node.Next ;
   }
 #endif
 
-  /*
-   * Set freeglut to be running
-   */
   fgState.ExecState = GLUT_EXEC_STATE_RUNNING ;
-
-  /*
-   * Enter the main loop itself.  Inside the loop, process events and check for loop exit.
-   */
   while ( fgState.ExecState == GLUT_EXEC_STATE_RUNNING )
   {
     glutMainLoopEvent () ;
 
-    /*
-     * If an event caused a window to be closed, do the actual closing here
-     */
-    fgCloseWindows () ;
-
-    /*
-     * If there are no more windows open, stop execution
-     */
     if ( fgStructure.Windows.First == NULL )
       fgState.ExecState = GLUT_EXEC_STATE_STOP ;
-  }
+    else
+    {
+      if ( fgState.IdleCallback )
+        fgState.IdleCallback ();
 
+      fgSleepForEvents();
+    }
+  }
 
-  /*
-   * If we got here by the user closing a window or by the application closing down, there may still be windows open.
-   */
-  fgCleanUpGlutsMess () ;
+  {
+    fgExecutionState execState = fgState.ActionOnWindowClose;
 
-  /*
-   * Check whether we return to the calling program or simply exit
-   */
-  if ( fgState.ActionOnWindowClose == GLUT_ACTION_EXIT )
-    exit ( 0 ) ;
+    /*
+     * When this loop terminates, destroy the display, state and structure
+     * of a freeglut session, so that another glutInit() call can happen
+     */
+    fgDeinitialize();
 
-  /*
-   * When this loop terminates, destroy the display, state and structure
-   * of a freeglut session, so that another glutInit() call can happen
-   */
-  fgDeinitialize();
+    if ( execState == GLUT_ACTION_EXIT )
+      exit ( 0 ) ;
+  }
 }
 
 /*
@@ -1147,9 +1149,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
       return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
 
 /*    printf ( "Window %3d message <%04x> %12d %12d\n", window?window->ID:0, uMsg, wParam, lParam ) ; */
-    /*
-     * Check what type of message are we receiving
-     */
     switch( uMsg )
     {
     case WM_CREATE:
@@ -1159,37 +1158,41 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
         assert( window != NULL );
 
-        /*
-         * We can safely store the window's handle now:
-         */
         window->Window.Handle = hWnd;
-
-        /*
-         * Get the window's device context
-         */
         window->Window.Device = GetDC( hWnd );
+        if ( fgState.BuildingAMenu )
+        {
+          unsigned int current_DisplayMode = fgState.DisplayMode ;
+          fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ;
+          fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
+          fgState.DisplayMode = current_DisplayMode ;
 
-        /*
-         * Setup the pixel format of our window
-         */
-        fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
+          if ( !fgStructure.MenuContext )
+          {
+            fgStructure.MenuContext = (SFG_MenuContext *)malloc ( sizeof(SFG_MenuContext) ) ;
+            fgStructure.MenuContext->Context = wglCreateContext( window->Window.Device );
+          }
+          else
+            wglMakeCurrent ( window->Window.Device, fgStructure.MenuContext->Context ) ;
 
-        /*
-         * Create or get the OpenGL rendering context now
-         */
-        if ( fgState.UseCurrentContext == TRUE )
-          window->Window.Context = wglGetCurrentContext();
-        else
+/*          window->Window.Context = wglGetCurrentContext () ;   */
           window->Window.Context = wglCreateContext( window->Window.Device );
+        }
+        else
+        {
+          fgSetupPixelFormat( window, FALSE, PFD_MAIN_PLANE );
 
-        /*
-         * Still, we'll be needing to explicitly resize the window
-         */
-        window->State.NeedToResize = TRUE;
+          if ( fgState.UseCurrentContext == TRUE )
+          {
+            window->Window.Context = wglGetCurrentContext();
+            if ( ! window->Window.Context )
+              window->Window.Context = wglCreateContext( window->Window.Device );
+          }
+          else
+            window->Window.Context = wglCreateContext( window->Window.Device );
+        }
 
-        /*
-         * Finally, have the window's device context released
-         */
+        window->State.NeedToResize = TRUE;
         ReleaseDC( window->Window.Handle, window->Window.Device );
         break;
 
@@ -1209,7 +1212,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
         if (LOWORD(wParam) != WA_INACTIVE)
         {
           /* glutSetCursor( fgStructure.Window->State.Cursor ); */
-               printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window, window->State.Cursor );
+                printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window, window->State.Cursor );
 
           glutSetCursor( window->State.Cursor );
         }
@@ -1224,63 +1227,48 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
          */
 #if 0
         if ((LOWORD(lParam) == HTCLIENT) &&
-           (fgStructure.Window->State.Cursor == GLUT_CURSOR_NONE))
-         SetCursor( NULL );
+            (fgStructure.Window->State.Cursor == GLUT_CURSOR_NONE))
+          SetCursor( NULL );
 #else
-       /* Set the cursor AND change it for this window class. */
-#      define MAP_CURSOR(a,b) case a: SetCursor( LoadCursor( NULL, b ) ); \
+        /* Set the cursor AND change it for this window class. */
+#        define MAP_CURSOR(a,b) case a: SetCursor( LoadCursor( NULL, b ) ); \
         break;
-       /* Nuke the cursor AND change it for this window class. */
-#      define ZAP_CURSOR(a,b) case a: SetCursor( NULL ); \
+        /* Nuke the cursor AND change it for this window class. */
+#        define ZAP_CURSOR(a,b) case a: SetCursor( NULL ); \
         break;
 
         if (LOWORD(lParam) == HTCLIENT)
-         switch( window->State.Cursor )
-         {
-               MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW     );
-               MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW,  IDC_ARROW     );
-               MAP_CURSOR( GLUT_CURSOR_INFO,        IDC_HELP      );
-               MAP_CURSOR( GLUT_CURSOR_DESTROY,     IDC_CROSS     );
-               MAP_CURSOR( GLUT_CURSOR_HELP,        IDC_HELP      );
-               MAP_CURSOR( GLUT_CURSOR_CYCLE,       IDC_SIZEALL   );
-               MAP_CURSOR( GLUT_CURSOR_SPRAY,       IDC_CROSS     );
-               MAP_CURSOR( GLUT_CURSOR_WAIT,            IDC_WAIT      );
-               MAP_CURSOR( GLUT_CURSOR_TEXT,        IDC_UPARROW   );
-               MAP_CURSOR( GLUT_CURSOR_CROSSHAIR,   IDC_CROSS     );
-               /* MAP_CURSOR( GLUT_CURSOR_NONE,        IDC_NO             ); */
-               ZAP_CURSOR( GLUT_CURSOR_NONE,        NULL          );
-
-               default:
-               MAP_CURSOR( GLUT_CURSOR_UP_DOWN,     IDC_ARROW     );
-         }
+          switch( window->State.Cursor )
+          {
+                MAP_CURSOR( GLUT_CURSOR_RIGHT_ARROW, IDC_ARROW     );
+                MAP_CURSOR( GLUT_CURSOR_LEFT_ARROW,  IDC_ARROW     );
+                MAP_CURSOR( GLUT_CURSOR_INFO,        IDC_HELP      );
+                MAP_CURSOR( GLUT_CURSOR_DESTROY,     IDC_CROSS     );
+                MAP_CURSOR( GLUT_CURSOR_HELP,        IDC_HELP           );
+                MAP_CURSOR( GLUT_CURSOR_CYCLE,       IDC_SIZEALL   );
+                MAP_CURSOR( GLUT_CURSOR_SPRAY,       IDC_CROSS     );
+                MAP_CURSOR( GLUT_CURSOR_WAIT,                 IDC_WAIT      );
+                MAP_CURSOR( GLUT_CURSOR_TEXT,        IDC_UPARROW   );
+                MAP_CURSOR( GLUT_CURSOR_CROSSHAIR,   IDC_CROSS     );
+                /* MAP_CURSOR( GLUT_CURSOR_NONE,        IDC_NO                   ); */
+                ZAP_CURSOR( GLUT_CURSOR_NONE,        NULL           );
+
+                default:
+                MAP_CURSOR( GLUT_CURSOR_UP_DOWN,     IDC_ARROW     );
+          }
 #endif
-       else
-         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
-       break;
+        else
+          lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
+        break;
 
     case WM_SHOWWINDOW:
-        /*
-         * We are now Visible!
-         */
         window->State.Visible = TRUE;
         window->State.Redisplay = TRUE;
         break;
 
     case WM_PAINT:
-        /*
-         * Start the painting job
-         */
-
         BeginPaint( hWnd, &ps );
-
-        /*
-         * Call the engine's main frame drawing method
-         */
         fghRedrawWindowByHandle( hWnd );
-
-        /*
-         * End the painting job, release the device context
-         */
         EndPaint( hWnd, &ps );
         break;
 
@@ -1294,10 +1282,10 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
           SFG_Window *iter ;
 
             wglMakeCurrent( NULL, NULL );
-            /* Step through the list of windows.  If the rendering context is notbeing used
+            /* Step through the list of windows.  If the rendering context is not being used
              * by another window, then we delete it.
              */
-            for ( iter = fgStructure.Windows.First; iter; iter = iter->Node.Next )
+            for ( iter = (SFG_Window *)fgStructure.Windows.First; iter; iter = (SFG_Window *)iter->Node.Next )
             {
               if ( ( iter->Window.Context == window->Window.Context ) && ( iter != window ) )
                 used = TRUE ;
@@ -1310,10 +1298,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
          * Put on a linked list of windows to be removed after all the callbacks have returned
          */
         fgAddToWindowDestroyList ( window, FALSE ) ;
-
-        /*
-         * Proceed with the window destruction
-         */
         DestroyWindow( hWnd );
         break;
 
@@ -1325,76 +1309,43 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
 
     case WM_MOUSEMOVE:
     {
-        /*
-         * The mouse cursor has moved. Remember the new mouse cursor's position
-         */
         window->State.MouseX = LOWORD( lParam );
         window->State.MouseY = HIWORD( lParam );
 
-        /*
-         * Fallback if there's an active menu hooked to this window
-         */
-        if( window->ActiveMenu != NULL )
+        if ( window->ActiveMenu != NULL )
         {
+            window->State.Redisplay = TRUE ;
+
             /*
-             * Let's make the window redraw as a result of the mouse motion.
+             * Since the window is a menu, make the parent window current
              */
-            window->State.Redisplay = TRUE ;
+            fgSetWindow ( window->ActiveMenu->ParentWindow ) ;
 
             break;
         }
 
-        /*
-         * Remember the current modifiers state.
-         */
         window->State.Modifiers = 
             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
 
-        /*
-         * Check if any of the mouse buttons is pressed...
-         */
         if( (wParam & MK_LBUTTON) || (wParam & MK_MBUTTON) || (wParam & MK_RBUTTON) )
         {
-            /*
-             * Yeah, indeed. We need to use the motion callback then:
-             */
             if( window->Callbacks.Motion != NULL )
             {
-                /*
-                 * Make sure the current window is set...
-                 */
                 fgSetWindow( window );
-
-                /*
-                 * Execute the active mouse motion callback now
-                 */
                 window->Callbacks.Motion( window->State.MouseX, window->State.MouseY );
             }
         }
         else
         {
-            /*
-             * All mouse buttons are up, execute the passive mouse motion callback
-             */
             if( window->Callbacks.Passive != NULL )
             {
-                /*
-                 * Make sure the current window is set
-                 */
                 fgSetWindow( window );
-
-                /*
-                 * Execute the passive mouse motion callback
-                 */
                 window->Callbacks.Passive( window->State.MouseX, window->State.MouseY );
             }
         }
 
-        /*
-         * Thrash the current modifiers state now
-         */
         window->State.Modifiers = 0xffffffff;
     }
     break;
@@ -1409,15 +1360,9 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
         GLboolean pressed = TRUE;
         int button;
 
-        /*
-         * The mouse cursor has moved. Remember the new mouse cursor's position
-         */
         window->State.MouseX = LOWORD( lParam );
         window->State.MouseY = HIWORD( lParam );
 
-        /*
-         * We're curious about the GLUT API button name...
-         */
         switch( uMsg )
         {
         case WM_LBUTTONDOWN: pressed = TRUE;  button = GLUT_LEFT_BUTTON;   break;
@@ -1429,16 +1374,10 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
         default:             pressed = FALSE; button = -1;                 break;
         }
 
-        /*
-         * The left and right mouse buttons might have been swapped...
-         */
         if( GetSystemMetrics( SM_SWAPBUTTON ) )
             if( button == GLUT_LEFT_BUTTON ) button = GLUT_RIGHT_BUTTON;
             else if( button == GLUT_RIGHT_BUTTON ) button = GLUT_LEFT_BUTTON;
 
-        /*
-         * Hey, what's up with you?
-         */
         if( button == -1 )
             return( DefWindowProc( hWnd, uMsg, lParam, wParam ) );
 
@@ -1460,12 +1399,13 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
             /* Save the current window and menu and set the current window to the window whose menu this is */
             SFG_Window *save_window = fgStructure.Window ;
             SFG_Menu *save_menu = fgStructure.Menu ;
-            fgSetWindow ( window ) ;
+            SFG_Window *parent_window = window->ActiveMenu->ParentWindow ;
+            fgSetWindow ( parent_window ) ;
             fgStructure.Menu = window->ActiveMenu ;
 
             /* Execute the menu callback */
             fgExecuteMenuCallback ( window->ActiveMenu ) ;
-            fgDeactivateMenu ( window ) ;
+            fgDeactivateMenu ( parent_window ) ;
 
             /* Restore the current window and menu */
             fgSetWindow ( save_window ) ;
@@ -1473,57 +1413,36 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
           }
           else  /* Outside the menu, deactivate the menu if it's a downclick */
           {
-            if ( pressed == TRUE ) fgDeactivateMenu ( window ) ;
+            if ( pressed == TRUE ) fgDeactivateMenu ( window->ActiveMenu->ParentWindow ) ;
           }
 
           /*
            * Let's make the window redraw as a result of the mouse click and menu activity.
            */
-          window->State.Redisplay = TRUE ;
+          if ( ! window->IsMenu ) window->State.Redisplay = TRUE ;
 
           break ;
         }
 
-        /*
-         * No active menu, let's check whether we need to activate one.
-         */
         if ( ( window->Menu[ button ] != NULL ) && ( pressed == TRUE ) )
         {
-            /*
-             * Let's make the window redraw as a result of the mouse click.
-             */
             window->State.Redisplay = TRUE ;
-
-            /*
-             * Activate the appropriate menu structure...
-             */
+            fgSetWindow( window );
             fgActivateMenu( window, button );
 
             break;
         }
 
-        /*
-         * Check if there is a mouse callback hooked to the window
-         */
         if( window->Callbacks.Mouse == NULL )
             break;
 
-        /*
-         * Set the current window
-         */
-        fgSetWindow( window );
+        fgSetWindow ( window );
 
-        /*
-         * Remember the current modifiers state.
-         */
-        window->State.Modifiers = 
+        fgStructure.Window->State.Modifiers = 
             ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
 
-        /*
-         * Finally execute the mouse callback
-         */
         window->Callbacks.Mouse(
             button,
             pressed == TRUE ? GLUT_DOWN : GLUT_UP,
@@ -1531,31 +1450,84 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
             window->State.MouseY
         );
 
-        /*
-         * Trash the modifiers state
-         */
-        window->State.Modifiers = 0xffffffff;
+        fgStructure.Window->State.Modifiers = 0xffffffff;
     }
     break;
 
+    case 0x020a:
+        /* Should be WM_MOUSEWHEEL but my compiler doesn't recognize it */
+      {
+        int wheel_number = LOWORD ( lParam ) ;
+        /* THIS IS SPECULATIVE -- John Fay, 10/2/03 */
+        int ticks = HIWORD ( lParam ) / 120 ;
+        /* Should be WHEEL_DELTA instead of 120 */
+        int direction = 1;
+
+        if( ticks < 0 )
+        {
+            direction = -1;
+            ticks = -ticks;
+        }
+
+        /*
+         * The mouse cursor has moved. Remember the new mouse cursor's position
+         */
+        /*        window->State.MouseX = LOWORD( lParam ); */
+        /* Need to adjust by window position, */
+        /*        window->State.MouseY = HIWORD( lParam ); */
+        /* change "lParam" to other parameter */
+
+        if ( ( window->Callbacks.MouseWheel == NULL ) &&
+             ( window->Callbacks.Mouse == NULL ) )
+            break;
+
+        fgSetWindow ( window );
+        fgStructure.Window->State.Modifiers = 
+            ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) ||
+                ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
+            ( ( (GetKeyState( VK_LCONTROL ) < 0 ) ||
+                ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
+            ( ( (GetKeyState( VK_LMENU    ) < 0 ) ||
+                ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
+
+        while( ticks-- )
+            if ( window->Callbacks.MouseWheel )
+                window->Callbacks.MouseWheel(
+                    wheel_number,
+                    direction,
+                    window->State.MouseX,
+                    window->State.MouseY
+                ) ;
+            else  /* No mouse wheel, call the mouse button callback twice */
+            {
+                /*
+                 * XXX The below assumes that you have no more than 3 mouse
+                 * XXX buttons.  Sorry.
+                 */
+                int button = wheel_number * 2 + 4;
+                button += (1 + direction)/2;
+                window->Callbacks.Mouse ( button, GLUT_DOWN,
+                                          window->State.MouseX,
+                                          window->State.MouseY ) ;
+                window->Callbacks.Mouse ( button, GLUT_UP,
+                                          window->State.MouseX,
+                                          window->State.MouseY ) ;
+            }
+
+        fgStructure.Window->State.Modifiers = 0xffffffff;
+      }
+      break ;
+
     case WM_SYSKEYDOWN:
     case WM_KEYDOWN:
     {
         int keypress = -1;
         POINT mouse_pos ;
 
-        /*
-         * Ignore the automatic key repetition if needed:
-         */
         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
             break;
 
         /*
-         * Set the current window
-         */
-        fgSetWindow( window );
-
-        /*
          * Remember the current modifiers state. This is done here in order 
          * to make sure the VK_DELETE keyboard callback is executed properly.
          */
@@ -1564,9 +1536,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
 
-        /*
-         * Set the mouse position
-         */
         GetCursorPos ( &mouse_pos ) ;
         ScreenToClient ( window->Window.Handle, &mouse_pos ) ;
 
@@ -1580,9 +1549,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
 
         switch( wParam )
         {
-            /*
-             * Most of the special characters can be handled automagically...
-             */
             KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
             KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
             KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
@@ -1595,31 +1561,23 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
             KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
             KEY( VK_INSERT, GLUT_KEY_INSERT    );
 
-            /*
-             * ...yet there is a small exception we need to have handled...
-             */
-            case VK_DELETE:
+        case VK_DELETE:
                 /*
                  * The delete key should be treated as an ASCII keypress:
                  */
                 if( window->Callbacks.Keyboard != NULL )
+                {
+                    fgSetWindow( window );
                     window->Callbacks.Keyboard( 127, window->State.MouseX, window->State.MouseY );
+                }
         }
 
-        /*
-         * Execute the special callback, if present, given the conversion was a success:
-         */
         if( (keypress != -1) && (window->Callbacks.Special != NULL) )
         {
-            /*
-             * Have the special callback executed:
-             */
+            fgSetWindow( window );
             window->Callbacks.Special( keypress, window->State.MouseX, window->State.MouseY );
         }
 
-        /*
-         * Thrash the modifiers register now
-         */
         window->State.Modifiers = 0xffffffff;
     }
     break;
@@ -1631,11 +1589,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
         POINT mouse_pos ;
 
         /*
-         * Set the current window
-         */
-        fgSetWindow( window );
-
-        /*
          * Remember the current modifiers state. This is done here in order 
          * to make sure the VK_DELETE keyboard callback is executed properly.
          */
@@ -1644,9 +1597,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
             ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
             ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
 
-        /*
-         * Set the mouse position
-         */
         GetCursorPos ( &mouse_pos ) ;
         ScreenToClient ( window->Window.Handle, &mouse_pos ) ;
 
@@ -1659,9 +1609,6 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
 
         switch( wParam )
         {
-          /*
-           * Most of the special characters can be handled automagically...
-           */
           KEY( VK_F1,     GLUT_KEY_F1        ); KEY( VK_F2,     GLUT_KEY_F2        );
           KEY( VK_F3,     GLUT_KEY_F3        ); KEY( VK_F4,     GLUT_KEY_F4        );
           KEY( VK_F5,     GLUT_KEY_F5        ); KEY( VK_F6,     GLUT_KEY_F6        );
@@ -1674,22 +1621,19 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
           KEY( VK_RIGHT,  GLUT_KEY_RIGHT     ); KEY( VK_DOWN,   GLUT_KEY_DOWN      );
           KEY( VK_INSERT, GLUT_KEY_INSERT    );
 
-          /*
-           * ...yet there is a small exception we need to have handled...
-           */
           case VK_DELETE:
             /*
              * The delete key should be treated as an ASCII keypress:
              */
             if( window->Callbacks.KeyboardUp != NULL )
+            {
+                fgSetWindow ( window ) ;
                 window->Callbacks.KeyboardUp( 127, window->State.MouseX, window->State.MouseY );
+            }
 
             break ;
           default:
             {
-              /*
-               * Call the KeyboardUp callback for a regular character if there is one.
-               */
               BYTE state[ 256 ];
               WORD code[ 2 ];
 
@@ -1699,24 +1643,19 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
                 wParam=code[ 0 ];
 
               if( window->Callbacks.KeyboardUp != NULL )
+              {
+                fgSetWindow( window );
                 window->Callbacks.KeyboardUp( (char)wParam, window->State.MouseX, window->State.MouseY );
+              }
             }
         }
 
-        /*
-         * Execute the special callback, if present, given the conversion was a success:
-         */
         if( (keypress != -1) && (window->Callbacks.SpecialUp != NULL) )
         {
-            /*
-             * Have the special callback executed:
-             */
+            fgSetWindow( window );
             window->Callbacks.SpecialUp( keypress, window->State.MouseX, window->State.MouseY );
         }
 
-        /*
-         * Thrash the modifiers register now
-         */
         window->State.Modifiers = 0xffffffff;
     }
     break;
@@ -1724,33 +1663,18 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
     case WM_SYSCHAR:
     case WM_CHAR:
     {
-        /*
-         * Ignore the automatic key repetition if needed:
-         */
         if( fgState.IgnoreKeyRepeat && (lParam & KF_REPEAT) )
             break;
 
-        /*
-         * Clear to go with the keyboard callback, if registered:
-         */
         if( window->Callbacks.Keyboard != NULL )
         {
-            /*
-             * Remember the current modifiers state
-             */
+            fgSetWindow( window );
             window->State.Modifiers = 
                 ( ( (GetKeyState( VK_LSHIFT   ) < 0 ) || ( GetKeyState( VK_RSHIFT   ) < 0 )) ? GLUT_ACTIVE_SHIFT : 0 ) |
                 ( ( (GetKeyState( VK_LCONTROL ) < 0 ) || ( GetKeyState( VK_RCONTROL ) < 0 )) ? GLUT_ACTIVE_CTRL  : 0 ) |
                 ( ( (GetKeyState( VK_LMENU    ) < 0 ) || ( GetKeyState( VK_RMENU    ) < 0 )) ? GLUT_ACTIVE_ALT   : 0 );
 
-            /*
-             * Have the special callback executed:
-             */
             window->Callbacks.Keyboard( (char)wParam, window->State.MouseX, window->State.MouseY );
-
-            /*
-             * Thrash the modifiers register now
-             */
             window->State.Modifiers = 0xffffffff;
         }
     }
@@ -1758,7 +1682,11 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
 
     case WM_CAPTURECHANGED :  /* User has finished resizing the window, force a redraw */
       if ( window->Callbacks.Display )
+      {
+        fgSetWindow( window );
+
         window->Callbacks.Display () ;
+      }
 
 /*      lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ) ; */
       break ;