Emulating the Mouse with the Keyboard

CHECKER1 works only if you have a mouse. We'll be adding a keyboard interface to the program shortly, as we did for the SYSMETS program in Chapter 3. However, adding a keyboard interface to a program that uses the mouse cursor for pointing purposes requires that we also must worry about displaying and moving the mouse cursor.

Even if a mouse device is not installed, Windows can still display a mouse cursor. Windows maintains a ”display count“ for this cursor. If a mouse is installed, the display count is initially 0; if not, the display count is initially -1. The mouse cursor is displayed only if the display count is 0 or positive. You can increment the display count by calling:

ShowCursor (TRUE) ;

and decrement it by calling:

ShowCursor (FALSE) ;

You do not need to determine if a mouse is installed before using ShowCursor. If you want to display the mouse cursor regardless of the presence of the mouse, simply increment the display count. After you increment the display count once, decrementing it will hide the cursor if no mouse is installed but leave it displayed if a mouse is present. The display count applies to all of Windows, so you should ensure that you increment and decrement the display count an equal number of times.

You may want to use the following simple logic in your window procedure:

WM_SETFOCUS :

ShowCursor (TRUE) ;

return 0 ;

WM_KILLFOCUS :

ShowCursor (FALSE) ;

return 0 ;

A window procedure receives the WM_SETFOCUS message when the window obtains the keyboard input focus and WM_KILLFOCUS when it loses the input focus. These are ideal times to display and hide the mouse cursor. First, the WM_SETFOCUS and WM_KILLFOCUS calls are balanced—that is, the window procedure will increment and decrement the mouse cursor display count an equal number of times. Second, for versions of Windows installed without a mouse, using the WM_SETFOCUS and WM_KILLFOCUS messages causes the cursor to be visible only when the window has the input focus. That is also the only time the user can move the cursor using the keyboard interface that you'll design.

Windows maintains a current mouse cursor position even if a mouse is not installed. If a mouse is not installed, and you display the mouse cursor, it may appear in any part of the display, and will remain in that position until you explicitly move it. You can obtain the cursor position by using:

GetCursorPos (lpPoint) ;

where lpPoint is a far pointer to a POINT structure. The function fills in the POINT fields with the x- and y-coordinates of the mouse. You can set the cursor position by using:

SetCursorPos (x, y) ;

In both cases, the x and y values are screen coordinates, not client-area coordinates. (This should be evident because the functions do not require a hwnd parameter.) As noted earlier, you can convert screen coordinates to client-area coordinates and vice versa by using ScreenToClient and ClientToScreen.

If you call GetCursorPos while processing a mouse message and convert to client-area coordinates, the coordinates may still be slightly different from those in lParam of the mouse message. The coordinates returned from GetCursorPos indicate the current position of the mouse. The coordinates in lParam of a mouse message are the coordinates of the mouse when the message was generated.

You'll probably want to write keyboard logic to move the mouse cursor with the keyboard arrow keys and simulate the mouse button with the Spacebar or Enter key. What you don't want to do is move the mouse cursor one pixel per keystroke. That forces a user to hold down an arrow key for more than a minute to move the mouse cursor from one side of the display to the other.

If you need to implement a keyboard interface to the mouse cursor but still maintain the ability to position the cursor at precise pixel locations, take a look at Windows' PAINTBRUSH program. When you hold down an arrow key, the mouse cursor starts moving slowly but then speeds up. You'll recall that the lParam parameter in WM_KEYDOWN messages indicates if the keystroke messages are the result of typematic action. This is an excellent application of that information.