Chromium Code Reviews| 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>( |