INPUT.C
/*========================================================================== 
 * 
 *  Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. 
 * 
 *  File:       input.c 
 *  Content:    Input routines for Space Donuts game 
 * 
 * 
 ***************************************************************************/ 
 
#include <dinput.h> 
#include "input.h" 
#include "resource.h" 
 
extern HWND hWndMain; 
extern DWORD ReadKeyboardInput(void); 
extern DWORD ReadJoystickInput(void); 
 
// allocate external variables 
DWORD (*ReadGameInput)(void) = ReadKeyboardInput; 
 
/* 
 *  We'll use up to the first ten input devices. 
 * 
 *  c_cpdiFound is the number of found devices. 
 *  g_rgpdiFound[0] is the array of found devices. 
 *  g_pdevCurrent is the device that we are using for input. 
 */ 
#define MAX_DINPUT_DEVICES 10 
int g_cpdevFound; 
LPDIRECTINPUTDEVICE2 g_rgpdevFound[MAX_DINPUT_DEVICES]; 
LPDIRECTINPUTDEVICE2 g_pdevCurrent; 
 
 
 
 
/*-------------------------------------------------------------------------- 
| AddInputDevice 
| 
| Records an input device in the array of found devices. 
| 
| In addition to stashing it in the array, we also add it to the device 
| menu so the user can pick it. 
| 
*-------------------------------------------------------------------------*/ 
 
void AddInputDevice(LPDIRECTINPUTDEVICE pdev, LPCDIDEVICEINSTANCE pdi) 
{ 
 
    if (g_cpdevFound < MAX_DINPUT_DEVICES) { 
 
        HRESULT hRes; 
 
        /* 
         *  Convert it to a Device2 so we can Poll() it. 
         */ 
 
        hRes = pdev->lpVtbl->QueryInterface( 
                    pdev, &IID_IDirectInputDevice2, 
                    (LPVOID *)&g_rgpdevFound[g_cpdevFound]); 
 
        if (SUCCEEDED(hRes)) { 
 
            HMENU hmenu; 
 
            /* 
             *  Add its description to the menu. 
             */ 
            hmenu = GetSubMenu(GetMenu(hWndMain), 0); 
 
            InsertMenu(hmenu, g_cpdevFound, MF_BYPOSITION | MF_STRING, 
                       IDC_DEVICES + g_cpdevFound, 
                       pdi->tszInstanceName); 
 
            g_cpdevFound++; 
        } 
    } 
} 
 
/*-------------------------------------------------------------------------- 
| 
| SetDIDwordProperty 
| 
| Set a DWORD property on a DirectInputDevice. 
| 
*-------------------------------------------------------------------------*/ 
 
HRESULT 
SetDIDwordProperty(LPDIRECTINPUTDEVICE pdev, REFGUID guidProperty, 
                   DWORD dwObject, DWORD dwHow, DWORD dwValue) 
{ 
   DIPROPDWORD dipdw; 
 
   dipdw.diph.dwSize       = sizeof(dipdw); 
   dipdw.diph.dwHeaderSize = sizeof(dipdw.diph); 
   dipdw.diph.dwObj        = dwObject; 
   dipdw.diph.dwHow        = dwHow; 
   dipdw.dwData            = dwValue; 
 
   return pdev->lpVtbl->SetProperty(pdev, guidProperty, &dipdw.diph); 
 
} 
 
/*-------------------------------------------------------------------------- 
| InitKeyboardInput 
| 
| Initializes DirectInput for the keyboard.  Creates a keyboard device, 
| sets the data format to our custom format, sets the cooperative level and 
| adds it to the menu. 
| 
*-------------------------------------------------------------------------*/ 
 
