Step 6: Retrieving Buffered Data from the Mouse

Once the mouse is acquired, your application can begin to retrieve data from it.

In the Scrawl sample, retrieval is triggered by a signaled event. In the WinMain function, the application sleeps until MsgWaitForMultipleObjects indicates that there is either a signal or a message. If there's a signal associated with the mouse, the Scrawl_OnMouseInput function is called. This function is a good illustration of how buffered input is handled, so we'll look at it in detail.

First the function makes sure the old cursor position will be cleaned up. Remember, Scrawl is maintaining its own cursor and is wholly responsible for drawing and erasing it.

void Scrawl_OnMouseInput(HWND hwnd)
{
    /* Invalidate the old cursor so it will be erased */
    InvalidateCursorRect(hwnd);
 

Now the function enters a loop to read and respond to the entire contents of the buffer. Because it retrieves just one item at a time, it needs only a single DIDEVICEOBJECTDATA structure to hold the data.

Another way to go about handling input would be to read the entire buffer at once and then loop through the retrieved items, responding to each one in turn. In that case, dwElements would be the size of the buffer, and od would be an array with the same number of elements.

while (!fDone) {
        DIDEVICEOBJECTDATA od;
        DWORD dwElements = 1;   // number of items to be retrieved
 

The application calls the IDirectInputDevice::GetDeviceData method in order to fetch the data. The second parameter tells DirectInput where to put the data, and the third tells it how many items are wanted. The final parameter would be DIGDD_PEEK if the data was to be left in the buffer, but in this case the data is not going to be needed again, so it is removed.

HRESULT hr = g_pMouse->GetDeviceData(
                             sizeof(DIDEVICEOBJECTDATA), 
                             &od,
                             &dwElements, 0);
 

Now the application checks to see if access to the device has been lost and, if so, tells itself to try to reacquire the mouse at the first opportunity. This step was discussed in Step 5: Managing Access to the Mouse.

if (hr == DIERR_INPUTLOST) {
            PostMessage(hwnd, WM_SYNCACQUIRE, 0, 0L);
            break;
        }
 

Next the application makes sure the call to the GetDeviceData method succeeded and that there was actually data to be retrieved. Remember, after the call to GetDeviceData the dwElements variable shows how many items were actually retrieved.

/* Unable to read data or no data available */
        if (FAILED(hr) || dwElements == 0) {
            break;
        }
 

If execution has proceeded to this point, everything is fine: the call succeeded and there is an item of data in the buffer. Now the application looks at the dwOfs member of the DIDEVICEOBJECTDATA structure to determine which object on the device reported a change of state, and calls helper functions to respond appropriately. The value of the dwData member, which gives information about what happened, is passed to these functions.

/* Look at the element to see what happened */
 
        switch (od.dwOfs) {

        /* DIMOFS_X: Mouse horizontal motion */
        case DIMOFS_X: UpdateCursorPosition(od.dwData, 0); break;

        /* DIMOFS_Y: Mouse vertical motion */
        case DIMOFS_Y: UpdateCursorPosition(0, od.dwData); break;

        /* DIMOFS_BUTTON0: Button 0 pressed or released */
        case DIMOFS_BUTTON0:

            if (od.dwData & 0x80) { /* Button pressed */
                fDone = 1;
                Scrawl_OnButton0Down(hwnd); /* Go into button-down
                                               mode */
            }
            break;

        /* DIMOFS_BUTTON1: Button 1 pressed or released */
        case DIMOFS_BUTTON1:
 
            if (!(od.dwData & 0x80)) {    /* Button released */
                fDone = 1;
                Scrawl_OnButton1Up(hwnd); /* Context menu time */
            }
        }
 
    }
 

Finally, the Scrawl_OnMouseInput sample function invalidates the screen rectangle occupied by the cursor, in case the cursor has been moved by one of the helper functions.

/* Invalidate the new cursor so it will be drawn */
    InvalidateCursorRect(hwnd);
}
 

Scrawl also collects mouse data in the Scrawl_OnButton0Down function. This is where the application keeps track of mouse movements while the primary button is being held down — that is, while the user is drawing. This function does not rely on event notification, but repeatedly polls the DirectInput buffer until the button is released.

A key point to note in the Scrawl_OnButton0Down function is that no actual drawing is done until all pending data has been read. The reason is that each horizontal or vertical movement of the mouse is reported as a separate event. (Both events are, however, placed in the buffer at the same time.) If a line were immediately drawn in response to each separate axis movement, a diagonal movement of the mouse would produce two lines at right angles.

Another way you can be sure that the movement in both axes is taken into account before responding in your application is to check the sequence numbers of the x-axis item and the y-axis item. If the numbers are the same, the two events took place simultaneously. For more information, see Time Stamps and Sequence Numbers.