Takeshi Nishimura\'s changes to make the Windows mouse wheel/button mapping match...
[freeglut] / src / freeglut_main.c
index 95b70de..7f62b84 100644 (file)
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #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
-#elif TARGET_HOST_WINCE
+#include <stdarg.h>
+#if TARGET_HOST_WIN32
+#    define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
+#else
+#    if HAVE_VPRINTF
+#        define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
+#    elif HAVE_DOPRNT
+#        define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
+#    else
+#        define VFPRINTF(s,f,a)
+#    endif
+#endif
+
+#if TARGET_HOST_WINCE
 
 typedef struct GXDisplayProperties GXDisplayProperties;
 typedef struct GXKeyList GXKeyList;
@@ -57,8 +57,15 @@ struct GXKeyList gxKeyList;
 
 #endif
 
-#ifndef MAX
-#define MAX(a,b) (((a)>(b)) ? (a) : (b))
+/*
+ * Try to get the maximum value allowed for ints, falling back to the minimum
+ * guaranteed by ISO C99 if there is no suitable header.
+ */
+#if HAVE_LIMITS_H
+#    include <limits.h>
+#endif
+#ifndef INT_MAX
+#    define INT_MAX 32767
 #endif
 
 #ifndef MIN
@@ -86,7 +93,7 @@ struct GXKeyList gxKeyList;
  */
 static void fghReshapeWindow ( SFG_Window *window, int width, int height )
 {
-    SFG_Window *current_window = fgStructure.Window;
+    SFG_Window *current_window = fgStructure.CurrentWindow;
 
     freeglut_return_if_fail( window != NULL );
 
@@ -97,9 +104,7 @@ static void fghReshapeWindow ( SFG_Window *window, int width, int height )
                    width, height );
     XFlush( fgDisplay.Display ); /* XXX Shouldn't need this */
 
-#elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
-
-#if !TARGET_HOST_WINCE
+#elif TARGET_HOST_WIN32
     {
         RECT winRect;
         int x, y, w, h;
@@ -149,7 +154,7 @@ static void fghReshapeWindow ( SFG_Window *window, int width, int height )
                       SWP_NOZORDER
         );
     }
