HOWTO: Detect When the Cursor Leaves the Window

ID: Q183107

The information in this article applies to:

SUMMARY

Microsoft Windows NT 4.0 and Microsoft Windows 98 provide the TrackMouseEvent API to notify a window that the mouse has left the window. Windows 95 does not provide any such notification. An application can detect when the mouse exits a window by starting a timer when the mouse enters the window, and using that timer to monitor the mouse position and find when it is no longer within the window.

MORE INFORMATION

The following sample code provides an example of how to implement mouse leave detection on Windows NT 3.51 and Windows 95 so that it follows TrackMouseEvent's interface. Implementation of the WM_MOUSEHOVER functionality is left to the discretion of the reader.

Sample Code

   #if(_WIN32_WINNT < 0x0400)
   #define WM_MOUSELEAVE   WM_USER+2
   #define TME_LEAVE               1

   typedef struct tagTRACKMOUSEEVENT {
       DWORD cbSize;
       DWORD dwFlags;
       HWND  hwndTrack;
   } TRACKMOUSEEVENT, *LPTRACKMOUSEEVENT;

   VOID CALLBACK
   TrackMouseTimerProc(HWND hWnd,UINT uMsg,UINT idEvent,DWORD dwTime) {
      RECT rect;
      POINT pt;

      GetClientRect(hWnd,&rect);
      MapWindowPoints(hWnd,NULL,(LPPOINT)&rect,2);
      GetCursorPos(&pt);
      if (!PtInRect(&rect,pt) || (WindowFromPoint(pt) != hWnd)) {
         if (!KillTimer(hWnd,idEvent)) {
            // Error killing the timer!
         }

         PostMessage(hWnd,WM_MOUSELEAVE,0,0);
      }
   }

   BOOL
   TrackMouseEvent(LPTRACKMOUSEEVENT ptme) {
      OutputDebugString(TEXT("TrackMouseEvent\n"));

      if (!ptme || ptme->cbSize < sizeof(TRACKMOUSEEVENT)) {
         OutputDebugString(TEXT("TrackMouseEvent: invalid "
                                "TRACKMOUSEEVENT structure\n"));
         return FALSE;
      }

      if (!IsWindow(ptme->hwndTrack)) {
         OutputDebugString(
            TEXT("TrackMouseEvent: invalid hwndTrack\n"));
         return FALSE;
      }

      if (!(ptme->dwFlags & TME_LEAVE)) {
         OutputDebugString(TEXT("TrackMouseEvent: invalid dwFlags\n"));
         return FALSE;
      }

      return SetTimer(ptme->hwndTrack, ptme->dwFlags,
                      100,(TIMERPROC)TrackMouseTimerProc);
   }
   #endif

   LRESULT CALLBACK
   MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
      TRACKMOUSEEVENT tme;
      static BOOL fInWindow;
      static BOOL fInMenu;
      switch (uMsg) {
         case WM_CREATE:
            fInWindow = FALSE;
            fInMenu = FALSE;
            return 0;

         case WM_MOUSEMOVE:
            if (!fInWindow) {
               fInWindow = TRUE;
               tme.cbSize = sizeof(TRACKMOUSEEVENT);
               tme.dwFlags = TME_LEAVE;
               tme.hwndTrack = hWnd;
               if (!TrackMouseEvent(&tme)) {
                  MessageBox(hWnd,
                             TEXT("TrackMouseEvent Failed"),
                             TEXT("Mouse Leave"),MB_OK);
               }
            }
         break;

         case WM_MOUSELEAVE:
            fInWindow = FALSE;
            if (!fInMenu)
               MessageBox(hWnd,TEXT("Elvis has left the building"),
                          TEXT("Mouse Leave"),MB_OK);
            break;
         case WM_ENTERMENULOOP:
            fInMenu = TRUE;
            break;

         case WM_EXITMENULOOP:
            fInMenu = FALSE;
            break;

         default:
            return DefWindowProc(hWnd,uMsg,wParam,lParam);
      }
      return FALSE;
   }

(c) Microsoft Corporation 1997, All Rights Reserved. Contributions by Rob Caplan, Microsoft Corporation

Additional query words:

Keywords          : kbcode kbInput kbMouse kbNTOS350 kbNTOS351 kbNTOS400 kbGrpUser kbWinOS95 kbWinOS98 kbWndw kbWndwMsg 
Issue type        : kbhowto

Last Reviewed: December 17, 1998