now that mouse capture was properly implemented, menus could be opened
[freeglut] / src / mswin / fg_main_mswin.c
index 7cd993e..baf4b3b 100644 (file)
@@ -321,37 +321,37 @@ static LRESULT fghWindowProcKeyPress(SFG_Window *window, UINT uMsg, GLboolean ke
         return 1;
 }
 
-void fghWindowUnderCursor(SFG_Window *window, SFG_Window **child_window)
+static SFG_Window* fghWindowUnderCursor(SFG_Window *window)
 {
     /* Check if the current window that the mouse is over is a child window
      * of the window the message was sent to. Some events only sent to main window,
      * and when handling some messages, we need to make sure that we process
      * callbacks on the child window instead. This mirrors how GLUT does things.
+     * returns either the original window or the found child.
      */
-    if (window && window->Children.First)
+    if (window && window->Children.First)   /* This window has childs */
     {
-        POINT mouse_pos;
         SFG_WindowHandleType hwnd;
-        SFG_Window* temp_window;
+        SFG_Window* child_window;
 
         /* Get mouse position at time of message */
-        DWORD mouse_pos_Dword = GetMessagePos();
-        mouse_pos.x = GET_X_LPARAM(mouse_pos_Dword);
-        mouse_pos.y = GET_Y_LPARAM(mouse_pos_Dword);
+        DWORD mouse_pos_dw = GetMessagePos();
+        POINT mouse_pos = {GET_X_LPARAM(mouse_pos_dw), GET_Y_LPARAM(mouse_pos_dw)};
         ScreenToClient( window->Window.Handle, &mouse_pos );
         
         hwnd = ChildWindowFromPoint(window->Window.Handle, mouse_pos);
         if (hwnd && hwnd!=window->Window.Handle)   /* can be NULL if mouse outside parent by the time we get here, or can be same as parent if we didn't find a child */
         {
-            temp_window = fgWindowByHandle(hwnd);
-            if (temp_window)    /* Verify we got a FreeGLUT window */
+            child_window = fgWindowByHandle(hwnd);
+            if (child_window)    /* Verify we got a FreeGLUT window */
             {
-                *child_window = temp_window;
                 /* ChildWindowFromPoint only searches immediate children, so search again to see if actually in grandchild or further descendant */
-                fghWindowUnderCursor(temp_window,child_window);
+                window = fghWindowUnderCursor(child_window);
             }
         }
     }
+
+    return window;
 }
 
 /*
@@ -361,6 +361,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
 {
     SFG_Window *window;
     LRESULT lRet = 1;
+    static int setCaptureActive = 0;
 
     FREEGLUT_INTERNAL_ERROR_EXIT_IF_NOT_INITIALISED ( "Event Handler" ) ;
 
@@ -518,18 +519,14 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
         lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
 
         SetActiveWindow( window->Window.Handle );
-        INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
         UpdateWindow ( hWnd );
 
         break;
 
     case WM_KILLFOCUS:
         {
-            SFG_Window* saved_window = fgStructure.CurrentWindow;
 /*            printf("WM_KILLFOCUS: %p\n", window ); */
             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
-            INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
-            fgSetWindow(saved_window);
 
             /* Check if there are any open menus that need to be closed */
             fgPlatformCheckMenuDeactivate();
@@ -553,11 +550,51 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
     case WM_SETCURSOR:
 /*      printf ( "Cursor event %x %x %x %x\n", window, window->State.Cursor, lParam, wParam ) ; */
         if( LOWORD( lParam ) == HTCLIENT )
-            fgSetCursor ( window, window->State.Cursor ) ;
+        {
+            if (!window->State.pWState.MouseTracking)
+            {
+                TRACKMOUSEEVENT tme;
+
+                /* Cursor just entered window, set cursor look */ 
+                fgSetCursor ( window, window->State.Cursor ) ;
+
+                /* If an EntryFunc callback is specified by the user, also
+                 * invoke that callback and start mouse tracking so that
+                 * we get a WM_MOUSELEAVE message
+                 */
+                if (FETCH_WCB( *window, Entry ))
+                {
+                    INVOKE_WCB( *window, Entry, ( GLUT_ENTERED ) );
+
+                    tme.cbSize = sizeof(TRACKMOUSEEVENT);
+                    tme.dwFlags = TME_LEAVE;
+                    tme.hwndTrack = window->Window.Handle;
+                    TrackMouseEvent(&tme);
+
+                    window->State.pWState.MouseTracking = GL_TRUE;
+                }
+            }
+        }
         else
+            /* Only pass non-client WM_SETCURSOR to DefWindowProc, or we get WM_SETCURSOR on parents of children as well */
             lRet = DefWindowProc( hWnd, uMsg, wParam, lParam );
         break;
 
