Index: content/browser/gamepad/gamepad_platform_data_fetcher_win.cc |
diff --git a/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc b/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc |
index 838a626d0184b093df64cb1ba6a2269dd9060022..3e1b0ce54c9080d3d13c1dde72b74e90a8827d5f 100644 |
--- a/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc |
+++ b/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc |
@@ -4,7 +4,23 @@ |
#include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" |
+#define DIRECTINPUT_VERSION 0x0800 |
scottmg
2013/02/14 04:33:22
this is defined twice (also in the header).
teravest
2013/02/14 17:52:18
Removed.
|
+#ifndef _WIN32_DCOM |
scottmg
2013/02/14 04:33:22
i'm assuming this is here for some reason, but i d
teravest
2013/02/14 17:52:18
This was present in the DirectX example that FillX
|
+#define _WIN32_DCOM |
+#endif |
+ |
+#include <basetsd.h> |
+#include <commctrl.h> |
+#include <dinput.h> |
+#include <dinputd.h> |
+#include <oleauto.h> |
+#include <wbemidl.h> |
+#include <windows.h> |
+ |
#include "base/debug/trace_event.h" |
+#include "base/stringprintf.h" |
+#include "base/win/scoped_bstr.h" |
+#include "base/win/scoped_com_initializer.h" |
#include "content/common/gamepad_messages.h" |
#include "content/common/gamepad_hardware_buffer.h" |
@@ -28,7 +44,7 @@ static const BYTE kDeviceSubTypeDrumKit = 8; |
static const BYTE kDeviceSubTypeGuitarBass = 11; |
static const BYTE kDeviceSubTypeArcadePad = 19; |
-float NormalizeAxis(SHORT value) { |
+float NormalizeXInputAxis(SHORT value) { |
return ((value + 32768.f) / 32767.5f) - 1.f; |
} |
@@ -48,105 +64,458 @@ const WebUChar* const GamepadSubTypeName(BYTE sub_type) { |
} |
} |
+bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad, |
+ std::string* vendor, |
+ std::string* product) { |
+ DIPROPDWORD prop; |
+ prop.diph.dwSize = sizeof(DIPROPDWORD); |
+ prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); |
+ prop.diph.dwObj = 0; |
+ prop.diph.dwHow = DIPH_DEVICE; |
+ |
+ if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &prop.diph))) |
+ return false; |
+ *vendor = base::StringPrintf("%04x", LOWORD(prop.dwData)); |
+ *product = base::StringPrintf("%04x", HIWORD(prop.dwData)); |
+ return true; |
+} |
+ |
+// Sets the deadzone value for all axes of a gamepad. |
+// deadzone values range from 0 (no deadzone) to 10,000 (entire range |
+// is dead). |
+bool SetDirectInputDeadZone(IDirectInputDevice8* gamepad, |
+ int deadzone) { |
+ DIPROPDWORD prop; |
+ prop.diph.dwSize = sizeof(DIPROPDWORD); |
+ prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); |
+ prop.diph.dwObj = 0; |
+ prop.diph.dwHow = DIPH_DEVICE; |
+ prop.dwData = deadzone; |
+ return SUCCEEDED(gamepad->SetProperty(DIPROP_DEADZONE, &prop.diph)); |
+} |
+ |
+struct InternalDirectInputDevice { |
+ IDirectInputDevice8* gamepad; |
+ GamepadStandardMappingFunction mapper; |
+ wchar_t id[WebGamepad::idLengthCap]; |
+}; |
+ |
+struct EnumDevicesContext { |
+ std::vector<uint32>* xinput_devices; |
+ IDirectInput8* directinput_interface; |
+ std::vector<InternalDirectInputDevice>* directinput_devices; |
+}; |
+ |
+// We define our own data format structure to attempt to get as many |
+// axes as possible. |
+struct JoyData { |
+ long axes[10]; |
+ char buttons[24]; |
+ DWORD pov; // Often used for D-pads. |
+}; |
+ |
+BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance, |
+ void* context) { |
+ EnumDevicesContext* ctxt = (EnumDevicesContext*) context; |
+ // Skip XInput devices. |
+ for (size_t i = 0; i < ctxt->xinput_devices->size(); ++i) { |
+ if ((*ctxt->xinput_devices)[i] == instance->guidProduct.Data1) |
+ return DIENUM_CONTINUE; |
+ } |
+ IDirectInputDevice8* gamepad; |
+ if (FAILED(ctxt->directinput_interface->CreateDevice(instance->guidInstance, |
+ &gamepad, |
+ NULL))) |
+ return DIENUM_CONTINUE; |
+ |
+#define MAKE_AXIS(i) \ |
+ {0, FIELD_OFFSET(JoyData, axes) + 4 * i, \ |
+ DIDFT_AXIS | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} |
+#define MAKE_BUTTON(i) \ |
+ {&GUID_Button, FIELD_OFFSET(JoyData, buttons) + i, \ |
+ DIDFT_BUTTON | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} |
+#define MAKE_POV() \ |
+ {&GUID_POV, FIELD_OFFSET(JoyData, pov), DIDFT_POV | DIDFT_OPTIONAL, 0} |
+ DIOBJECTDATAFORMAT rgodf[] = { |
+ MAKE_AXIS(0), |
+ MAKE_AXIS(1), |
+ MAKE_AXIS(2), |
+ MAKE_AXIS(3), |
+ MAKE_AXIS(4), |
+ MAKE_AXIS(5), |
+ MAKE_AXIS(6), |
+ MAKE_AXIS(7), |
+ MAKE_AXIS(8), |
+ MAKE_AXIS(9), |
+ MAKE_BUTTON(0), |
+ MAKE_BUTTON(1), |
+ MAKE_BUTTON(2), |
+ MAKE_BUTTON(3), |
+ MAKE_BUTTON(4), |
+ MAKE_BUTTON(5), |
+ MAKE_BUTTON(6), |
+ MAKE_BUTTON(7), |
+ MAKE_BUTTON(8), |
+ MAKE_BUTTON(9), |
+ MAKE_BUTTON(10), |
+ MAKE_BUTTON(11), |
+ MAKE_BUTTON(12), |
+ MAKE_BUTTON(13), |
+ MAKE_BUTTON(14), |
+ MAKE_BUTTON(15), |
+ MAKE_BUTTON(16), |
+ MAKE_POV(), |
+ }; |
+#undef MAKE_AXIS |
+#undef MAKE_BUTTON |
+#undef MAKE_POV |
+ |
+ DIDATAFORMAT df = { |
+ sizeof (DIDATAFORMAT), |
+ sizeof (DIOBJECTDATAFORMAT), |
+ DIDF_ABSAXIS, |
+ sizeof (JoyData), |
+ sizeof (rgodf) / sizeof (rgodf[0]), |
+ rgodf |
+ }; |
+ |
+ // If we can't set the data format on the device, don't add it to our |
+ // list, since we won't know how to read data from it. |
+ if (FAILED(gamepad->SetDataFormat(&df))) |
+ return DIENUM_CONTINUE; |
+ |
+ InternalDirectInputDevice device; |
+ device.gamepad = gamepad; |
+ std::string vendor; |
+ std::string product; |
+ if (!GetDirectInputVendorProduct(gamepad, &vendor, &product)) |
+ return DIENUM_CONTINUE; |
+ |
+ wcscpy_s(device.id, WebGamepad::idLengthCap, instance->tszInstanceName); |
scottmg
2013/02/14 04:33:22
i think this should be tszProductName not tszInsta
teravest
2013/02/14 17:52:18
Done.
|
+ |
+ // Set the dead zone to 10% of the axis length for all axes. This |
+ // gives us a larger space for what's "neutral" so the controls don't |
+ // slowly drift. |
+ SetDirectInputDeadZone(gamepad, 1000); |
+ device.mapper = GetGamepadStandardMappingFunction(vendor, product); |
scottmg
2013/02/14 04:33:22
if there's a mapper to standard gamepad style, the
teravest
2013/02/14 17:52:18
Done.
|
+ if (device.mapper) |
+ ctxt->directinput_devices->push_back(device); |
+ return DIENUM_CONTINUE; |
+} |
+ |
+// Adapted from the DirectInput samples in the DirectX SDK. |
+// http://goo.gl/53Sfw |
+void FillXInputDeviceList(std::vector<uint32>* xinput_devices) { |
+ xinput_devices->clear(); |
+ base::win::ScopedCOMInitializer com_initializer; |
scottmg
2013/02/14 04:33:22
cpu can confirm, but I think this should only be d
teravest
2013/02/14 17:52:18
Deleted FillXInputDeviceList().
|
+ if (com_initializer.succeeded()) |
+ return; |
scottmg
2013/02/14 04:33:22
return on success?
teravest
2013/02/14 17:52:18
Deleted FillXInputDeviceList().
|
+ |
+ // Create WMI |
+ IWbemLocator* tmp_wbem_locator = NULL; |
+ HRESULT hr = CoCreateInstance(__uuidof(WbemLocator), |
+ NULL, |
+ CLSCTX_INPROC_SERVER, |
+ __uuidof(IWbemLocator), |
+ (void**)&tmp_wbem_locator); |
+ if (FAILED(hr) || tmp_wbem_locator == NULL) |
+ return; |
+ scoped_ptr<IWbemLocator> wbem_locator(tmp_wbem_locator); |
+ |
+ // Connect to WMI |
+ base::win::ScopedBstr name_space(L"\\\\.\\root\\cimv2"); |
scottmg
2013/02/14 04:33:22
what a lovely api they've cooked up here. :P
|
+ IWbemServices* tmp_wbem_services = NULL; |
+ hr = wbem_locator->ConnectServer(name_space, NULL, NULL, 0L, |
+ 0L, NULL, NULL, &tmp_wbem_services); |
+ if (FAILED(hr) || tmp_wbem_services == NULL) |
+ return; |
+ scoped_ptr<IWbemServices> wbem_services(tmp_wbem_services); |
+ |
+ // Switch security level to IMPERSONATE |
+ CoSetProxyBlanket(wbem_services.get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, |
+ NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, |
+ NULL, 0); |
+ |
+ // Get list of Win32_PNPEntity devices |
+ base::win::ScopedBstr class_name(L"Win32_PNPEntity"); |
+ IEnumWbemClassObject* tmp_enum_devices = NULL; |
+ hr = wbem_services->CreateInstanceEnum(class_name, 0, NULL, |
+ &tmp_enum_devices); |
+ if (FAILED(hr) || tmp_enum_devices == NULL) |
+ return; |
+ scoped_ptr<IEnumWbemClassObject> enum_devices(tmp_enum_devices); |
+ |
+ // Loop over all devices |
+ while (true) { |
+ unsigned long devices_returned; |
scottmg
2013/02/14 04:33:22
i think keep this as ULONG since it's primarily in
teravest
2013/02/14 17:52:18
Deleted FillXInputDeviceList().
|
+ IWbemClassObject* devices[20] = {0}; |
+ // Get 20 at a time |
+ hr = enum_devices->Next(10000, 20, devices, &devices_returned); |
+ if (FAILED(hr)) |
+ return; |
+ if (devices_returned == 0) |
+ break; |
+ |
+ for (unsigned i = 0; i < devices_returned; i++) { |
scottmg
2013/02/14 04:33:22
ULONG, or at least match type above
teravest
2013/02/14 17:52:18
Deleted FillXInputDeviceList().
|
+ if (!devices[i]) |
+ continue; |
+ // For each device, get its device ID |
+ base::win::ScopedBstr device_id(L"DeviceID"); |
+ VARIANT var; |
+ hr = devices[i]->Get(device_id, 0L, &var, NULL, NULL); |
+ if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) { |
+ // Check if the device ID contains "IG_". If it does, then it's an |
+ // XInput device |
+ // Unfortunately this information can not be found by just using |
+ // DirectInput |
+ if (wcsstr(var.bstrVal, L"IG_")) { |
+ // If it does, then get the VID/PID from var.bstrVal |
+ DWORD dwPid = 0, dwVid = 0; |
+ WCHAR* strVid = wcsstr(var.bstrVal, L"VID_"); |
+ if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) |
scottmg
2013/02/14 04:33:22
should these be <= 0?
teravest
2013/02/14 17:52:18
Deleted FillXInputDeviceList().
|
+ dwVid = 0; |
+ WCHAR* strPid = wcsstr(var.bstrVal, L"PID_"); |
+ if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) |
+ dwPid = 0; |
+ DWORD dwVidPid = MAKELONG(dwVid, dwPid); |
+ |
+ // Add the VID/PID to a linked list |
scottmg
2013/02/14 04:33:22
not a linked list
teravest
2013/02/14 17:52:18
Stale comment removed.
|
+ xinput_devices->push_back(dwVidPid); |
+ } |
+ } |
+ devices[i]->Release(); |
+ } |
+ } |
+} |
+ |
} // namespace |
GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() |
: xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), |
- xinput_available_(GetXinputDllFunctions()) { |
+ xinput_available_(GetXInputDllFunctions()) { |
+ directinput_available_ = |
+ SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), |
+ DIRECTINPUT_VERSION, |
+ IID_IDirectInput8, |
+ (void **)&directinput_interface_, |
+ NULL)); |
} |
GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { |
} |
+void GamepadPlatformDataFetcherWin::EnumerateDevices( |
+ WebGamepads* pads) { |
+ TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); |
+ pad_state_.clear(); |
+ for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
scottmg
2013/02/14 04:33:22
this approach changes what happens on insert and r
teravest
2013/02/14 17:52:18
I figured that XInput devices should always take p
|
+ PadState s; |
+ s.status = DISCONNECTED; |
+ pad_state_.push_back(s); |
+ } |
+ |
+ for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
+ WebGamepad &pad = pads->items[i]; |
+ if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) { |
+ pad_state_[i].status = XINPUT_CONNECTED; |
+ continue; |
+ } |
+ } |
+ |
+ if (directinput_available_) { |
+ FillXInputDeviceList(&xinput_devices_); |
+ struct EnumDevicesContext ctxt; |
scottmg
2013/02/14 04:33:22
at least "context" or enum_devices_context if it's
teravest
2013/02/14 17:52:18
Done.
|
+ std::vector<InternalDirectInputDevice> directinput_gamepads; |
+ ctxt.xinput_devices = &xinput_devices_; |
+ ctxt.directinput_interface = directinput_interface_; |
+ ctxt.directinput_devices = &directinput_gamepads; |
+ |
+ directinput_interface_->EnumDevices( |
+ DI8DEVCLASS_GAMECTRL, |
+ &DirectInputEnumDevicesCallback, |
+ &ctxt, |
+ DIEDFL_ATTACHEDONLY); |
+ |
+ // Fill the "disconnected" pad state entries with our DirectInput |
+ // gamepads. |
+ unsigned pad_state_index = 0; |
+ unsigned directinput_index = 0; |
+ while (pad_state_index < WebGamepads::itemsLengthCap && |
+ directinput_index < directinput_gamepads.size()) { |
+ if (pad_state_[pad_state_index].status != DISCONNECTED) { |
scottmg
2013/02/14 04:33:22
i think it would be clearer to have a local = &pad
teravest
2013/02/14 17:52:18
Done.
|
+ ++pad_state_index; |
+ continue; |
+ } |
+ WebGamepad &pad = pads->items[pad_state_index]; |
+ pad.connected = true; |
+ wcscpy_s(pad.id, WebGamepad::idLengthCap, |
+ directinput_gamepads[directinput_index].id); |
+ pad_state_[pad_state_index].status = DIRECTINPUT_CONNECTED; |
+ pad_state_[pad_state_index].directinput_gamepad = |
+ directinput_gamepads[directinput_index].gamepad; |
+ pad_state_[pad_state_index].mapper = |
+ directinput_gamepads[directinput_index].mapper; |
+ ++directinput_index; |
+ ++pad_state_index; |
+ } |
+ } |
+} |
+ |
+ |
void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, |
- bool devices_changed_hint) { |
+ bool devices_changed_hint) { |
TRACE_EVENT0("GAMEPAD", "GetGamepadData"); |
- // If there's no XInput DLL on the system, early out so that we don't |
- // call any other XInput functions. |
- if (!xinput_available_) { |
+ if (!xinput_available_ && !directinput_available_) { |
pads->length = 0; |
return; |
} |
- pads->length = WebGamepads::itemsLengthCap; |
- |
+ // A note on XInput devices: |
// If we got notification that system devices have been updated, then |
// run GetCapabilities to update the connected status and the device |
// identifier. It can be slow to do to both GetCapabilities and |
// GetState on unconnected devices, so we want to avoid a 2-5ms pause |
// here by only doing this when the devices are updated (despite |
// documentation claiming it's OK to call it any time). |
- if (devices_changed_hint) { |
- for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
- WebGamepad& pad = pads->items[i]; |
- TRACE_EVENT1("GAMEPAD", "GetCapabilities", "id", i); |
- XINPUT_CAPABILITIES caps; |
- DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); |
- if (res == ERROR_DEVICE_NOT_CONNECTED) { |
- pad.connected = false; |
- } else { |
- pad.connected = true; |
- base::swprintf(pad.id, |
- WebGamepad::idLengthCap, |
- L"Xbox 360 Controller (XInput STANDARD %ls)", |
- GamepadSubTypeName(caps.SubType)); |
- } |
- } |
- } |
+ if (devices_changed_hint) |
+ EnumerateDevices(pads); |
- // We've updated the connection state if necessary, now update the actual |
- // data for the devices that are connected. |
for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
WebGamepad& pad = pads->items[i]; |
+ if (pad_state_[i].status == XINPUT_CONNECTED) |
+ GetXInputPadData(i, &pad); |
+ else if (pad_state_[i].status == DIRECTINPUT_CONNECTED) |
+ GetDirectInputPadData(i, &pad); |
+ } |
+ pads->length = WebGamepads::itemsLengthCap; |
+} |
- // We rely on device_changed and GetCapabilities to tell us that |
- // something's been connected, but we will mark as disconnected if |
- // GetState returns that we've lost the pad. |
- if (!pad.connected) |
- continue; |
+bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( |
+ int i, |
+ WebGamepad* pad) const { |
+ DCHECK(pad); |
+ TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i); |
+ XINPUT_CAPABILITIES caps; |
+ DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); |
+ if (res == ERROR_DEVICE_NOT_CONNECTED) { |
+ pad->connected = false; |
+ return false; |
+ } else { |
+ pad->connected = true; |
+ base::swprintf(pad->id, |
+ WebGamepad::idLengthCap, |
+ L"Xbox 360 Controller (XInput STANDARD %ls)", |
+ GamepadSubTypeName(caps.SubType)); |
+ return true; |
+ } |
+} |
+ |
+void GamepadPlatformDataFetcherWin::GetXInputPadData( |
+ int i, |
+ WebGamepad* pad) { |
+ // We rely on device_changed and GetCapabilities to tell us that |
+ // something's been connected, but we will mark as disconnected if |
+ // GetState returns that we've lost the pad. |
+ if (!pad->connected) |
+ return; |
- XINPUT_STATE state; |
- memset(&state, 0, sizeof(XINPUT_STATE)); |
- TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); |
- DWORD dwResult = xinput_get_state_(i, &state); |
- TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); |
- |
- if (dwResult == ERROR_SUCCESS) { |
- pad.timestamp = state.dwPacketNumber; |
- pad.buttonsLength = 0; |
-#define ADD(b) pad.buttons[pad.buttonsLength++] = \ |
- ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); |
- ADD(XINPUT_GAMEPAD_A); |
- ADD(XINPUT_GAMEPAD_B); |
- ADD(XINPUT_GAMEPAD_X); |
- ADD(XINPUT_GAMEPAD_Y); |
- ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); |
- ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); |
- pad.buttons[pad.buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; |
- pad.buttons[pad.buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; |
- ADD(XINPUT_GAMEPAD_BACK); |
- ADD(XINPUT_GAMEPAD_START); |
- ADD(XINPUT_GAMEPAD_LEFT_THUMB); |
- ADD(XINPUT_GAMEPAD_RIGHT_THUMB); |
- ADD(XINPUT_GAMEPAD_DPAD_UP); |
- ADD(XINPUT_GAMEPAD_DPAD_DOWN); |
- ADD(XINPUT_GAMEPAD_DPAD_LEFT); |
- ADD(XINPUT_GAMEPAD_DPAD_RIGHT); |
+ XINPUT_STATE state; |
+ memset(&state, 0, sizeof(XINPUT_STATE)); |
+ TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); |
+ DWORD dwResult = xinput_get_state_(i, &state); |
+ TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); |
+ |
+ if (dwResult == ERROR_SUCCESS) { |
+ pad->timestamp = state.dwPacketNumber; |
+ pad->buttonsLength = 0; |
+#define ADD(b) pad->buttons[pad->buttonsLength++] = \ |
+ ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); |
+ ADD(XINPUT_GAMEPAD_A); |
+ ADD(XINPUT_GAMEPAD_B); |
+ ADD(XINPUT_GAMEPAD_X); |
+ ADD(XINPUT_GAMEPAD_Y); |
+ ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); |
+ ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); |
+ pad->buttons[pad->buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; |
+ pad->buttons[pad->buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; |
+ ADD(XINPUT_GAMEPAD_BACK); |
+ ADD(XINPUT_GAMEPAD_START); |
+ ADD(XINPUT_GAMEPAD_LEFT_THUMB); |
+ ADD(XINPUT_GAMEPAD_RIGHT_THUMB); |
+ ADD(XINPUT_GAMEPAD_DPAD_UP); |
+ ADD(XINPUT_GAMEPAD_DPAD_DOWN); |
+ ADD(XINPUT_GAMEPAD_DPAD_LEFT); |
+ ADD(XINPUT_GAMEPAD_DPAD_RIGHT); |
#undef ADD |
- pad.axesLength = 0; |
- // XInput are +up/+right, -down/-left, we want -up/-left. |
- pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbLX); |
- pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbLY); |
- pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbRX); |
- pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbRY); |
- } else { |
- pad.connected = false; |
- } |
+ pad->axesLength = 0; |
+ // XInput are +up/+right, -down/-left, we want -up/-left. |
+ pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbLX); |
+ pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbLY); |
+ pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbRX); |
+ pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbRY); |
+ } else { |
+ pad->connected = false; |
+ } |
+} |
+ |
+void GamepadPlatformDataFetcherWin::GetDirectInputPadData( |
+ int index, |
+ WebGamepad* pad) { |
+ if (!pad->connected) |
+ return; |
+ |
+ IDirectInputDevice8* gamepad = pad_state_[index].directinput_gamepad; |
+ if (FAILED(gamepad->Poll())) { |
+ HRESULT hr; |
+ while ((hr = gamepad->Acquire()) == DIERR_INPUTLOST) { } |
scottmg
2013/02/14 04:33:22
this loop worries me. is there any guarantee we wo
teravest
2013/02/14 17:52:18
I like that better.
Reading through the docs, Acq
|
+ return; |
} |
+ |
+ JoyData state; |
+ if (FAILED(gamepad->GetDeviceState(sizeof(JoyData), &state))) { |
+ pad->connected = false; |
+ return; |
+ } |
+ |
+ WebGamepad raw; |
+ raw.connected = true; |
+ for (int i = 0; i < 16; i++) |
+ raw.buttons[i] = (state.buttons[i] & 0x80) ? 1.0 : 0.0; |
+ |
+ // We map the POV (often a D-pad) into the buttons 16-19. |
scottmg
2013/02/14 04:33:22
this seems like something that should go into the
teravest
2013/02/14 17:52:18
The range of legal values for a POV should always
|
+ // DirectInput gives pov measurements in hundredths of degrees, |
+ // clockwise from "North". |
+ // We use 22.5 degree slices so we can handle diagonal D-raw presses. |
+ static const int arc_segment = 2250; // 22.5 degrees = 1/16 circle |
+ if (state.pov > arc_segment && state.pov < 7 * arc_segment) |
+ raw.buttons[19] = 1.0; |
+ else |
+ raw.buttons[19] = 0.0; |
+ |
+ if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment) |
+ raw.buttons[17] = 1.0; |
+ else |
+ raw.buttons[17] = 0.0; |
+ |
+ if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment) |
+ raw.buttons[18] = 1.0; |
+ else |
+ raw.buttons[18] = 0.0; |
+ |
+ if (state.pov < 3 * arc_segment || |
+ (state.pov > 13 * arc_segment && state.pov < 36000)) |
+ raw.buttons[16] = 1.0; |
+ else |
+ raw.buttons[16] = 0.0; |
+ |
+ for (int i = 0; i < 10; i++) |
+ raw.axes[i] = state.axes[i]; |
+ |
+ pad_state_[index].mapper(raw, pad); |
} |
-bool GamepadPlatformDataFetcherWin::GetXinputDllFunctions() { |
+bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { |
xinput_get_capabilities_ = NULL; |
xinput_get_state_ = NULL; |
xinput_enable_ = static_cast<XInputEnableFunc>( |