Fixed mousewheel callbacks under X11. (bug #247, github issue #66)
[freeglut] / src / x11 / fg_main_x11.c
index cc57185..e3e2a22 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * freeglut_main_x11.c
+ * fg_main_x11.c
  *
  * The X11-specific windows message processing methods.
  *
 
 #include <GL/freeglut.h>
 #include "../fg_internal.h"
-#ifdef HAVE_ERRNO_H
-#    include <errno.h>
-#endif
+#include <errno.h>
 #include <stdarg.h>
-#ifdef  HAVE_VFPRINTF
-#    define VFPRINTF(s,f,a) vfprintf((s),(f),(a))
-#elif defined(HAVE__DOPRNT)
-#    define VFPRINTF(s,f,a) _doprnt((f),(a),(s))
-#else
-#    define VFPRINTF(s,f,a)
-#endif
 
 
 /*
 #    define MIN(a,b) (((a)<(b)) ? (a) : (b))
 #endif
 
+extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
+extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
+extern void fgPlatformFullScreenToggle( SFG_Window *win );
+extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );
+extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
+extern void fgPlatformPushWindow( SFG_Window *window );
+extern void fgPlatformPopWindow( SFG_Window *window );
+extern void fgPlatformHideWindow( SFG_Window *window );
+extern void fgPlatformIconifyWindow( SFG_Window *window );
+extern void fgPlatformShowWindow( SFG_Window *window );
+
 /* used in the event handling code to match and discard stale mouse motion events */
 static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
 
@@ -63,8 +65,7 @@ static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg);
  * TODO BEFORE THE STABLE RELEASE:
  *
  * There are some issues concerning window redrawing under X11, and maybe
- * some events are not handled. The Win32 version lacks some more features,
- * but seems acceptable for not demanding purposes.
+ * some events are not handled.
  *
  * Need to investigate why the X11 version breaks out with an error when
  * closing a window (using the window manager, not glutDestroyWindow)...
@@ -115,10 +116,8 @@ void fgPlatformSleepForEvents( fg_time_t msec )
         wait.tv_usec = (msec % 1000) * 1000;
         err = select( socket+1, &fdset, NULL, NULL, &wait );
 
-#ifdef HAVE_ERRNO_H
         if( ( -1 == err ) && ( errno != EINTR ) )
             fgWarning ( "freeglut select() error: %d", errno );
-#endif
     }
 }
 
@@ -644,29 +643,25 @@ void fgPlatformProcessSingleEvent ( void )
         case CreateNotify:
         case ConfigureNotify:
             {
-                int width, height;
+                int width, height, x, y;
                 if( event.type == CreateNotify ) {
                     GETWINDOW( xcreatewindow );
                     width = event.xcreatewindow.width;
                     height = event.xcreatewindow.height;
+                    x = event.xcreatewindow.x;
+                    y = event.xcreatewindow.y;
                 } else {
                     GETWINDOW( xconfigure );
                     width = event.xconfigure.width;
                     height = event.xconfigure.height;
+                    x = event.xconfigure.x;
+                    y = event.xconfigure.y;
                 }
 
-                if( ( width != window->State.pWState.OldWidth ) ||
-                    ( height != window->State.pWState.OldHeight ) )
-                {
-                    SFG_Window *current_window = fgStructure.CurrentWindow;
-
-                    window->State.pWState.OldWidth = width;
-                    window->State.pWState.OldHeight = height;
-                    INVOKE_WCB( *window, Reshape, ( width, height ) );
-                    glutPostRedisplay( );
-                    if( window->IsMenu )
-                        fgSetWindow( current_window );
-                }
+                /* Update state and call callback, if there was a change */
+                fghOnPositionNotify(window, x, y, GL_FALSE);
+                /* Update state and call callback, if there was a change */
+                fghOnReshapeNotify(window, width, height, GL_FALSE);
             }
             break;
 
@@ -692,7 +687,7 @@ void fgPlatformProcessSingleEvent ( void )
             if( event.xexpose.count == 0 )
             {
                 GETWINDOW( xexpose );
-                window->State.Redisplay = GL_TRUE;
+                window->State.WorkMask |= GLUT_DISPLAY_WORK;
             }
             break;
 
