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
Listing 7-6: Initializing DirectInput for mouse data retrieval
      {create the DirectInput object}

      DirectInputCreate(hInstance, DIRECTINPUT_VERSION, FDirectInput, nil);

      {create the DirectInputDevice object for the primary mouse}

      FDirectInput.CreateDevice(@GUID_SysMouse, FMouseDevice, nil);

      {set the data format for mouse data}

      FMouseDevice.SetDataFormat(c_dfDIMouse);

      {set the appropriate cooperative level}

      FMouseDevice.SetCooperativeLevel(Handle,DISCL_FOREGROUND or DISCL_EXCLUSIVE);
With this accomplished, we should now set the size of the buffer that will hold our mouse input data. This property takes only a simple DWORD of information, so we can use a TDIPropDWord structure in our call to SetProperty. After initializing the appropriate size members, we should set the diph.dwObj member to zero and the diph.dwHow member to DIPH_DEVICE, indicating that this property affects the entire device. Setting the dwData member to 64 indicates we want to set the buffer size to hold 64 “data packets” (this number is arbitrary, and can be higher or lower as dictated by the application requirements). Thus, our buffer will be capable of holding 64 individual instances of mouse input, be that axis movement, button presses, etc. If we do not retrieve and flush this buffer before this limit is reached, any subsequent input data will be lost. This process is illustrated below. Listing 7-7: Setting the size of the buffer
    procedure TfrmDXAppMain.FormActivate(Sender: TObject);

    var

     .

     .

     .

      {the mouse property structure}

      MouseProp: TDIPropDWord;

    begin

     .

     .

     .

      {initialize our property structure}

      MouseProp.diph.dwSize := SizeOf(TDIPropDWord);

      MouseProp.diph.dwHeaderSize := SizeOf(TDIPropHeader);

      MouseProp.diph.dwObj := 0;

      MouseProp.diph.dwHow := DIPH_DEVICE;

      MouseProp.dwData := 64;           // we want 64 data packets’

      {set the size of the buffer property}

      DXCheck( FMouseDevice.SetProperty(DIPROP_BUFFERSIZE, MouseProp.diph) );
Event Notification As we’ve previously discussed, DirectInput has a method known as event notification where it can signal the application that an input event has occurred on some device. This releases the application from the requirement of polling the device through every iteration of the game loop, but it introduces some serious implications. This technique works by using a Windows event object. Basically, an event is little more than a flag that exists in a “signaled” or “non-signaled” state, and is used for thread synchronization. Using the IDirectInputDevice’s SetEventNotification method you can pass the device an event, which will be set to a signaled state when new input is available. The only way this is useful is in a call to one of the Win32 API wait functions. These functions halt execution of a thread until the event object passed into the function becomes signaled. This is useless in a single-threaded application, as it would halt the execution of the primary thread unless the mouse is moving, making for a boring game indeed. This is why we must use this technique in a multithreaded application. We’ll see one of these wait functions in action below. We will actually want to create two events, one which is passed to DirectInput to signal the availability of new mouse input and one to signal the thread to terminate. We can create these events using the Win32 API function CreateEvent, which is defined as:
    function CreateEvent(

    lpEventAttributes: PSecurityAttributes;  // pointer to security attributes

    bManualReset: BOOL;                      // flag for manual reset event

    bInitialState: BOOL;                     // flag for initial state

    lpName: PChar                            // name of the event object

    ): THandle;                              // returns a handle of the event object
For our purposes, we can set the first parameter to nil, and the second and third parameters to FALSE. We can set the final parameter to an appropriate name for the event, but it is unimportant to our application. These events will be stored in an array for easy management. We will also create a TCriticalSection object, which we will use to synchronize the main thread and our secondary input thread. Using the critical section object will be covered below when we read the mouse data from the buffer. Now that we’ve created our events, we need to tell DirectInput which event to signal when mouse input becomes available. This is accomplished by calling the IDirectInputDevice’s SetEventNotification method, defined as:
    function SetEventNotification(

    hEvent: THandle                          // a handle to the event to be signaled

    ): HResult;                              // returns a DirectX error code
The only parameter to this method is the handle of the event to be signaled, which we just created above. The following example demonstrates the setup required for initializing event notification. Listing 7-8: Initializing event notification
      {create our events, one for indicating new mouse input data, one for

       indicating that the application is terminating}

      FMouseEvents[MOUSEEVENT] := CreateEvent(nil, FALSE, FALSE, MouseEvent’);

      FMouseEvents[QUITEVENT] := CreateEvent(nil, FALSE, FALSE, QuitEvent’);

      {create our critical section synchronization object}

      FCritSec := TCriticalSection.Create;

      {set the event notification}

      DXCheck( FMouseDevice.SetEventNotification(FMouseEvents[MOUSEEVENT]) );
Secondary Thread Creation Now that the critical section and events have been set up, we need to create our secondary thread. This second thread is where we will perform all input data retrieval. We used the File | New menu to create a new TThread object, using the Execute method as the location for our code that retrieves mouse input. We’ll examine the details of this code below when we cover reading input data. To actually create the thread, we call our TThread object’s Create method, passing its only parameter a value of TRUE to indicate we want the thread to be suspended upon creation. The thread should be suspended as we have not yet acquired the mouse. We then set its Priority property to tpTimeCritical, indicating we want it to be immediately responsive, and its FreeOnTerminate property to TRUE, indicating it should free itself when it is terminated. These steps are illustrated in the following listing. Listing 7-9: Creating the secondary input thread
      {create our secondary input thread}

      FMouseThread := TMouseThread.Create(TRUE);

      {we want it to be supremely responsive}

      FMouseThread.Priority := tpTimeCritical;

      {indicate it should free itself when it terminates}

      FMouseThread.FreeOnTerminate := TRUE;
Device Acquisition Now that the events have been created and set up and the secondary input thread has been initialized, we can acquire the device by calling the Acquire method. As a final step, we must begin executing the input thread by calling the thread’s Resume method. We are now finally ready to start retrieving buffered, event-driven mouse input data.
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.