mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-11-24 08:06:13 +00:00
523 lines
15 KiB
C++
523 lines
15 KiB
C++
|
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
||
|
// Copyright (C) 2010 Winch Gate Property Limited
|
||
|
//
|
||
|
// This program is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Affero General Public License as
|
||
|
// published by the Free Software Foundation, either version 3 of the
|
||
|
// License, or (at your option) any later version.
|
||
|
//
|
||
|
// This program is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU Affero General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Affero General Public License
|
||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
|
||
|
#include "stdmisc.h"
|
||
|
#include "di_game_device.h"
|
||
|
#include "nel/misc/game_device_events.h"
|
||
|
#include <cstddef>
|
||
|
#include <memory>
|
||
|
#include <algorithm>
|
||
|
|
||
|
#ifdef NL_OS_WINDOWS
|
||
|
|
||
|
namespace NLMISC
|
||
|
{
|
||
|
|
||
|
//============================================================================
|
||
|
CDIGameDevice::CDIGameDevice() : _Device(NULL)
|
||
|
{
|
||
|
::memset(&_CurrentState, 0, sizeof(_CurrentState));
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
CDIGameDevice::~CDIGameDevice()
|
||
|
{
|
||
|
if (_Device)
|
||
|
{
|
||
|
_Device->Unacquire();
|
||
|
_Device->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
CDIGameDevice *CDIGameDevice::createGameDevice(IDirectInput8 *di8,
|
||
|
HWND hwnd,
|
||
|
CDIEventEmitter *diEventEmitter,
|
||
|
const CGameDeviceDesc &desc,
|
||
|
REFGUID rguid) throw(EDirectInput)
|
||
|
{
|
||
|
nlassert(diEventEmitter);
|
||
|
nlassert(di8);
|
||
|
std::auto_ptr<CDIGameDevice> dev(new CDIGameDevice);
|
||
|
//
|
||
|
|
||
|
HRESULT r = di8->CreateDevice(rguid, &dev->_Device, NULL);
|
||
|
if (r != DI_OK) throw EDirectInputGameDeviceNotCreated();
|
||
|
|
||
|
r = dev->_Device->SetDataFormat(pJoyDataFormat);
|
||
|
nlassert(r == DI_OK);
|
||
|
//
|
||
|
r = dev->_Device->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_EXCLUSIVE);
|
||
|
if (r != DI_OK) throw EDirectInputCooperativeLevelFailed();
|
||
|
//
|
||
|
//
|
||
|
dev->_Desc = desc;
|
||
|
dev->_EventEmitter = diEventEmitter;
|
||
|
dev->querryControls();
|
||
|
return dev.release();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
void CDIGameDevice::begin(CEventServer *server)
|
||
|
{
|
||
|
nlassert(_Device);
|
||
|
HRESULT r;
|
||
|
r = _Device->Poll();
|
||
|
if (r == DIERR_INPUTLOST || r == DIERR_NOTACQUIRED)
|
||
|
{
|
||
|
r = _Device->Acquire();
|
||
|
if (r != DI_OK) return;
|
||
|
r = _Device->Poll();
|
||
|
if (r != DI_OK) return;
|
||
|
}
|
||
|
|
||
|
CDIJoyState newState;
|
||
|
r = _Device->GetDeviceState(sizeof(CDIJoyState), &newState);
|
||
|
if (r != DI_OK) return;
|
||
|
|
||
|
uint k;
|
||
|
//////////
|
||
|
// Axis //
|
||
|
//////////
|
||
|
for (k = 0; k < MaxNumAxis; ++k)
|
||
|
{
|
||
|
CAxis &axis = _Axis[k];
|
||
|
if (axis.Present)
|
||
|
{
|
||
|
|
||
|
if (((LONG *) &newState)[k] != ((LONG *) &_CurrentState)[k]) // state changed ?
|
||
|
{
|
||
|
// update position
|
||
|
axis.Value = 2.f * (((LONG *) &newState)[k] - axis.Min) / (float) (axis.Max - axis.Min) - 1.f;
|
||
|
// create event
|
||
|
CGDAxisMoved *event = new CGDAxisMoved((IGameDevice::TAxis) k, axis.Value, this, _EventEmitter);
|
||
|
// update state
|
||
|
((LONG *) &_CurrentState)[k] = ((LONG *) &newState)[k];
|
||
|
//
|
||
|
server->postEvent(event);
|
||
|
//
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////
|
||
|
// Buttons //
|
||
|
/////////////
|
||
|
for (k = 0; k < _Buttons.size(); ++k)
|
||
|
{
|
||
|
CButton &bt = _Buttons[k];
|
||
|
if ((newState.rgbButtons[k] & 0x80) != (_CurrentState.rgbButtons[k] & 0x80))
|
||
|
{
|
||
|
bool pushed = (newState.rgbButtons[k] & 0x80) != 0;
|
||
|
// update the state of the button
|
||
|
bt.Pushed = pushed;
|
||
|
CGDButton *event;
|
||
|
if (pushed) event = new CGDButtonDown(k, this, _EventEmitter);
|
||
|
else event = new CGDButtonUp(k, this, _EventEmitter);
|
||
|
// update state
|
||
|
_CurrentState.rgbButtons[k] = newState.rgbButtons[k];
|
||
|
server->postEvent(event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////
|
||
|
// Sliders //
|
||
|
/////////////
|
||
|
for (k = 0; k < _Sliders.size(); ++k)
|
||
|
{
|
||
|
CSlider &sl = _Sliders[k];
|
||
|
if (newState.rglSlider[k] != _CurrentState.rglSlider[k]) // state changed ?
|
||
|
{
|
||
|
// update position
|
||
|
sl.Pos = ( newState.rglSlider[k] - sl.Min) / (float) (sl.Max - sl.Min);
|
||
|
// create event
|
||
|
CGDSliderMoved *event = new CGDSliderMoved(sl.Pos, k, this, _EventEmitter);
|
||
|
// update state
|
||
|
_CurrentState.rglSlider[k] = newState.rglSlider[k];
|
||
|
//
|
||
|
server->postEvent(event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////
|
||
|
// POVs //
|
||
|
//////////
|
||
|
for (k = 0; k < _POVs.size(); ++k)
|
||
|
{
|
||
|
CPOV &pov = _POVs[k];
|
||
|
if (newState.rgdwPOV[k] != _CurrentState.rgdwPOV[k]) // state changed ?
|
||
|
{
|
||
|
DWORD value = newState.rgdwPOV[k];
|
||
|
|
||
|
pov.Centered = (LOWORD(value) == 0xFFFF);
|
||
|
if (!pov.Centered)
|
||
|
{
|
||
|
// update position
|
||
|
pov.Angle = value / 100.f;
|
||
|
}
|
||
|
// create event
|
||
|
CGDPOVChanged *event = new CGDPOVChanged(pov.Centered, pov.Angle, k, this, _EventEmitter);
|
||
|
// update state
|
||
|
_CurrentState.rgdwPOV[k] = newState.rgdwPOV[k];
|
||
|
//
|
||
|
server->postEvent(event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
void CDIGameDevice::poll(CInputDeviceServer *dev)
|
||
|
{
|
||
|
// buffered datas not supported
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
void CDIGameDevice::submit(IInputDeviceEvent *deviceEvent, CEventServer *server)
|
||
|
{
|
||
|
// should never be called, buffered datas not supported
|
||
|
nlassert(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
//============================================================================
|
||
|
/** Tool fct : tests whether a DIDEVICEOBJECTINSTANCE contains a controls name and return it,
|
||
|
* or build a default one
|
||
|
*/
|
||
|
static void BuildCtrlName(LPCDIDEVICEOBJECTINSTANCE lpddoi,
|
||
|
std::string &destName,
|
||
|
const char *defaultName)
|
||
|
{
|
||
|
if (lpddoi->dwSize >= offsetof(DIDEVICEOBJECTINSTANCE, tszName) + sizeof(TCHAR[MAX_PATH]))
|
||
|
{
|
||
|
destName = (::strcmp("N/A", lpddoi->tszName) == 0) ? defaultName
|
||
|
: lpddoi->tszName;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
destName = defaultName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
// A callback to enumerate the controls of a device
|
||
|
static BOOL CALLBACK DIEnumDeviceObjectsCallback
|
||
|
(
|
||
|
LPCDIDEVICEOBJECTINSTANCE lpddoi,
|
||
|
LPVOID pvRef
|
||
|
)
|
||
|
{
|
||
|
|
||
|
CDIGameDevice *gd = (CDIGameDevice *) pvRef;
|
||
|
return gd->processEnumObject(lpddoi);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//=======================================================================
|
||
|
// get range for an axis
|
||
|
static HRESULT GetDIAxisRange(LPDIRECTINPUTDEVICE8 device, uint offset, DWORD type, sint &min, sint &max)
|
||
|
{
|
||
|
DIPROPRANGE diprg;
|
||
|
diprg.diph.dwSize = sizeof(DIPROPRANGE);
|
||
|
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
||
|
diprg.diph.dwHow = DIPH_BYOFFSET;
|
||
|
diprg.diph.dwObj = offset;
|
||
|
|
||
|
// Set the range for the axis
|
||
|
HRESULT r = device->GetProperty(DIPROP_RANGE, &diprg.diph);
|
||
|
|
||
|
if (r == DIERR_OBJECTNOTFOUND)
|
||
|
{
|
||
|
// try from its ID
|
||
|
diprg.diph.dwHow = DIPH_BYID;
|
||
|
diprg.diph.dwObj = type;
|
||
|
|
||
|
// Set the range for the axis
|
||
|
HRESULT r = device->GetProperty(DIPROP_RANGE, &diprg.diph);
|
||
|
if (r != DI_OK)
|
||
|
{
|
||
|
// setup default values ...
|
||
|
min = 0;
|
||
|
max = 65535;
|
||
|
return r;
|
||
|
}
|
||
|
}
|
||
|
else if (r != DI_OK)
|
||
|
{
|
||
|
min = 0;
|
||
|
max = 65535;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* switch (r)
|
||
|
{
|
||
|
default:
|
||
|
nlinfo("ok");
|
||
|
break;
|
||
|
case DIERR_INVALIDPARAM:
|
||
|
nlinfo("invalid param");
|
||
|
break;
|
||
|
case DIERR_NOTEXCLUSIVEACQUIRED:
|
||
|
nlinfo("DIERR_NOTEXCLUSIVEACQUIRED");
|
||
|
break;
|
||
|
case DIERR_NOTINITIALIZED:
|
||
|
nlinfo("DIERR_NOTINITIALIZED");
|
||
|
break;
|
||
|
case DIERR_OBJECTNOTFOUND:
|
||
|
nlinfo("DIERR_OBJECTNOTFOUND");
|
||
|
break;
|
||
|
case DIERR_UNSUPPORTED:
|
||
|
nlinfo("DIERR_UNSUPPORTED");
|
||
|
break;
|
||
|
}*/
|
||
|
|
||
|
|
||
|
min = (sint) diprg.lMin;
|
||
|
max = (sint) diprg.lMax;
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
BOOL CDIGameDevice::processEnumObject(LPCDIDEVICEOBJECTINSTANCE lpddoi)
|
||
|
{
|
||
|
// the dwSize field gives us the size of the objects, and the available fields
|
||
|
// has this object the field guidType and dwOfs ?
|
||
|
if (lpddoi->dwSize < offsetof(DIDEVICEOBJECTINSTANCE, dwOfs) + sizeof(DWORD)) return DIENUM_CONTINUE;
|
||
|
|
||
|
uint ctrlType = (uint) lpddoi->dwType;
|
||
|
|
||
|
///////////////////////////////////////////
|
||
|
// axis, we only support absolute ones //
|
||
|
///////////////////////////////////////////
|
||
|
|
||
|
if (lpddoi->guidType == GUID_XAxis && (ctrlType & DIDFT_ABSAXIS) )
|
||
|
{
|
||
|
GetDIAxisRange(_Device, lpddoi->dwOfs, lpddoi->dwType, _Axis[XAxis].Min, _Axis[XAxis].Max);
|
||
|
BuildCtrlName(lpddoi, _Axis[XAxis].Name, "X Axis");
|
||
|
_Axis[XAxis].Present = true;
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
if (lpddoi->guidType == GUID_YAxis && (ctrlType & DIDFT_ABSAXIS))
|
||
|
{
|
||
|
GetDIAxisRange(_Device, lpddoi->dwOfs, lpddoi->dwType, _Axis[YAxis].Min, _Axis[YAxis].Max);
|
||
|
BuildCtrlName(lpddoi, _Axis[YAxis].Name, "Y Axis");
|
||
|
_Axis[YAxis].Present = true;
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
|
||
|
if (lpddoi->guidType == GUID_ZAxis && (ctrlType & DIDFT_ABSAXIS))
|
||
|
{
|
||
|
GetDIAxisRange(_Device, lpddoi->dwOfs, lpddoi->dwType, _Axis[ZAxis].Min, _Axis[ZAxis].Max);
|
||
|
BuildCtrlName(lpddoi, _Axis[ZAxis].Name, "Z Axis");
|
||
|
_Axis[ZAxis].Present = true;
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
if (lpddoi->guidType == GUID_RxAxis && (ctrlType & DIDFT_ABSAXIS))
|
||
|
{
|
||
|
GetDIAxisRange(_Device, lpddoi->dwOfs, lpddoi->dwType, _Axis[RXAxis].Min, _Axis[RXAxis].Max);
|
||
|
BuildCtrlName(lpddoi, _Axis[RXAxis].Name, "RX Axis");
|
||
|
_Axis[RXAxis].Present = true;
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
if (lpddoi->guidType == GUID_RyAxis && (ctrlType & DIDFT_ABSAXIS))
|
||
|
{
|
||
|
GetDIAxisRange(_Device, lpddoi->dwOfs, lpddoi->dwType, _Axis[RYAxis].Min, _Axis[RYAxis].Max);
|
||
|
BuildCtrlName(lpddoi, _Axis[RYAxis].Name, "RY Axis");
|
||
|
_Axis[RYAxis].Present = true;
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
if (lpddoi->guidType == GUID_RzAxis && (ctrlType & DIDFT_ABSAXIS))
|
||
|
{
|
||
|
GetDIAxisRange(_Device, lpddoi->dwOfs, lpddoi->dwType, _Axis[RZAxis].Min, _Axis[RZAxis].Max);
|
||
|
BuildCtrlName(lpddoi, _Axis[RZAxis].Name, "RZ Axis");
|
||
|
_Axis[RZAxis].Present = true;
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// has this object the field dwType ?
|
||
|
if (lpddoi->dwSize < offsetof(DIDEVICEOBJECTINSTANCE, dwType) + sizeof(DWORD)) return DIENUM_CONTINUE;
|
||
|
|
||
|
|
||
|
uint type = lpddoi->dwType;
|
||
|
/////////////
|
||
|
// Buttons //
|
||
|
/////////////
|
||
|
if (type & DIDFT_BUTTON)
|
||
|
{
|
||
|
if (_Buttons.size() < MaxNumButtons)
|
||
|
{
|
||
|
_Buttons.push_back(CButton());
|
||
|
uint buttonIndex = _Buttons.size() - 1;
|
||
|
char defaultButtonName[32];
|
||
|
smprintf(defaultButtonName, 32, "BUTTON %d", buttonIndex + 1);
|
||
|
BuildCtrlName(lpddoi, _Buttons[buttonIndex].Name, defaultButtonName);
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////
|
||
|
// Sliders //
|
||
|
/////////////
|
||
|
if (type & DIDFT_ABSAXIS)
|
||
|
{
|
||
|
if (_Sliders.size() < MaxNumSliders)
|
||
|
{
|
||
|
_Sliders.push_back(CSlider());
|
||
|
uint sliderIndex = _Sliders.size() - 1;
|
||
|
GetDIAxisRange(_Device, lpddoi->dwOfs, lpddoi->dwType, _Sliders[sliderIndex].Min, _Sliders[sliderIndex].Max);
|
||
|
char defaultSliderName[32];
|
||
|
smprintf(defaultSliderName, 32, "SLIDER %d", sliderIndex + 1);
|
||
|
BuildCtrlName(lpddoi, _Sliders[sliderIndex].Name, defaultSliderName);
|
||
|
}
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////
|
||
|
// POVs //
|
||
|
//////////
|
||
|
if (type & DIDFT_POV)
|
||
|
{
|
||
|
if (_POVs.size() < MaxNumPOVs)
|
||
|
{
|
||
|
_POVs.push_back(CPOV());
|
||
|
uint povIndex = _POVs.size() - 1;
|
||
|
char defaultPOVName[16];
|
||
|
smprintf(defaultPOVName, 16, "POV %d", povIndex + 1);
|
||
|
BuildCtrlName(lpddoi, _POVs[povIndex].Name, defaultPOVName);
|
||
|
}
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
|
||
|
return DIENUM_CONTINUE;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
void CDIGameDevice::querryControls()
|
||
|
{
|
||
|
HRESULT r = _Device->EnumObjects(&DIEnumDeviceObjectsCallback, (LPVOID) this, DIDFT_ALL);
|
||
|
nlassert(r == DI_OK);
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
bool CDIGameDevice::setBufferSize(uint size)
|
||
|
{
|
||
|
// uisually not supported by this kind of devices
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
uint CDIGameDevice::getBufferSize() const
|
||
|
{
|
||
|
// uisually not supported by this kind of devices
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
uint CDIGameDevice::getNumButtons() const
|
||
|
{
|
||
|
return _Buttons.size();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
bool CDIGameDevice::hasAxis(TAxis axis) const
|
||
|
{
|
||
|
nlassert(axis < MaxNumAxis);
|
||
|
return _Axis[axis].Present;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
uint CDIGameDevice::getNumSliders() const
|
||
|
{
|
||
|
return _Sliders.size();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
uint CDIGameDevice::getNumPOV() const
|
||
|
{
|
||
|
return _POVs.size();
|
||
|
}
|
||
|
//============================================================================
|
||
|
const char *CDIGameDevice::getButtonName(uint index) const
|
||
|
{
|
||
|
nlassert(index < _Buttons.size());
|
||
|
return _Buttons[index].Name.c_str();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
const char *CDIGameDevice::getAxisName(TAxis axis) const
|
||
|
{
|
||
|
nlassert(axis < MaxNumAxis);
|
||
|
nlassert(hasAxis(axis)); // ! Not an axis of this device
|
||
|
return _Axis[axis].Name.c_str();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
const char *CDIGameDevice::getSliderName(uint index) const
|
||
|
{
|
||
|
nlassert(index < _Sliders.size());
|
||
|
return _Sliders[index].Name.c_str();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
const char *CDIGameDevice::getPOVName(uint index) const
|
||
|
{
|
||
|
nlassert(index < _POVs.size());
|
||
|
return _POVs[index].Name.c_str();
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
bool CDIGameDevice::getButtonState(uint index) const
|
||
|
{
|
||
|
nlassert(index < _Buttons.size());
|
||
|
return _Buttons[index].Pushed;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
float CDIGameDevice::getAxisValue(TAxis axis) const
|
||
|
{
|
||
|
nlassert(axis < MaxNumAxis);
|
||
|
nlassert(hasAxis(axis)); // ! Not an axis of this device
|
||
|
return _Axis[axis].Value;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
float CDIGameDevice::getSliderPos(uint index) const
|
||
|
{
|
||
|
nlassert(index < _Sliders.size());
|
||
|
return _Sliders[index].Pos;
|
||
|
}
|
||
|
|
||
|
//============================================================================
|
||
|
float CDIGameDevice::getPOVAngle(uint index) const
|
||
|
{
|
||
|
nlassert(index < _POVs.size());
|
||
|
return _POVs[index].Angle;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
} // NLMISC
|
||
|
|
||
|
|
||
|
#endif // NL_OS_WINDOWS
|