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..7d81496f3484288056f52c62eacde8f75884c349 100644 |
| --- a/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc |
| +++ b/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc |
| @@ -4,7 +4,21 @@ |
| #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" |
| +#define DIRECTINPUT_VERSION 0x0800 |
| +#ifndef _WIN32_DCOM |
| +#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/string_util.h" |
| #include "content/common/gamepad_messages.h" |
| #include "content/common/gamepad_hardware_buffer.h" |
| @@ -28,7 +42,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 +62,492 @@ const WebUChar* const GamepadSubTypeName(BYTE sub_type) { |
| } |
| } |
| +// A nicer version of the DirectX SAFE_RELEASE macro. |
| +template <class T> |
| +void SafeRelease(T** p) { |
| + if (*p) |
| + (*p)->Release(); |
| + *p = NULL; |
| +} |
| + |
| +bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad, |
| + std::string* vendor, |
| + std::string* product) { |
| + DIPROPDWORD property; |
| + property.diph.dwSize = sizeof(DIPROPDWORD); |
| + property.diph.dwHeaderSize = sizeof(DIPROPHEADER); |
| + property.diph.dwObj = 0; |
| + property.diph.dwHow = DIPH_DEVICE; |
| + |
| + if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &property.diph))) |
| + return false; |
| + int vendorData = LOWORD(property.dwData); |
| + int productData = HIWORD(property.dwData); |
| + std::stringstream vendorstream; |
| + vendorstream << std::setw(4) << std::setfill('0') << std::hex << vendorData; |
|
scottmg
2013/02/13 19:19:07
http://google-styleguide.googlecode.com/svn/trunk/
teravest
2013/02/13 21:59:22
Done.
|
| + *vendor = vendorstream.str(); |
| + |
| + std::stringstream productstream; |
| + productstream << std::setw(4) << std::setfill('0') << std::hex << productData; |
| + *product = productstream.str(); |
| + 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 have to define our own structure here; I was unable to link the |
| +// "global" joydata2 structure from directx. This one is smaller anyway. |
|
scottmg
2013/02/13 19:19:07
What? Why?
teravest
2013/02/13 21:59:22
I didn't want to muck around with how we were link
|
| +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); |
| + |
| + SetDirectInputDeadZone(gamepad, 1000); |
|
scottmg
2013/02/13 19:19:07
Where did this 1000 come from?
teravest
2013/02/13 21:59:22
I've added a comment here explaining what this is.
|
| + device.mapper = GetGamepadStandardMappingFunction(vendor, product); |
| + 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(); |
| + IWbemServices* pIWbemServices = NULL; |
| + IEnumWbemClassObject* pEnumDevices = NULL; |
| + IWbemLocator* pIWbemLocator = NULL; |
| + IWbemClassObject* pDevices[20] = {0}; |
| + BSTR bstrDeviceID = NULL; |
| + BSTR bstrClassName = NULL; |
| + BSTR bstrNamespace = NULL; |
| + DWORD uReturned = 0; |
| + bool bCleanupCOM = false; |
| + UINT iDevice = 0; |
| + VARIANT var; |
| + HRESULT hr; |
| + |
| + // CoInit if needed |
| + hr = CoInitialize(NULL); |
|
scottmg
2013/02/13 19:19:07
We shouldn't be doing this here.
teravest
2013/02/13 21:59:22
I've changed this to use a ScopedCOMInitializer. O
|
| + bCleanupCOM = SUCCEEDED(hr); |
| + |
| + // Create WMI |
| + hr = CoCreateInstance(__uuidof(WbemLocator), |
| + NULL, |
| + CLSCTX_INPROC_SERVER, |
| + __uuidof(IWbemLocator), |
| + (void**)&pIWbemLocator); |
|
scottmg
2013/02/13 19:19:07
Please fix all the identifiers to match Chromium s
teravest
2013/02/13 21:59:22
Done.
|
| + if (FAILED(hr) || pIWbemLocator == NULL ) |
| + goto LCleanup; |
| + |
| + // Create BSTRs for WMI |
| + bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); |
| + if (bstrNamespace == NULL) |
| + goto LCleanup; |
| + bstrDeviceID = SysAllocString(L"DeviceID"); |
| + if (bstrDeviceID == NULL) |
| + goto LCleanup; |
| + bstrClassName = SysAllocString(L"Win32_PNPEntity"); |
| + if (bstrClassName == NULL) |
| + goto LCleanup; |
| + |
| + // Connect to WMI |
| + hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, |
| + 0L, NULL, NULL, &pIWbemServices ); |
| + if (FAILED(hr) || pIWbemServices == NULL) |
| + goto LCleanup; |
| + |
| + // Switch security level to IMPERSONATE |
| + CoSetProxyBlanket(pIWbemServices, 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 |
| + hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, |
| + &pEnumDevices); |
| + if (FAILED(hr) || pEnumDevices == NULL ) |
| + goto LCleanup; |
| + |
| + // Loop over all devices |
| + while (true) { |
| + // Get 20 at a time |
| + hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned); |
| + if (FAILED(hr)) |
| + goto LCleanup; |
| + if (uReturned == 0) |
| + break; |
| + |
| + for (iDevice = 0; iDevice < uReturned; iDevice++) { |
| + if (!pDevices[iDevice]) |
| + continue; |
| + // For each device, get its device ID |
| + hr = pDevices[iDevice]->Get(bstrDeviceID, 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) |
| + 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 |
| + xinput_devices->push_back(dwVidPid); |
| + } |
| + } |
| + SafeRelease(&pDevices[iDevice]); |
| + } |
| + } |
| + |
| +LCleanup: |
| + if (bstrNamespace) |
| + SysFreeString(bstrNamespace); |
| + if (bstrDeviceID) |
| + SysFreeString(bstrDeviceID); |
| + if (bstrClassName) |
| + SysFreeString(bstrClassName); |
| + for (iDevice = 0; iDevice < 20; iDevice++) |
| + SafeRelease(&pDevices[iDevice]); |
| + SafeRelease(&pEnumDevices); |
| + SafeRelease(&pIWbemLocator); |
| + SafeRelease(&pIWbemServices); |
| +} |
| + |
| } // namespace |
| GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() |
| : xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), |
| - xinput_available_(GetXinputDllFunctions()) { |
| + xinput_available_(GetXInputDllFunctions()), |
| + directinput_dll_(FilePath(FILE_PATH_LITERAL("dinput8.dll"))), |
| + directinput_available_(GetDirectInputDllFunctions()) { |
| } |
| GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { |
| } |
| +void GamepadPlatformDataFetcherWin::EnumerateDevices( |
| + WebGamepads* pads) { |
| + TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); |
| + pad_state_.clear(); |
| + for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| + 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; |
| + 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) { |
| + ++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; |
| + return; |
|
scottmg
2013/02/13 19:19:07
nit: remove
teravest
2013/02/13 21:59:22
Done.
|
| +} |
| - // 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; |
| } |
| } |
| -bool GamepadPlatformDataFetcherWin::GetXinputDllFunctions() { |
| +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) { } |
| + 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. |
| + // 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; |
| + else |
| + raw.buttons[19] = 0; |
| + |
| + if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment) |
| + raw.buttons[17] = 1; |
| + else |
| + raw.buttons[17] = 0; |
| + |
| + if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment) |
| + raw.buttons[18] = 1; |
| + else |
| + raw.buttons[18] = 0; |
| + |
| + if (state.pov < 3 * arc_segment || |
| + (state.pov > 13 * arc_segment && state.pov < 36000)) |
| + raw.buttons[16] = 1; |
| + else |
| + raw.buttons[16] = 0; |
| + |
| + for (int i = 0; i < 10; i++) |
| + raw.axes[i] = state.axes[i]; |
| + |
| + pad_state_[index].mapper(raw, pad); |
| +} |
| + |
| +bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { |
| xinput_get_capabilities_ = NULL; |
| xinput_get_state_ = NULL; |
| xinput_enable_ = static_cast<XInputEnableFunc>( |
| @@ -165,4 +566,18 @@ bool GamepadPlatformDataFetcherWin::GetXinputDllFunctions() { |
| return true; |
| } |
| +bool GamepadPlatformDataFetcherWin::GetDirectInputDllFunctions() { |
| + directinput_create_ = static_cast<DirectInputCreateFunc>( |
|
scottmg
2013/02/13 19:19:07
According to http://msdn.microsoft.com/en-us/libra
teravest
2013/02/13 21:59:22
Cool. I'm still trying to figure out how to do thi
|
| + directinput_dll_.GetFunctionPointer("DirectInput8Create")); |
| + if (!directinput_create_) |
| + return false; |
| + if (FAILED(directinput_create_(GetModuleHandle(NULL), |
| + DIRECTINPUT_VERSION, |
| + IID_IDirectInput8, |
| + (void **)&directinput_interface_, |
| + NULL))) |
| + return false; |
| + return true; |
| +} |
| + |
| } // namespace content |