The implementation of the UI of an application sometimes requires the capture of the mouse. The following situations come to mind:
The action of the SetCapture API is somewhat complex, and not well documented in the Platform SDK. You can unserstand how best to use SetCapture in you application if you understand the following limitations of using mouse capture:
Only one window can have the mouse capture at a time. A window can request mouse capture by calling the SetCapture API, and that window has the mouse capture untill either the ReleaseCapture API or SetCapture is called directing the mouse capture to a diffrent window.
In addition, there are two types of capture, that I call foreground and background capture. Foreground capture is obtained when the follwoing two conditions are met:
Otherwise (if the current thread is not the foreground thread, or no mouse buttons are held down) the window merely gets background capture.
If, at any time, all the mouse buttons are released, the mouse capture will automatically revert to the bacground
Here are the diffrences between foreground, and background capture:
To implement a drag operation in your application you would implement the following message handlers:
WM_LBUTTONDOWN WM_RBUTTONDOWN | A drag operation generally starts when the user clicks on something, and begins to move the mouse. If the user is clicking on something draggable use the DragDetect API to dectect if a drag operation is beginning. Once the beginning of a drag operation is confirmed call SetCapture(). Note that various drag enabled controls detect when a drag begins and send their parent a message such as LVN_BEGINDRAG. |
WM_LBUTTONUP WM_RBUTTONUP | If the mouse up finishes the drag (see the below Remarks for more info on why a mouse up might not finish a drag operation) ReleaseCapture must be called to allow other windows access to mouse messages. |
WM_CHAR | A drag operation can ususally be aborted by pressing ESC. If required abort the drag operation and call ReleaseCapture. |
WM_CANCELMODE | The window maanger sends this message when it detects a change that requires that an application cancel any modal state it has entered. Abort the drag operation and call ReleaseCapture. |
WM_CAPTURECHANGED | The capture has been cleared, or some other window has obtained it. It probably makes no sense to continue your drag operation, so it should be aborted. As you have explicitly lost capture you don't need to call ReleaseCapture. |
WM_SETCURSOR | mouse capture interrupts the normal flow of mouse processing. WM_SETCURSOR messages are not dispatched to a window that has mouse capture - if the cursor should be set to indicate the drag via SetCursor when the drag operation begins - if the cursor needs to change to provide feedback to the user it should be set in resoponse to WM_MOUSEMOVE. |
With the exception of the mouse down or initial drag operation begin detection most applications implement their drag code in a modal loop to prevent cluttering up the main applications window procedure.
Also note that most system drag operations allows the user to "swap" buttons during a drag by pressing the other button, and then releasing the initial button. If the drag operation is not implemented in a modal loop this situation would ahve to be specially catered for to prevent another drag operation being launched.
There are two variations of this API available:
Use TrackMouseEvent if you can ignore windows 95 as a target. Use TrackMouseEvent if you need to target windows 95, and can assume the machine has at least IE3 installed. If you need TrackMouseEvent functionality on Windows 95 and cannot assume at least IE3 then the following quick hack demonstrates the basic functionality
A full custom implementation of TrackMouseEvent would have to implement a message hook so it could hook messages intended for any window. Any window that needs to detect mouse enter, leave or hover events can use code like this:
#define TID_POLLMOUSE 100
#define MOUSE_POLL_DELAY 500
case WM_MOUSEMOVE:
SetTimer(hwnd,TID_POLLMOUSE,MOUSE_POLL_DELAY,NULL);
break;
case WM_TIMER:
RECT rc;
POINT pt;
GetWindowRect(hwnd,&rc);
GetCursorPos(&pt);
if(PtInRect(&rc,pt))
{
PostMessage(hwnd,WM_MOUSEHOVER,0,0L);
break;
}
PostMessage(hwnd,WM_MOUSELEAVE,0,0L);
KillTimer(hwnd,TID_POLLMOUSE);
break;
A more responsive version could be made by using SetCapture to detect more quickly when the mouse leaves
#define TID_POLLMOUSE 100
#define MOUSE_POLL_DELAY 500
case WM_MOUSEMOVE:
RECT rc;
POINT pt;
GetWindowRect(hwnd,&rc);
pt.x = GET_X_LPARAM(lParam);
pt.y = GET_Y_LPARAM(lParam);
if(PtInRect(&rc,pt))
{
SetTimer(hwnd,TID_POLLMOUSE,MOUSE_POLL_DELAY,NULL);
if(hwnd != GetCapture())
{
SetCapture(hwnd);
PostMessage(hwnd,WM_MOUSEENTER,0,0L);
}
break;
}
ReleaseCapture();
KillTimer(hwnd,TID_POLLMOUSE);
PostMessage(hwnd,WM_MOUSELEAVE,0,0L);
break;
case WM_TIMER:
GetWindowRect(hwnd,&rc);
GetCursorPos(&pt);
if(PtInRect(&rc,pt))
{
PostMessage(hwnd,WM_MOUSEHOVER,0,0L);
break;
}
ReleaseCapture();
KillTimer(hwnd,TID_POLLMOUSE);
PostMessage(hwnd,WM_MOUSELEAVE,0,0L);
break;
The code samples provided differ in a number of ways from the TrackMouseEvent APIs:
Please feel free to expand the code to fit your own requirements.
More information on this topic can be found in the following KB articles:
Q135865 HOWTO: Use Win32 API to Draw a Dragging Rectangle on Screen DC Q183107 HOWTO: Detect When the Cursor Leaves the Window