Win32 Series - Capturing the Mouse
来源:互联网 发布:mac dns 编辑:程序博客网 时间:2024/05/21 15:50
Capturing the Mouse
A window procedure normally receives mouse messages only when the mouse cursor is positioned over the client or nonclient area of the window. A program might need to receive mouse messages when the mouse is outside the window. If so, the program can "capture" the mouse. Don't worry: it won't bite.
Blocking Out a Rectangle
To examine why capturing the mouse might be necessary, let's look at the BLOKOUT1 program shown in Figure 7-9. This program may seem functional, but it has a nasty flaw.
Figure 7-9. The BLOKOUT1 program.
BLOKOUT1.C
/*----------------------------------------- BLOKOUT1.C -- Mouse Button Demo Program (c) Charles Petzold, 1998 -----------------------------------------*/#include <windows.h>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ static TCHAR szAppName[] = TEXT ("BlokOut1") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Mouse Button Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ;}void DrawBoxOutline (HWND hwnd, POINT ptBeg, POINT ptEnd){ HDC hdc ; hdc = GetDC (hwnd) ; SetROP2 (hdc, R2_NOT) ; SelectObject (hdc, GetStockObject (NULL_BRUSH)) ; Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ; ReleaseDC (hwnd, hdc) ;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static BOOL fBlocking, fValidBox ; static POINT ptBeg, ptEnd, ptBoxBeg, ptBoxEnd ; HDC hdc ; PAINTSTRUCT ps ; switch (message) { case WM_LBUTTONDOWN : ptBeg.x = ptEnd.x = LOWORD (lParam) ; ptBeg.y = ptEnd.y = HIWORD (lParam) ; DrawBoxOutline (hwnd, ptBeg, ptEnd) ; SetCursor (LoadCursor (NULL, IDC_CROSS)) ; fBlocking = TRUE ; return 0 ; case WM_MOUSEMOVE : if (fBlocking) { SetCursor (LoadCursor (NULL, IDC_CROSS)) ; DrawBoxOutline (hwnd, ptBeg, ptEnd) ; ptEnd.x = LOWORD (lParam) ; ptEnd.y = HIWORD (lParam) ; DrawBoxOutline (hwnd, ptBeg, ptEnd) ; } return 0 ; case WM_LBUTTONUP : if (fBlocking) { DrawBoxOutline (hwnd, ptBeg, ptEnd) ; ptBoxBeg = ptBeg ; ptBoxEnd.x = LOWORD (lParam) ; ptBoxEnd.y = HIWORD (lParam) ; SetCursor (LoadCursor (NULL, IDC_ARROW)) ; fBlocking = FALSE ; fValidBox = TRUE ; InvalidateRect (hwnd, NULL, TRUE) ; } return 0 ; case WM_CHAR : if (fBlocking & wParam == '\x1B') // i.e., Escape { DrawBoxOutline (hwnd, ptBeg, ptEnd) ; SetCursor (LoadCursor (NULL, IDC_ARROW)) ; fBlocking = FALSE ; } return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; if (fValidBox) { SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ; Rectangle (hdc, ptBoxBeg.x, ptBoxBeg.y, ptBoxEnd.x, ptBoxEnd.y) ; } if (fBlocking) { SetROP2 (hdc, R2_NOT) ; SelectObject (hdc, GetStockObject (NULL_BRUSH)) ; Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ; } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ;}
This program demonstrates a little something that might be implemented in a Windows drawing program. You begin by depressing the left mouse button to indicate one corner of a rectangle. You then drag the mouse. The program draws an outlined rectangle with the opposite corner at the current mouse position. When you release the mouse, the program fills in the rectangle. Figure 7-10 shows one rectangle already drawn and another in progress.
Figure 7-10. The BLOKOUT1 display.
So, what's the problem?
Try this: Press the left mouse button within BLOKOUT1's client area and then move the cursor outside the window. The program stops receiving WM_MOUSEMOVE messages. Now release the button. BLOKOUT1 doesn't get that WM_BUTTONUP message because the cursor is outside the client area. Move the cursor back within BLOKOUT1's client area. The window procedure still thinks the button is pressed.
This is not good. The program doesn't know what's going on.
The Capture Solution
BLOKOUT1 shows some common program functionality, but the code is obviously flawed. This is the type of problem for which mouse capturing was invented. If the user is dragging the mouse, it should be no big deal if the cursor drifts out of the window for a moment. The program should still be in control of the mouse.
Capturing the mouse is easier than baiting a mousetrap. You need only call
SetCapture (hwnd) ;
After this function call Windows sends all mouse messages to the window procedure for the window whose handle ishwnd. The mouse messages always come through as client-area messages, even when the mouse is in a nonclient area of the window. ThelParam parameter still indicates the position of the mouse in client-area coordinates. Thesex andy coordinates, however, can be negative if the mouse is to the left of or above the client area. When you want to release the mouse, call
ReleaseCapture () ;
which will returns things to normal.
In the 32-bit versions of Windows, mouse capturing is a bit more restrictive than it was in earlier versions of Windows. Specifically, if the mouse has been captured, and if a mouse button is not currently down, and if the mouse cursor passes over another window, the window underneath the cursor will receive the mouse messages rather than the window that captured the mouse. This is necessary to prevent one program from messing up the whole system by capturing the mouse and not releasing it.
To avoid problems, your program should capture the mouse only when the button is depressed in your client area. You should release the capture when the button is released.
The BLOKOUT2 Program
The BLOKOUT2 program that demonstrates mouse capturing is shown in Figure 7-11.
Figure 7-11. The BLOKOUT2 program.
BLOKOUT2.C
/*--------------------------------------------------- BLOKOUT2.C -- Mouse Button & Capture Demo Program (c) Charles Petzold, 1998 ---------------------------------------------------*/#include <windows.h>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ static TCHAR szAppName[] = TEXT ("BlokOut2") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("Program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("Mouse Button & Capture Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ;}void DrawBoxOutline (HWND hwnd, POINT ptBeg, POINT ptEnd){ HDC hdc ; hdc = GetDC (hwnd) ; SetROP2 (hdc, R2_NOT) ; SelectObject (hdc, GetStockObject (NULL_BRUSH)) ; Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ; ReleaseDC (hwnd, hdc) ;}LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){ static BOOL fBlocking, fValidBox ; static POINT ptBeg, ptEnd, ptBoxBeg, ptBoxEnd ; HDC hdc ; PAINTSTRUCT ps ; switch (message) { case WM_LBUTTONDOWN : ptBeg.x = ptEnd.x = LOWORD (lParam) ; ptBeg.y = ptEnd.y = HIWORD (lParam) ; DrawBoxOutline (hwnd, ptBeg, ptEnd) ; SetCapture (hwnd) ; SetCursor (LoadCursor (NULL, IDC_CROSS)) ; fBlocking = TRUE ; return 0 ; case WM_MOUSEMOVE : if (fBlocking) { SetCursor (LoadCursor (NULL, IDC_CROSS)) ; DrawBoxOutline (hwnd, ptBeg, ptEnd) ; ptEnd.x = LOWORD (lParam) ; ptEnd.y = HIWORD (lParam) ; DrawBoxOutline (hwnd, ptBeg, ptEnd) ; } return 0 ; case WM_LBUTTONUP : if (fBlocking) { DrawBoxOutline (hwnd, ptBeg, ptEnd) ; ptBoxBeg = ptBeg ; ptBoxEnd.x = LOWORD (lParam) ; ptBoxEnd.y = HIWORD (lParam) ; ReleaseCapture () ; SetCursor (LoadCursor (NULL, IDC_ARROW)) ; fBlocking = FALSE ; fValidBox = TRUE ; InvalidateRect (hwnd, NULL, TRUE) ; } return 0 ; case WM_CHAR : if (fBlocking & wParam == '\x1B') // i.e., Escape { DrawBoxOutline (hwnd, ptBeg, ptEnd) ; ReleaseCapture () ; SetCursor (LoadCursor (NULL, IDC_ARROW)) ; fBlocking = FALSE ; } return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; if (fValidBox) { SelectObject (hdc, GetStockObject (BLACK_BRUSH)) ; Rectangle (hdc, ptBoxBeg.x, ptBoxBeg.y, ptBoxEnd.x, ptBoxEnd.y) ; } if (fBlocking) { SetROP2 (hdc, R2_NOT) ; SelectObject (hdc, GetStockObject (NULL_BRUSH)) ; Rectangle (hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y) ; } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ;}
BLOKOUT2 is the same as BLOKOUT1, except with three new lines of code: a call toSetCapture during the WM_LBUTTONDOWN message and calls toReleaseCapture during the WM_LBUTTONDOWN and WM_CHAR messages. And check this out: Make the window smaller than the screen size, begin blocking out a rectangle within the client area, and then move the mouse cursor outside the client and to the right or bottom, and finally release the mouse button. The program will have the coordinates of the entire rectangle. Just enlarge the window to see it.
Capturing the mouse isn't something suited only for oddball applications. You should do it anytime you need to track WM_MOUSEMOVE messages after a mouse button has been depressed in your client area until the mouse button is released. Your program will be simpler, and the user's expectations will have been met.
- Win32 Series - Capturing the Mouse
- Win32 Series - The Mouse Wheel
- Win32 Series - Client-Area Mouse Messages
- Win32 Series - Nonclient-Area Mouse Messages
- Win32 Series - The Device Context
- Win32 Series - The Button Class
- Win32 Series - The Edit Class
- Win32 Series - The Listbox Class
- Win32 Series - The Caret (Not the Cursor)
- Win32 Series - The GDI Mapping Mode
- Win32 Series - Using the Timer: Three Methods
- Win32 Series - The Scroll Bar Class
- Win32 Series - The Common Dialog Boxes
- Win32 Series - Simple Use of the Clipboard
- Win32 Series - The Bit-Block Transfer
- Win32 Series - The GDI Bitmap Object
- Win32 Series - The DIB File Format
- Win32 Series - Using the Timer for a Clock
- c++中操作符重载形参与转换构造函数的冲突
- iOS学习之UINavigationController详解与使用(一)添加UIBarButtonItem
- YUV格式分析
- 如何修改ecshop中logo文件的尺寸(包括头部的logo,以及底部的logo两部分)
- mysql存储过程的写法
- Win32 Series - Capturing the Mouse
- NSNotificationCenter消息通信机制介绍(KVO)
- 正则表达式应用:分割字符
- Eclipse 每次打开workspace目录记录位置?
- android recovery 系统代码分析 -- 选择进入
- Mybatis 中 if test字符串比较问题
- iOS学习之UINavigationController详解与使用(二)页面切换和segmentedController
- 求解多边形内核
- DWS 根据密钥的 加密解密算法