class QDXJoy | .h |
constructor | QDXJoy(int _n) Creates an interface to joystick device #n (n=index of attached controllers) |
destructor | ~QDXJoy() |
Flat functions |
EnumCallback | BOOL CALLBACK EnumCallback(const DIDEVICEINSTANCE *pdidInstance,void *ctx) Called when a joystick is found in QDXJoy ctor |
qlog | qlog(QLOG_INFO,"joy %p found",joy); // Get an interface to the enumerated joystick hr=qDXInput->GetPDI()->CreateDeviceEx(pdidInstance->guidInstance, IID_IDirectInputDevice2,(void**)&dxJoyLocal,NULL); // This may fail, for example, if the user just plugs out the controller if(FAILED(hr)) return DIENUM_CONTINUE; // Store in class joy->_SetInputDevice(dxJoyLocal); // We're done return DIENUM_STOP; } BOOL CALLBACK EnumAxesCallback(const DIDEVICEOBJECTINSTANCE *pdidoi,void *ctx) Called for every axis on the controller |
qlog | qlog(QLOG_INFO,"QDXJoy; axis %d, range %d-%d\n",pdidoi->dwOfs,diprg.lMin,diprg.lMax); return DIENUM_CONTINUE; } |
class QDXJoy | .h |
_SetInputDevice | void _SetInputDevice(LPDIRECTINPUTDEVICE2 joy) Used in creating the joystick device (called from EnumCallback) |
IsOpen | bool IsOpen() Returns TRUE if a joystick device has been opened |
Open | bool Open() Open the joystick |
Flat functions |
qlog(QLOG_INFO,"Joystick found, FF=%d\n",IsForceFeedbac | qlog(QLOG_INFO,"Joystick found, FF=%d\n",IsForceFeedback()); |
class QDXJoy | .h |
Close | void Close() Close the joystick device |
Acquire | bool Acquire() Acquire the joystick for use |
Unacquire | bool Unacquire() Unacquire the joystick; somebody else may have it |
Poll | void Poll() |
IsForceFeedback | bool IsForceFeedback() Returns TRUE if the joystick is capable of force feedback |
DisableAutoCenter | bool DisableAutoCenter() Turn off auto-centering for joystick Returns TRUE if succesful |
EnableAutoCenter | bool EnableAutoCenter() Turn on auto-centering for joystick Returns TRUE if succesful |
class QDXFFEffect | .h |
constructor | QDXFFEffect(QDXJoy *joy) |
destructor | ~QDXFFEffect() |
Start | void Start() |
Stop | void Stop() |
Release | void Release() |
SetupConstantForce | bool SetupConstantForce() Create an effect for a constant force. Returns TRUE if ok. |
UpdateConstantForce | bool UpdateConstantForce(int fx,int fy) Update the constant force effect |
/*
* QDXJoy - a DirectX joystick
* 04-09-00: Created! (Win32) (heavily based on the JoyStImm MS SDK sample)
* NOTES:
* - Perhaps QJoystick would be a better name, but this is tied to DirectX
* and a more general joystick class might be needed above this one.
* - Only attached joysticks will be found
* BUGS:
* - Always finds and uses FIRST joystick, instead of searching for #n
* - Expects a QSHELL to exist (for exclusive access when our window is current)
* (C) MarketGraph/Ruud van Gaal
*/
#include <qlib/dxjoy.h>
#include <qlib/app.h>
#include <math.h>
#include <qlib/debug.h>
DEBUG_ENABLE
/************
* JOYSTICKS *
************/
QDXJoy::QDXJoy(int _n)
// Creates an interface to joystick device #n (n=index of attached controllers)
{
int i;
qdbg("QDXJoy ctor\n");
x=y=z=0;
rx=ry=rz=0;
for(i=0;i<MAX_BUTTON;i++)
button[i]=0;
#ifdef WIN32
dxJoy=0;
memset(&diDevCaps,0,sizeof(diDevCaps));
n=_n;
// Make sure DirectInput is up
QDXINPUT_OPEN;
if(!qDXInput->IsOpen())return;
Open();
#endif
}
QDXJoy::~QDXJoy()
{
Close();
}
#ifdef WIN32
BOOL CALLBACK EnumCallback(const DIDEVICEINSTANCE *pdidInstance,void *ctx)
// Called when a joystick is found in QDXJoy ctor
{
QDXJoy *joy;
HRESULT hr;
LPDIRECTINPUTDEVICE2 dxJoyLocal;
joy=(QDXJoy*)ctx;
qlog(QLOG_INFO,"joy %p found",joy);
// Get an interface to the enumerated joystick
hr=qDXInput->GetPDI()->CreateDeviceEx(pdidInstance->guidInstance,
IID_IDirectInputDevice2,(void**)&dxJoyLocal,NULL);
// This may fail, for example, if the user just plugs out the controller
if(FAILED(hr))
return DIENUM_CONTINUE;
// Store in class
joy->_SetInputDevice(dxJoyLocal);
// We're done
return DIENUM_STOP;
}
BOOL CALLBACK EnumAxesCallback(const DIDEVICEOBJECTINSTANCE *pdidoi,void *ctx)
// Called for every axis on the controller
{
QDXJoy *joy;
joy=(QDXJoy*)ctx;
DIPROPRANGE diprg;
diprg.diph.dwSize=sizeof(DIPROPRANGE);
diprg.diph.dwHeaderSize=sizeof(DIPROPHEADER);
diprg.diph.dwHow=DIPH_BYOFFSET;
diprg.diph.dwObj=pdidoi->dwOfs; // Specify the enumerated axis
diprg.lMin=-1000;
diprg.lMax=1000;
// Set range for the axis
if(FAILED(joy->GetDXJoy()->SetProperty(DIPROP_RANGE,&diprg.diph)))
{ qwarn("QDXJoy: Can't set axis range (axis=%d)",pdidoi->dwOfs);
return DIENUM_STOP;
}
if(FAILED(joy->GetDXJoy()->GetProperty(DIPROP_RANGE,&diprg.diph)))
{
qwarn("QDXJoy: Can't get axis range (axis=%d)",pdidoi->dwOfs);
return DIENUM_STOP;
}
qlog(QLOG_INFO,"QDXJoy; axis %d, range %d-%d\n",pdidoi->dwOfs,diprg.lMin,diprg.lMax);
return DIENUM_CONTINUE;
}
void QDXJoy::_SetInputDevice(LPDIRECTINPUTDEVICE2 joy)
// Used in creating the joystick device (called from EnumCallback)
{
dxJoy=joy;
}
#endif
/**********
* ATTRIBS *
**********/
bool QDXJoy::IsOpen()
// Returns TRUE if a joystick device has been opened
{
#ifdef WIN32
if(dxJoy)return TRUE;
#endif
return FALSE;
}
bool QDXJoy::Open()
// Open the joystick
{
#ifdef WIN32
// Look for a joystick (that is currently attached)
HRESULT hr;
hr=qDXInput->GetPDI()->EnumDevices(DIDEVTYPE_JOYSTICK,EnumCallback,this,
DIEDFL_ATTACHEDONLY /*|DIEDFL_FORCEFEEDBACK*/);
if(FAILED(hr)||dxJoy==0)
{ qwarn("QDXJoy: can't create/find joystick #%d",n);
return FALSE;
}
// Set data format to "simple joystick"
hr=dxJoy->SetDataFormat(&c_dfDIJoystick);
if(FAILED(hr))
{ qwarn("QDXJoy: can't set data format for joystick #%d",n);
return FALSE;
}
// Set cooperative level
// Currently we want exclusive access for our main window
hr=dxJoy->SetCooperativeLevel(QSHELL->GetQXWindow()->GetHWND(),
//DISCL_EXCLUSIVE|DISCL_BACKGROUND);
DISCL_EXCLUSIVE|DISCL_FOREGROUND);
if(FAILED(hr))
{ qwarn("QDXJoy: can't set cooperative level for joystick #%d (%s)",
n,qDXInput->Err2Str(hr));
qlog(QLOG_INFO,"hdc=0x%x, err_invalidparam=%x, notinitialized=%x, e_handle=%x\n",
QSHELL->GetQXWindow()->GetHDC(),DIERR_INVALIDPARAM,DIERR_NOTINITIALIZED,E_HANDLE);
return FALSE;
} else
{
qlog(QLOG_INFO,"hr=%x (%s)\n",hr,qDXInput->Err2Str(hr));
}
// Determine how many axes the joystick has
diDevCaps.dwSize=sizeof(DIDEVCAPS);
hr=dxJoy->GetCapabilities(&diDevCaps);
if(FAILED(hr))
{ qwarn("QDXJoy: can't get capabilities for joystick #%d",n);
return FALSE;
}
qlog(QLOG_INFO,"Joystick found, FF=%d\n",IsForceFeedback());
// Enumerate the axes and set the range of all axes to match something decent
dxJoy->EnumObjects(EnumAxesCallback,(void*)this,DIDFT_AXIS);
#endif
return TRUE;
}
void QDXJoy::Close()
// Close the joystick device
{
#ifdef WIN32
if(dxJoy)
{
Unacquire();
dxJoy->Release();
dxJoy=0;
}
#endif
}
/**********
* ACQUIRE *
**********/
bool QDXJoy::Acquire()
// Acquire the joystick for use
{
#ifdef WIN32
if(dxJoy)
{ dxJoy->Acquire();
} else return FALSE;
#endif
return TRUE;
}
bool QDXJoy::Unacquire()
// Unacquire the joystick; somebody else may have it
{
#ifdef WIN32
if(dxJoy)
{ dxJoy->Unacquire();
} else return FALSE;
#endif
return TRUE;
}
/**********
* POLLING *
**********/
void QDXJoy::Poll()
{
#ifdef WIN32
DIJOYSTATE js;
HRESULT hr;
if(!dxJoy)return;
// Hm, does this ever end the loop?
do
{
hr=dxJoy->Poll();
if(FAILED(hr))
{
static int count;
// Don't overdue error reporting
if(count<5)
{ qwarn("QDXJoy:Poll(); can't poll controller (%s)",qDXInput->Err2Str(hr));
if(++count==5)
qwarn("QDXJoy: too many poll failures; no more reporting");
}
//return;
}
// Get the device's state
hr=dxJoy->GetDeviceState(sizeof(DIJOYSTATE),&js);
if(hr==DIERR_INPUTLOST)
{
// Input stream has been interrupted
// Reacquire and try to move on
hr=dxJoy->Acquire();
if(FAILED(hr))
{ qwarn("QDXJoy:Poll(); input lost and can't reacquire, err=%x",hr);
return;
}
}
} while(hr==DIERR_INPUTLOST);
if(FAILED(hr))
{
qwarn("QDXJoy: Can't poll controller");
return;
}
// Take over joystick state
x=js.lX;
y=js.lY;
z=js.lZ;
rx=js.lRx;
ry=js.lRy;
rz=js.lRz;
for(int i=0;i<MAX_BUTTON;i++)
button[i]=(js.rgbButtons[i]&0x80)>>7;
// There are still 2 axes and the POV which we may use...
#endif
}
/*************************
* FORCE FEEDBACK SUPPORT *
*************************/
bool QDXJoy::IsForceFeedback()
// Returns TRUE if the joystick is capable of force feedback
{
#ifndef WIN32
return FALSE;
#else
if(!dxJoy)return FALSE;
// Capabilities are already reported in diDevCaps;
if(diDevCaps.dwFlags&DIDC_FORCEFEEDBACK)
return TRUE;
return FALSE;
#endif
}
bool QDXJoy::DisableAutoCenter()
// Turn off auto-centering for joystick
// Returns TRUE if succesful
{
#ifdef WIN32
DIPROPDWORD dw;
HRESULT hr;
dw.diph.dwSize=sizeof(DIPROPDWORD);
dw.diph.dwHeaderSize=sizeof(DIPROPHEADER);
dw.diph.dwObj=0;
dw.diph.dwHow=DIPH_DEVICE;
dw.dwData=0; //DIPROPAUTOCENTER_OFF;
hr=dxJoy->SetProperty(DIPROP_AUTOCENTER,&dw.diph);
if(FAILED(hr))
{ qwarn("QDXJoy:DisableAutoCenter() failed (%s)",qDXInput->Err2Str(hr));
return FALSE;
}
return TRUE;
#else
return FALSE;
#endif
}
bool QDXJoy::EnableAutoCenter()
// Turn on auto-centering for joystick
// Returns TRUE if succesful
{
#ifdef WIN32
DIPROPDWORD dw;
HRESULT hr;
dw.diph.dwSize=sizeof(DIPROPDWORD);
dw.diph.dwHeaderSize=sizeof(DIPROPHEADER);
dw.diph.dwObj=0;
dw.diph.dwHow=DIPH_DEVICE;
dw.dwData=DIPROPAUTOCENTER_ON;
hr=dxJoy->SetProperty(DIPROP_AUTOCENTER,&dw.diph);
if(FAILED(hr))
{ qwarn("QDXJoy:DisableAutoCenter() failed (%s)",qDXInput->Err2Str(hr));
return FALSE;
}
return TRUE;
#else
return FALSE;
#endif
}
/*************************
* FORCE FEEDBACK EFFECTS *
*************************/
QDXFFEffect::QDXFFEffect(QDXJoy *joy)
{
#ifdef WIN32
dxJoy=joy;
pDIEffect=0;
#endif
}
QDXFFEffect::~QDXFFEffect()
{
#ifdef WIN32
Release();
#endif
}
void QDXFFEffect::Start()
{
#ifdef WIN32
if(pDIEffect)
{ HRESULT hr;
hr=pDIEffect->Start(1,0);
if(FAILED(hr))
{ qwarn("Can't start FF effect (%s)",qDXInput->Err2Str(hr));
}
}
#endif
}
void QDXFFEffect::Stop()
{
#ifdef WIN32
if(pDIEffect)
pDIEffect->Stop();
#endif
}
void QDXFFEffect::Release()
{
#ifdef WIN32
if(pDIEffect)
{ pDIEffect->Release();
pDIEffect=0;
}
#endif
}
bool QDXFFEffect::SetupConstantForce()
// Create an effect for a constant force. Returns TRUE if ok.
{
#ifdef WIN32
HRESULT hr;
DWORD rgdwAxes[2]={ DIJOFS_X,DIJOFS_Y };
LONG rglDirection[2]={ 0,0 };
ZeroMemory(&diEffect,sizeof(diEffect));
force.constantForce.lMagnitude=0;
//force.constantForce.lMagnitude=5000;
diEffect.dwSize=sizeof(DIEFFECT);
diEffect.dwFlags=DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS;
diEffect.dwDuration=INFINITE;
diEffect.dwSamplePeriod=0;
diEffect.dwGain=DI_FFNOMINALMAX;
diEffect.dwTriggerButton=DIEB_NOTRIGGER;
diEffect.dwTriggerRepeatInterval=0;
diEffect.cAxes=2;
diEffect.rgdwAxes=rgdwAxes;
diEffect.rglDirection=rglDirection;
diEffect.lpEnvelope=0;
diEffect.cbTypeSpecificParams=sizeof(DICONSTANTFORCE);
diEffect.lpvTypeSpecificParams=&force.constantForce;
diEffect.dwStartDelay=0;
hr=dxJoy->GetDXJoy()->CreateEffect(GUID_ConstantForce,&diEffect,&pDIEffect,0);
if(FAILED(hr))
{
qwarn("QDXFFEffect:SetupConstantForce() failed (%s)",qDXInput->Err2Str(hr));
return FALSE;
}
return TRUE;
#endif
}
bool QDXFFEffect::UpdateConstantForce(int fx,int fy)
// Update the constant force effect
{
#ifdef WIN32
HRESULT hr;
LONG rglDirection[2]={ fx,fy };
int len;
ZeroMemory(&diEffect,sizeof(diEffect));
len=sqrt((float)(fx*fx+fy*fy));
force.constantForce.lMagnitude=len;
diEffect.dwSize=sizeof(DIEFFECT);
diEffect.dwFlags=DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS;
diEffect.cAxes=2;
diEffect.rglDirection=rglDirection;
diEffect.lpEnvelope=0;
diEffect.cbTypeSpecificParams=sizeof(DICONSTANTFORCE);
diEffect.lpvTypeSpecificParams=&force.constantForce;
diEffect.dwStartDelay=0;
hr=pDIEffect->SetParameters(&diEffect,DIEP_DIRECTION|DIEP_TYPESPECIFICPARAMS|DIEP_START);
if(FAILED(hr))
{
qwarn("QDXFFEffect:UpdateConstantForce() failed (%s)",qDXInput->Err2Str(hr));
return FALSE;
}
return TRUE;
#endif
}