@@ -812,11 +807,8 @@ void fgPlatformProcessSingleEvent ( void )
         case ButtonRelease:
         case ButtonPress:
         {
-            GLboolean pressed = GL_TRUE;
-            int button;
-
-            if( event.type == ButtonRelease )
-                pressed = GL_FALSE ;
+            GLboolean pressed;
+            int button, x, y;
 
             /*
              * A mouse button has been pressed or released. Traditionally,
@@ -834,59 +826,46 @@ void fgPlatformProcessSingleEvent ( void )
              */
             button = event.xbutton.button - 1;
 
+            pressed = event.type == ButtonPress ? GL_TRUE : GL_FALSE;
+            x = event.xbutton.x;
+            y = event.xbutton.y;
+
             /*
              * 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.
              */
-            if( fgCheckActiveMenu( window, button, pressed,
-                                   event.xbutton.x, event.xbutton.y ) )
+            if(fgCheckActiveMenu( window, button, pressed, x, y))
                 break;
 
             /*
              * Check if there is a mouse or mouse wheel callback hooked to the
              * window
              */
-            if( ! FETCH_WCB( *window, Mouse ) &&
-                ! FETCH_WCB( *window, MouseWheel ) )
+            if(!FETCH_WCB(*window, Mouse) && !FETCH_WCB(*window, MouseWheel))
                 break;
 
-            fgState.Modifiers = fgPlatformGetModifiers( event.xbutton.state );
+            fgState.Modifiers = fgPlatformGetModifiers(event.xbutton.state);
 
-            /* 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,
-                                              event.xbutton.y )
-                );
-            else
-            {
-                /*
-                 * 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).
-                 *
-                 * XXX Note that {button} has already been decremented
-                 * XXX in mapping from X button numbering to GLUT.
-                                *
-                                * XXX Should add support for partial wheel turns as Windows does -- 5/27/11
-                 */
-                int wheel_number = (button - glutDeviceGet ( GLUT_NUM_MOUSE_BUTTONS )) / 2;
-                int direction = -1;
-                if( button % 2 )
-                    direction = 1;
-
-                if( pressed )
-                    INVOKE_WCB( *window, MouseWheel, ( wheel_number,
-                                                       direction,
-                                                       event.xbutton.x,
-                                                       event.xbutton.y )
-                    );
+            /* Finally execute the mouse or mouse wheel callback.
+             * The mouse wheel is reported as buttons 4 (down) and 5 (up) by
+             * the X server. "button" has been converted to 0-based above, so
+             * that's 3 and 4 for us.
+             * If a wheel callback hasn't been registered, we simply treat them
+             * as button presses and pass them to the mouse handler. This is
+             * important for compatibility with the original GLUT.
+             */
+            if(button < 3 || button > 4 || !FETCH_WCB(*window, MouseWheel)) {
+                INVOKE_WCB(*window, Mouse, (button, pressed ? GLUT_DOWN : GLUT_UP, x, y));
+            } else {
+                if(pressed) {
+                    int dir = button & 1 ? 1 : -1;
+                    /* there's no way to know if X buttons after 5 are more
+                     * wheels/wheel axes, or regular buttons. So we'll only
+                     * ever invoke the wheel CB for wheel 0.
+                     */
+                    INVOKE_WCB(*window, MouseWheel, (0, dir, x, y));
+                }
             }
             fgState.Modifiers = INVALID_MODIFIERS;
         }
@@ -895,8 +874,10 @@ void fgPlatformProcessSingleEvent ( void )
         case KeyRelease:
         case KeyPress:
         {
-            FGCBKeyboard keyboard_cb;
-            FGCBSpecial special_cb;
+            FGCBKeyboardUC keyboard_cb;
+            FGCBSpecialUC special_cb;
+            FGCBUserData keyboard_ud;
+            FGCBUserData special_ud;
 
             GETWINDOW( xkey );
             GETMOUSE( xkey );
@@ -918,32 +899,36 @@ void fgPlatformProcessSingleEvent ( void )
                     if ( event.xkey.keycode<256 )            /* XQueryKeymap is limited to 256 keycodes    */
                     {
                         if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) )
-                            window->State.KeyRepeating = GL_TRUE;
+                            window->State.pWState.KeyRepeating = GL_TRUE;
                         else
-                            window->State.KeyRepeating = GL_FALSE;
+                            window->State.pWState.KeyRepeating = GL_FALSE;
                     }
                 }
             }
             else
