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 is a signal associated with the mouse, the OnMouseInput function is called. This function is a good illustration of how buffered input is handled, as shown in the following process.

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

VOID OnMouseInput(HWND hWnd)
{
    /* Invalidate the old cursor so that it will be erased */
    InvalidateCursorRect(hWnd);

Next, 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.

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

Note    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.

The application calls the IDirectInputDevice8::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 needed again, and so it is removed.

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

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

        if (hr == DIERR_INPUTLOST) 
        {
            SetAcquire();
            break;
        }

Next, the application makes sure the call to the IDirectInputDevice8::GetDeviceData method succeeded and that there was actually data to be retrieved. Note that after the call to IDirectInputDevice8::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, 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 it calls helper functions to respond appropriately. The value of the dwData member, which contains information about what occurred with the device object, is passed to these functions.

        /* View the element to see what occurred */

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

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

            // DIMOFS_BUTTON0: Right button pressed or released 
            case DIMOFS_BUTTON0:

            // DIMOFS_BUTTON1: Left button pressed or released 
            case DIMOFS_BUTTON1:
                // Is the right button or a swapped left button down?
                if((g_bSwapMouseButtons && 
                               DIMOFS_BUTTON1 == od.dwOfs) ||
                   (!g_bSwapMouseButtons && 
                               DIMOFS_BUTTON0 == od.dwOfs))
                {
                    if (od.dwData & 0x80)  // Left button pressed, so
                                                // go into button-down mode
                    {
                        bDone = TRUE;
                        OnLeftButtonDown(hWnd);
                    }
                // Is the left button or a swapped right button down?
                if((g_bSwapMouseButtons && 
                               DIMOFS_BUTTON0 == od.dwOfs) ||
                   (!g_bSwapMouseButtons && 
                               DIMOFS_BUTTON1 == od.dwOfs))
                {
                    if(!(od.dwData & 0x80))  // button release, so
                                             // check shortcut menu
                    {  
                        bDone = TRUE;
                        OnRightButtonUp(hWnd); 
                    }
                }
                break;        
        }

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

    // Invalidate the new cursor so that it will be drawn 
    InvalidateCursorRect(hWnd);
}

Scrawl also collects mouse data in the OnLeftButtonDown 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.

Note that in the OnLeftButtonDown function no drawing is done until all pending data has been read. This is because 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 mouse movement 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.

For additional DirectInput tutorials, see DirectInput Tutorials.