Bug 1011859 - Order gamepad axes properly. r=jimm, a=sledru
authorTed Mielczarek <ted@mielczarek.org>
Thu, 22 May 2014 09:14:13 -0400
changeset 192363 0bcc74404878
parent 192362 eac674ed7cfe
child 192364 586ed41fa2d1
push id3584
push userryanvm@gmail.com
push date2014-05-22 13:22 +0000
Treeherderresults
reviewersjimm, sledru
bugs1011859
milestone30.0
Bug 1011859 - Order gamepad axes properly. r=jimm, a=sledru
hal/windows/WindowsGamepad.cpp
--- a/hal/windows/WindowsGamepad.cpp
+++ b/hal/windows/WindowsGamepad.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/Services.h"
 
 namespace {
 
 using mozilla::dom::GamepadService;
 using mozilla::Mutex;
 using mozilla::MutexAutoLock;
 
+const unsigned kMaxAxes = 32;
 const LONG kMaxAxisValue = 65535;
 const DWORD BUTTON_DOWN_MASK = 0x80;
 // Multiple devices-changed notifications can be sent when a device
 // is connected, because USB devices consist of multiple logical devices.
 // Therefore, we wait a bit after receiving one before looking for
 // device changes.
 const uint32_t kDevicesChangedStableDelay = 200;
 
@@ -51,16 +52,17 @@ struct Gamepad {
   int globalID;
   // A somewhat unique string consisting of the USB vendor/product IDs,
   // and the controller name.
   char idstring[128];
   // USB vendor and product IDs
   int vendorID;
   int productID;
   // Information about the physical device.
+  DWORD axes[kMaxAxes];
   int numAxes;
   int numHats;
   int numButtons;
   // The human-readable device name.
   char name[128];
   // The DirectInput device.
   nsRefPtr<IDirectInputDevice8> device;
   // A handle that DirectInput signals when there is new data from
@@ -387,16 +389,26 @@ WindowsGamepadService::EnumObjectsCallba
   DIPROPRANGE dp;
   dp.diph.dwHeaderSize = sizeof(DIPROPHEADER);
   dp.diph.dwSize = sizeof(DIPROPRANGE);
   dp.diph.dwHow = DIPH_BYID;
   dp.diph.dwObj = lpddoi->dwType;
   dp.lMin = 0;
   dp.lMax = kMaxAxisValue;
   gamepad->device->SetProperty(DIPROP_RANGE, &dp.diph);
+  // Find what the dwOfs of this object in the c_dfDIJoystick data format is.
+  for (DWORD i = 0; i < c_dfDIJoystick.dwNumObjs; i++) {
+    if (c_dfDIJoystick.rgodf[i].pguid &&
+        IsEqualGUID(*c_dfDIJoystick.rgodf[i].pguid, lpddoi->guidType) &&
+        gamepad->numAxes < kMaxAxes) {
+      gamepad->axes[gamepad->numAxes] = c_dfDIJoystick.rgodf[i].dwOfs;
+      gamepad->numAxes++;
+      break;
+    }
+  }
   return DIENUM_CONTINUE;
 }
 
 // static
 BOOL CALLBACK
 WindowsGamepadService::EnumCallback(LPCDIDEVICEINSTANCE lpddi,
                                     LPVOID pvRef) {
   WindowsGamepadService* self =
@@ -408,18 +420,17 @@ WindowsGamepadService::EnumCallback(LPCD
       if (memcmp(&lpddi->guidInstance, &self->mGamepads[i].guidInstance,
                  sizeof(GUID)) == 0) {
         self->mGamepads[i].present = true;
         return DIENUM_CONTINUE;
       }
     }
   }
 
-  Gamepad gamepad;
-  memset(&gamepad, 0, sizeof(Gamepad));
+  Gamepad gamepad = {};
   if (self->dinput->CreateDevice(lpddi->guidInstance,
                                  getter_AddRefs(gamepad.device),
                                  nullptr)
       == DI_OK) {
     gamepad.present = true;
     memcpy(&gamepad.guidInstance, &lpddi->guidInstance, sizeof(GUID));
 
     DIDEVICEINSTANCE info;
@@ -436,23 +447,24 @@ WindowsGamepadService::EnumCallback(LPCD
     dp.diph.dwHow = DIPH_DEVICE;
     if (gamepad.device->GetProperty(DIPROP_VIDPID, &dp.diph) == DI_OK) {
       sprintf(gamepad.idstring, "%x-%x-%s",
               LOWORD(dp.dwData), HIWORD(dp.dwData), gamepad.name);
     }
     DIDEVCAPS caps;
     caps.dwSize = sizeof(DIDEVCAPS);
     if (gamepad.device->GetCapabilities(&caps) == DI_OK) {
-      gamepad.numAxes = caps.dwAxes;
       gamepad.numHats = caps.dwPOVs;
       gamepad.numButtons = caps.dwButtons;
       //XXX: handle polled devices?
       // (caps.dwFlags & DIDC_POLLEDDATAFORMAT || caps.dwFlags & DIDC_POLLEDDEVICE)
     }
     // Set min/max range for all axes on the device.
+    // Axes will be gathered in EnumObjectsCallback.
+    gamepad.numAxes = 0;
     gamepad.device->EnumObjects(EnumObjectsCallback, &gamepad, DIDFT_AXIS);
     // Set up structure for setting buffer size for buffered data
     dp.diph.dwHeaderSize = sizeof(DIPROPHEADER);
     dp.diph.dwSize = sizeof(DIPROPDWORD);
     dp.diph.dwObj = 0;
     dp.diph.dwHow = DIPH_DEVICE;
     dp.dwData = 64; // arbitrary
     // Create event so DInput can signal us when there's new data.
@@ -573,25 +585,30 @@ WindowsGamepadService::DInputThread(LPVO
           // now read each buffered event
           //TODO: read more than one event at a time
           DIDEVICEOBJECTDATA data;
           DWORD readCount = sizeof(data) / sizeof(DIDEVICEOBJECTDATA);
           if (device->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
                                     &data, &readCount, 0) == DI_OK) {
             //TODO: data.dwTimeStamp
             GamepadEvent::Type type = GamepadEvent::Unknown;
-            int which;
+            int which = -1;
             if (data.dwOfs >= DIJOFS_BUTTON0 && data.dwOfs < DIJOFS_BUTTON(32)) {
               type = GamepadEvent::Button;
               which = data.dwOfs - DIJOFS_BUTTON0;
             }
             else if(data.dwOfs >= DIJOFS_X  && data.dwOfs < DIJOFS_SLIDER(2)) {
               // axis/slider
               type = GamepadEvent::Axis;
-              which = (data.dwOfs - DIJOFS_X) / sizeof(LONG);
+              for (int a = 0; a < self->mGamepads[i].numAxes; a++) {
+                if (self->mGamepads[i].axes[a] == data.dwOfs) {
+                  which = a;
+                  break;
+                }
+              }
             }
             else if (data.dwOfs >= DIJOFS_POV(0) && data.dwOfs < DIJOFS_POV(4)) {
               HatState hatState;
               HatPosToAxes(data.dwData, hatState);
               which = (data.dwOfs - DIJOFS_POV(0)) / sizeof(DWORD);
               // Only send out axis move events for the axes that moved
               // in this hat move.
               if (hatState.x != self->mGamepads[i].hatState[which].x) {
@@ -604,17 +621,17 @@ WindowsGamepadService::DInputThread(LPVO
                 else {
                   type = GamepadEvent::HatY;
                 }
               }
               self->mGamepads[i].hatState[which].x = hatState.x;
               self->mGamepads[i].hatState[which].y = hatState.y;
             }
 
-            if (type != GamepadEvent::Unknown) {
+            if (type != GamepadEvent::Unknown && which != -1) {
               nsRefPtr<GamepadEvent> event =
                 new GamepadEvent(self->mGamepads[i], type, which, data.dwData);
               NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
             }
           }
           items--;
         }
       }