--- a/dom/gamepad/GamepadManager.cpp
+++ b/dom/gamepad/GamepadManager.cpp
@@ -681,17 +681,22 @@ GamepadManager::VibrateHaptic(uint32_t a
if (aControllerIdx >= VR_GAMEPAD_IDX_OFFSET) {
uint32_t index = aControllerIdx - VR_GAMEPAD_IDX_OFFSET;
mVRChannelChild->AddPromise(mPromiseID, promise);
mVRChannelChild->SendVibrateHaptic(index, aHapticIndex,
aIntensity, aDuration,
mPromiseID);
} else {
- // TODO: Bug 680289, implement for standard gamepads
+ for (const auto& channelChild: mChannelChildren) {
+ channelChild->AddPromise(mPromiseID, promise);
+ channelChild->SendVibrateHaptic(aControllerIdx, aHapticIndex,
+ aIntensity, aDuration,
+ mPromiseID);
+ }
}
++mPromiseID;
return promise.forget();
}
//Override nsIIPCBackgroundChildCreateCallback
void
--- a/dom/gamepad/GamepadManager.h
+++ b/dom/gamepad/GamepadManager.h
@@ -82,17 +82,17 @@ class GamepadManager final : public nsIO
already_AddRefed<Gamepad> GetGamepad(uint32_t aIndex) const;
// Receive GamepadChangeEvent messages from parent process to fire DOM events
void Update(const GamepadChangeEvent& aGamepadEvent);
// Trigger vibrate haptic event to gamepad channels.
already_AddRefed<Promise> VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
double aIntensity, double aDuration,
- nsIGlobalObject* aGlobal, ErrorResult& aRv);
+ nsIGlobalObject* aGlobal);
protected:
GamepadManager();
~GamepadManager() {};
// Fire a gamepadconnected or gamepaddisconnected event for the gamepad
// at |aIndex| to all windows that are listening and have received
// gamepad input.
--- a/dom/gamepad/GamepadPlatformService.cpp
+++ b/dom/gamepad/GamepadPlatformService.cpp
@@ -81,27 +81,29 @@ GamepadPlatformService::NotifyGamepadCha
for(uint32_t i = 0; i < mChannelParents.Length(); ++i) {
mChannelParents[i]->DispatchUpdateEvent(e);
}
}
uint32_t
GamepadPlatformService::AddGamepad(const char* aID,
GamepadMappingType aMapping,
- uint32_t aNumButtons, uint32_t aNumAxes)
+ GamepadHand aHand,
+ uint32_t aNumButtons, uint32_t aNumAxes,
+ uint32_t aHaptics)
{
// This method is called by monitor thread populated in
// platform-dependent backends
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(!NS_IsMainThread());
uint32_t index = ++mGamepadIndex;
GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index,
- aMapping, GamepadHand::_empty, GamepadServiceType::Standard,
- aNumButtons, aNumAxes, 0);
+ aMapping, aHand, GamepadServiceType::Standard,
+ aNumButtons, aNumAxes, aHaptics);
NotifyGamepadChange<GamepadAdded>(a);
return index;
}
void
GamepadPlatformService::RemoveGamepad(uint32_t aIndex)
{
@@ -147,16 +149,29 @@ GamepadPlatformService::NewAxisMoveEvent
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(!NS_IsMainThread());
GamepadAxisInformation a(aIndex, GamepadServiceType::Standard,
aAxis, aValue);
NotifyGamepadChange<GamepadAxisInformation>(a);
}
void
+GamepadPlatformService::NewPoseEvent(uint32_t aIndex,
+ const GamepadPoseState& aPose)
+{
+ // This method is called by monitor thread populated in
+ // platform-dependent backends
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(!NS_IsMainThread());
+ GamepadPoseInformation a(aIndex, GamepadServiceType::Standard,
+ aPose);
+ NotifyGamepadChange<GamepadPoseInformation>(a);
+}
+
+void
GamepadPlatformService::ResetGamepadIndexes()
{
// This method is called by monitor thread populated in
// platform-dependent backends
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(!NS_IsMainThread());
mGamepadIndex = 0;
}
--- a/dom/gamepad/GamepadPlatformService.h
+++ b/dom/gamepad/GamepadPlatformService.h
@@ -33,33 +33,37 @@ class GamepadPlatformService final
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GamepadPlatformService)
public:
//Get the singleton service
static already_AddRefed<GamepadPlatformService> GetParentService();
// Add a gamepad to the list of known gamepads, and return its index.
uint32_t AddGamepad(const char* aID, GamepadMappingType aMapping,
- uint32_t aNumButtons, uint32_t aNumAxes);
+ GamepadHand aHand, uint32_t aNumButtons,
+ uint32_t aNumAxes, uint32_t aNumHaptics);
// Remove the gamepad at |aIndex| from the list of known gamepads.
void RemoveGamepad(uint32_t aIndex);
// Update the state of |aButton| for the gamepad at |aIndex| for all
// windows that are listening and visible, and fire one of
// a gamepadbutton{up,down} event at them as well.
// aPressed is used for digital buttons, aValue is for analog buttons.
void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
double aValue);
// When only a digital button is available the value will be synthesized.
void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
// Update the state of |aAxis| for the gamepad at |aIndex| for all
// windows that are listening and visible, and fire a gamepadaxismove
// event at them as well.
void NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue);
+ // Update the state of |aState| for the gamepad at |aIndex| for all
+ // windows that are listening and visible.
+ void NewPoseEvent(uint32_t aIndex, const GamepadPoseState& aState);
// When shutting down the platform communications for gamepad, also reset the
// indexes.
void ResetGamepadIndexes();
//Add IPDL parent instance
void AddChannelParent(GamepadEventChannelParent* aParent);
--- a/dom/gamepad/GamepadServiceTest.cpp
+++ b/dom/gamepad/GamepadServiceTest.cpp
@@ -108,28 +108,30 @@ GamepadServiceTest::DestroyPBackgroundAc
// operations.
mPendingOperations.Clear();
}
}
already_AddRefed<Promise>
GamepadServiceTest::AddGamepad(const nsAString& aID,
GamepadMappingType aMapping,
+ GamepadHand aHand,
uint32_t aNumButtons,
uint32_t aNumAxes,
+ uint32_t aNumHaptics,
ErrorResult& aRv)
{
if (mShuttingDown) {
return nullptr;
}
GamepadAdded a(nsString(aID), 0,
- aMapping, GamepadHand::_empty,
+ aMapping, aHand,
GamepadServiceType::Standard,
- aNumButtons, aNumAxes, 0);
+ aNumButtons, aNumAxes, aNumHaptics);
GamepadChangeEvent e(a);
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
RefPtr<Promise> p = Promise::Create(go, aRv);
if (aRv.Failed()) {
return nullptr;
}
@@ -226,16 +228,97 @@ GamepadServiceTest::NewAxisMoveEvent(uin
mChild->SendGamepadTestEvent(id, e);
} else {
PendingOperation op(id, e);
mPendingOperations.AppendElement(op);
}
}
void
+GamepadServiceTest::NewPoseMove(uint32_t aIndex,
+ const Nullable<Float32Array>& aOrient,
+ const Nullable<Float32Array>& aPos,
+ const Nullable<Float32Array>& aAngVelocity,
+ const Nullable<Float32Array>& aAngAcceleration,
+ const Nullable<Float32Array>& aLinVelocity,
+ const Nullable<Float32Array>& aLinAcceleration)
+{
+ if (mShuttingDown) {
+ return;
+ }
+
+ GamepadPoseState poseState;
+ poseState.flags = GamepadCapabilityFlags::Cap_Orientation |
+ GamepadCapabilityFlags::Cap_Position |
+ GamepadCapabilityFlags::Cap_AngularAcceleration |
+ GamepadCapabilityFlags::Cap_LinearAcceleration;
+ if (!aOrient.IsNull()) {
+ const Float32Array& value = aOrient.Value();
+ value.ComputeLengthAndData();
+ MOZ_ASSERT(value.Length() == 4);
+ poseState.orientation[0] = value.Data()[0];
+ poseState.orientation[1] = value.Data()[1];
+ poseState.orientation[2] = value.Data()[2];
+ poseState.orientation[3] = value.Data()[3];
+ }
+ if (!aPos.IsNull()) {
+ const Float32Array& value = aPos.Value();
+ value.ComputeLengthAndData();
+ MOZ_ASSERT(value.Length() == 3);
+ poseState.position[0] = value.Data()[0];
+ poseState.position[1] = value.Data()[1];
+ poseState.position[2] = value.Data()[2];
+ }
+ if (!aAngVelocity.IsNull()) {
+ const Float32Array& value = aAngVelocity.Value();
+ value.ComputeLengthAndData();
+ MOZ_ASSERT(value.Length() == 3);
+ poseState.angularVelocity[0] = value.Data()[0];
+ poseState.angularVelocity[1] = value.Data()[1];
+ poseState.angularVelocity[2] = value.Data()[2];
+ }
+ if (!aAngAcceleration.IsNull()) {
+ const Float32Array& value = aAngAcceleration.Value();
+ value.ComputeLengthAndData();
+ MOZ_ASSERT(value.Length() == 3);
+ poseState.angularAcceleration[0] = value.Data()[0];
+ poseState.angularAcceleration[1] = value.Data()[1];
+ poseState.angularAcceleration[2] = value.Data()[2];
+ }
+ if (!aLinVelocity.IsNull()) {
+ const Float32Array& value = aLinVelocity.Value();
+ value.ComputeLengthAndData();
+ MOZ_ASSERT(value.Length() == 3);
+ poseState.linearVelocity[0] = value.Data()[0];
+ poseState.linearVelocity[1] = value.Data()[1];
+ poseState.linearVelocity[2] = value.Data()[2];
+ }
+ if (!aLinAcceleration.IsNull()) {
+ const Float32Array& value = aLinAcceleration.Value();
+ value.ComputeLengthAndData();
+ MOZ_ASSERT(value.Length() == 3);
+ poseState.linearAcceleration[0] = value.Data()[0];
+ poseState.linearAcceleration[1] = value.Data()[1];
+ poseState.linearAcceleration[2] = value.Data()[2];
+ }
+
+ GamepadPoseInformation a(aIndex, GamepadServiceType::Standard,
+ poseState);
+ GamepadChangeEvent e(a);
+
+ uint32_t id = ++mEventNumber;
+ if (mChild) {
+ mChild->SendGamepadTestEvent(id, e);
+ } else {
+ PendingOperation op(id, e);
+ mPendingOperations.AppendElement(op);
+ }
+}
+
+void
GamepadServiceTest::FlushPendingOperations()
{
for (uint32_t i=0; i < mPendingOperations.Length(); ++i) {
PendingOperation op = mPendingOperations[i];
if (op.mPromise) {
mChild->AddPromise(op.mID, op.mPromise);
}
mChild->SendGamepadTestEvent(op.mID, op.mEvent);
--- a/dom/gamepad/GamepadServiceTest.h
+++ b/dom/gamepad/GamepadServiceTest.h
@@ -26,26 +26,38 @@ class GamepadServiceTest final : public
public:
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GamepadServiceTest,
DOMEventTargetHelper)
GamepadMappingType NoMapping() const { return GamepadMappingType::_empty; }
GamepadMappingType StandardMapping() const { return GamepadMappingType::Standard; }
+ GamepadHand NoHand() const { return GamepadHand::_empty; }
+ GamepadHand LeftHand() const { return GamepadHand::Left; }
+ GamepadHand RightHand() const { return GamepadHand::Right; }
already_AddRefed<Promise> AddGamepad(const nsAString& aID,
GamepadMappingType aMapping,
+ GamepadHand aHand,
uint32_t aNumButtons,
uint32_t aNumAxes,
+ uint32_t aNumHaptics,
ErrorResult& aRv);
void RemoveGamepad(uint32_t aIndex);
void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
void NewButtonValueEvent(uint32_t aIndex, uint32_t aButton, bool aPressed, double aValue);
void NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue);
+ void NewPoseMove(uint32_t aIndex,
+ const Nullable<Float32Array>& aOrient,
+ const Nullable<Float32Array>& aPos,
+ const Nullable<Float32Array>& aAngVelocity,
+ const Nullable<Float32Array>& aAngAcceleration,
+ const Nullable<Float32Array>& aLinVelocity,
+ const Nullable<Float32Array>& aLinAcceleration);
void Shutdown();
static already_AddRefed<GamepadServiceTest> CreateTestService(nsPIDOMWindowInner* aWindow);
nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
private:
--- a/dom/gamepad/android/AndroidGamepad.cpp
+++ b/dom/gamepad/android/AndroidGamepad.cpp
@@ -24,17 +24,18 @@ public:
GamepadPlatformService::GetParentService();
if (!service) {
return;
}
if (aAdded) {
const int svc_id = service->AddGamepad(
"android", GamepadMappingType::Standard,
- kStandardGamepadButtons, kStandardGamepadAxes);
+ GamepadHand::_empty, kStandardGamepadButtons,
+ kStandardGamepadAxes, 0); // TODO: Bug 680289, implement gamepad haptics for Android
java::AndroidGamepadManager::OnGamepadAdded(aID, svc_id);
} else {
service->RemoveGamepad(aID);
}
}
static void
--- a/dom/gamepad/cocoa/CocoaGamepad.cpp
+++ b/dom/gamepad/cocoa/CocoaGamepad.cpp
@@ -279,18 +279,20 @@ DarwinGamepadService::DeviceAdded(IOHIDD
CFNumberGetValue(productIdRef, kCFNumberIntType, &productId);
char product_name[128];
CFStringGetCString(productRef, product_name,
sizeof(product_name), kCFStringEncodingASCII);
char buffer[256];
sprintf(buffer, "%x-%x-%s", vendorId, productId, product_name);
uint32_t index = service->AddGamepad(buffer,
mozilla::dom::GamepadMappingType::_empty,
+ mozilla::dom::GamepadHand::_empty,
(int)mGamepads[slot].numButtons(),
- (int)mGamepads[slot].numAxes());
+ (int)mGamepads[slot].numAxes(),
+ 0); // TODO: Bug 680289, implement gamepad haptics for cocoa
mGamepads[slot].mSuperIndex = index;
}
void
DarwinGamepadService::DeviceRemoved(IOHIDDeviceRef device)
{
RefPtr<GamepadPlatformService> service =
GamepadPlatformService::GetParentService();
--- a/dom/gamepad/ipc/GamepadEventChannelChild.cpp
+++ b/dom/gamepad/ipc/GamepadEventChannelChild.cpp
@@ -33,10 +33,30 @@ GamepadEventChannelChild::RecvGamepadUpd
const GamepadChangeEvent& aGamepadEvent)
{
DebugOnly<nsresult> rv =
NS_DispatchToMainThread(new GamepadUpdateRunnable(aGamepadEvent));
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
return IPC_OK();
}
+void
+GamepadEventChannelChild::AddPromise(const uint32_t& aID, dom::Promise* aPromise)
+{
+ MOZ_ASSERT(!mPromiseList.Get(aID, nullptr));
+ mPromiseList.Put(aID, aPromise);
+}
+
+mozilla::ipc::IPCResult
+GamepadEventChannelChild::RecvReplyGamepadVibrateHaptic(const uint32_t& aPromiseID)
+{
+ RefPtr<dom::Promise> p;
+ if (!mPromiseList.Get(aPromiseID, getter_AddRefs(p))) {
+ MOZ_CRASH("We should always have a promise.");
+ }
+
+ p->MaybeResolve(true);
+ mPromiseList.Remove(aPromiseID);
+ return IPC_OK();
+}
+
} // namespace dom
} // namespace mozilla
--- a/dom/gamepad/ipc/GamepadEventChannelChild.h
+++ b/dom/gamepad/ipc/GamepadEventChannelChild.h
@@ -6,19 +6,25 @@
#ifndef mozilla_dom_GamepadEventChannelChild_h_
#define mozilla_dom_GamepadEventChannelChild_h_
namespace mozilla{
namespace dom{
class GamepadEventChannelChild final : public PGamepadEventChannelChild
{
- public:
+public:
GamepadEventChannelChild() {}
~GamepadEventChannelChild() {}
virtual mozilla::ipc::IPCResult
RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) override;
+ virtual mozilla::ipc::IPCResult
+ RecvReplyGamepadVibrateHaptic(const uint32_t& aPromiseID) override;
+ void AddPromise(const uint32_t& aID, dom::Promise* aPromise);
+
+private:
+ nsRefPtrHashtable<nsUint32HashKey, dom::Promise> mPromiseList;
};
}// namespace dom
}// namespace mozilla
#endif
--- a/dom/gamepad/ipc/GamepadEventChannelParent.cpp
+++ b/dom/gamepad/ipc/GamepadEventChannelParent.cpp
@@ -68,16 +68,32 @@ GamepadEventChannelParent::RecvGamepadLi
RefPtr<GamepadPlatformService> service =
GamepadPlatformService::GetParentService();
MOZ_ASSERT(service);
service->RemoveChannelParent(this);
Unused << Send__delete__(this);
return IPC_OK();
}
+mozilla::ipc::IPCResult
+GamepadEventChannelParent::RecvVibrateHaptic(const uint32_t& aControllerIdx,
+ const uint32_t& aHapticIndex,
+ const double& aIntensity,
+ const double& aDuration,
+ const uint32_t& aPromiseID)
+{
+ // TODO: Bug 680289, implement for standard gamepads
+
+ if (SendReplyGamepadVibrateHaptic(aPromiseID)) {
+ return IPC_OK();
+ }
+
+ return IPC_FAIL(this, "SendReplyGamepadVibrateHaptic fail.");
+}
+
void
GamepadEventChannelParent::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnBackgroundThread();
// It may be called because IPDL child side crashed, we'll
// not receive RecvGamepadListenerRemoved in that case
if (mHasGamepadListener) {
--- a/dom/gamepad/ipc/GamepadEventChannelParent.h
+++ b/dom/gamepad/ipc/GamepadEventChannelParent.h
@@ -12,16 +12,21 @@ namespace dom{
class GamepadEventChannelParent final : public PGamepadEventChannelParent
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GamepadEventChannelParent)
GamepadEventChannelParent();
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual mozilla::ipc::IPCResult RecvGamepadListenerAdded() override;
virtual mozilla::ipc::IPCResult RecvGamepadListenerRemoved() override;
+ virtual mozilla::ipc::IPCResult RecvVibrateHaptic(const uint32_t& aControllerIdx,
+ const uint32_t& aHapticIndex,
+ const double& aIntensity,
+ const double& aDuration,
+ const uint32_t& aPromiseID) override;
void DispatchUpdateEvent(const GamepadChangeEvent& aEvent);
bool HasGamepadListener() const { return mHasGamepadListener; }
private:
~GamepadEventChannelParent() {}
bool mHasGamepadListener;
nsCOMPtr<nsIThread> mBackgroundThread;
};
--- a/dom/gamepad/ipc/GamepadTestChannelChild.h
+++ b/dom/gamepad/ipc/GamepadTestChannelChild.h
@@ -15,15 +15,16 @@ class GamepadTestChannelChild final : pu
{
public:
GamepadTestChannelChild() {}
~GamepadTestChannelChild() {}
void AddPromise(const uint32_t& aID, Promise* aPromise);
private:
virtual mozilla::ipc::IPCResult RecvReplyGamepadIndex(const uint32_t& aID,
const uint32_t& aIndex) override;
- nsRefPtrHashtable<nsUint32HashKey, Promise> mPromiseList;
+
+ nsRefPtrHashtable<nsUint32HashKey, dom::Promise> mPromiseList;
};
}// namespace dom
}// namespace mozilla
#endif
--- a/dom/gamepad/ipc/GamepadTestChannelParent.cpp
+++ b/dom/gamepad/ipc/GamepadTestChannelParent.cpp
@@ -19,18 +19,20 @@ GamepadTestChannelParent::RecvGamepadTes
GamepadPlatformService::GetParentService();
MOZ_ASSERT(service);
if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
const GamepadAdded& a = aEvent.get_GamepadAdded();
nsCString gamepadID;
LossyCopyUTF16toASCII(a.id(), gamepadID);
uint32_t index = service->AddGamepad(gamepadID.get(),
static_cast<GamepadMappingType>(a.mapping()),
+ a.hand(),
a.num_buttons(),
- a.num_axes());
+ a.num_axes(),
+ a.num_haptics());
if (!mShuttingdown) {
Unused << SendReplyGamepadIndex(aID, index);
}
return IPC_OK();
}
if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
const GamepadRemoved& a = aEvent.get_GamepadRemoved();
service->RemoveGamepad(a.index());
@@ -41,16 +43,21 @@ GamepadTestChannelParent::RecvGamepadTes
service->NewButtonEvent(a.index(), a.button(), a.pressed(), a.value());
return IPC_OK();
}
if (aEvent.type() == GamepadChangeEvent::TGamepadAxisInformation) {
const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
service->NewAxisMoveEvent(a.index(), a.axis(), a.value());
return IPC_OK();
}
+ if (aEvent.type() == GamepadChangeEvent::TGamepadPoseInformation) {
+ const GamepadPoseInformation& a = aEvent.get_GamepadPoseInformation();
+ service->NewPoseEvent(a.index(), a.pose_state());
+ return IPC_OK();
+ }
NS_WARNING("Unknown event type.");
return IPC_FAIL_NO_REASON(this);
}
mozilla::ipc::IPCResult
GamepadTestChannelParent::RecvShutdownChannel()
{
--- a/dom/gamepad/ipc/PGamepadEventChannel.ipdl
+++ b/dom/gamepad/ipc/PGamepadEventChannel.ipdl
@@ -7,15 +7,18 @@ include GamepadEventTypes;
namespace mozilla {
namespace dom {
async protocol PGamepadEventChannel {
manager PBackground;
parent:
async GamepadListenerAdded();
async GamepadListenerRemoved();
+ async VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
+ double aIntensity, double aDuration, uint32_t aPromiseID);
child:
async __delete__();
async GamepadUpdate(GamepadChangeEvent aGamepadEvent);
+ async ReplyGamepadVibrateHaptic(uint32_t aPromiseID);
};
}
}
--- a/dom/gamepad/linux/LinuxGamepad.cpp
+++ b/dom/gamepad/linux/LinuxGamepad.cpp
@@ -142,18 +142,20 @@ LinuxGamepadService::AddDevice(struct ud
char numAxes = 0, numButtons = 0;
ioctl(fd, JSIOCGAXES, &numAxes);
gamepad.numAxes = numAxes;
ioctl(fd, JSIOCGBUTTONS, &numButtons);
gamepad.numButtons = numButtons;
gamepad.index = service->AddGamepad(gamepad.idstring,
mozilla::dom::GamepadMappingType::_empty,
+ mozilla::dom::GamepadHand::_empty,
gamepad.numButtons,
- gamepad.numAxes);
+ gamepad.numAxes,
+ 0); // TODO: Bug 680289, implement gamepad haptics for Linux.
gamepad.source_id =
g_io_add_watch(channel,
GIOCondition(G_IO_IN | G_IO_ERR | G_IO_HUP),
OnGamepadData,
GINT_TO_POINTER(gamepad.index));
g_io_channel_unref(channel);
--- a/dom/gamepad/windows/WindowsGamepad.cpp
+++ b/dom/gamepad/windows/WindowsGamepad.cpp
@@ -498,18 +498,20 @@ WindowsGamepadService::ScanForXInputDevi
Gamepad gamepad(kStandardGamepadAxes,
kStandardGamepadButtons,
true,
kXInputGamepad);
gamepad.userIndex = i;
gamepad.state = state;
gamepad.id = service->AddGamepad("xinput",
GamepadMappingType::Standard,
+ GamepadHand::_empty,
kStandardGamepadButtons,
- kStandardGamepadAxes);
+ kStandardGamepadAxes,
+ 0); // TODO: Bug 680289, implement gamepad haptics for Windows.
mGamepads.AppendElement(gamepad);
}
return found;
}
void
WindowsGamepadService::ScanForDevices()
@@ -781,18 +783,20 @@ WindowsGamepadService::GetRawGamepad(HAN
gamepad.handle = handle;
for (unsigned i = 0; i < gamepad.numAxes; i++) {
gamepad.axes[i].caps = axes[i];
}
gamepad.id = service->AddGamepad(gamepad_id,
GamepadMappingType::_empty,
+ GamepadHand::_empty,
gamepad.numButtons,
- gamepad.numAxes);
+ gamepad.numAxes,
+ 0);
mGamepads.AppendElement(gamepad);
return true;
}
bool
WindowsGamepadService::HandleRawInput(HRAWINPUT handle)
{
if (!mHID) {
--- a/dom/tests/mochitest/gamepad/mochitest.ini
+++ b/dom/tests/mochitest/gamepad/mochitest.ini
@@ -2,11 +2,12 @@
support-files =
gamepad_frame.html
gamepad_frame_state.html
mock_gamepad.js
[test_check_timestamp.html]
[test_gamepad.html]
[test_gamepad_connect_events.html]
+[test_gamepad_extensions.html]
[test_gamepad_frame_state_sync.html]
[test_gamepad_hidden_frame.html]
[test_navigator_gamepads.html]
--- a/dom/tests/mochitest/gamepad/test_check_timestamp.html
+++ b/dom/tests/mochitest/gamepad/test_check_timestamp.html
@@ -19,18 +19,20 @@ var firstPress = true;
var testOver = false;
SimpleTest.waitForExplicitFinish();
runGamepadTest(checkTimestamp);
function checkTimestamp(){
GamepadService.addGamepad("test gamepad 1",
GamepadService.standardMapping,
+ GamepadService.noHand,
4,
- 2).then(function(i) {
+ 2,
+ 0).then(function(i) {
index = i;
// Press a button to make the gamepad visible
// to the page.
GamepadService.newButtonEvent(index, 0, true);
GamepadService.newButtonEvent(index, 0, true);
ok(true, "test");
});
}
--- a/dom/tests/mochitest/gamepad/test_gamepad.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad.html
@@ -28,18 +28,20 @@ window.addEventListener("gamepadbuttonup
});
runGamepadTest(startTest);
function startTest() {
// Add a gamepad
GamepadService.addGamepad("test gamepad", // id
GamepadService.standardMapping,
+ GamepadService.noHand,
4,
- 2).then(function(i) {
+ 2,
+ 0).then(function(i) {
index = i;
// Simulate button events on the gamepad we added
GamepadService.newButtonEvent(index, 0, true);
});
}
function connecthandler(e) {
ok(e.gamepad.timestamp <= performance.now(),
--- a/dom/tests/mochitest/gamepad/test_gamepad_connect_events.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad_connect_events.html
@@ -26,18 +26,20 @@ function pressButton() {
function startTests() {
window.addEventListener("gamepadbuttondown", function() {
// Wait to ensure that all frames received the button press as well.
SpecialPowers.executeSoon(tests[testNum++]);
});
GamepadService.addGamepad("test gamepad", // id
GamepadService.standardMapping,
+ GamepadService.noHand,
4, // buttons
- 2).then(function(i) {
+ 2,
+ 0).then(function(i) {
gamepad_index = i;
gamepad_connected()
});
}
var f1, f2;
function gamepad_connected() {
f1 = document.getElementById('f1');
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/gamepad/test_gamepad_extensions.html
@@ -0,0 +1,125 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test gamepad</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript" src="mock_gamepad.js"></script>
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ poseadd,
+ posecheck,
+ haptictest
+];
+var gamepad_index = 0;
+var testNum = 0;
+var poseOrient = new Float32Array([-0.203, -0.235, 0.740, -0.596]);
+var posePos = new Float32Array([-0.0233, -0.707, -0.763]);
+var poseAngVel = new Float32Array([-0.0008, 0.00147, 0.001]);
+var poseAngAcc = new Float32Array([-0.494, 0.476, -0.241]);
+var poseLinVel = new Float32Array([0.003,0.024,-0.068]);
+var poseLinAcc = new Float32Array([-1.211,21.427,-2.348]);
+
+window.addEventListener("gamepadconnected", connecthandler);
+window.addEventListener("gamepadbuttondown", function() {
+ // Wait to ensure that all frames received the button press as well.
+ SpecialPowers.executeSoon(tests[testNum++]);
+});
+
+function pressButton() {
+ GamepadService.newButtonEvent(gamepad_index, 0, true);
+ GamepadService.newButtonEvent(gamepad_index, 0, false);
+}
+
+runGamepadTest(startTest);
+
+function startTest() {
+ SpecialPowers.pushPrefEnv({ "set": [["dom.gamepad.extensions.enabled", true]] });
+ // Add a gamepad
+ GamepadService.addGamepad("test gamepad", // id
+ GamepadService.standardMapping,
+ GamepadService.leftHand,
+ 4,
+ 2,
+ 1).then(function(i) {
+ gamepad_index = i;
+ // Simulate button events on the gamepad we added
+ pressButton();
+ });
+}
+
+function connecthandler(e) {
+ ok(e.gamepad.timestamp <= performance.now(),
+ "gamepad.timestamp should less than or equal to performance.now()");
+ is(e.gamepad.index, 0, "correct gamepad index");
+ is(e.gamepad.id, "test gamepad", "correct gamepad name");
+ is(e.gamepad.mapping, "standard", "standard mapping");
+ is(e.gamepad.hand, "left", "left hand");
+ is(e.gamepad.buttons.length, 4, "correct number of buttons");
+ is(e.gamepad.axes.length, 2, "correct number of axes");
+ is(e.gamepad.hapticActuators.length, 1, "correct number of haptics");
+}
+
+function checkValueInFloat32Array(array1, array2) {
+ if (array1.length != array2.length) {
+ return false;
+ }
+ var index = 0;
+ while (index < array2.length) {
+ if (array1[index] != array2[index]) {
+ return false;
+ }
+ ++index;
+ }
+ return true;
+}
+
+function poseadd() {
+ GamepadService.newPoseMove(gamepad_index, poseOrient,
+ posePos, poseAngVel, poseAngAcc,
+ poseLinVel, poseLinAcc);
+ pressButton();
+}
+
+function posecheck() {
+ var gamepads = navigator.getGamepads();
+ var pose = gamepads[0].pose;
+ is(gamepads[0].pose.hasOrientation, true,
+ "correct gamepadPose hasOrientation");
+ is(gamepads[0].pose.hasPosition, true,
+ "correct gamepadPose hasPosition");
+ is(checkValueInFloat32Array(pose.orientation, poseOrient), true,
+ "correct gamepadPose orientation");
+ is(checkValueInFloat32Array(pose.position, posePos), true,
+ "correct gamepadPose position");
+ is(checkValueInFloat32Array(pose.angularVelocity, poseAngVel), true,
+ "correct gamepadPose angularVelocity");
+ is(checkValueInFloat32Array(pose.angularAcceleration, poseAngAcc), true,
+ "correct gamepadPose angularAcceleration");
+ is(checkValueInFloat32Array(pose.linearVelocity, poseLinVel), true,
+ "correct gamepadPose linearVelocity");
+ is(checkValueInFloat32Array(pose.linearAcceleration, poseLinAcc), true,
+ "correct gamepadPose linearAcceleration");
+ pressButton();
+}
+
+function haptictest() {
+ var gamepads = navigator.getGamepads();
+ var hapticActuators = gamepads[0].hapticActuators[0];
+ hapticActuators.pulse(1, 100).then(function(result) {
+ is(result, true, "gamepad hapticActuators test success.");
+ GamepadService.removeGamepad(gamepad_index);
+ SimpleTest.finish();
+ });
+}
+
+</script>
+</body>
+</html>
+
--- a/dom/tests/mochitest/gamepad/test_gamepad_frame_state_sync.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad_frame_state_sync.html
@@ -21,18 +21,20 @@ function setFrameVisible(f, visible) {
}
var frames_loaded = 0;
function startTest() {
frames_loaded++;
if (frames_loaded == 2) {
GamepadService.addGamepad("test gamepad", // id
GamepadService.standardMapping,
+ GamepadService.noHand,
4, // buttons
- 2).then(function(i) {
+ 2,
+ 0).then(function(i) {
index = i;
gamepad_loaded();
});
}
}
var f1, f2;
function gamepad_loaded() {
f1 = document.getElementById('f1');
--- a/dom/tests/mochitest/gamepad/test_gamepad_hidden_frame.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad_hidden_frame.html
@@ -29,18 +29,20 @@ function setFrameVisible(f, visible) {
}
var frames_loaded = 0;
function startTest() {
frames_loaded++;
if (frames_loaded == 2) {
GamepadService.addGamepad("test gamepad", // id
GamepadService.standardMapping,
+ GamepadService.noHand,
4, // buttons
- 2).then(function(i) {
+ 2,
+ 0).then(function(i) {
index = i;
gamepad_loaded();
});
}
}
var f1, f2;
function gamepad_loaded() {
f1 = document.getElementById('f1');
--- a/dom/tests/mochitest/gamepad/test_navigator_gamepads.html
+++ b/dom/tests/mochitest/gamepad/test_navigator_gamepads.html
@@ -37,18 +37,20 @@ window.addEventListener("gamepaddisconne
runGamepadTest(startTest)
function startTest() {
// gamepads should be empty first
is(navigator.getGamepads().length, 0, "should be zero gamepads exposed");
// Add a gamepad
GamepadService.addGamepad("test gamepad 1", // id
GamepadService.standardMapping,
+ GamepadService.noHand,
4, // buttons
- 2).then(function(index) {
+ 2,
+ 0).then(function(index) {
internal_index1 = index;
// Press a button to make the gamepad visible to the page.
GamepadService.newButtonEvent(internal_index1, 0, true);
});
}
var content_index1 = 0;
var internal_index2;
@@ -60,18 +62,20 @@ function check_first_gamepad(e) {
is(e.gamepad.id, "test gamepad 1", "correct gamepad name");
var gamepads = navigator.getGamepads();
is(gamepads.length, 1, "should have one gamepad exposed");
is(gamepads[e.gamepad.index], e.gamepad, "right gamepad exposed at index");
is(gamepads[content_index1], e.gamepad, "gamepad counter working correctly");
// Add a second gamepad, should automatically show up.
GamepadService.addGamepad("test gamepad 2", // id
GamepadService.standardMapping,
+ GamepadService.noHand,
4, // buttons
- 2).then(function(index) {
+ 2,
+ 0).then(function(index) {
internal_index2 = index;
GamepadService.newButtonEvent(internal_index2, 0, true);
});
ok(true, "Done checking first gamepad");
}
function check_second_gamepad(e) {
ok(true, "Checking second gamepad");
--- a/dom/webidl/GamepadServiceTest.webidl
+++ b/dom/webidl/GamepadServiceTest.webidl
@@ -2,30 +2,42 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
[Pref="dom.gamepad.test.enabled"]
interface GamepadServiceTest
{
readonly attribute GamepadMappingType noMapping;
readonly attribute GamepadMappingType standardMapping;
+ readonly attribute GamepadHand noHand;
+ readonly attribute GamepadHand leftHand;
+ readonly attribute GamepadHand rightHand;
[Throws]
Promise<unsigned long> addGamepad(DOMString id,
GamepadMappingType mapping,
+ GamepadHand hand,
unsigned long numButtons,
- unsigned long numAxes);
+ unsigned long numAxes,
+ unsigned long numHaptics);
void removeGamepad(unsigned long index);
void newButtonEvent(unsigned long index,
unsigned long button,
boolean pressed);
void newButtonValueEvent(unsigned long index,
unsigned long button,
boolean pressed,
double value);
void newAxisMoveEvent(unsigned long index,
unsigned long axis,
double value);
+ void newPoseMove(unsigned long index,
+ Float32Array? orient,
+ Float32Array? pos,
+ Float32Array? angVelocity,
+ Float32Array? angAcceleration,
+ Float32Array? linVelocity,
+ Float32Array? linAcceleration);
};
\ No newline at end of file