BOOL InitKeyboardInput(LPDIRECTINPUT pdi) 
{ 
   LPDIRECTINPUTDEVICE pdev; 
   DIDEVICEINSTANCE di; 
 
   // create the DirectInput keyboard device 
   if(pdi->lpVtbl->CreateDevice(pdi, &GUID_SysKeyboard, &pdev, NULL) != DI_OK) 
   { 
      OutputDebugString("IDirectInput::CreateDevice FAILED\n"); 
      return FALSE; 
   } 
 
   // set keyboard data format 
   if(pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIKeyboard) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
   // set the cooperative level 
   if(pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain, 
      DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
   // set buffer size 
   if (SetDIDwordProperty(pdev, DIPROP_BUFFERSIZE, 0, DIPH_DEVICE, KEYBUFSIZE) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEVICE) FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
   // 
   // Get the name of the primary keyboard so we can add it to the menu. 
   // 
   di.dwSize = sizeof(di); 
   if (pdev->lpVtbl->GetDeviceInfo(pdev, &di) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::GetDeviceInfo FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
   // 
   // Add it to our list of devices.  If AddInputDevice succeeds, 
   // he will do an AddRef. 
   // 
   AddInputDevice(pdev, &di); 
   pdev->lpVtbl->Release(pdev); 
 
   return TRUE; 
} 
 
/*-------------------------------------------------------------------------- 
| InitJoystickInput 
| 
| Initializes DirectInput for an enumerated joystick device. 
| Creates the device, device, sets the standard joystick data format, 
| sets the cooperative level and adds it to the menu. 
| 
| If any step fails, just skip the device and go on to the next one. 
| 
*-------------------------------------------------------------------------*/ 
 
BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef) 
{ 
   LPDIRECTINPUT pdi = pvRef; 
   LPDIRECTINPUTDEVICE pdev; 
   DIPROPRANGE diprg; 
 
   // create the DirectInput joystick device 
   if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK) 
   { 
      OutputDebugString("IDirectInput::CreateDevice FAILED\n"); 
      return DIENUM_CONTINUE; 
   } 
 
   // set joystick data format 
   if(pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return DIENUM_CONTINUE; 
   } 
 
   // set the cooperative level 
   if(pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain, 
      DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return DIENUM_CONTINUE; 
   } 
 
   // set X-axis range to (-1000 ... +1000) 
   // This lets us test against 0 to see which way the stick is pointed. 
 
   diprg.diph.dwSize       = sizeof(diprg); 
   diprg.diph.dwHeaderSize = sizeof(diprg.diph); 
   diprg.diph.dwObj        = DIJOFS_X; 
   diprg.diph.dwHow        = DIPH_BYOFFSET; 
   diprg.lMin              = -1000; 
   diprg.lMax              = +1000; 
 
   if(pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
   // 
   // And again for Y-axis range 
   // 
   diprg.diph.dwObj        = DIJOFS_Y; 
 
   if(pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
   // set X axis dead zone to 50% (to avoid accidental turning) 
   // Units are ten thousandths, so 50% = 5000/10000. 
   if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
 
   // set Y axis dead zone to 50% (to avoid accidental thrust) 
   // Units are ten thousandths, so 50% = 5000/10000. 
   if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000) != DI_OK) 
   { 
      OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n"); 
      pdev->lpVtbl->Release(pdev); 
      return FALSE; 
   } 
 
 
   // 
   // Add it to our list of devices.  If AddInputDevice succeeds, 
   // he will do an AddRef. 
   // 
   AddInputDevice(pdev, pdinst); 
   pdev->lpVtbl->Release(pdev); 
 
   return DIENUM_CONTINUE; 
} 
 
/*-------------------------------------------------------------------------- 
| InitInput 
| 
| Initializes DirectInput for the keyboard and all joysticks. 
| 
| For each input device, add it to the menu. 
| 
*-------------------------------------------------------------------------*/ 
 
BOOL InitInput(HINSTANCE hInst, HWND hWnd) 
{ 
   LPDIRECTINPUT pdi; 
   BOOL fRc; 
 
   // Note: Joystick support is a DirectX 5.0 feature. 
   // Since we also want to run on DirectX 3.0, we will start out 
   // with DirectX 3.0 to make sure that at least we get the keyboard. 
 
   // create the DirectInput interface object 
   if(DirectInputCreate(hInst, 0x0300, &pdi, NULL) != DI_OK) 
   { 
      OutputDebugString("DirectInputCreate 3.0 FAILED\n"); 
      return FALSE; 
   } 
 
   fRc = InitKeyboardInput(pdi); 
   pdi->lpVtbl->Release(pdi);       // Finished with DX 3.0 
 
   if (!fRc) { 
      return FALSE; 
   } 
 
   // create the DirectInput 5.0 interface object 
   if(DirectInputCreate(hInst, DIRECTINPUT_VERSION, &pdi, NULL) == DI_OK) 
   { 
 
      // 
      // Enumerate the joystick devices.  If it doesn't work, oh well, 
      // at least we got the keyboard. 
      // 
 
      pdi->lpVtbl->EnumDevices(pdi, DIDEVTYPE_JOYSTICK, 
                               InitJoystickInput, pdi, DIEDFL_ATTACHEDONLY); 
 
      pdi->lpVtbl->Release(pdi);    // Finished with DX 5.0. 
 
   } else { 
      OutputDebugString("DirectInputCreate 5.0 FAILED - no joystick support\n"); 
   } 
 
   // Default device is the keyboard 
   PickInputDevice(0); 
 
   // if we get here, we were successful 
   return TRUE; 
} 
 
