DirectInput Input Mapper

Om Sharma
Microsoft Corporation
6 June 1999

Abstract

The "Genre and Control definations" documents lists a number of common game genres and primary actions for each genre. An action expresses what application behavior should result from the user's operation of the control.

How games use Actions

This example illustrates how a car racing game would use the Dinput mapper to configure its controls. The enumerated array eGameActions supplies the codes that Dinput uses to communicate the state of controllers. An application should enumerate all the actions that it plans to expose to users. Axis and hatswitch actions should have button or key equivalents, to allow users to configure those actions on less capable devices.


enum eGameActions =
{   
    eA_STEER,       /* Steering */		
    eB_STEER_LEFT,  /* Use button to steer to the left */
    eB_STEER_RIGHT, /* Use button to steer to the right */
    eA_ACCELERATE,  /* Accelerate */
    eB_ACCELERATE,  /* Use button to accelerate */
    eB_DEACCELERATE,/* Use button to deaccelerate */
    eA_BRAKE,       /* Brake */
    eB_UPSHIFT,     /* Shift to higher gear */
    eB_DOWNSHIFT,   /* Shift to lower gear */ 
    eB_CYCLEVIEW,   /* Cycle to next view */        		
    eB_CONFIGCAR,   /* Configure vehicle */		
    eB_COURSEVIEW,  /* Toggle course view */
    eB_DRIVERVIEW,  /* View from Drivers seat */
    eA_VOLUME,      /* CD Volume */
    eB_BRAKEBIAS,   /* Brake Bias */
};                            

The rgActions array is used to bind game action codes to virtual device controls. A game should use predefined action codes, in this case DICARCONTROLLER_*, to refer to controls on a virtual car controller. Dinput will examine all connected devices and enumurate devices suitable for the maping. The rgActions should only refer to a single virtual controller genre and keyboard and mouse.

DIACTIONS rgActions[]=
{
// Genre Defined controls
    /*************      ******************              ****************
    Game Action         Virtual Controller              User Readable 
    Code                Action Code                     Label For Action
    **************      ******************              ****************/
//Genre defined axes		
    {eA_STEER,      DIDEVTYPE_CARCONTROLLER,  ABAXIS_STEER,       "Steer",        0x0},
    {eA_ACCELERATE, DIDEVTYPE_CARCONTROLLER,  ABAXIS_ACCEL,       "Accelerate",   0x0},
    {eA_BRAKE,      DIDEVTYPE_CARCONTROLLER,  ABAXIS_BRAKE,       "Brake",        0x0},

// addition axes not defined as part of the genre
    {eA_VOLUME,     DIDEVTYPE_CARCONTROLLER,  ABAXIS_ANY,         "CD Volume",    0x0},
//  ..more game specific actions      

//Genre defined buttons   
    {eB_UPSHIFT,    DIDEVTYPE_CARCONTROLLER,  BUTTON_UPSHIFT,     "Upshift",      0x0},   
    {eB_DOWNSHIFT,  DIDEVTYPE_CARCONTROLLER,  BUTTON_DWNSHIFT,    "DownShift",    0x0},
    {eB_CYCLEVIEW,  DIDEVTYPE_CARCONTROLLER,  BUTTON_VIEWS,       "Change View",  0x0},
    {eB_CONFIGCAR,  DIDEVTYPE_CARCONTROLLER,  BUTTON_CONFIGURECAR,"Configure",    0x0}, 

// Additional actions not defined in the car controller genre 
// Listed in order of importance.
    {eB_DRIVERVIEW, DIDEVTYPE_KEYBOARD,       DIK_1,              "Driver View",  0x0},
    {eB_COURSEVIEW, DIDEVTYPE_KEYBOARD,       DIK_C,              "Course View",  0x0},
    {eB_BRAKEBIAS,  DIDEVTYPE_KEYBOARD,       DIK_B,              "Brake Bias",   0x0},
// ... more game specific actions.
			
// Equivalent mappings for keyboard
    {eB_STEER_L,    DIDEVTYPE_KEYBOARD,       DIK_LEFT,           "Steer Left",   0x0},
    {eB_STEER_R,    DIDEVTYPE_KEYBOARD,       DIK_RIGHT,          "Steer Right",  0x0},
    {eB_ACCEL_MORE, DIDEVTYPE_KEYBOARD,       DIK_UP,             "Accelerate",   0x0},
    {eB_ACCEL_LESS, DIDEVTYPE_KEYBOARD,       DIK_DOWN,           "DeAccelerate", 0x0},
//  ... additional mappings for keyboard      

// Equivalent mappings for mouse.
    {eB_UPSHIFT,    DIDEVTYPE_MOUSE,          BUTTON_1,           "UpShift",      0x0},        
    {eB_DOWNSHIFT,  DIDEVTYPE_MOUSE,          BUTTON_2,           "DownShift",    0x0},
    {eB_CYCLEVIEW,  DIDEVTYPE_MOUSE,          BUTTON_3,           "Cycle View",   0x0},
//  ... additional mappings for mouse

};

The last few elements of the rgActions array provide specific equivalents for controls on keyboard and mice. The controls on these devices are fairly standard, so there is no need to provide indirections. The predefined semantic DI_EQUIV allows the application to specify equivalent mappings for a control. This flag allows DINPUT to distuingish between a primary control and a equivalent control. Equivalent controls only avaliable on the keyboard and mice.

Including these controls in one rgSemantic makes the application code simpler. The input processing code of the application can loop through active input devices and processes data from {eAch device in a similar manner.

Now that the semantic mappings have been defined, the application can use the IDirectInput8::EnumDevicesByActions method in order to enumerate devices that are suitable for the virtual car controller.

InitializeInput()
{
    IDirectInput* pDI;

    hr =pDI->EnumDevicesByActions( 
	pDI,                // IDirectInput interface 
	&GUID_Application,  // Unique GUID for {eAch application
	rgActions,          // Action array
	TEXT("BillG"),      // UserName, 0x0},=>CurrentUser
	fnDIEnumDevices,    // Device Enumeration function
	NULL,               // User variable
	0x0                 // Enumeration flags		 
	); 

    if(FAILED(hr)
    {
      // No device suitable for car controller genre
	goto panic;
    }
      
    pDi->Rel{eAse();
}

The number of complete devices determines how Dinput configures partial devices. {eAch application is required to provide a GUID that uniquely identifies it. Dinput uses this GUID to keep track of user specified configuration, or to enable enhanced configuration information provided by the hardware vendor that is specific to the application.

BOOL fnDIEnumDevices( 
    IDirectInputDevice8* pDiDev, 	
    UINT nID, 	
    LPCDIDEVICEINSTANCE lpDiDI,
    PVOID pv 
	)
{
    // Display the device mapping
    // If a user changes the mapping, 
    // the application will need to renogotiate the mapping.
	
    hr = pDiDev->DisplayDeviceConfiguration();

    if( hr = DIERR_CONFIGCHANGE )
    {
        // User has changed the device configuration, redo the mapping
        g_bRedoConfig = TRUE;
        return DIENUM_STOP;
    }else
    {
        // If you decide to keep the device, you need to AddRef
        pDiDev->AddRef();
        g_pDIDevice[nID] = pDiDev;
        // Stash away a copy of the interface pointer
    }

    return DIENUM_CONTINUE; // Look for other devices
}

If the user changes the device configuration, this function will return an error code of DIERR_CONFIGCHANGE. This is your cue to renegotiate the mapping.