#include "pch.h"
#include "dinput.h"
//////////////////////////////////////////////////////////////////////////////
//
// DDWrapers
//
//////////////////////////////////////////////////////////////////////////////
class DIDeviceCaps : public TZeroFillWithSize<DIDEVCAPS> {
public:
};
class DIDeviceInstance : public TZeroFillWithSize<DIDEVICEINSTANCE> {
public:
};
class DIDataFormat : public TZeroFillWithSize<DIDATAFORMAT> {
public:
};
class DIObjectDataFormat : public TZeroFill<DIOBJECTDATAFORMAT> {
public:
};
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
const DIDATAFORMAT* g_pdfDIMouse;
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
class DDInputObject : public IObject {
private:
ZString m_strName;
DWORD m_dwType;
GUID m_guidType;
public:
DDInputObject(const ZString& strName, DWORD dwType, const GUID& guidType) :
m_strName(strName),
m_dwType(dwType),
m_guidType(guidType)
{
}
const ZString& GetName() const
{
return m_strName;
}
DWORD GetDWType() const
{
return m_dwType;
}
const GUID& GetGUID() const
{
return m_guidType;
}
};
class ValueDDInputObject : public DDInputObject {
private:
TRef<ModifiableNumber> m_pnumber;
public:
ValueDDInputObject(const ZString& strName, DWORD dwID, const GUID& guidType) :
DDInputObject(strName, dwID, guidType),
m_pnumber(new ModifiableNumber(0))
{
}
ModifiableNumber* GetValue() const
{
return m_pnumber;
}
};
class ButtonDDInputObject : public DDInputObject {
private:
TRef<ModifiableBoolean> m_pbool;
public:
ButtonDDInputObject(const ZString& strName, DWORD dwID, const GUID& guidType) :
DDInputObject(strName, dwID, guidType),
m_pbool(new ModifiableBoolean(false))
{
}
ModifiableBoolean* GetValue() const
{
return m_pbool;
}
};
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
class MouseInputStreamImpl : public MouseInputStream {
private:
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
BOOL EnumObjectsCallback(
LPCDIDEVICEOBJECTINSTANCE pddoi
) {
if (
pddoi->dwType & DIDFT_AXIS
|| pddoi->dwType & DIDFT_POV
) {
int index;
if (pddoi->guidType == GUID_XAxis) {
index = 0;
} else if (pddoi->guidType == GUID_YAxis) {
index = 1;
} else if (pddoi->guidType == GUID_ZAxis) {
index = 2;
} else {
index = -1;
}
ValueDDInputObject* pobject =
new ValueDDInputObject(
pddoi->tszName,
pddoi->dwType,
pddoi->guidType
);
if (index == -1) {
m_vvalueObject.PushEnd(pobject);
} else {
m_vvalueObject.Set(index, pobject);
}
} else if (pddoi->dwType & DIDFT_PSHBUTTON) {
ButtonDDInputObject* pobject =
new ButtonDDInputObject(
pddoi->tszName,
pddoi->dwType,
pddoi->guidType
);
m_vbuttonObject.PushEnd(pobject);
}
return DIENUM_CONTINUE;
}
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
static BOOL CALLBACK StaticEnumObjectsCallback(
LPCDIDEVICEOBJECTINSTANCE lpddoi,
LPVOID pvRef
) {
MouseInputStreamImpl* pthis = (MouseInputStreamImpl*)pvRef;
return pthis->EnumObjectsCallback(lpddoi);
}
//////////////////////////////////////////////////////////////////////////////
//
// members
//
//////////////////////////////////////////////////////////////////////////////
// TRef<IDirectInputDevice2> m_pdid;
TRef<IDirectInputDevice7> m_pdid; // mdvalley: DInput7
TRef<ButtonEvent::SourceImpl> m_pbuttonEventSource;
DIDeviceCaps m_didc;
DIDeviceInstance m_didi;
TVector<TRef<ValueDDInputObject > > m_vvalueObject;
TVector<TRef<ButtonDDInputObject> > m_vbuttonObject;
Rect m_rect;
Point m_point;
float m_z;
bool m_bBuffered;
bool m_bEnabled;
int m_threshold1;
int m_threshold2;
int m_acceleration;
float m_sensitivity;
public:
//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
// MouseInputStreamImpl(IDirectInputDevice2* pdid, HWND hwnd) :
MouseInputStreamImpl(IDirectInputDevice7* pdid, HWND hwnd) : // mdvalley: DInput7
m_pdid(pdid),
m_rect(0, 0, 0, 0),
m_point(0, 0),
m_vvalueObject(3),
m_bEnabled(false),
m_bBuffered(true),
m_pbuttonEventSource(ButtonEvent::Source::Create())
{
//
// Are we running on NT
//
/*!!!
OSVERSIONINFO osvi = { sizeof(osvi) };
ZVerify(GetVersionEx(&osvi));
if ((VER_PLATFORM_WIN32_NT & osvi.dwPlatformId) != 0) {
m_bBuffered = false;
}
*/
//
// Enumerate the buttons and values
//
DDCall(m_pdid->EnumObjects(StaticEnumObjectsCallback, this, DIDFT_ALL));
//
// Setup the device
//
SetupDevice();
//
// We only need mouse input when we are in the foreground
//
DDCall(m_pdid->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND));
//
// Get the mouse acceleration values
//
int pvalue[3];
ZVerify(SystemParametersInfo(SPI_GETMOUSE, 0, pvalue, 0));
m_threshold1 = pvalue[0];
m_threshold2 = pvalue[1];
m_acceleration = pvalue[2];
m_sensitivity = 1.0f;
/* !!! this only works on NT50
int speed;
ZVerify(SystemParametersInfo(SPI_GETMOUSESPEED, 0, &speed, 0));
if (speed <= 2) {
m_sensitivity = float(speed) / 32.0f;
} else if(speed >= 3 && speed <= 10 ) {
m_sensitivity = float(speed-2) / 8.0f;
} else {
m_sensitivity = float(speed-6) / 4.0f;
}
*/
}
void SetupDevice()
{
//
// Set the data format
//
DDCall(m_pdid->SetDataFormat(g_pdfDIMouse));
//
// Make some buffer space
//
if (m_bBuffered) {
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = 32;
DDCall(m_pdid->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph));
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Methods
//
//////////////////////////////////////////////////////////////////////////////
void DoClip()
{
if (
m_rect.XMax() > m_rect.XMin()
&& m_rect.YMax() > m_rect.YMin()
) {
if (m_point.X() < m_rect.XMin()) {
m_point.SetX(m_rect.XMin());
}
if (m_point.X() >= m_rect.XMax()) {
m_point.SetX(m_rect.XMax() - 1);
}
if (m_point.Y() < m_rect.YMin()) {
m_point.SetY(m_rect.YMin());
}
if (m_point.Y() >= m_rect.YMax()) {
m_point.SetY(m_rect.YMax() - 1);
}
}
}
float CalculateDelta(int delta)
{
if (abs(delta) > m_threshold1 && m_acceleration >= 1) {
if (abs(delta) > m_threshold2 && m_acceleration >= 2) {
return float(delta) * 4.0f * m_sensitivity;
} else {
return float(delta) * 2.0f * m_sensitivity;
}
}
return float(delta) * m_sensitivity;
}
void DeltaPosition(int& dx, int& dy)
{
if (dx != 0 || dy != 0) {
//ZDebugOutput("MouseMove: (" + ZString(dx) + ", " + ZString(dy) + ")\n");
m_point.SetX(m_point.X() + CalculateDelta(dx));
m_point.SetY(m_point.Y() - CalculateDelta(dy));
dx = 0;
dy = 0;
//
// Clip to the screen rect if required
//
DoClip();
//
// Update outputs
//
m_vvalueObject[0]->GetValue()->SetValue(m_point.X());
m_vvalueObject[1]->GetValue()->SetValue(m_point.Y());
}
}
void DeltaWheel(int dz)
{
if (dz != 0 ) {
//ZDebugOutput("MouseDZ: " + ZString(dz) + "\n");
m_z += float(dz);
if (m_vvalueObject.GetCount() >= 3) {
m_vvalueObject[0]->GetValue()->SetValue(m_z);
}
}
}
void ButtonChanged(int index, bool bDown)
{
//ZDebugOutput("MouseButton: " + ZString(index) + (bDown ? " down" : " up") + "\n");
m_vbuttonObject[index]->GetValue()->SetValue(bDown);
m_pbuttonEventSource->Trigger(ButtonEventData(index, bDown));
}
void UpdateBuffered()
{
//
// Get the data
//
DIDEVICEOBJECTDATA didod;
DWORD count = 1;
int dx = 0;
int dy = 0;
while (count == 1) {
HRESULT hr = m_pdid->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), &didod, &count, 0);
if (FAILED(hr)) {
return;
}
if (count == 1) {
//
// Unpack the data
//
switch (didod.dwOfs) {
case DIMOFS_BUTTON0:
DeltaPosition(dx, dy);
ButtonChanged(0, ((didod.dwData & 0x80) != 0));
break;
case DIMOFS_BUTTON1:
ButtonChanged(1, ((didod.dwData & 0x80) != 0));
break;
case DIMOFS_BUTTON2:
ButtonChanged(2, ((didod.dwData & 0x80) != 0));
break;
case DIMOFS_BUTTON3:
ButtonChanged(3, ((didod.dwData & 0x80) != 0));
break;
// mdvalley: More buttons
case DIMOFS_BUTTON4:
ButtonChanged(4, ((didod.dwData & 0x80) != 0));
break;
case DIMOFS_BUTTON5:
ButtonChanged(5, ((didod.dwData & 0x80) != 0));
break;
case DIMOFS_BUTTON6:
ButtonChanged(6, ((didod.dwData & 0x80) != 0));
break;
case DIMOFS_BUTTON7:
ButtonChanged(7, ((didod.dwData & 0x80) != 0));
break;
case DIMOFS_X:
dx += int(didod.dwData);
break;
case DIMOFS_Y:
dy += int(didod.dwData);
break;
case DIMOFS_Z:
DeltaWheel(int(didod.dwData));
break;
}
}
//
// do the mouse change
//
DeltaPosition(dx, dy);
}
}
void UpdatePolled()
{
//
// Poll the device
//
m_pdid->Poll();
//
// Get the data
//
// DIMOUSESTATE dims;
DIMOUSESTATE2 dims; // mdvalley: Mousestate2 allows 8 buttons
DDCall(m_pdid->GetDeviceState(sizeof(dims), &dims));
//
// Unpack the data
//
int dx = int(dims.lX);
int dy = int(dims.lY);
int dz = int(dims.lZ);
DeltaPosition(dx, dy);
DeltaWheel(dz);
int count = m_vbuttonObject.GetCount();
for (int index = 0; index < count; index++) {
bool bDown = ((dims.rgbButtons[index] & 0x80) != 0);
ModifiableBoolean* pbool = m_vbuttonObject[index]->GetValue();
if (bDown != pbool->GetValue()) {
pbool->SetValue(bDown);
ButtonChanged(index, bDown);
}
}
}
void Update()
{
//ZDebugOutput("Mouse Update\n");
if (m_bEnabled) {
//ZDebugOutput("Mouse Enabled\n");
HRESULT hr = m_pdid->Acquire();
//
// We have to handle the case where another app has captured the
// mouse, since we may have been switched out.
//
if (hr != DIERR_OTHERAPPHASPRIO) {
DDCall(hr);
if (m_bBuffered) {
UpdateBuffered();
} else {
UpdatePolled();
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// MouseInputStream
//
//////////////////////////////////////////////////////////////////////////////
void SetClipRect(const Rect& rect)
{
m_rect = rect;
DoClip();
}
void SetPosition(const Point& point)
{
m_point = point;
DoClip();
}
void SetWheelPosition(float pos)
{
m_z = pos;
}
const Point& GetPosition()
{
return m_point;
}
void SetEnabled(bool bEnabled)
{
if (m_bEnabled != bEnabled) {
m_bEnabled = bEnabled;
if (m_bEnabled) {
//DDCall(m_pdid->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND));
} else {
//DDCall(m_pdid->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND));
DDCall(m_pdid->Unacquire());
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// InputStream
//
//////////////////////////////////////////////////////////////////////////////
int GetValueCount()
{
return m_vvalueObject.GetCount();
}
int GetButtonCount()
{
return m_vbuttonObject.GetCount();
}
Boolean* IsDown(int id)
{
if (id < m_vbuttonObject.GetCount()) {
return m_vbuttonObject[id]->GetValue();
} else {
return NULL;
}
}
Number* GetValue(int id)
{
if (id < m_vvalueObject.GetCount()) {
return m_vvalueObject[id]->GetValue();
} else {
return NULL;
}
}
ButtonEvent::Source* GetEventSource()
{
return m_pbuttonEventSource;
}
};
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
class JoystickInputStreamImpl : public JoystickInputStream {
private:
// TRef<IDirectInputDevice2> m_pdid;
TRef<IDirectInputDevice7> m_pdid; // mdvalley: DInput7
DIDeviceCaps m_didc;
DIDeviceInstance m_didi;
TVector<TRef<ValueDDInputObject > > m_vvalueObject;
TVector<TRef<ButtonDDInputObject> > m_vbuttonObject;
BYTE* m_pbyteData;
DWORD m_sizeData;
bool m_bFocus;
TRef<IDirectInputEffect> m_peffectBounce;
TRef<IDirectInputEffect> m_peffectFire;
TRef<IDirectInputEffect> m_peffectExplode;
public:
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
BOOL EnumObjectsCallback(
LPCDIDEVICEOBJECTINSTANCE pddoi
) {
if (
pddoi->dwType & DIDFT_AXIS
|| pddoi->dwType & DIDFT_POV
) {
int index;
if (pddoi->guidType == GUID_XAxis ) {
index = 0;
} else if (pddoi->guidType == GUID_YAxis ) {
index = 1;
} else if (pddoi->guidType == GUID_Slider) {
index = 2;
} else if (pddoi->guidType == GUID_RzAxis) {
index = 3;
} else if (pddoi->guidType == GUID_POV ) {
index = 4;
} else {
index = -1;
}
ValueDDInputObject* pobject =
new ValueDDInputObject(
pddoi->tszName,
pddoi->dwType,
pddoi->guidType
);
if (index == -1) {
m_vvalueObject.PushEnd(pobject);
} else {
m_vvalueObject.Set(index, pobject);
}
} else if (pddoi->dwType & DIDFT_PSHBUTTON) {
ButtonDDInputObject* pobject =
new ButtonDDInputObject(
pddoi->tszName,
pddoi->dwType,
pddoi->guidType
);
m_vbuttonObject.PushEnd(pobject);
}
return DIENUM_CONTINUE;
}
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
static BOOL CALLBACK StaticEnumObjectsCallback(
LPCDIDEVICEOBJECTINSTANCE lpddoi,
LPVOID pvRef
) {
JoystickInputStreamImpl* pthis = (JoystickInputStreamImpl*)pvRef;
return pthis->EnumObjectsCallback(lpddoi);
}
//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
// JoystickInputStreamImpl(IDirectInputDevice2* pdid, HWND hwnd) :
JoystickInputStreamImpl(IDirectInputDevice7* pdid, HWND hwnd) : // mdvalley: DInput7
m_pdid(pdid),
m_bFocus(false),
m_vvalueObject(5)
{
DDCall(m_pdid->GetCapabilities(&m_didc));
DDCall(m_pdid->GetDeviceInfo(&m_didi));
//
// Enumerate the buttons and values
//
DDCall(m_pdid->EnumObjects(StaticEnumObjectsCallback, this, DIDFT_ALL));
//
// Remove any holes in the value vector
//
int index;
int countValues = m_vvalueObject.GetCount();
index = 0;
while (index < countValues) {
if (m_vvalueObject[index] == NULL) {
if (index != countValues - 1) {
m_vvalueObject.Set(index, m_vvalueObject[countValues - 1]);
}
countValues--;
m_vvalueObject.SetCount(countValues);
} else {
index++;
}
}
//
// Build the data format
//
int countButtons = m_vbuttonObject.GetCount();
m_sizeData = NextMultipleOf(4, countValues * 4 + countButtons * 1);
DIDataFormat didf;
didf.dwObjSize = sizeof(DIOBJECTDATAFORMAT);
didf.dwFlags = 0;
didf.dwDataSize = m_sizeData;
didf.dwNumObjs = countValues + countButtons;
DIObjectDataFormat* pdiodf = new DIObjectDataFormat[didf.dwNumObjs];
didf.rgodf = pdiodf;
for (index = 0; index < countValues; index++) {
ValueDDInputObject* pobject = m_vvalueObject[index];
DIOBJECTDATAFORMAT& diodf = didf.rgodf[index];
diodf.pguid = (GUID*)&(pobject->GetGUID());
diodf.dwOfs = index * 4;
diodf.dwType = pobject->GetDWType();
diodf.dwFlags = DIDOI_ASPECTPOSITION;
}
for (index = 0; index < countButtons; index++) {
ButtonDDInputObject* pobject = m_vbuttonObject[index];
DIOBJECTDATAFORMAT& diodf = didf.rgodf[countValues + index];
diodf.pguid = (GUID*)&(pobject->GetGUID());
diodf.dwOfs = countValues * 4 + index;
diodf.dwType = pobject->GetDWType();
diodf.dwFlags = DIDOI_ASPECTPOSITION;
}
DDCall(m_pdid->SetDataFormat(&didf));
delete pdiodf;
//
// Allocate a data receptical
//
m_pbyteData = new BYTE[m_sizeData];
//
// We only need joystick input when we are in the foreground
//
DDCall(m_pdid->SetCooperativeLevel(hwnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND));
//
// Set ranges
//
SetRanges();
}
~JoystickInputStreamImpl()
{
delete m_pbyteData;
}
//////////////////////////////////////////////////////////////////////////////
//
// Implementation methods
//
//////////////////////////////////////////////////////////////////////////////
void SetRanges()
{
m_pdid->Unacquire();
int countValues = m_vvalueObject.GetCount();
for (int index = 0; index < countValues; index++) {
if (m_vvalueObject[index] != NULL) {
if ((m_vvalueObject[index]->GetDWType() & DIDFT_POV) == 0) {
DIPROPRANGE dipr;
dipr.diph.dwSize = sizeof(dipr);
dipr.diph.dwHeaderSize = sizeof(dipr.diph);
dipr.diph.dwHow = DIPH_BYID;
dipr.diph.dwObj = m_vvalueObject[index]->GetDWType();
dipr.lMin = -100000;
dipr.lMax = 100000;
DDCall(m_pdid->SetProperty(DIPROP_RANGE, &dipr.diph));
}
//
//
//
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
DDCall(m_pdid->GetProperty(DIPROP_BUFFERSIZE, &dipdw.diph));
}
}
}
void Update()
{
HRESULT hr = m_pdid->Acquire();
//
// We have to handle the case where another app has captured the
// joystick, since we may be switched out.
// Or the joystick may have been unplugged.
//
if (SUCCEEDED(hr)) {
//
// Poll the device
//
m_pdid->Poll();
//
// The MS Gamepad will return error from this funtion if the mode button is pressed
// Don't assert in that case. Just don't unpack the data.
//
hr = m_pdid->GetDeviceState(m_sizeData, m_pbyteData);
if (SUCCEEDED(hr)) {
//
// Unpack the data
//
int countValues = m_vvalueObject.GetCount();
int countButtons = m_vbuttonObject.GetCount();
for (int index = 0; index < countValues; index++) {
if ((m_vvalueObject[index]->GetDWType() & DIDFT_POV) != 0) {
int ivalue = ((int*)m_pbyteData)[index];
float value;
if (ivalue == -1) {
value = -2;
} else if (ivalue > 18000) {
value = float(ivalue - 36000) / 18000;
} else {
value = float(ivalue) / 18000;
}
//ZDebugOutput("RawHat: " + ZString(ivalue) + ", " + ZString(value) + "\n");
m_vvalueObject[index]->GetValue()->SetValue(value);
} else {
float value = float(((int*)m_pbyteData)[index]) / 100000;
value = bound(value, -1.0f, 1.0f);
m_vvalueObject[index]->GetValue()->SetValue(value);
}
}
for (int index = 0; index < countButtons; index++) {
m_vbuttonObject[index]->GetValue()->SetValue(
m_pbyteData[countValues * 4 + index] != 0
);
}
}
}
}
void SetFocus(bool bFocus)
{
if (m_bFocus != bFocus) {
m_bFocus = bFocus;
if (m_bFocus) {
//
// Aquire the joystick
//
// !!! this doesn't work. I guess that at the time when this gets called
// DInput doesn't think we are foreground yet.
// DDCall(m_pdid->Acquire());
} else {
// !!! this gets called automatically
// DDCall(m_pdid->Unacquire());
}
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Force Feedback
//
//////////////////////////////////////////////////////////////////////////////
static BOOL CALLBACK EnumEffectTypeProc(LPCDIEFFECTINFO pei, LPVOID pv)
{
GUID *pguidEffect = NULL;
// report back the guid of the effect we enumerated
if (pv) {
pguidEffect = (GUID *)pv;
*pguidEffect = pei->guid;
}
// BUGBUG - look at this some more....
return DIENUM_STOP;
}
void CreateEffects()
{
GUID guidEffect;
DIEFFECT diEffect;
DIENVELOPE diEnvelope;
DWORD rgdwAxes[2];
LONG rglDirections[2];
DICONSTANTFORCE dicf;
DIPERIODIC dipf;
//
// initialize DIEFFECT and DIENVELOPE structures
//
ZeroMemory(&diEffect, sizeof(DIEFFECT));
ZeroMemory(&diEnvelope, sizeof(DIENVELOPE));
//
// these fields are the same for all effects we will be creating
//
diEffect.dwSize = sizeof(DIEFFECT);
diEffect.dwSamplePeriod = 0; // use default sample period
diEffect.dwTriggerButton = DIEB_NOTRIGGER;
diEffect.dwTriggerRepeatInterval = 0;
diEffect.rgdwAxes = rgdwAxes;
diEffect.rglDirection = rglDirections;
diEffect.dwGain = 7500; // todo: gain selected by user
//
// both the "bounce" and "fire" effects will be based on the first
// constant force effect enumerated
//
//
// don't check for errors. DInput sometimes return error, but then works anyway.
//
//DDCall(
m_pdid->EnumEffects(
(LPDIENUMEFFECTSCALLBACK)EnumEffectTypeProc,
&guidEffect,
DIEFT_CONSTANTFORCE
//)
);
//
// Create the bounce effect
//
dicf.lMagnitude = 10000;
rgdwAxes[0] = DIJOFS_X;
rgdwAxes[1] = DIJOFS_Y;
rglDirections[0] = 0;
rglDirections[1] = 0;
diEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
diEffect.dwDuration = 200000;
diEffect.cAxes = 2;
diEffect.lpEnvelope = NULL;
diEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
diEffect.lpvTypeSpecificParams = &dicf;
//DDCall(
m_pdid->CreateEffect(
guidEffect,
&diEffect,
&m_peffectBounce,
NULL
//)
);
//
// Create the fire effect
//
dicf.lMagnitude = 10000;
rgdwAxes[0] = DIJOFS_Y;
rglDirections[0] = 1;
diEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN;
diEffect.dwDuration = 20000;
diEffect.cAxes = 1;
diEffect.lpEnvelope = NULL;
diEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
diEffect.lpvTypeSpecificParams = &dicf;
//DDCall(
m_pdid->CreateEffect(
guidEffect,
&diEffect,
&m_peffectFire,
NULL
//)
);
//
// the "explode" effect will be based on the first
// periodic effect enumerated
//
//DDCall(
m_pdid->EnumEffects(
(LPDIENUMEFFECTSCALLBACK)EnumEffectTypeProc,
&guidEffect,
DIEFT_PERIODIC
//)
);
//
// Create the explode effect.
// We want to shape the explode effect so that it starts
// at it's peak and then fades out
//
diEnvelope.dwSize = sizeof(DIENVELOPE);
diEnvelope.dwAttackLevel = 0;
diEnvelope.dwAttackTime = 0;
diEnvelope.dwFadeLevel = 0;
diEnvelope.dwFadeTime = 1000000;
dipf.dwMagnitude = 10000;
dipf.lOffset = 0;
dipf.dwPhase = 0;
dipf.dwPeriod = 100000;
rgdwAxes[0] = DIJOFS_X;
rglDirections[0] = 0;
diEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN;
diEffect.dwDuration = 1000000;
diEffect.cAxes = 1;
diEffect.lpEnvelope = &diEnvelope;
diEffect.cbTypeSpecificParams = sizeof(DIPERIODIC);
diEffect.lpvTypeSpecificParams = &dipf;
//DDCall(
m_pdid->CreateEffect(
guidEffect,
&diEffect,
&m_peffectExplode,
NULL
//)
);
}
void PlayFFEffect(short effectID, LONG lDirection)
{
//
// !!! Don't check for errors here. Sometimes these functions will return error
//
switch (effectID) {
case 0:
if (m_peffectFire) {
m_peffectFire->Start(1, 0);
}
break;
case 1:
if (m_peffectBounce) {
DIEFFECT diEffect;
LONG rglDirections[2] = { 0, 0 };
ZeroMemory(&diEffect, sizeof(DIEFFECT));
diEffect.dwSize = sizeof(DIEFFECT);
rglDirections[0] = lDirection * 100;
diEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR;
diEffect.cAxes = 2;
diEffect.rglDirection = rglDirections;
m_peffectBounce->SetParameters(&diEffect, DIEP_DIRECTION);
m_peffectBounce->Start(1, 0);
}
break;
case 2:
if (m_peffectExplode) {
m_peffectExplode->Start(1, 0);
}
break;
}
}
//////////////////////////////////////////////////////////////////////////////
//
// JoystickInputStream methods
//
//////////////////////////////////////////////////////////////////////////////
bool HasForceFeedback()
{
return (m_didc.dwFlags & DIDC_FORCEFEEDBACK) != 0;
}
ZString GetShortDescription(int index)
{
return "Joy " + ZString(index);
}
ZString GetDescription()
{
return m_didi.tszInstanceName;
}
ZString GetValueDescription(int id)
{
return m_vvalueObject[id]->GetName();
}
//////////////////////////////////////////////////////////////////////////////
//
// InputStream methods
//
//////////////////////////////////////////////////////////////////////////////
int GetValueCount()
{
return m_vvalueObject.GetCount();
}
int GetButtonCount()
{
return m_vbuttonObject.GetCount();
}
Boolean* IsDown(int id)
{
if (id < m_vbuttonObject.GetCount()) {
return m_vbuttonObject[id]->GetValue();
} else {
return NULL;
}
}
Number* GetValue(int id)
{
if (id < m_vvalueObject.GetCount()) {
return m_vvalueObject[id]->GetValue();
} else {
return NULL;
}
}
ButtonEvent::Source* GetEventSource()
{
ZUnimplemented();
return NULL;
}
};
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
class InputEngineImpl : public InputEngine {
private:
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
bool EnumDeviceCallback(LPDIDEVICEINSTANCE pdidi)
{
TRef<IDirectInputDevice> pdid;
// TRef<IDirectInputDevice2> pdid2;
TRef<IDirectInputDevice7> pdid2; // mdvalley: DInput7
DDCall(m_pdi->CreateDevice( pdidi->guidInstance, &pdid, NULL));
// DDCall(pdid->QueryInterface(IID_IDirectInputDevice2, (void**)&pdid2));
DDCall(pdid->QueryInterface(IID_IDirectInputDevice7, (void**)&pdid2));
switch (pdidi->dwDevType & 0xff) {
case DIDEVTYPE_MOUSE:
{
if (m_pmouseInputStream == NULL) {
m_pmouseInputStream = new MouseInputStreamImpl(pdid2, m_hwnd);
}
}
break;
case DIDEVTYPE_JOYSTICK:
{
TRef<JoystickInputStreamImpl> pjoystickInputStream =
new JoystickInputStreamImpl(pdid2, m_hwnd);
m_vjoystickInputStream.PushEnd(pjoystickInputStream);
}
break;
}
return DIENUM_CONTINUE;
}
//////////////////////////////////////////////////////////////////////////////
//
//
//
//////////////////////////////////////////////////////////////////////////////
static BOOL CALLBACK StaticEnumDeviceCallback(LPDIDEVICEINSTANCE pdidi, LPVOID pv)
{
InputEngineImpl* pthis = (InputEngineImpl*)pv;
return pthis->EnumDeviceCallback(pdidi);
}
//////////////////////////////////////////////////////////////////////////////
//
// data members
//
//////////////////////////////////////////////////////////////////////////////
HWND m_hwnd;
bool m_bFocus;
TRef<IDirectInput> m_pdi;
TVector<TRef<JoystickInputStreamImpl> > m_vjoystickInputStream;
TRef<MouseInputStreamImpl> m_pmouseInputStream;
HINSTANCE m_hdinput;
public:
//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
typedef HRESULT (WINAPI *PFNDirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter);
InputEngineImpl(HWND hwnd) :
m_hwnd(hwnd),
m_bFocus(false)
{
//
// Create the direct input object
//
#ifdef Dynamic_DInput
m_hdinput = ::LoadLibrary("dinput.dll");
ZAssert(m_hdinput != NULL);
PFNDirectInputCreate pfn = (PFNDirectInputCreate)::GetProcAddress(m_hdinput, "DirectInputCreateA");
ZAssert(pfn != NULL);
DDCall(pfn(
GetModuleHandle(NULL),
DIRECTINPUT_VERSION,
&m_pdi,
NULL
));
//
// grab the address of a few dinput globals
//
// g_pdfDIMouse = (DIDATAFORMAT*)::GetProcAddress(m_hdinput, "c_dfDIMouse");
g_pdfDIMouse = (DIDATAFORMAT*)::GetProcAddress(m_hdinput, "c_dfDIMouse2"); // mdvalley: Mouse2 for more buttons
ZAssert(g_pdfDIMouse != NULL);
#else
DDCall(DirectInputCreate(
GetModuleHandle(NULL),
DIRECTINPUT_VERSION,
&m_pdi,
NULL
));
// g_pdfDIMouse = &c_dfDIMouse;
g_pdfDIMouse = &c_dfDIMouse2; // mdvalley: Mouse2 for more buttons
#endif
//
// If we failed then exit the app
//
if (m_pdi == NULL) {
::MessageBox(NULL, "Error initializing DirectInput. Check your installation", "Error", MB_OK);
_exit(0);
}
//
// Enumerate the devices
//
EnumerateJoysticks();
}
//////////////////////////////////////////////////////////////////////////////
//
// InputEngine methods
//
//////////////////////////////////////////////////////////////////////////////
void EnumerateJoysticks()
{
//
// Free up the old devices
//
m_vjoystickInputStream.SetEmpty();
//
// Enumerate all of the devices
//
DDCall(m_pdi->EnumDevices(
0,//DIDEVTYPE_JOYSTICK,
(LPDIENUMDEVICESCALLBACK)StaticEnumDeviceCallback,
this,
DIEDFL_ATTACHEDONLY
));
}
int GetJoystickCount()
{
return m_vjoystickInputStream.GetCount();
}
JoystickInputStream* GetJoystick(int index)
{
if (
index >= 0
&& index < m_vjoystickInputStream.GetCount()
) {
return m_vjoystickInputStream[index];
} else {
return NULL;
}
}
MouseInputStream* GetMouse()
{
return m_pmouseInputStream;
}
void Update()
{
if (m_bFocus) {
m_pmouseInputStream->Update();
int count = m_vjoystickInputStream.GetCount();
for(int index = 0; index < count; index++) {
m_vjoystickInputStream[index]->Update();
}
}
}
void SetFocus(bool bFocus)
{
if (m_bFocus != bFocus) {
m_bFocus = bFocus;
int count = m_vjoystickInputStream.GetCount();
for(int index = 0; index < count; index++) {
m_vjoystickInputStream[index]->SetFocus(m_bFocus);
}
}
}
};
TRef<InputEngine> CreateInputEngine(HWND hwnd)
{
return new InputEngineImpl(hwnd);
}