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 |