/*-------------------------------------------------------------------------- 
| CleanupInput 
| 
| Cleans up all DirectInput objects. 
*-------------------------------------------------------------------------*/ 
void CleanupInput(void) 
{ 
   int idev; 
 
   // make sure the device is unacquired 
   // it doesn't harm to unacquire a device that isn't acquired 
 
   if (g_pdevCurrent) 
   { 
      IDirectInputDevice_Unacquire(g_pdevCurrent); 
   } 
 
   // release all the devices we created 
   for (idev = 0; idev < g_cpdevFound; idev++) 
   { 
      if (g_rgpdevFound[idev]) { 
         IDirectInputDevice_Release(g_rgpdevFound[idev]); 
         g_rgpdevFound[idev] = 0; 
      } 
   } 
 
} 
 
 
/*-------------------------------------------------------------------------- 
| ReacquireInput 
| 
| Reacquires the current input device.  If Acquire() returns S_FALSE, 
| that means 
| that we are already acquired and that DirectInput did nothing. 
*-------------------------------------------------------------------------*/ 
BOOL ReacquireInput(void) 
{ 
    HRESULT hRes; 
 
    // if we have a current device 
    if(g_pdevCurrent) 
    { 
       // acquire the device 
       hRes = IDirectInputDevice_Acquire(g_pdevCurrent); 
       if(SUCCEEDED(hRes)) 
       { 
          // acquisition successful 
          return TRUE; 
       } 
       else 
       { 
          // acquisition failed 
          return FALSE; 
       } 
    } 
    else 
    { 
       // we don't have a current device 
       return FALSE; 
    } 
 
} 
 
 
/*-------------------------------------------------------------------------- 
| ReadKeyboardInput 
| 
| Requests keyboard data and performs any needed processing. 
*-------------------------------------------------------------------------*/ 
DWORD ReadKeyboardInput(void) 
{ 
   DIDEVICEOBJECTDATA      rgKeyData[KEYBUFSIZE]; 
   DWORD                   dwEvents; 
   DWORD                   dw; 
   static DWORD            dwKeyState = 0; 
   HRESULT                 hRes; 
 
   // get data from the keyboard 
   dwEvents = KEYBUFSIZE; 
   hRes = IDirectInputDevice_GetDeviceData(g_pdevCurrent, 
                                           sizeof(DIDEVICEOBJECTDATA), 
                                           rgKeyData, &dwEvents, 0); 
 
   if(hRes != DI_OK) 
   { 
      // did the read fail because we lost input for some reason? 
      // if so, then attempt to reacquire.  If the second acquire 
      // fails, then the error from GetDeviceData will be 
      // DIERR_NOTACQUIRED, so we won't get stuck an infinite loop. 
      if(hRes == DIERR_INPUTLOST) 
         ReacquireInput(); 
 
      // return the fact that we did not read any data 
      return 0; 
   } 
 
      // process the data 
      for(dw = 0; dw < dwEvents; dw++) 
      { 
         switch(rgKeyData[dw].dwOfs) 
         { 
         // fire 
         case DIK_SPACE: 
            if(rgKeyData[dw].dwData & 0x80) 
               dwKeyState |= KEY_FIRE; 
            else 
               dwKeyState &= (DWORD)~KEY_FIRE; 
            break; 
 
         // stop 
         case DIK_NUMPAD5: 
            if(rgKeyData[dw].dwData & 0x80) 
               dwKeyState |= KEY_STOP; 
            else 
               dwKeyState &= ~KEY_STOP; 
            break; 
 
         // shield 
         case DIK_NUMPAD7: 
            if(rgKeyData[dw].dwData & 0x80) 
               dwKeyState |= KEY_SHIELD; 
            else 
               dwKeyState &= ~KEY_SHIELD; 
            break; 
 
         // thrust 
         case DIK_UP: 
         case DIK_NUMPAD8: 
            if(rgKeyData[dw].dwData & 0x80) 
               dwKeyState |= KEY_UP; 
            else 
               dwKeyState &= ~KEY_UP; 
            break; 
 
         // reverse thrust 
         case DIK_DOWN: 
         case DIK_NUMPAD2: 
            if(rgKeyData[dw].dwData & 0x80) 
               dwKeyState |= KEY_DOWN; 
            else 
               dwKeyState &= ~KEY_DOWN; 
            break; 
 
         // rotate left 
         case DIK_LEFT: 
         case DIK_NUMPAD4: 
            if(rgKeyData[dw].dwData & 0x80) 
               dwKeyState |= KEY_LEFT; 
            else 
               dwKeyState &= ~KEY_LEFT; 
            break; 
 
         // rotate right 
         case DIK_RIGHT: 
         case DIK_NUMPAD6: 
            if(rgKeyData[dw].dwData & 0x80) 
               dwKeyState |= KEY_RIGHT; 
            else 
               dwKeyState &= ~KEY_RIGHT; 
            break; 
         } 
 
   } 
 
   // return the state of the keys to the caller 
   return dwKeyState; 
 
} 
 
