/*==========================================================================
*
* 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;
}
}