WM_MOUSELEAVE not being generated when left mouse button is held - c++

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.

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.

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);

As waiting for a WM_MOUSELEAVE is unreliable, the best solution I've found is to directly look at the mouse position during a WM_MOUSEMOVE. I compare the mouse position against the client area and if the position is outside then I handle that as a mouse leave.
I also make sure to call SetCapture when the mouse position is within the client area and ReleaseCapture when it leaves.

Related

how to detect when mouse leaves the window win32

I am creating an application with the win32 api and I have a boolean isLeftClickPressed which is set to true when I receive a WM_LBUTTONDOWN message. when I receive a WM_LBUTTONUP message I set it to false. this works fine, however if I hold down the left mouse button and move my cursor out of the window and release my mouse, it never send a WM_LBUTTONUP message. I am not looking for a way to get around this rather just to find out when my mouse leaves the window and then set isLeftClickPressed to false.
any help would be appreciated.
Call TrackMouseEvent and check for WM_MOUSELEAVE

What does capturing the mouse mean in reference to the GetCapture() function?

I'm reading about the GetCapture() function , which is part of the mfc.
I'm still unclear as to what it does, as well as what it means to capture the mouse , as it says here:
http://msdn.microsoft.com/en-us/library/dxa5eaaa(v=vs.80).aspx
in my book it's used in this way:
void CSketcherView::OnLButtonUp(UINY nFlags, CPoint point)
{
if(this == GetCapture())
ReleaseCapture(); // Stop capturing mouse messages
// ... add information to document
}
So what does GetCapture() return? and what does "capturing" the mouse mean?
As a concrete example for the purpose of mouse capture:
Take a window with two pushbuttons. Click on one of them and keep the mouse button held down. Now drag the mouse cursor over to the second pushbutton and release the mouse button. The first pushbutton will receive a WM_LBUTTONUP message, but the second won't, even though the mouse cursor is on top of it.
When that first pushbutton received the WM_LBUTTONDOWN message, it captured the mouse. While a window is capturing the mouse, it guarantees that it will receive all subsequent mouse events (particularly the WM_LBUTTONUP message), even if the mouse has been dragged outside of its window bounds. This is important so that it can match button-down to button-up messages and maintain proper state. It's also important for usability (if you click on one button and accidentally move away to another button, you neither want to trigger the first nor the second button).
Capturing the mouse usually means you will stil get Mouse events even when the mouse cursor position is outside the bounds of your window
The GetCapture function simply returns the current window that has the mouse capture.

MFC C++: does setfocus set the capture as well?

I have a CDialog that is calling DoModal(), the dialog is opened, everything else is disabled, but for some reason I have only the keyboard capture and not the mouse.
The mouse capture is still on the last item it was on.
If I call setfocus before the DoModal, it doesn't work, but if I do setCapture before the DoModal it works.
Can anyone explain it to me?
What is the problem? I want to understand why I need to call to setCapture or releaseCapture before the DoModal (btw- releaseCapture works as well...)
There is no such thing as 'keyboard capture'. There is just keyboard focus.
A window that has the keyboard focus is the one that receives the keyboard related messages.
Mouse messages are normally sent to the window just beneath the mouse cursor, without regard to the focus. That is unless the mouse is "captured", in this case mouse messages are sent to the windows that has captured the mouse, no matter where the mouse cursor is.
That said, do you really need to capture the mouse? Actually that is rarely necessary. And it should be done only as a consequence to a user action, never on your own.
PS. Due to security concerns, in Windows there are actually two different kinds of mouse captures:
Local captures: mouse messages are sent to the capturing window, only if the mouse is over a window that belongs to the same application.
Global capture: mouse messages are sent to the capturing window, no matter where the mouse cursor is.
The SetCapture function creates a global capture only if it is called while processing a WM_?BUTTON_DOWN message.

What is the proper way of handling a mouse drag?

I need to implement mouse drag events which look something like this:
class MouseDragEvent
{
public:
uint m_btn;
uint m_x, m_y;
uint m_delta_x, m_delta_y;
};
I think I will need to check for WM_LBUTTONDOWN and WM_LBUTTONUP messages and manually find the change in x and y. Is there a drag message or a better way?
Start by detecting WM_LBUTTONDOWN. Record the starting coordinates where the mouse button was pressed. Check for WM_MOUSEMOVE, and when the mouse has moved outside the rectangle determined by GetSystemParameters(SM_CXDRAG) and GetSystemParameters(SM_CYDRAG) use SetCapture to capture the mouse. At this point continue responding to WM_MOUSEMOVE and check for WM_LBUTTONUP. You might want to change the mouse cursor at this point. Also check for WM_CAPTURECHANGED, which means the drag has been aborted. After the drag is complete call ReleaseCapture.
Edit: Most of this process can be automated with the DragDetect function. Call this function from the WM_LBUTTONDOWN handler.
There are drag-and-drop APIs in Windows (e.g. RegisterDragDrop), but concerned with the from- and to- windows (often in different applications), not the coordinates.
If you want to deal with delta-x and delta-y coordinated, then processing button down and button up messages is appropriate.
Did you try this:
http://www.codeproject.com/KB/miscctrl/rdragdropfiles.aspx
This may also be helpful:
http://msdn.microsoft.com/en-us/magazine/cc163915.aspx

How to check if a mouse is over a control

How does one check if the mouse is over a certain HWND? I have tried using the WM_MOUSELEAVE and WM_MOUSEMOVE messages to keep track, but if you click a button and drag the mouse out of the button, it doesn't receive the WM_MOUSELEAVE until the mouse is released, which is too late, because:
When you click a button, the WM_COMMAND message is only sent if:
1. The mouse was originally depressed over the button
2. The mouse is over the button
3. The mouse is released over the button
I need to replicate this functionality.
To duplicate this functionality, just call SetCapture() so that mouse messages are sent to your window even if the mouse leaves it. You can read the current mouse position as it moves and determine if it is still over your window/button (I'm still not 100% sure what you are doing). And, when the mouse button is released, you can call ReleaseCapture() to restore where mouse messages are sent.
EDIT: Oh, and you can use the Windows API function WindowFromPoint() to determine which window the mouse is over.
This is built-in to Windows, it is called 'mouse capture', SetCapture(hWnd). It ensures you get mouse messages even though the mouse has moved outside of the window. You call it on the WM_LBUTTONDOWN message notification.