/*-------------------------------------------------------------------------- 
| ReadJoystickInput 
| 
| Requests joystick data and performs any needed processing. 
| 
*-------------------------------------------------------------------------*/ 
DWORD ReadJoystickInput(void) 
{ 
   DWORD                   dwKeyState; 
   HRESULT                 hRes; 
   DIJOYSTATE              js; 
 
   // poll the joystick to read the current state 
   hRes = IDirectInputDevice2_Poll(g_pdevCurrent); 
 
   // get data from the joystick 
   hRes = IDirectInputDevice_GetDeviceState(g_pdevCurrent, 
                                            sizeof(DIJOYSTATE), &js); 
 
   if(hRes != DI_OK) 
   { 
      // did the read fail because we lost input for some reason? 
      // if so, then attempt to reacquire.  If the second acquire 
      // fails, then the error from GetDeviceData will be 
      // DIERR_NOTACQUIRED, so we won't get stuck an infinite loop. 
      if(hRes == DIERR_INPUTLOST) 
         ReacquireInput(); 
 
      // return the fact that we did not read any data 
      return 0; 
   } 
 
   // 
   // Now study the position of the stick and the buttons. 
   // 
 
   dwKeyState = 0; 
 
   if (js.lX < 0) { 
      dwKeyState |= KEY_LEFT; 
   } else if (js.lX > 0) { 
      dwKeyState |= KEY_RIGHT; 
   } 
 
   if (js.lY < 0) { 
      dwKeyState |= KEY_UP; 
   } else if (js.lY > 0) { 
      dwKeyState |= KEY_DOWN; 
   } 
 
   if (js.rgbButtons[0] & 0x80) { 
      dwKeyState |= KEY_FIRE; 
   } 
 
   if (js.rgbButtons[1] & 0x80) { 
      dwKeyState |= KEY_SHIELD; 
   } 
 
   if (js.rgbButtons[2] & 0x80) { 
      dwKeyState |= KEY_STOP; 
   } 
 
   return dwKeyState; 
 
} 
 
/*-------------------------------------------------------------------------- 
| PickInputDevice 
| 
| Make the n'th input device the one that we will use for game play. 
| 
*-------------------------------------------------------------------------*/ 
 
BOOL PickInputDevice(int n) 
{ 
    if (n < g_cpdevFound) { 
 
        /* 
         *  Unacquire the old device. 
         */ 
        if (g_pdevCurrent) { 
            IDirectInputDevice_Unacquire(g_pdevCurrent); 
        } 
 
        /* 
         *  Move to the new device. 
         */ 
        g_pdevCurrent = g_rgpdevFound[n]; 
 
        /* 
         *  Set ReadGameInput to the appropriate handler. 
         */ 
        if (n == 0) { 
            ReadGameInput = ReadKeyboardInput; 
        } else { 
            ReadGameInput = ReadJoystickInput; 
        } 
 
        CheckMenuRadioItem(GetSubMenu(GetMenu(hWndMain), 0), 
                           IDC_DEVICES, IDC_DEVICES + g_cpdevFound - 1, 
                           IDC_DEVICES + n, MF_BYCOMMAND); 
 
        ReacquireInput(); 
 
        return TRUE; 
    } else { 
        return FALSE; 
    } 
}