Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
Reading Input Data As we previously mentioned, our secondary thread will handle the task of retrieving the input data from the buffer. Thus, all of the code we are about to review is located in the Execute method of our secondary TThread object. The first item of note in the Execute method concerns the notification events we created. After checking for termination, the very first task we perform in the thread is to call the WaitForMultipleObjects Win32 API function. This function halts execution of the thread until one or more event objects become signaled (known as blocking the thread). The WaitForMultipleObjects function is defined as:
    function WaitForMultipleObjects(

    nCount: DWORD;                        // the number of objects in the array

    lpHandles: PWOHandleArray;            // a pointer to an array of object handles

    bWaitAll: BOOL;                       // wait flag

    dwMilliseconds: DWORD                 // timeout interval

    ): DWORD;                             // returns an array index
We pass in our array of object handles and the number of objects in the array in the first two parameters. We only want the thread to be blocked until either of the events becomes signaled, so we set the third parameter to FALSE. We want the thread to wait indefinitely until one of the events is signaled, so we set the final parameter to INFINITE. This function call will not return until either of the events becomes signaled. When the function does return, we need to check its return value. We subtract the constant WAIT_OBJECT_0 from this return value to get the index in the array of the object that became signaled. If the Quit event was signaled (indicating we are terminating the application), we should immediately terminate the thread and exit the Execute method. Otherwise, we know it was the mouse event that became signaled, and we are ready to start retrieving mouse input data.
| Note: We set the Quit event to signaled using the SetEvent Win32 API function in the main form’s OnDestroy event. We must also use the CloseHandle Win32 API function to dispose of the event handles we created. See the full example on the CD for details.
Once past this, we make use of our critical section object. A critical section is a device that is used to synchronize access to variables and objects from multiple threads. This is accomplished by calling the TCriticalSection’s Enter method. If one thread calls the Enter method of a critical section after another thread has already called it, this second thread is blocked until the first thread calls the TCriticalSection’s Leave method. At this point, the other thread becomes unblocked, and any other thread will be blocked in a similar fashion until the current thread calls the Leave method. By using critical sections, we can allow only one thread at a time access to specific variables and objects. This is important, as this secondary input thread will be drawing to the primary DirectDraw surface, and surfaces can only be accessed by one thread at a time. It will also be updating global variables that track the mouse cursor, as well as a list of mouse click events that are used by the primary thread to determine if the user clicked on something interactive. Now we are ready to being retrieving input data from our buffer. This is accomplished by calling the IDirectInputDevice’s GetDeviceData method. The GetDeviceData method is defined as:
    function GetDeviceData(

    cbObjectData: DWORD;             // the size of the device data structure

    rgdod: PDIDeviceObjectData;      // a pointer to the device data structure

    var pdwInOut: DWORD;             // a variable indicating the number of elements

    dwFlags: DWORD                   // data removal flags

    ): HResult;                      // returns a DirectX error code
The first parameter is set to the size of a TDIDeviceObjectData structure. The second parameter is a pointer to an array of TDIDeviceObjectData structures which, upon return, will contain the buffered device data. The third parameter indicates how many elements to retrieve, and should be equal to or less than the number of elements in the array. When this method returns, it will contain the actual number of elements retrieved. The last parameter indicates if the data should be removed from the buffer. Setting this to zero removes the data from the buffer; a value of DIGDD_PEEK leaves the buffer intact.
| Note: You do not have to remove the entire contents of the buffer at once. You may remove them one at a time or in similar small increments, if desired.
This method will return one TDIDeviceObjectData structure for each individual input event that has occurred on the device since the last time data was retrieved. This means there is one event for a button press, one for a button release (even if it was the same button), one for every individual movement along each axis, etc. This is why it is important to set the buffer size to an appropriate number, as it is easy to overflow the buffer if a lot of activity is occurring. The TDIDeviceObjectData structure contains all the information necessary to report any input event on the specific device. The TDIDeviceObjectData structure is defined as:
    TDIDeviceObjectData = packed record

      dwOfs: DWORD;                          // the device object identifier

      dwData: DWORD;                         // the data

      dwTimeStamp: DWORD;                    // when the event occurred

      dwSequence: DWORD;                     // the event sequence number

    end;
The first member, dwOfs, indicates what actually caused the input. This value is very specific to the type of device being used. In the case of buffered keyboard data, this value will be one of the DIK_* key values. For mice, this could be DIMOFS_X, DIMOFS_Y, or DIMOFS_Z to indicate movement along one of the axes, or DIMOFS_BUTTON0, DIMOFS_BUTTON1, or DIMOFS_BUTTON0 to indicate a change of state in one of the mouse buttons. There are similar DIJOFS_* values defined for various buttons and axes on joysticks. The dwData member contains the data for the input event, and again is very specific to both the device and the object on that device that signaled the input event. In the case of one of the axes, dwData will contain either relative or absolute axis movement, in arbitrary units. In the case of buttons, if the high bit is set, the button was pressed; if it is clear, the button was released. If we enter a loop to retrieve one input event at a time, we can examine all of these members and update various global variables accordingly. In our example on the CD, we add any axis movement to global mouse cursor variables, and then ultimately clip them to the boundaries of our DirectDraw screen resolution. While our example is concerned only with button presses, we do store each mouse button press in a list of button press objects, storing the location of the press and the button that was pressed. We can use this later in our application logic to determine if the user clicked on an interactive game element.
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.