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.
Related
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
I learned Mouse actions and mouse listeners in Java, but not in C++. Anybody know how to do it?
The documentation for SetCapture() says:
This will cause all mouse messages to be routed toward your app's window.
This seems to be the listener function, but I wonder if it can be trimmed down to only listening for one mouse message?
By mouse actions, I mean a mouse click ("mouse left button down and mouse left button up"), moving the mouse to a certain position, etc.
What is the most appropriate way of getting the mouse cursor position or button state on Windows (Windows 7 and above)? I previously used DirectInput, but I am not longer using it and do not wish to. I saw there is GetCursorPos, however, I do not see anything for getting mouse button states. I have read previously that just reading the window messages (such as WM_LBUTTONUP) was considered "slow" for real time applications, so I do not know of any other option.
If you want to poll/query the current cursor position, you can use GetCursorPos. To see the button states, use GetAsyncKeyState.
If you are implementing a message loop in a window, the notification you will get for a mouse movement is WM_MOUSEMOVE. You will be notified of mouse inputs through the notifications listed here.
WM_LBUTTONUP is as good as any window message, for windowed games is great because it is generated only when the mouse clicks the client area, so you can resize and move the window freely.
As an alternative to direct input, you can use raw inputs which take up some more code to initialize, but it's the best way to go with the mouse movement since WM_INPUT is generated when the physical mouse moves, not the cursor, so you can clip the cursor in the client area without worrying that the user may hit the side of the clip rect and the mouse movement messages won't be generated anymore.
link
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.
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.