The CONNECT program, shown in Figure 4-1, does some simple mouse processing to let you get a good feel for how Windows sends your program mouse messages.
CONNECT.MAK
#-----------------------
# CONNECT.MAK make file
#-----------------------
connect.exe : connect.obj connect.def
link connect, /align:16, NUL, /nod slibcew libw, connect
rc connect.exe
connect.obj : connect.c
cl -c -Gsw -Ow -W2 -Zp connect.c
CONNECT.C
/*--------------------------------------------------
CONNECT.C -- Connect-the-Dots Mouse Demo Program
(c) Charles Petzold, 1990
--------------------------------------------------*/
#include <windows.h>
#define MAXPOINTS 1000
long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szAppName[] = "Connect" ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
if (!hPrevInstance)
{
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 = GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
RegisterClass (&wndclass) ;
}
hwnd = CreateWindow (szAppName, "Connect-the-Dots Mouse Demo",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, nCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)
{
static POINT points[MAXPOINTS] ;
static short nCount ;
HDC hdc ;
PAINTSTRUCT ps ;
short i, j ;
switch (message)
{
case WM_LBUTTONDOWN :
nCount = 0 ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_MOUSEMOVE :
if (wParam & MK_LBUTTON && nCount < MAXPOINTS)
{
points [nCount++] = MAKEPOINT (lParam) ;
hdc = GetDC (hwnd) ;
SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0L) ;
ReleaseDC (hwnd, hdc) ;
}
return 0 ;
case WM_LBUTTONUP :
InvalidateRect (hwnd, NULL, FALSE) ;
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
for (i = 0 ; i < nCount - 1 ; i++)
for (j = i ; j < nCount ; j++)
{
MoveTo (hdc, points[i].x, points[i].y) ;
LineTo (hdc, points[j].x, points[j].y) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
CONNECT.DEF
;------------------------------------
; CONNECT.DEF module definition file
;------------------------------------
NAME CONNECT
DESCRIPTION 'Mouse Connect Program (c) Charles Petzold, 1990'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
CONNECT processes three mouse messages:
WM_LBUTTONDOWN—CONNECT clears the client area.
WM_MOUSEMOVE—If the left button is down, CONNECT draws a black dot on the client area at the mouse position.
WM_LBUTTONUP—CONNECT connects every dot drawn in the client area to every other dot. Sometimes this results in a pretty design; sometimes in a dense blob. (See Figure 4-2.)
To use CONNECT, bring the mouse cursor into the client area, press the left button, move the mouse around a little, and release the left button. CONNECT works best for a curved pattern of a few dots, which you can draw by moving the mouse quickly while the left button is depressed. CONNECT uses several simple Graphics Device Interface (GDI) functions. SetPixel draws a one-pixel dot of a particular color, in this case black. (On high-resolution displays, the pixel may be nearly invisible.) Drawing the lines requires two functions: MoveTo marks the x-coordinate and y-coordinate of the beginning of the line, and LineTo draws the line.
If you move the mouse cursor out of the client area before releasing the button, CONNECT does not connect the dots, because it doesn't receive the WM_LBUTTONUP message. If you move the mouse back into the client area and press the left button again, CONNECT clears the client area. (If you want to continue a design after releasing the
button outside the client area, press the left button again while the mouse is outside the client area and then move the mouse back inside.)
CONNECT stores a maximum of 1000 points. The number of lines it draws is equal to:
(P) ´ (P - 1)
where P is the number of points. With 1000 points, this involves almost 500,000 lines, which can take several minutes to draw. For anything but a demonstration program, this is too long for a Windows program to hog system resources.
If CONNECT is busy drawing lines, you can press the mouse button, move the mouse around, and release the mouse button, but nothing will happen. CONNECT does not receive these messages because it is busy and not making any GetMessage calls. After CONNECT finishes drawing the lines, it does not receive these messages because the mouse button has been released already. In this respect, the mouse is not like the keyboard. Windows treats every keystroke as if it were important. However, if a mouse button is pressed and released in the client area while a program is busy, the mouse clicks are discarded.
Now try this: While CONNECT is engaged in a lengthy drawing routine, hold down the mouse button and move the mouse around. After CONNECT is finished drawing, it will retrieve the WM_LBUTTONDOWN message from the queue (and clear the client area) because the button is currently down. However, it receives only the WM_MOUSEMOVE messages that occur after it receives the WM_LBUTTONDOWN message.
Sometimes the word ”tracking“ is used to refer to the way that programs process mouse movement. Tracking does not mean, however, that your program sits in a loop in its window procedure attempting to follow the mouse's movements on the display. The window procedure instead processes each mouse message as it comes and then quickly exits.