-                window->State.KeyRepeating = GL_FALSE;
+                window->State.pWState.KeyRepeating = GL_FALSE;
 
             /* Cease processing this event if it is auto repeated */
 
-            if (window->State.KeyRepeating)
+            if (window->State.pWState.KeyRepeating)
             {
-                if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE;
+                if (event.type == KeyPress) window->State.pWState.KeyRepeating = GL_FALSE;
                 break;
             }
 
             if( event.type == KeyPress )
             {
-                keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, Keyboard ));
-                special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, Special  ));
+                keyboard_cb = (FGCBKeyboardUC)( FETCH_WCB( *window, Keyboard ));
+                special_cb  = (FGCBSpecialUC) ( FETCH_WCB( *window, Special  ));
+                keyboard_ud = FETCH_USER_DATA_WCB( *window, Keyboard );
+                special_ud  = FETCH_USER_DATA_WCB( *window, Special  );
             }
             else
             {
-                keyboard_cb = (FGCBKeyboard)( FETCH_WCB( *window, KeyboardUp ));
-                special_cb  = (FGCBSpecial) ( FETCH_WCB( *window, SpecialUp  ));
+                keyboard_cb = (FGCBKeyboardUC)( FETCH_WCB( *window, KeyboardUp ));
+                special_cb  = (FGCBSpecialUC) ( FETCH_WCB( *window, SpecialUp  ));
+                keyboard_ud = FETCH_USER_DATA_WCB( *window, KeyboardUp );
+                special_ud  = FETCH_USER_DATA_WCB( *window, SpecialUp  );
             }
 
             /* Is there a keyboard/special callback hooked for this window? */
@@ -968,7 +953,8 @@ void fgPlatformProcessSingleEvent ( void )
                         fgSetWindow( window );
                         fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
                         keyboard_cb( asciiCode[ 0 ],
-                                     event.xkey.x, event.xkey.y
+                                     event.xkey.x, event.xkey.y,
+                                     keyboard_ud
                         );
                         fgState.Modifiers = INVALID_MODIFIERS;
                     }
@@ -1036,7 +1022,7 @@ void fgPlatformProcessSingleEvent ( void )
                     {
                         fgSetWindow( window );
                         fgState.Modifiers = fgPlatformGetModifiers( event.xkey.state );
-                        special_cb( special, event.xkey.x, event.xkey.y );
+                        special_cb( special, event.xkey.x, event.xkey.y, special_ud );
                         fgState.Modifiers = INVALID_MODIFIERS;
                     }
                 }
@@ -1071,3 +1057,55 @@ void fgPlatformMainLoopPreliminaryWork ( void )
 {
 }
 
+
+/* deal with work list items */
+void fgPlatformInitWork(SFG_Window* window)
+{
+    /* Notify windowStatus/visibility, position and size get notified on window creation with message handlers above 
+     * XXX CHECK: do the messages happen too early like on windows, so client code cannot have registered
+     * a callback yet and the message is thus never received by client?
+     * -> this is a no-op
+     */
+     return;
+}
+
+void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
+{
+    if (workMask & GLUT_FULL_SCREEN_WORK)
+        fgPlatformFullScreenToggle( window );
+    if (workMask & GLUT_POSITION_WORK)
+        fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );
+    if (workMask & GLUT_SIZE_WORK)
+        fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );
+    if (workMask & GLUT_ZORDER_WORK)
+    {
+        if (window->State.DesiredZOrder < 0)
+            fgPlatformPushWindow( window );
+        else
+            fgPlatformPopWindow( window );
+    }
+}
+
+void fgPlatformVisibilityWork(SFG_Window* window)
+{
+    /* Visibility status of window gets updated in the window message handlers above 
+     * XXX: is this really the case? check
+     */
+    SFG_Window *win = window;
+    switch (window->State.DesiredVisibility)
+    {
+    case DesireHiddenState:
+        fgPlatformHideWindow( window );
+        break;
+    case DesireIconicState:
+        /* Call on top-level window */
+        while (win->Parent)
+            win = win->Parent;
+        fgPlatformIconifyWindow( win );
+        break;
+    case DesireNormalState:
+        fgPlatformShowWindow( window );
+        break;
+    }
+}
+