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

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.

Related

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.

Getting mouse cursor position and button state on Windows

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

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.

Constraining window position to desktop working area

I want to allow a user to drag my Win32 window around only inside the working area of the desktop. In other words, they shouldn't be able to have any part of the window extend outside the monitor(s) nor should the window overlap the taskbar.
I'd like to do it in a way that does cause any stuttering. Handling WM_MOVE messages and calling MoveWindow() to reposition the window if it goes off works, but I don't like the flickering effect that's caused by MoveWindow().
I also tried handling WM_MOVING which prevents the need to call MoveWindow() by altering the destination rectangle before the move actually happens. This resolves the flickering problem, but another issue I run into is that the cursor some times gets aways from the window when a drag occurs allowing the user to drag the window around while the cursor is not even inside the window.
How do I constrain my window without running into these issues?
Windows are, ultimately, positioned via the SetWindowPos API.
SetWindowPos starts by validating its parameters by sending the window being sized or moved a WM_WINDOWPOSCHANGING message, and then a WM_WINDOWPOSCHANGED message notifying the window proc of the changed size and/or position.
DefWindowProc handling of these messages is to, in turn, send WM_GETMINMAXINFO and then WM_SIZE or WM_MOVE messages.
Anyway, handle WM_WINDOWPOSCHANGING to filter both user, and code, based attempts to position a window out of bounds.
Keep in mind that users with multi-monitor setups may have a desktop that extends into negative x- and y-coordinates, or that is not rectangular. Also, some users use alternative window managers such as LiteStep, which implement virtual desktops by moving them off-screen; if you try to fight this, your application will break for these users.
You can do this by handling the WM_MOVING message and changing the RECT pointed to by the lParam.
lParam: Pointer to a RECT structure with the current position of the window, in screen coordinates. To change the position of the drag rectangle, an application must change the members of this structure.
you may also want to handle WM_ENTERSIZEMOVE to know when the window is beginning to move, and WM_EXITSIZEMOVE
WM_GETMINMAXINFO is what you seem to be looking for.

WM_MOUSELEAVE not being generated when left mouse button is held

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.