+    case WM_MOUSELEAVE:
+        {
+            /* NB: This message is only received when a EntryFunc callback
+             * is specified by the user, as that is the only condition under
+             * which mouse tracking is setup in WM_SETCURSOR handler above
+             */
+            SFG_Window* saved_window = fgStructure.CurrentWindow;
+            INVOKE_WCB( *window, Entry, ( GLUT_LEFT ) );
+            fgSetWindow(saved_window);
+
+            window->State.pWState.MouseTracking = GL_FALSE;
+            lRet = 0;   /* As per docs, must return zero */
+        }
+        break;
+
     case WM_SHOWWINDOW:
         window->State.Visible = GL_TRUE;
         window->State.Redisplay = GL_TRUE;
@@ -587,6 +624,17 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
          */
         return 0;
 
+    case WM_CANCELMODE:
+        /*
+         * The window manager sends this message when it detects a change
+         * that requires that an application cancel any modal state it has
+         * entered. If we've called SetCapture in the mouse button handler,
+         * call ReleaseCapture.
+         */
+        if (setCaptureActive)
+            ReleaseCapture();
+        break;
+
     case WM_MOUSEMOVE:
     {
 #if defined(_WIN32_WCE)
@@ -700,17 +748,20 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
                                window->State.MouseX, window->State.MouseY ) )
             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.
+        /* Set capture so that the window captures all the mouse messages
+         *
+         * The mouse is not released from the window until all buttons have
+         * been released, even if the user presses a button in another window.
+         * This is consistent with the behavior on X11.
          */
         if ( pressed == GL_TRUE )
-          SetCapture ( window->Window.Handle ) ;
-        else
+        {
+            if (!setCaptureActive)
+                SetCapture ( window->Window.Handle ) ;
+            setCaptureActive = 1; /* Set to false in WM_CAPTURECHANGED handler */
+        }
+        else if (!GetAsyncKeyState(VK_LBUTTON) && !GetAsyncKeyState(VK_MBUTTON) && !GetAsyncKeyState(VK_RBUTTON))
+          /* Make sure all mouse buttons are released before releasing capture */
           ReleaseCapture () ;
 
         if( ! FETCH_WCB( *window, Mouse ) )
@@ -729,6 +780,9 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
         );
 
         fgState.Modifiers = INVALID_MODIFIERS;
+
+        /* As per docs, should return zero */
+        lRet = 0;
     }
     break;
 
@@ -738,9 +792,7 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
         int wheel_number = LOWORD( wParam );
         short ticks = ( short )HIWORD( wParam );
 
-        fghWindowUnderCursor(window, &child_window);
-        if (child_window)
-            window = child_window;
+        window = fghWindowUnderCursor(window);
 
                fgState.MouseWheelTicks += ticks;
         if ( abs ( fgState.MouseWheelTicks ) >= WHEEL_DELTA )
@@ -797,28 +849,23 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
     case WM_SYSKEYDOWN:
     case WM_KEYDOWN:
     {
-        SFG_Window *child_window = NULL;
-        fghWindowUnderCursor(window, &child_window);
-        lRet = fghWindowProcKeyPress(child_window?child_window:window,uMsg,GL_TRUE,wParam,lParam);
+        window = fghWindowUnderCursor(window);
+        lRet = fghWindowProcKeyPress(window,uMsg,GL_TRUE,wParam,lParam);
     }
     break;
 
     case WM_SYSKEYUP:
     case WM_KEYUP:
     {
-        SFG_Window *child_window = NULL;
-        fghWindowUnderCursor(window, &child_window);
-        lRet = fghWindowProcKeyPress(child_window?child_window:window,uMsg,GL_FALSE,wParam,lParam);
+        window = fghWindowUnderCursor(window);
+        lRet = fghWindowProcKeyPress(window,uMsg,GL_FALSE,wParam,lParam);
     }
     break;
 
     case WM_SYSCHAR:
     case WM_CHAR:
     {
-      SFG_Window *child_window = NULL;
-      fghWindowUnderCursor(window, &child_window);
-      if (child_window)
-        window = child_window;
+      window = fghWindowUnderCursor(window);
 
       if( (fgState.KeyRepeat==GLUT_KEY_REPEAT_OFF || window->State.IgnoreKeyRepeat==GL_TRUE) && (HIWORD(lParam) & KF_REPEAT) )
             break;
@@ -833,10 +880,12 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR
     break;
 
     case WM_CAPTURECHANGED:
+        if (!lParam || !fgWindowByHandle((HWND)lParam))
+            /* Capture released or capture taken by non-FreeGLUT window */
+            setCaptureActive = 0;
         /* User has finished resizing the window, force a redraw */
         INVOKE_WCB( *window, Display, ( ) );
-
-        /*lRet = DefWindowProc( hWnd, uMsg, wParam, lParam ); */
+        lRet = 0;   /* Per docs, should return zero */
         break;
 
         /* Other messages that I have seen and which are not handled already */