Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WM_MOUSELEAVE not being generated when left mouse button is held

Tags:

c++

winapi

mouse

In my Win32 app, I don't get WM_MOUSELEAVE messages when I hold down the left mouse button and quickly move the mouse pointer out of the window. But If I, holding down the left mouse button, start from the inside of the window and move slowly past the window edge, it'll generate a WM_MOUSELEAVE.

If I don't hold the left mouse button, I get WM_MOUSELEAVE messages every time no matter how fast the mouse pointer moves off the window.

What's the difference? What can I do to handle both cases properly?

EDIT: If I left click and hold, move out of the window and then let go of the left mouse button I get the WM_MOUSELEAVE message. But it's way too late.

like image 689
djcouchycouch Avatar asked Feb 27 '23 13:02

djcouchycouch


2 Answers

On Windows 7, I was trying to make an owner-drawn button. I subclassed the button in order to get more accurate mouseenter/mouseleave events, essentially. When doing this, I used TrackMouseEvent when I got a WM_MOUSEMOVE because this is only posted when the mouse is over the button. If not already set, I would then set a boolean to specify that the mouse is over the button as well as call TrackMouseEvent so that whenever the mouse left, I could unset my boolean. However, like you, I was not getting the WM_MOUSELEAVE when I pressed and held the left mouse button on the owner drawn button, then dragging the mouse out. Upon releasing the mouse outside of the button, I suddenly get the WM_MOUSELEAVE message - way too late.

I determined that the reason for this behavior was that the default button proc's handling for WM_LBUTTONDOWN calls SetCapture, and releases it later. The usage of SetCapture is what is breaking our reception of the WM_MOUSELEAVE event. However, as a side-effect of SetCapture being called, we will get WM_MOUSEMOVE events even if the control is not under the mouse. Thus, my workaround is duplicating the logic in WM_MOUSELEAVE in the WM_MOUSEMOVE handler to unset my boolean that indicates the mouse is over the button if I get a mousemove event that is outside of my button's area. If SetCapture isn't actually used inside WM_LBUTTONDOWN for the default button proc, then we'll already be getting our WM_MOUSELEAVE message and the code will still work... so this workaround works in both cases.

Your problem sounds like it is likely identical to mine, so hopefully this helps you.

like image 87
Jacob McIntosh Avatar answered Apr 27 '23 07:04

Jacob McIntosh


WM_MOUSELEAVE is so that you can detect the mouse leaving your window when you don't have capture. When you have capture, you are responsible for detecting that yourself (if you care).

so It doesn't make any sense to SetCapture AND TrackMouseEvent at the same time, you would use one or the other.

Now, if it would be more convenient for you to see the WM_MOUSELEAVE messages while you have capture, it's a relatively simple matter to do that by yourself in your message pump.

You would just add code that looks something like this between the GetMessage() and the DispatchMessage() calls in your message pump.

  GetMessage(pmsg, ...);

  .....

  if ((IS_WITHIN(pmsg->message, WM_MOUSEFIRST, WM_MOUSELAST) ||
       IS_WITHIN(pmsg->message, WM_NCMOUSEMOVE, WM_NCMBUTTONDBLCLK)) &&
       MyMouseLeaveDetection(pmsg, g_hwndNotifyMouseLeave))
     {
     MSG msg = *pmsg;
     msg.message = WM_MOUSELEAVE;
     msg.hwnd    = g_hwndNotifyMouseLeave; // window that want's 
     msg.lParam  = 0xFFFFFFFF;
     g_hwndNotifyMouseLeave = NULL;

     DispatchMessage (&msg);
     }

 .....
 TranslateMessage(pmsg);
 DispatchMessage(pmsg);
like image 21
John Knoeller Avatar answered Apr 27 '23 06:04

John Knoeller