6.4.1 Moving the Cursor

To move the cursor directly from your application, use the SetCursorPos function. This function is useful for letting the user move the cursor by using the keyboard.

To move the cursor, use the WM_KEYDOWN message and filter for the virtual-key values of the arrow keys: VK_LEFT, VK_RIGHT, VK_UP, and VK_DOWN. For each keystroke, the application should update the position of the cursor. The following example shows how to retrieve the cursor position and convert the coordinates to client coordinates:

case WM_KEYDOWN:
    if (wParam != VK_LEFT && wParam != VK_RIGHT
        && wParam != VK_UP && wParam != VK_DOWN)
        break;

    GetCursorPos(&ptCursor);

    /* Convert screen coordinates to client coordinates. */

    ScreenToClient(hwnd, &ptCursor);

    switch (wParam) {

        /*
         * Adjust the cursor position according to which key
         * was pressed. Accelerate the movement by adding the
         * repeat variable to the cursor position.
         */

        case VK_LEFT:
            ptCursor.x -= repeat;
            break;

        case VK_RIGHT:
            ptCursor.x += repeat;
            break;

        case VK_UP:
            ptCursor.y -= repeat;
            break;

        case VK_DOWN:
            ptCursor.y += repeat;
            break;

        default:
            return NULL;
    }

    repeat++;       /* increases repeat rate */

    /* Ensure that the cursor doesn't go outside client area. */

    GetClientRect(hwnd, &Rect);

    if (ptCursor.x >= Rect.right)
        ptCursor.x = Rect.right - 1;
    else
        if (ptCursor.x < Rect.left)
            ptCursor.x = Rect.left;

    if (ptCursor.y >= Rect.bottom)
        ptCursor.y = Rect.bottom - 1;
    else
        if (ptCursor.y < Rect.top)
            ptCursor.y = Rect.top;

    /* Convert the coordinates to screen coordinates. */

    ClientToScreen(hwnd, &ptCursor);
    SetCursorPos(ptCursor.x, ptCursor.y);
    break;

case WM_KEYUP:
    repeat = 1; /* clears repeat count */
    break;

In this example, the first if statement filters for the virtual-key values of the arrow keys: VK_LEFT, VK_RIGHT, VK_UP, and VK_DOWN. After this filtering operation, the GetCursorPos function retrieves the current cursor position. If the mouse is available, the user could potentially move the cursor with the mouse at any time; therefore, there is no guarantee that the position values saved on the previous keystroke are correct.

After retrieving the current cursor position, the application calls the ScreenToClient function to convert the cursor position to client coordinates. The application does this for two reasons: Mouse messages give the mouse position in client coordinates, and client coordinates do not need to be updated if the window moves. In other words, it is convenient to use client coordinates, because the system uses them and because it usually means less work for the application.

In the example, the repeat variable provides accelerated cursor motion. Advancing the cursor one unit for each keystroke can be frustrating for users if they need to move to the other side of the screen. You can accelerate the cursor motion by increasing the number of units the cursor advances when the user holds down a key. When the user holds down a key, Windows sends multiple WM_KEYDOWN messages without matching WM_KEYUP messages. To accelerate the cursor, you simply increase the number of units to advance each time a WM_KEYDOWN message is received.

After accelerating cursor motion, the application calls the GetClientRect function to retrieve the current size of the client area and store it in the Rect structure. This information is useful for ensuring that the cursor motion remains within the client area.

Following the call to GetClientRect, the if statements check the current cursor position to ensure that it is within the client area. The application then adjusts the cursor position, if necessary.

In preparation for the SetCursorPos function, the ClientToScreen function converts the values in the ptCursor structure from client coordinates to screen coordinates. Because SetCursorPos requires screen coordinates rather than client coordinates, you must convert the coordinates before calling SetCursorPos.

The SetCursorPos function moves the cursor to the desired location.

Within the WM_KEYUP case, the application restores the initial value of the repeat variable when the user releases the key.