-#endif /* TARGET_HOST_WINCE */
+#endif
 
     /*
      * XXX Should update {window->State.OldWidth, window->State.OldHeight}
@@ -163,8 +168,6 @@ static void fghReshapeWindow ( SFG_Window *window, int width, int height )
         glViewport( 0, 0, width, height );
     }
 
-#endif
-
     /*
      * Force a window redraw.  In Windows at least this is only a partial
      * solution:  if the window is increasing in size in either dimension,
@@ -184,7 +187,7 @@ static void fghReshapeWindow ( SFG_Window *window, int width, int height )
  */
 static void fghRedrawWindow ( SFG_Window *window )
 {
-    SFG_Window *current_window = fgStructure.Window;
+    SFG_Window *current_window = fgStructure.CurrentWindow;
 
     freeglut_return_if_fail( window );
     freeglut_return_if_fail( FETCH_WCB ( *window, Display ) );
@@ -351,8 +354,8 @@ void fgError( const char *fmt, ... )
 
     fprintf( stderr, "freeglut ");
     if( fgState.ProgramName )
-        fprintf (stderr, "(%s): ", fgState.ProgramName);
-    vfprintf( stderr, fmt, ap );
+        fprintf( stderr, "(%s): ", fgState.ProgramName );
+    VFPRINTF( stderr, fmt, ap );
     fprintf( stderr, "\n" );
 
     va_end( ap );
@@ -372,7 +375,7 @@ void fgWarning( const char *fmt, ... )
     fprintf( stderr, "freeglut ");
     if( fgState.ProgramName )
         fprintf( stderr, "(%s): ", fgState.ProgramName );
-    vfprintf( stderr, fmt, ap );
+    VFPRINTF( stderr, fmt, ap );
     fprintf( stderr, "\n" );
 
     va_end( ap );
@@ -402,6 +405,7 @@ static void fghCheckJoystickCallback( SFG_Window* w, SFG_Enumerator* e)
 static int fghHaveJoystick( void )
 {
     SFG_Enumerator enumerator;
+
     enumerator.found = GL_FALSE;
     enumerator.data = NULL;
     fgEnumWindows( fghCheckJoystickCallback, &enumerator );
@@ -419,6 +423,7 @@ static void fghHavePendingRedisplaysCallback( SFG_Window* w, SFG_Enumerator* e)
 static int fghHavePendingRedisplays (void)
 {
     SFG_Enumerator enumerator;
+
     enumerator.found = GL_FALSE;
     enumerator.data = NULL;
     fgEnumWindows( fghHavePendingRedisplaysCallback, &enumerator );
@@ -451,8 +456,10 @@ static void fghSleepForEvents( void )
         return;
 
     msec = fghNextTimer( );
-    if( fghHaveJoystick( ) )     /* XXX Use GLUT timers for joysticks... */
-        msec = MIN( msec, 10 );  /* XXX Dumb; forces granularity to .01sec */
+    /* XXX Use GLUT timers for joysticks... */
+    /* XXX Dumb; forces granularity to .01sec */
+    if( fghHaveJoystick( ) && ( msec > 10 ) )     
+        msec = 10;
 
 #if TARGET_HOST_UNIX_X11
     /*
@@ -478,7 +485,7 @@ static void fghSleepForEvents( void )
         wait.tv_usec = (msec % 1000) * 1000;
         err = select( socket+1, &fdset, NULL, NULL, &wait );
 
-        if( -1 == err )
+        if( ( -1 == err ) && ( errno != EINTR ) )
             fgWarning ( "freeglut select() error: %d", errno );
     }
 #elif TARGET_HOST_WIN32 || TARGET_HOST_WINCE
@@ -527,7 +534,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
     window->State.MouseX = event.a.x;            \
     window->State.MouseY = event.a.y;
 
-    freeglut_assert_ready;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
 
     while( XPending( fgDisplay.Display ) )
     {
@@ -581,6 +588,8 @@ void FGAPIENTRY glutMainLoopEvent( void )
                 if( ( width != window->State.OldWidth ) ||
                     ( height != window->State.OldHeight ) )
                 {
+                    SFG_Window *current_window = fgStructure.CurrentWindow;
+
                     window->State.OldWidth = width;
                     window->State.OldHeight = height;
                     if( FETCH_WCB( *window, Reshape ) )
@@ -591,6 +600,8 @@ void FGAPIENTRY glutMainLoopEvent( void )
                         glViewport( 0, 0, width, height );
                     }
                     glutPostRedisplay( );
+                    if( window->IsMenu )
+                        fgSetWindow( current_window );
                 }
             }
             break;
@@ -617,8 +628,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
             if( event.xexpose.count == 0 )
             {
                 GETWINDOW( xexpose );
-                fgSetWindow( window );
-                glutPostRedisplay( );
+                window->State.Redisplay = GL_TRUE;
             }
             break;
 
@@ -684,6 +694,10 @@ void FGAPIENTRY glutMainLoopEvent( void )
         case LeaveNotify:
             GETWINDOW( xcrossing );
             GETMOUSE( xcrossing );
+            if( ( event.type == LeaveNotify ) && window->IsMenu &&
+                window->ActiveMenu && window->ActiveMenu->IsActive )
+                fgUpdateMenuHighlight( window->ActiveMenu );
+
             INVOKE_WCB( *window, Entry, ( ( EnterNotify == event.type ) ?
                                           GLUT_ENTERED :
                                           GLUT_LEFT ) );
@@ -703,8 +717,8 @@ void FGAPIENTRY glutMainLoopEvent( void )
                     window->ActiveMenu->Window->State.MouseY =
                         event.xmotion.y_root - window->ActiveMenu->Y;
                 }
-                window->ActiveMenu->Window->State.Redisplay = GL_TRUE ;
-                fgSetWindow( window->ActiveMenu->ParentWindow );
+
+                fgUpdateMenuHighlight( window->ActiveMenu );
 
                 break;
             }
@@ -752,93 +766,13 @@ void FGAPIENTRY glutMainLoopEvent( void )
             button = event.xbutton.button - 1;
 
             /*
-             * XXX This comment is replicated in the WIN32 section and
-             * XXX maybe also in the menu code.  Can we move the info
-             * XXX to one central place and *reference* it from here?
-             *
              * Do not execute the application's mouse callback if a menu
              * is hooked to this button.  In that case an appropriate
              * private call should be generated.
-             * Near as I can tell, this is the menu behaviour:
-             *  - Down-click the menu button, menu not active:  activate
-             *    the menu with its upper left-hand corner at the mouse
-             *    location.
-             *  - Down-click any button outside the menu, menu active:
-             *    deactivate the menu
-             *  - Down-click any button inside the menu, menu active:
-             *    select the menu entry and deactivate the menu
-             *  - Up-click the menu button, menu not active:  nothing happens
-             *  - Up-click the menu button outside the menu, menu active:
-             *    nothing happens
-             *  - Up-click the menu button inside the menu, menu active:
-             *    select the menu entry and deactivate the menu
              */
-            /* Window has an active menu, it absorbs any mouse click */
-            if( window->ActiveMenu )
-            {
-                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;
-                }
-
-                /* In the menu, invoke the callback and deactivate the menu*/
-                if( fgCheckActiveMenu( window->ActiveMenu->Window,
-                                       window->ActiveMenu ) )
-                {
-                    /*
-                     * 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 if( pressed )
-                    /*
-                     * Outside the menu, deactivate if it's a downclick
-                     *
-                     * XXX This isn't enough.  A downclick outside of
-                     * XXX the interior of our freeglut windows should also
-                     * XXX deactivate the menu.  This is more complicated.
-                     */
-                    fgDeactivateMenu( window->ActiveMenu->ParentWindow );
-
-                /*
-                 * XXX Why does an active menu require a redisplay at
-                 * XXX this point?  If this can come out cleanly, then
-                 * XXX it probably should do so; if not, a comment should
-                 * XXX explain it.
-                 */
-                window->State.Redisplay = GL_TRUE;
-                break;
-            }
-
-            /* No active menu, let's check whether we need to activate one. */
-            if( ( 0 <= button ) &&
-                ( FREEGLUT_MAX_MENUS > button ) &&
-                ( window->Menu[ button ] ) &&
-                pressed )
-            {
-                /* XXX Posting a requisite Redisplay seems bogus. */
-                window->State.Redisplay = GL_TRUE;
-                fgSetWindow( window );
-                fgActivateMenu( window, button );
+            if( fgCheckActiveMenu( window, button, pressed,
+                                   event.xbutton.x_root, event.xbutton.y_root ) )
                 break;
-            }
 
             /*
              * Check if there is a mouse or mouse wheel callback hooked to the
@@ -850,12 +784,8 @@ void FGAPIENTRY glutMainLoopEvent( void )
 
             fgState.Modifiers = fghGetXModifiers( &event );
 
-            /*
-             * Finally execute the mouse or mouse wheel callback
-             *
-             * XXX Use a symbolic constant, *not* "4"!  ("3, sire!")
-             */
-            if( ( button < 3 ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
+            /* Finally execute the mouse or mouse wheel callback */
+            if( ( button < glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS ) ) || ( ! FETCH_WCB( *window, MouseWheel ) ) )
                 INVOKE_WCB( *window, Mouse, ( button,
                                               pressed ? GLUT_DOWN : GLUT_UP,
                                               event.xbutton.x,
@@ -875,7 +805,7 @@ void FGAPIENTRY glutMainLoopEvent( void )
                  * XXX Note that {button} has already been decremeted
                  * XXX in mapping from X button numbering to GLUT.
                  */
-                int wheel_number = (button - 3) / 2;
+                int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
                 int direction = -1;
                 if( button % 2 )
                     direction = 1;
@@ -935,18 +865,16 @@ void FGAPIENTRY glutMainLoopEvent( void )
 
             if( event.type == KeyPress )
             {
-                keyboard_cb = FETCH_WCB( *window, Keyboard );
-                special_cb  = FETCH_WCB( *window, Special  );
+                keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
+                special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, Special  ));
             }
             else
             {
-                keyboard_cb = FETCH_WCB( *window, KeyboardUp );
-                special_cb  = FETCH_WCB( *window, SpecialUp  );
+                keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
+                special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp  ));
             }
 
-            /*
-             * Is there a keyboard/special callback hooked for this window?
-             */
+            /* Is there a keyboard/special callback hooked for this window? */
             if( keyboard_cb || special_cb )
             {
                 XComposeStatus composeStatus;
@@ -954,21 +882,15 @@ void FGAPIENTRY glutMainLoopEvent( void )
                 KeySym keySym;
                 int len;
 
-                /*
-                 * Check for the ASCII/KeySym codes associated with the event:
-                 */
+                /* Check for the ASCII/KeySym codes associated with the event: */
                 len = XLookupString( &event.xkey, asciiCode, sizeof(asciiCode),
                                      &keySym, &composeStatus
                 );
 
-                /*
-                 * GLUT API tells us to have two separate callbacks...
-                 */
+                /* GLUT API tells us to have two separate callbacks... */
                 if( len > 0 )
                 {
-                    /*
-                     * ...one for the ASCII translateable keypresses...
-                     */
+                    /* ...one for the ASCII translateable keypresses... */
                     if( keyboard_cb )
                     {
                         fgSetWindow( window );
@@ -1048,6 +970,8 @@ void FGAPIENTRY glutMainLoopEvent( void )
 
     MSG stMsg;
 
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoopEvent" );
+
     while( PeekMessage( &stMsg, NULL, 0, 0, PM_NOREMOVE ) )
     {
         if( GetMessage( &stMsg, NULL, 0, 0 ) == 0 )
@@ -1088,7 +1012,7 @@ void FGAPIENTRY glutMainLoop( void )
     SFG_Window *window = (SFG_Window *)fgStructure.Windows.First ;
 #endif
 
-    freeglut_assert_ready;
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutMainLoop" );
 
 #if TARGET_HOST_WIN32 || TARGET_HOST_WINCE
     /*
@@ -1104,7 +1028,7 @@ void FGAPIENTRY glutMainLoop( void )
     {
         if ( FETCH_WCB( *window, Visibility ) )
         {
-            SFG_Window *current_window = fgStructure.Window ;
+            SFG_Window *current_window = fgStructure.CurrentWindow ;
 
             INVOKE_WCB( *window, Visibility, ( window->State.Visible ) );
             fgSetWindow( current_window );
@@ -1135,7 +1059,13 @@ void FGAPIENTRY glutMainLoop( void )
         else
         {
             if( fgState.IdleCallback )
+            {
+                if( fgStructure.CurrentWindow &&
+                    fgStructure.CurrentWindow->IsMenu )
+                    /* fail safe */
+                    fgSetWindow( window );
                 fgState.IdleCallback( );
+            }
 
             fghSleepForEvents( );
         }
@@ -1158,6 +1088,7 @@ void FGAPIENTRY glutMainLoop( void )
  */
 void FGAPIENTRY glutLeaveMainLoop( void )
 {
+    FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutLeaveMainLoop" );
     fgState.ExecState = GLUT_EXEC_STATE_STOP ;
 }
 
@@ -1183,10 +1114,14 @@ static int fghGetWin32Modifiers (void)
 LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
                                LPARAM lParam )
 {
-    SFG_Window* window = fgWindowByHandle( hWnd );
+    SFG_Window* window;
     PAINTSTRUCT ps;
     LONG lRet = 1;
 
+    FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
+
+    window = fgWindowByHandle( hWnd );
+
     if ( ( window == NULL ) && ( uMsg != WM_CREATE ) )
       return DefWindowProc( hWnd, uMsg, wParam, lParam );
 
@@ -1197,7 +1132,8 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
     case WM_CREATE:
         /* The window structure is passed as the creation structure paramter... */
         window = (SFG_Window *) (((LPCREATESTRUCT) lParam)->lpCreateParams);
-        assert( window != NULL );
+        FREEGLUT_INTERNAL_ERROR_EXIT ( ( window != NULL ), "Cannot create window",
+                                       "fgWindowProc" );
 
         window->Window.Handle = hWnd;
         window->Window.Device = GetDC( hWnd );
@@ -1296,56 +1232,19 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
     case WM_ACTIVATE:
         if (LOWORD(wParam) != WA_INACTIVE)
         {
-            /* glutSetCursor( fgStructure.Window->State.Cursor ); */
-/*            printf("WM_ACTIVATE: glutSetCursor( %p, %d)\n", window,
+/*            printf("WM_ACTIVATE: fgSetCursor( %p, %d)\n", window,
                    window->State.Cursor ); */
-            glutSetCursor( window->State.Cursor );
+            fgSetCursor( window, window->State.Cursor );
         }
 
         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
         break;
 #endif
 
-        /*
-         * XXX Why not re-use some common code with the glutSetCursor()
-         * XXX function (or perhaps invoke glutSetCursor())?
-         * XXX That is, why are we duplicating code, here, from
-         * XXX glutSetCursor()?  The WIN32 code should be able to just
-         * XXX call glutSetCurdsor() instead of defining two macros
-         * XXX and implementing a nested case in-line.
-         */
     case WM_SETCURSOR:
-        /* 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 );  \
-    break;
-
+/*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
         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     );
-            }
+            fgSetCursor ( window, window->State.Cursor ) ;
         else
             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
         break;
@@ -1375,6 +1274,20 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
          */
         return 0;
 
+    /* XXX For a future patch:  we need a mouse entry event.  Unfortunately Windows
+     * XXX doesn't give us one, so we will probably need a "MouseInWindow" flag in
+     * XXX the SFG_Window structure.  Set it to true to begin with and then have the
+     * XXX WM_MOUSELEAVE code set it to false.  Then when we get a WM_MOUSEMOVE event,
+     * XXX if the flag is false we invoke the Entry callback and set the flag to true.
+     */
+    case 0x02a2:  /* This is the message we get when the mouse is leaving the window */
+        if( window->IsMenu &&
+            window->ActiveMenu && window->ActiveMenu->IsActive )
+            fgUpdateMenuHighlight( window->ActiveMenu );
+
+        INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
+        break ;
+
     case WM_MOUSEMOVE:
     {
 #if TARGET_HOST_WINCE
@@ -1384,11 +1297,14 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
         window->State.MouseX = LOWORD( lParam );
         window->State.MouseY = HIWORD( lParam );
 #endif /* TARGET_HOST_WINCE */
+        /* Restrict to [-32768, 32767] to match X11 behaviour       */
+        /* See comment in "freeglut_developer" mailing list 10/4/04 */
+        if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
+        if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
 
         if ( window->ActiveMenu )
         {
-            window->State.Redisplay = GL_TRUE;
-            fgSetWindow ( window->ActiveMenu->ParentWindow );
+            fgUpdateMenuHighlight( window->ActiveMenu );
             break;
         }
 
@@ -1425,6 +1341,11 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
         window->State.MouseY = HIWORD( lParam );
 #endif /* TARGET_HOST_WINCE */
 
+        /* Restrict to [-32768, 32767] to match X11 behaviour       */
+        /* See comment in "freeglut_developer" mailing list 10/4/04 */
+        if ( window->State.MouseX > 32767 ) window->State.MouseX -= 65536;
+        if ( window->State.MouseY > 32767 ) window->State.MouseY -= 65536;
+
         switch( uMsg )
         {
         case WM_LBUTTONDOWN:
@@ -1472,73 +1393,26 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
             return DefWindowProc( hWnd, uMsg, lParam, wParam );
 
         /*
-         * XXX This comment is duplicated in two other spots.
-         * XXX Can we centralize it?
-         *
-         * Do not execute the application's mouse callback if a
-         * menu is hooked to this button.
-         * In that case an appropriate private call should be generated.
-         * Near as I can tell, this is the menu behaviour:
-         *  - Down-click the menu button, menu not active:  activate
-         *    the menu with its upper left-hand corner at the mouse location.
-         *  - Down-click any button outside the menu, menu active:
-         *    deactivate the menu
-         *  - Down-click any button inside the menu, menu active:
-         *    select the menu entry and deactivate the menu
-         *  - Up-click the menu button, menu not active:  nothing happens
-         *  - Up-click the menu button outside the menu, menu active:
-         *    nothing happens
-         *  - Up-click the menu button inside the menu, menu active:
-         *    select the menu entry and deactivate the menu
+         * Do not execute the application's mouse callback if a menu
+         * is hooked to this button.  In that case an appropriate
+         * private call should be generated.
          */
-        /* Window has an active menu, it absorbs any mouse click */
-        if( window->ActiveMenu )
-        {
-            /* Outside the menu, deactivate the menu if it's a downclick */
-            if( ! fgCheckActiveMenu( window, window->ActiveMenu ) )
-            {
-                if( pressed )
-                    fgDeactivateMenu( window->ActiveMenu->ParentWindow );
-            }
-            else  /* In 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;
-            }
-
-            /*
-             * Let's make the window redraw as a result of the mouse
-             * click and menu activity.
-             */
-            if( ! window->IsMenu )
-                window->State.Redisplay = GL_TRUE;
-
+        if( fgCheckActiveMenu( window, button, pressed,
+                               window->State.MouseX, window->State.MouseY ) )
             break;
-        }
 
-        if( window->Menu[ button ] && pressed )
-        {
-            window->State.Redisplay = GL_TRUE;
-            fgSetWindow( window );
-            fgActivateMenu( window, button );
-
-            break;
-        }
+        /* Set capture so that the window captures all the mouse messages */
+        /*
+         * XXX - Multiple button support:  Under X11, the mouse is not released
+         * XXX - from the window until all buttons have been released, even if the
+         * XXX - user presses a button in another window.  This will take more
+         * XXX - code changes than I am up to at the moment (10/5/04).  The present
+         * XXX - is a 90 percent solution.
+         */
+        if ( pressed == GL_TRUE )
+          SetCapture ( window->Window.Handle ) ;
+        else
+          ReleaseCapture () ;
 
         if( ! FETCH_WCB( *window, Mouse ) )
             break;
@@ -1603,11 +1477,14 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
             else  /* No mouse wheel, call the mouse button callback twice */
             {
                 /*
+                 * Map wheel zero to button 3 and 4; +1 to 3, -1 to 4
+                 *  "    "   one                     +1 to 5, -1 to 6, ...
+                 *
                  * XXX The below assumes that you have no more than 3 mouse
                  * XXX buttons.  Sorry.
                  */
-                int button = wheel_number*2 + 4;
-                if( direction > 0 )
+                int button = wheel_number * 2 + 3;
+                if( direction < 0 )
                     ++button;
                 INVOKE_WCB( *window, Mouse,
                             ( button, GLUT_DOWN,
@@ -1615,7 +1492,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
                 );
                 INVOKE_WCB( *window, Mouse,
                             ( button, GLUT_UP,
-                              window->State.MouseX, window->State.MouseX )
+                              window->State.MouseX, window->State.MouseY )
                 );
             }
 
@@ -1644,9 +1521,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
         window->State.MouseX = mouse_pos.x;
         window->State.MouseY = mouse_pos.y;
 
-        /*
-         * Convert the Win32 keystroke codes to GLUTtish way
-         */
+        /* Convert the Win32 keystroke codes to GLUTtish way */
 #       define KEY(a,b) case a: keypress = b; break;
 
         switch( wParam )
@@ -1674,9 +1549,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
             KEY( VK_INSERT, GLUT_KEY_INSERT    );
 
         case VK_DELETE:
-            /*
-             * The delete key should be treated as an ASCII keypress:
-             */
+            /* The delete key should be treated as an ASCII keypress: */
             INVOKE_WCB( *window, Keyboard,
                         ( 127, window->State.MouseX, window->State.MouseY )
             );
@@ -1762,9 +1635,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
             KEY( VK_INSERT, GLUT_KEY_INSERT    );
 
           case VK_DELETE:
-              /*
-               * The delete key should be treated as an ASCII keypress:
-               */
+              /* The delete key should be treated as an ASCII keypress: */
               INVOKE_WCB( *window, KeyboardUp,
                           ( 127, window->State.MouseX, window->State.MouseY )
               );
@@ -1821,9 +1692,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
         /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
         break;
 
-        /*
-         * Other messages that I have seen and which are not handled already
-         */
+        /* Other messages that I have seen and which are not handled already */
     case WM_SETTEXT:  /* 0x000c */
         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
         /* Pass it on to "DefWindowProc" to set the window text */
@@ -1942,9 +1811,7 @@ LRESULT CALLBACK fgWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam,
         break;
 
     default:
-        /*
-         * Handle unhandled messages
-         */
+        /* Handle unhandled messages */
         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
         break;
     }