2014-02-10 18:54:46 +00:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
|
|
|
// Licensed under GPLv2
|
|
|
|
// Refer to the license.txt file included.
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2014-02-17 10:18:15 +00:00
|
|
|
#include <algorithm>
|
2013-06-17 00:07:10 +00:00
|
|
|
#include <map>
|
|
|
|
#include <sstream>
|
2014-02-19 01:17:31 +00:00
|
|
|
|
|
|
|
#include "InputCommon/ControllerInterface/DInput/DInput.h"
|
|
|
|
#include "InputCommon/ControllerInterface/DInput/DInputJoystick.h"
|
2013-06-27 03:26:21 +00:00
|
|
|
|
2010-04-02 02:48:24 +00:00
|
|
|
namespace ciface
|
|
|
|
{
|
2010-07-03 08:04:10 +00:00
|
|
|
namespace DInput
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
|
|
|
|
2014-02-17 04:51:41 +00:00
|
|
|
#define DATA_BUFFER_SIZE 32
|
2010-06-05 05:30:23 +00:00
|
|
|
|
2014-08-20 03:06:27 +00:00
|
|
|
static const GUID s_known_xinput_guids[] = {
|
|
|
|
// ValveStreamingGamepad
|
|
|
|
{ MAKELONG(0x28DE, 0x11FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } },
|
|
|
|
// IID_X360WiredGamepad
|
|
|
|
{ MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } },
|
|
|
|
// IID_X360WirelessGamepad
|
|
|
|
{ MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } },
|
|
|
|
};
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2013-06-17 00:07:10 +00:00
|
|
|
void InitJoystick(IDirectInput8* const idi8, std::vector<Core::Device*>& devices, HWND hwnd)
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2010-06-21 03:12:16 +00:00
|
|
|
std::list<DIDEVICEINSTANCE> joysticks;
|
2013-01-01 02:49:22 +00:00
|
|
|
idi8->EnumDevices( DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, (LPVOID)&joysticks, DIEDFL_ATTACHEDONLY );
|
2010-04-02 02:48:24 +00:00
|
|
|
|
|
|
|
// this is used to number the joysticks
|
|
|
|
// multiple joysticks with the same name shall get unique ids starting at 0
|
2014-02-17 04:51:41 +00:00
|
|
|
std::map< std::basic_string<TCHAR>, int> name_counts;
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2014-01-31 00:51:21 +00:00
|
|
|
for (DIDEVICEINSTANCE& joystick : joysticks)
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
|
|
|
// skip XInput Devices
|
2014-08-20 03:06:27 +00:00
|
|
|
if (std::find(std::begin(s_known_xinput_guids), std::end(s_known_xinput_guids),
|
|
|
|
joystick.guidProduct) != std::end(s_known_xinput_guids))
|
|
|
|
{
|
2010-04-02 02:48:24 +00:00
|
|
|
continue;
|
2014-08-20 03:06:27 +00:00
|
|
|
}
|
2013-06-27 03:06:17 +00:00
|
|
|
|
2010-04-02 02:48:24 +00:00
|
|
|
LPDIRECTINPUTDEVICE8 js_device;
|
2014-03-09 20:14:26 +00:00
|
|
|
if (SUCCEEDED(idi8->CreateDevice(joystick.guidInstance, &js_device, nullptr)))
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2010-06-12 02:08:01 +00:00
|
|
|
if (SUCCEEDED(js_device->SetDataFormat(&c_dfDIJoystick)))
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2010-10-11 22:19:47 +00:00
|
|
|
if (FAILED(js_device->SetCooperativeLevel(GetAncestor(hwnd, GA_ROOT), DISCL_BACKGROUND | DISCL_EXCLUSIVE)))
|
2010-06-05 05:30:23 +00:00
|
|
|
{
|
|
|
|
//PanicAlert("SetCooperativeLevel(DISCL_EXCLUSIVE) failed!");
|
|
|
|
// fall back to non-exclusive mode, with no rumble
|
2014-03-09 20:14:26 +00:00
|
|
|
if (FAILED(js_device->SetCooperativeLevel(nullptr, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
|
2010-06-05 05:30:23 +00:00
|
|
|
{
|
|
|
|
//PanicAlert("SetCooperativeLevel failed!");
|
|
|
|
js_device->Release();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-31 00:51:21 +00:00
|
|
|
Joystick* js = new Joystick(/*&*i, */js_device, name_counts[joystick.tszInstanceName]++);
|
2013-04-19 13:21:45 +00:00
|
|
|
// only add if it has some inputs/outputs
|
2010-06-05 05:30:23 +00:00
|
|
|
if (js->Inputs().size() || js->Outputs().size())
|
|
|
|
devices.push_back(js);
|
|
|
|
else
|
|
|
|
delete js;
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
else
|
2010-06-05 05:30:23 +00:00
|
|
|
{
|
|
|
|
//PanicAlert("SetDataFormat failed!");
|
|
|
|
js_device->Release();
|
|
|
|
}
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVICE8 device, const unsigned int index )
|
|
|
|
: m_device(device)
|
|
|
|
, m_index(index)
|
|
|
|
//, m_name(TStringToString(lpddi->tszInstanceName))
|
|
|
|
{
|
2010-10-09 07:24:33 +00:00
|
|
|
// seems this needs to be done before GetCapabilities
|
|
|
|
// polled or buffered data
|
|
|
|
DIPROPDWORD dipdw;
|
|
|
|
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
|
|
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
|
|
dipdw.diph.dwObj = 0;
|
|
|
|
dipdw.diph.dwHow = DIPH_DEVICE;
|
|
|
|
dipdw.dwData = DATA_BUFFER_SIZE;
|
|
|
|
// set the buffer size,
|
|
|
|
// if we can't set the property, we can't use buffered data
|
|
|
|
m_buffered = SUCCEEDED(m_device->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph));
|
|
|
|
|
|
|
|
// seems this needs to be done after SetProperty of buffer size
|
|
|
|
m_device->Acquire();
|
|
|
|
|
2010-04-02 02:48:24 +00:00
|
|
|
// get joystick caps
|
|
|
|
DIDEVCAPS js_caps;
|
|
|
|
js_caps.dwSize = sizeof(js_caps);
|
2010-09-02 00:03:25 +00:00
|
|
|
if (FAILED(m_device->GetCapabilities(&js_caps)))
|
|
|
|
return;
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2013-04-19 13:21:45 +00:00
|
|
|
// max of 32 buttons and 4 hats / the limit of the data format I am using
|
2010-04-02 02:48:24 +00:00
|
|
|
js_caps.dwButtons = std::min((DWORD)32, js_caps.dwButtons);
|
|
|
|
js_caps.dwPOVs = std::min((DWORD)4, js_caps.dwPOVs);
|
|
|
|
|
2010-09-02 00:03:25 +00:00
|
|
|
//m_must_poll = (js_caps.dwFlags & DIDC_POLLEDDATAFORMAT) != 0;
|
2010-06-05 05:30:23 +00:00
|
|
|
|
2010-04-02 02:48:24 +00:00
|
|
|
// buttons
|
2011-03-14 01:20:11 +00:00
|
|
|
for (u8 i = 0; i != js_caps.dwButtons; ++i)
|
2011-03-14 21:07:28 +00:00
|
|
|
AddInput(new Button(i, m_state_in.rgbButtons[i]));
|
2010-09-02 00:03:25 +00:00
|
|
|
|
2010-04-02 02:48:24 +00:00
|
|
|
// hats
|
2011-03-14 01:20:11 +00:00
|
|
|
for (u8 i = 0; i != js_caps.dwPOVs; ++i)
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
|
|
|
// each hat gets 4 input instances associated with it, (up down left right)
|
2011-03-14 01:20:11 +00:00
|
|
|
for (u8 d = 0; d != 4; ++d)
|
2012-05-02 06:31:38 +00:00
|
|
|
AddInput(new Hat(i, m_state_in.rgdwPOV[i], d));
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
2010-09-02 00:03:25 +00:00
|
|
|
|
2010-04-02 02:48:24 +00:00
|
|
|
// get up to 6 axes and 2 sliders
|
2010-09-02 00:03:25 +00:00
|
|
|
DIPROPRANGE range;
|
|
|
|
range.diph.dwSize = sizeof(range);
|
|
|
|
range.diph.dwHeaderSize = sizeof(range.diph);
|
|
|
|
range.diph.dwHow = DIPH_BYOFFSET;
|
2010-08-29 03:45:56 +00:00
|
|
|
// screw EnumObjects, just go through all the axis offsets and try to GetProperty
|
|
|
|
// this should be more foolproof, less code, and probably faster
|
|
|
|
for (unsigned int offset = 0; offset < DIJOFS_BUTTON(0) / sizeof(LONG); ++offset)
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2010-08-29 03:45:56 +00:00
|
|
|
range.diph.dwObj = offset * sizeof(LONG);
|
2012-12-30 02:41:48 +00:00
|
|
|
// try to set some nice power of 2 values (128) to match the GameCube controls
|
|
|
|
range.lMin = -(1 << 7);
|
|
|
|
range.lMax = (1 << 7);
|
2010-08-29 03:45:56 +00:00
|
|
|
m_device->SetProperty(DIPROP_RANGE, &range.diph);
|
2013-04-19 13:21:45 +00:00
|
|
|
// but I guess not all devices support setting range
|
|
|
|
// so I getproperty right afterward incase it didn't set.
|
|
|
|
// This also checks that the axis is present
|
2010-06-21 03:12:16 +00:00
|
|
|
if (SUCCEEDED(m_device->GetProperty(DIPROP_RANGE, &range.diph)))
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2010-08-29 03:45:56 +00:00
|
|
|
const LONG base = (range.lMin + range.lMax) / 2;
|
2011-03-14 21:07:28 +00:00
|
|
|
const LONG& ax = (&m_state_in.lX)[offset];
|
2011-03-14 01:20:11 +00:00
|
|
|
|
2010-08-29 03:45:56 +00:00
|
|
|
// each axis gets a negative and a positive input instance associated with it
|
2013-01-17 21:41:18 +00:00
|
|
|
AddAnalogInputs(new Axis(offset, ax, base, range.lMin-base),
|
|
|
|
new Axis(offset, ax, base, range.lMax-base));
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-05 10:28:32 +00:00
|
|
|
// force feedback
|
2010-06-21 03:12:16 +00:00
|
|
|
std::list<DIDEVICEOBJECTINSTANCE> objects;
|
2014-02-05 10:28:32 +00:00
|
|
|
if (SUCCEEDED(m_device->EnumObjects(DIEnumDeviceObjectsCallback, (LPVOID)&objects, DIDFT_AXIS)))
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2014-08-20 03:06:27 +00:00
|
|
|
InitForceFeedback(m_device, (int)objects.size());
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClearInputState();
|
|
|
|
}
|
|
|
|
|
|
|
|
Joystick::~Joystick()
|
|
|
|
{
|
|
|
|
m_device->Unacquire();
|
|
|
|
m_device->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joystick::ClearInputState()
|
|
|
|
{
|
|
|
|
ZeroMemory(&m_state_in, sizeof(m_state_in));
|
|
|
|
// set hats to center
|
2014-01-31 00:51:21 +00:00
|
|
|
memset(m_state_in.rgdwPOV, 0xFF, sizeof(m_state_in.rgdwPOV));
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string Joystick::GetName() const
|
|
|
|
{
|
2010-06-21 03:12:16 +00:00
|
|
|
return GetDeviceName(m_device);
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int Joystick::GetId() const
|
|
|
|
{
|
|
|
|
return m_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Joystick::GetSource() const
|
|
|
|
{
|
2010-06-21 03:12:16 +00:00
|
|
|
return DINPUT_SOURCE_NAME;
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// update IO
|
|
|
|
|
|
|
|
bool Joystick::UpdateInput()
|
|
|
|
{
|
2010-06-05 05:30:23 +00:00
|
|
|
HRESULT hr = 0;
|
|
|
|
|
2010-09-02 00:03:25 +00:00
|
|
|
// just always poll,
|
2013-04-19 13:21:45 +00:00
|
|
|
// MSDN says if this isn't needed it doesn't do anything
|
2010-09-02 00:03:25 +00:00
|
|
|
m_device->Poll();
|
2010-04-06 22:43:12 +00:00
|
|
|
|
2010-09-02 00:03:25 +00:00
|
|
|
if (m_buffered)
|
|
|
|
{
|
|
|
|
DIDEVICEOBJECTDATA evtbuf[DATA_BUFFER_SIZE];
|
|
|
|
DWORD numevents = DATA_BUFFER_SIZE;
|
2010-06-05 05:30:23 +00:00
|
|
|
hr = m_device->GetDeviceData(sizeof(*evtbuf), evtbuf, &numevents, 0);
|
2010-06-12 02:08:01 +00:00
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
2010-06-05 05:30:23 +00:00
|
|
|
{
|
2010-06-12 02:08:01 +00:00
|
|
|
for (LPDIDEVICEOBJECTDATA evt = evtbuf; evt != (evtbuf + numevents); ++evt)
|
2010-06-05 05:30:23 +00:00
|
|
|
{
|
|
|
|
// all the buttons are at the end of the data format
|
|
|
|
// they are bytes rather than longs
|
|
|
|
if (evt->dwOfs < DIJOFS_BUTTON(0))
|
2010-06-12 02:08:01 +00:00
|
|
|
*(DWORD*)(((BYTE*)&m_state_in) + evt->dwOfs) = evt->dwData;
|
2010-06-05 05:30:23 +00:00
|
|
|
else
|
2010-06-12 02:08:01 +00:00
|
|
|
((BYTE*)&m_state_in)[evt->dwOfs] = (BYTE)evt->dwData;
|
2010-06-05 05:30:23 +00:00
|
|
|
}
|
|
|
|
|
2010-10-03 04:29:34 +00:00
|
|
|
// seems like this needs to be done maybe...
|
2010-06-12 02:08:01 +00:00
|
|
|
if (DI_BUFFEROVERFLOW == hr)
|
2010-09-02 00:03:25 +00:00
|
|
|
hr = m_device->GetDeviceState(sizeof(m_state_in), &m_state_in);
|
2010-06-05 05:30:23 +00:00
|
|
|
}
|
|
|
|
}
|
2010-09-02 00:03:25 +00:00
|
|
|
else
|
2013-04-15 02:53:10 +00:00
|
|
|
{
|
2010-09-02 00:03:25 +00:00
|
|
|
hr = m_device->GetDeviceState(sizeof(m_state_in), &m_state_in);
|
2013-04-15 02:53:10 +00:00
|
|
|
}
|
2010-04-06 22:43:12 +00:00
|
|
|
|
|
|
|
// try reacquire if input lost
|
2010-06-05 05:30:23 +00:00
|
|
|
if (DIERR_INPUTLOST == hr || DIERR_NOTACQUIRED == hr)
|
2010-04-06 22:43:12 +00:00
|
|
|
hr = m_device->Acquire();
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2010-06-12 12:57:28 +00:00
|
|
|
return SUCCEEDED(hr);
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// get name
|
|
|
|
|
|
|
|
std::string Joystick::Button::GetName() const
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
2011-03-14 21:07:28 +00:00
|
|
|
ss << "Button " << (int)m_index;
|
2010-04-02 02:48:24 +00:00
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Joystick::Axis::GetName() const
|
|
|
|
{
|
|
|
|
std::ostringstream ss;
|
|
|
|
// axis
|
2010-07-16 03:43:11 +00:00
|
|
|
if (m_index < 6)
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2010-07-16 03:43:11 +00:00
|
|
|
ss << "Axis " << (char)('X' + (m_index % 3));
|
2011-03-14 21:07:28 +00:00
|
|
|
if (m_index > 2)
|
2010-04-02 02:48:24 +00:00
|
|
|
ss << 'r';
|
|
|
|
}
|
|
|
|
// slider
|
|
|
|
else
|
2013-04-15 02:53:10 +00:00
|
|
|
{
|
2011-03-14 21:07:28 +00:00
|
|
|
ss << "Slider " << (int)(m_index - 6);
|
2013-04-15 02:53:10 +00:00
|
|
|
}
|
2010-04-02 02:48:24 +00:00
|
|
|
|
2011-03-14 21:07:28 +00:00
|
|
|
ss << (m_range < 0 ? '-' : '+');
|
2010-04-02 02:48:24 +00:00
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Joystick::Hat::GetName() const
|
|
|
|
{
|
2010-07-16 03:43:11 +00:00
|
|
|
static char tmpstr[] = "Hat . .";
|
|
|
|
tmpstr[4] = (char)('0' + m_index);
|
|
|
|
tmpstr[6] = "NESW"[m_direction];
|
|
|
|
return tmpstr;
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// get / set state
|
|
|
|
|
2011-03-14 01:20:11 +00:00
|
|
|
ControlState Joystick::Axis::GetState() const
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2011-03-14 01:20:11 +00:00
|
|
|
return std::max(0.0f, ControlState(m_axis - m_base) / m_range);
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
2011-03-14 01:20:11 +00:00
|
|
|
ControlState Joystick::Button::GetState() const
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
2011-03-14 01:20:11 +00:00
|
|
|
return ControlState(m_button > 0);
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
2011-03-14 01:20:11 +00:00
|
|
|
ControlState Joystick::Hat::GetState() const
|
2010-04-02 02:48:24 +00:00
|
|
|
{
|
|
|
|
// can this func be simplified ?
|
2013-04-19 13:21:45 +00:00
|
|
|
// hat centered code from MSDN
|
2011-03-14 01:20:11 +00:00
|
|
|
if (0xFFFF == LOWORD(m_hat))
|
2010-04-02 02:48:24 +00:00
|
|
|
return 0;
|
2014-01-31 00:51:21 +00:00
|
|
|
|
2011-03-14 01:20:11 +00:00
|
|
|
return (abs((int)(m_hat / 4500 - m_direction * 2 + 8) % 8 - 4) > 2);
|
2010-04-02 02:48:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|