Bug 852944 - Gamepad API IPC; r=ted, r=baku

This commit is contained in:
Kyle Machulis 2015-04-23 15:05:29 -07:00
parent 5fdce942ec
commit b9e62b0591
33 changed files with 757 additions and 371 deletions

View File

@ -187,6 +187,7 @@
#include "mozilla/dom/StructuredCloneTags.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/Gamepad.h"
#include "mozilla/dom/GamepadService.h"
#endif
@ -13367,6 +13368,15 @@ void
nsGlobalWindow::AddGamepad(uint32_t aIndex, Gamepad* aGamepad)
{
MOZ_ASSERT(IsInnerWindow());
// Create the index we will present to content based on which indices are
// already taken, as required by the spec.
// https://w3c.github.io/gamepad/gamepad.html#widl-Gamepad-index
int index = 0;
while(mGamepadIndexSet.Contains(index)) {
++index;
}
mGamepadIndexSet.Put(index);
aGamepad->SetIndex(index);
mGamepads.Put(aIndex, aGamepad);
}
@ -13374,6 +13384,12 @@ void
nsGlobalWindow::RemoveGamepad(uint32_t aIndex)
{
MOZ_ASSERT(IsInnerWindow());
nsRefPtr<Gamepad> gamepad;
if (!mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
return;
}
// Free up the index we were using so it can be reused
mGamepadIndexSet.Remove(gamepad->Index());
mGamepads.Remove(aIndex);
}
@ -13384,8 +13400,8 @@ nsGlobalWindow::EnumGamepadsForGet(const uint32_t& aKey, Gamepad* aData,
{
nsTArray<nsRefPtr<Gamepad> >* array =
static_cast<nsTArray<nsRefPtr<Gamepad> >*>(aUserArg);
array->EnsureLengthAtLeast(aKey + 1);
(*array)[aKey] = aData;
array->EnsureLengthAtLeast(aData->Index() + 1);
(*array)[aData->Index()] = aData;
return PL_DHASH_NEXT;
}
@ -13404,6 +13420,7 @@ nsGlobalWindow::GetGamepad(uint32_t aIndex)
{
MOZ_ASSERT(IsInnerWindow());
nsRefPtr<Gamepad> gamepad;
if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
return gamepad.forget();
}

View File

@ -52,6 +52,7 @@
#include "Units.h"
#include "nsComponentManagerUtils.h"
#include "nsSize.h"
#include "nsCheapSets.h"
#define DEFAULT_HOME_PAGE "www.mozilla.org"
#define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
@ -1590,6 +1591,7 @@ protected:
// Indicates whether this window wants gamepad input events
bool mHasGamepad : 1;
#ifdef MOZ_GAMEPAD
nsCheapSet<nsUint32HashKey> mGamepadIndexSet;
nsRefPtrHashtable<nsUint32HashKey, mozilla::dom::Gamepad> mGamepads;
bool mHasSeenGamepadInput;
#endif

View File

@ -0,0 +1,105 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/dom/GamepadFunctions.h"
#include "mozilla/dom/GamepadService.h"
#include "nsHashKeys.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/unused.h"
namespace mozilla {
namespace dom {
namespace GamepadFunctions {
namespace {
// Increments as gamepads are added
uint32_t gGamepadIndex = 0;
}
template<class T>
void
NotifyGamepadChange(const T& aInfo)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
GamepadChangeEvent e(aInfo);
nsTArray<ContentParent*> t;
ContentParent::GetAll(t);
for(uint32_t i = 0; i < t.Length(); ++i) {
unused << t[i]->SendGamepadUpdate(e);
}
// If we have a GamepadService in the main process, send directly to it.
if (GamepadService::IsServiceRunning()) {
nsRefPtr<GamepadService> svc = GamepadService::GetService();
svc->Update(e);
}
}
uint32_t
AddGamepad(const char* aID,
GamepadMappingType aMapping,
uint32_t aNumButtons, uint32_t aNumAxes)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
int index = gGamepadIndex;
gGamepadIndex++;
GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index,
(uint32_t)aMapping, aNumButtons, aNumAxes);
gGamepadIndex++;
NotifyGamepadChange<GamepadAdded>(a);
return index;
}
void
RemoveGamepad(uint32_t aIndex)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
GamepadRemoved a(aIndex);
NotifyGamepadChange<GamepadRemoved>(a);
}
void
NewButtonEvent(uint32_t aIndex, uint32_t aButton,
bool aPressed, double aValue)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
GamepadButtonInformation a(aIndex, aButton, aPressed, aValue);
NotifyGamepadChange<GamepadButtonInformation>(a);
}
void
NewButtonEvent(uint32_t aIndex, uint32_t aButton,
bool aPressed)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
// When only a digital button is available the value will be synthesized.
NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L);
}
void
NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
double aValue)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
GamepadAxisInformation a(aIndex, aAxis, aValue);
NotifyGamepadChange<GamepadAxisInformation>(a);
}
void
ResetGamepadIndexes()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
gGamepadIndex = 0;
}
} // namespace GamepadFunctions
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef mozilla_dom_GamepadFunctions_h_
#define mozilla_dom_GamepadFunctions_h_
#include "mozilla/dom/GamepadBinding.h"
namespace mozilla {
namespace dom {
namespace GamepadFunctions {
// Functions for building and transmitting IPDL messages through the HAL
// sandbox. Used by platform specific Gamepad implementations
// 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);
// 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);
// When shutting down the platform communications for gamepad, also reset the
// indexes.
void ResetGamepadIndexes();
} // namespace GamepadFunctions
} // namespace dom
} // namespace mozilla
#endif

View File

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "mozilla/dom/GamepadMonitoring.h"
#include "mozilla/dom/GamepadFunctions.h"
#include "mozilla/dom/PContentParent.h"
namespace mozilla {
namespace dom {
using namespace GamepadFunctions;
void
MaybeStopGamepadMonitoring()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
nsTArray<ContentParent*> t;
ContentParent::GetAll(t);
for(uint32_t i = 0; i < t.Length(); ++i) {
if (t[i]->HasGamepadListener()) {
return;
}
}
StopGamepadMonitoring();
ResetGamepadIndexes();
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef mozilla_dom_GamepadMonitoring_h_
#define mozilla_dom_GamepadMonitoring_h_
namespace mozilla {
namespace dom {
// Functions for platform specific gamepad monitoring.
void MaybeStopGamepadMonitoring();
// These two functions are implemented in the platform specific service files
// (linux/LinuxGamepad.cpp, cocoa/CocoaGamepad.cpp, etc)
void StartGamepadMonitoring();
void StopGamepadMonitoring();
} // namespace dom
} // namespace mozilla
#endif

View File

@ -2,13 +2,18 @@
* 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/. */
#include "mozilla/Hal.h"
#include "mozilla/dom/GamepadService.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Gamepad.h"
#include "mozilla/dom/GamepadAxisMoveEvent.h"
#include "mozilla/dom/GamepadButtonEvent.h"
#include "mozilla/dom/GamepadEvent.h"
#include "mozilla/dom/GamepadMonitoring.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPtr.h"
#include "GamepadService.h"
#include "Gamepad.h"
#include "nsAutoPtr.h"
#include "nsIDOMEvent.h"
#include "nsIDOMDocument.h"
@ -20,10 +25,6 @@
#include "nsThreadUtils.h"
#include "mozilla/Services.h"
#include "mozilla/dom/GamepadAxisMoveEvent.h"
#include "mozilla/dom/GamepadButtonEvent.h"
#include "mozilla/dom/GamepadEvent.h"
#include <cstddef>
namespace mozilla {
@ -82,7 +83,11 @@ GamepadService::BeginShutdown()
mTimer->Cancel();
}
if (mStarted) {
mozilla::hal::StopMonitoringGamepadStatus();
if (XRE_GetProcessType() == GeckoProcessType_Default) {
MaybeStopGamepadMonitoring();
} else {
ContentChild::GetSingleton()->SendGamepadListenerRemoved();
}
mStarted = false;
}
// Don't let windows call back to unregister during shutdown
@ -99,7 +104,6 @@ GamepadService::AddListener(nsGlobalWindow* aWindow)
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsInnerWindow());
if (mShuttingDown) {
return;
}
@ -109,10 +113,13 @@ GamepadService::AddListener(nsGlobalWindow* aWindow)
}
if (!mStarted && mEnabled) {
mozilla::hal::StartMonitoringGamepadStatus();
if (XRE_GetProcessType() == GeckoProcessType_Default) {
StartGamepadMonitoring();
} else {
ContentChild::GetSingleton()->SendGamepadListenerAdded();
}
mStarted = true;
}
mListeners.AppendElement(aWindow);
}
@ -139,8 +146,20 @@ GamepadService::RemoveListener(nsGlobalWindow* aWindow)
}
}
uint32_t
GamepadService::AddGamepad(const char* aId,
already_AddRefed<Gamepad>
GamepadService::GetGamepad(uint32_t aIndex)
{
nsRefPtr<Gamepad> gamepad;
if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
return gamepad.forget();
}
return nullptr;
}
void
GamepadService::AddGamepad(uint32_t aIndex,
const nsAString& aId,
GamepadMappingType aMapping,
uint32_t aNumButtons,
uint32_t aNumAxes)
@ -148,63 +167,40 @@ GamepadService::AddGamepad(const char* aId,
//TODO: bug 852258: get initial button/axis state
nsRefPtr<Gamepad> gamepad =
new Gamepad(nullptr,
NS_ConvertUTF8toUTF16(nsDependentCString(aId)),
0,
aId,
0, // index is set by global window
aMapping,
aNumButtons,
aNumAxes);
int index = -1;
for (uint32_t i = 0; i < mGamepads.Length(); i++) {
if (!mGamepads[i]) {
mGamepads[i] = gamepad;
index = i;
break;
}
}
if (index == -1) {
mGamepads.AppendElement(gamepad);
index = mGamepads.Length() - 1;
}
gamepad->SetIndex(index);
NewConnectionEvent(index, true);
return index;
// We store the gamepad related to its index given by the parent process.
mGamepads.Put(aIndex, gamepad);
NewConnectionEvent(aIndex, true);
}
void
GamepadService::RemoveGamepad(uint32_t aIndex)
{
if (aIndex < mGamepads.Length()) {
mGamepads[aIndex]->SetConnected(false);
NewConnectionEvent(aIndex, false);
// If this is the last entry in the list, just remove it.
if (aIndex == mGamepads.Length() - 1) {
mGamepads.RemoveElementAt(aIndex);
} else {
// Otherwise just null it out and leave it, so the
// indices of the following entries remain valid.
mGamepads[aIndex] = nullptr;
}
nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
if (!gamepad) {
NS_WARNING("Trying to delete gamepad with invalid index");
return;
}
}
void
GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed)
{
// Synthesize a value: 1.0 for pressed, 0.0 for unpressed.
NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L);
gamepad->SetConnected(false);
NewConnectionEvent(aIndex, false);
mGamepads.Remove(aIndex);
}
void
GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
double aValue)
{
if (mShuttingDown || aIndex >= mGamepads.Length()) {
nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
if (mShuttingDown || !gamepad) {
return;
}
mGamepads[aIndex]->SetButton(aButton, aPressed, aValue);
gamepad->SetButton(aButton, aPressed, aValue);
// Hold on to listeners in a separate array because firing events
// can mutate the mListeners array.
@ -227,15 +223,15 @@ GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed,
first_time = true;
}
nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
if (gamepad) {
gamepad->SetButton(aButton, aPressed, aValue);
nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
if (listenerGamepad) {
listenerGamepad->SetButton(aButton, aPressed, aValue);
if (first_time) {
FireConnectionEvent(listeners[i], gamepad, true);
FireConnectionEvent(listeners[i], listenerGamepad, true);
}
if (mNonstandardEventsEnabled) {
// Fire event
FireButtonEvent(listeners[i], gamepad, aButton, aValue);
FireButtonEvent(listeners[i], listenerGamepad, aButton, aValue);
}
}
}
@ -266,10 +262,11 @@ GamepadService::FireButtonEvent(EventTarget* aTarget,
void
GamepadService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue)
{
if (mShuttingDown || aIndex >= mGamepads.Length()) {
nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
if (mShuttingDown || !gamepad) {
return;
}
mGamepads[aIndex]->SetAxis(aAxis, aValue);
gamepad->SetAxis(aAxis, aValue);
// Hold on to listeners in a separate array because firing events
// can mutate the mListeners array.
@ -292,15 +289,15 @@ GamepadService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue)
first_time = true;
}
nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
if (gamepad) {
gamepad->SetAxis(aAxis, aValue);
nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
if (listenerGamepad) {
listenerGamepad->SetAxis(aAxis, aValue);
if (first_time) {
FireConnectionEvent(listeners[i], gamepad, true);
FireConnectionEvent(listeners[i], listenerGamepad, true);
}
if (mNonstandardEventsEnabled) {
// Fire event
FireAxisMoveEvent(listeners[i], gamepad, aAxis, aValue);
FireAxisMoveEvent(listeners[i], listenerGamepad, aAxis, aValue);
}
}
}
@ -332,7 +329,9 @@ GamepadService::FireAxisMoveEvent(EventTarget* aTarget,
void
GamepadService::NewConnectionEvent(uint32_t aIndex, bool aConnected)
{
if (mShuttingDown || aIndex >= mGamepads.Length()) {
nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
if (mShuttingDown || !gamepad) {
return;
}
@ -358,10 +357,10 @@ GamepadService::NewConnectionEvent(uint32_t aIndex, bool aConnected)
SetWindowHasSeenGamepad(listeners[i], aIndex);
nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
if (gamepad) {
nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
if (listenerGamepad) {
// Fire event
FireConnectionEvent(listeners[i], gamepad, aConnected);
FireConnectionEvent(listeners[i], listenerGamepad, aConnected);
}
}
} else {
@ -374,11 +373,11 @@ GamepadService::NewConnectionEvent(uint32_t aIndex, bool aConnected)
// deal with the hassle of syncing the state of removed gamepads.
if (WindowHasSeenGamepad(listeners[i], aIndex)) {
nsRefPtr<Gamepad> gamepad = listeners[i]->GetGamepad(aIndex);
if (gamepad) {
gamepad->SetConnected(false);
nsRefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
if (listenerGamepad) {
listenerGamepad->SetConnected(false);
// Fire event
FireConnectionEvent(listeners[i], gamepad, false);
FireConnectionEvent(listeners[i], listenerGamepad, false);
listeners[i]->RemoveGamepad(aIndex);
}
}
@ -409,11 +408,19 @@ GamepadService::FireConnectionEvent(EventTarget* aTarget,
void
GamepadService::SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad)
{
if (mShuttingDown || !mEnabled || aIndex > mGamepads.Length()) {
nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
if (mShuttingDown || !mEnabled || !gamepad) {
return;
}
aGamepad->SyncState(mGamepads[aIndex]);
aGamepad->SyncState(gamepad);
}
// static
bool
GamepadService::IsServiceRunning()
{
return !!gGamepadServiceSingleton;
}
// static
@ -461,8 +468,13 @@ GamepadService::SetWindowHasSeenGamepad(nsGlobalWindow* aWindow,
if (aHasSeen) {
aWindow->SetHasSeenGamepadInput(true);
nsCOMPtr<nsISupports> window = ToSupports(aWindow);
nsRefPtr<Gamepad> gamepad = mGamepads[aIndex]->Clone(window);
aWindow->AddGamepad(aIndex, gamepad);
nsRefPtr<Gamepad> gamepad = GetGamepad(aIndex);
MOZ_ASSERT(gamepad);
if (!gamepad) {
return;
}
nsRefPtr<Gamepad> clonedGamepad = gamepad->Clone(window);
aWindow->AddGamepad(aIndex, clonedGamepad);
} else {
aWindow->RemoveGamepad(aIndex);
}
@ -486,13 +498,16 @@ GamepadService::TimeoutHandler(nsITimer* aTimer, void* aClosure)
}
if (self->mListeners.Length() == 0) {
mozilla::hal::StopMonitoringGamepadStatus();
if (XRE_GetProcessType() == GeckoProcessType_Default) {
MaybeStopGamepadMonitoring();
} else {
ContentChild::GetSingleton()->SendGamepadListenerRemoved();
}
self->mStarted = false;
if (!self->mGamepads.IsEmpty()) {
self->mGamepads.Clear();
}
}
}
void
GamepadService::StartCleanupTimer()
@ -510,71 +525,26 @@ GamepadService::StartCleanupTimer()
}
}
/*
* Implementation of the test service. This is just to provide a simple binding
* of the GamepadService to JavaScript via XPCOM so that we can write Mochitests
* that add and remove fake gamepads, avoiding the platform-specific backends.
*/
NS_IMPL_ISUPPORTS(GamepadServiceTest, nsIGamepadServiceTest)
GamepadServiceTest* GamepadServiceTest::sSingleton = nullptr;
// static
already_AddRefed<GamepadServiceTest>
GamepadServiceTest::CreateService()
void
GamepadService::Update(const GamepadChangeEvent& aEvent)
{
if (sSingleton == nullptr) {
sSingleton = new GamepadServiceTest();
if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
const GamepadAdded& a = aEvent.get_GamepadAdded();
AddGamepad(a.index(), a.id(),
static_cast<GamepadMappingType>(a.mapping()),
a.num_buttons(), a.num_axes());
} else if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
const GamepadRemoved& a = aEvent.get_GamepadRemoved();
RemoveGamepad(a.index());
} else if (aEvent.type() == GamepadChangeEvent::TGamepadButtonInformation) {
const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
NewButtonEvent(a.index(), a.button(), a.pressed(), a.value());
} else if (aEvent.type() == GamepadChangeEvent::TGamepadAxisInformation) {
const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
NewAxisMoveEvent(a.index(), a.axis(), a.value());
} else {
MOZ_CRASH("We shouldn't be here!");
}
nsRefPtr<GamepadServiceTest> service = sSingleton;
return service.forget();
}
GamepadServiceTest::GamepadServiceTest()
{
/* member initializers and constructor code */
nsRefPtr<GamepadService> service = GamepadService::GetService();
}
/* uint32_t addGamepad (in string id, in unsigned long mapping, in unsigned long numButtons, in unsigned long numAxes); */
NS_IMETHODIMP GamepadServiceTest::AddGamepad(const char* aID,
uint32_t aMapping,
uint32_t aNumButtons,
uint32_t aNumAxes,
uint32_t* aRetval)
{
*aRetval = gGamepadServiceSingleton->AddGamepad(aID,
static_cast<GamepadMappingType>(aMapping),
aNumButtons,
aNumAxes);
return NS_OK;
}
/* void removeGamepad (in uint32_t index); */
NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
{
gGamepadServiceSingleton->RemoveGamepad(aIndex);
return NS_OK;
}
/* void newButtonEvent (in uint32_t index, in uint32_t button,
in boolean pressed); */
NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
uint32_t aButton,
bool aPressed)
{
gGamepadServiceSingleton->NewButtonEvent(aIndex, aButton, aPressed);
return NS_OK;
}
/* void newAxisMoveEvent (in uint32_t index, in uint32_t axis,
in double value); */
NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
uint32_t aAxis,
double aValue)
{
gGamepadServiceSingleton->NewAxisMoveEvent(aIndex, aAxis, aValue);
return NS_OK;
}
} // namespace dom

View File

@ -6,7 +6,6 @@
#define mozilla_dom_GamepadService_h_
#include <stdint.h>
#include "Gamepad.h"
#include "nsAutoPtr.h"
#include "nsCOMArray.h"
#include "nsIGamepadServiceTest.h"
@ -15,11 +14,14 @@
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsTArray.h"
// Needed for GamepadMappingType
#include "mozilla/dom/GamepadBinding.h"
namespace mozilla {
namespace dom {
class EventTarget;
class GamepadChangeEvent;
class Gamepad;
class GamepadService : public nsIObserver
{
@ -27,6 +29,8 @@ class GamepadService : public nsIObserver
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
// Returns true if we actually have a service up and running
static bool IsServiceRunning();
// Get the singleton service
static already_AddRefed<GamepadService> GetService();
// Return true if the API is preffed on.
@ -39,9 +43,10 @@ class GamepadService : public nsIObserver
// Indicate that |aWindow| should no longer receive gamepad events.
void RemoveListener(nsGlobalWindow* aWindow);
// Add a gamepad to the list of known gamepads, and return its index.
uint32_t AddGamepad(const char* aID, GamepadMappingType aMapping,
// Add a gamepad to the list of known gamepads.
void AddGamepad(uint32_t aIndex, const nsAString& aID, GamepadMappingType aMapping,
uint32_t aNumButtons, uint32_t aNumAxes);
// Remove the gamepad at |aIndex| from the list of known gamepads.
void RemoveGamepad(uint32_t aIndex);
@ -51,8 +56,6 @@ class GamepadService : public nsIObserver
// 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
@ -62,6 +65,11 @@ class GamepadService : public nsIObserver
// Synchronize the state of |aGamepad| to match the gamepad stored at |aIndex|
void SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad);
// Returns gamepad object if index exists, null otherwise
already_AddRefed<Gamepad> GetGamepad(uint32_t aIndex);
// Receive GamepadChangeEvent messages from parent process to fire DOM events
void Update(const GamepadChangeEvent& aGamepadEvent);
protected:
GamepadService();
virtual ~GamepadService() {};
@ -115,37 +123,13 @@ class GamepadService : public nsIObserver
// Gamepads connected to the system. Copies of these are handed out
// to each window.
nsTArray<nsRefPtr<Gamepad> > mGamepads;
nsRefPtrHashtable<nsUint32HashKey, Gamepad> mGamepads;
// Inner windows that are listening for gamepad events.
// has been sent to that window.
nsTArray<nsRefPtr<nsGlobalWindow> > mListeners;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIFocusManager> mFocusManager;
nsCOMPtr<nsIObserver> mObserver;
};
// Service for testing purposes
class GamepadServiceTest : public nsIGamepadServiceTest
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIGAMEPADSERVICETEST
GamepadServiceTest();
static already_AddRefed<GamepadServiceTest> CreateService();
private:
static GamepadServiceTest* sSingleton;
virtual ~GamepadServiceTest() {};
};
} // namespace dom
} // namespace mozilla
#define NS_GAMEPAD_TEST_CID \
{ 0xfb1fcb57, 0xebab, 0x4cf4, \
{ 0x96, 0x3b, 0x1e, 0x4d, 0xb8, 0x52, 0x16, 0x96 } }
#define NS_GAMEPAD_TEST_CONTRACTID "@mozilla.org/gamepad-test;1"
#endif // mozilla_dom_GamepadService_h_

View File

@ -0,0 +1,92 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "GamepadServiceTest.h"
#include "mozilla/dom/GamepadService.h"
#include "mozilla/dom/GamepadFunctions.h"
using namespace mozilla::dom;
/*
* Implementation of the test service. This is just to provide a simple binding
* of the GamepadService to JavaScript via XPCOM so that we can write Mochitests
* that add and remove fake gamepads, avoiding the platform-specific backends.
*/
NS_IMPL_ISUPPORTS(GamepadServiceTest, nsIGamepadServiceTest)
GamepadServiceTest* GamepadServiceTest::sSingleton = nullptr;
// static
already_AddRefed<GamepadServiceTest>
GamepadServiceTest::CreateService()
{
if (sSingleton == nullptr) {
sSingleton = new GamepadServiceTest();
}
nsRefPtr<GamepadServiceTest> service = sSingleton;
return service.forget();
}
GamepadServiceTest::GamepadServiceTest() :
mService(GamepadService::GetService())
{
}
GamepadServiceTest::~GamepadServiceTest()
{
}
/* uint32_t addGamepad(in unsigned long index, in string id, in unsigned long mapping, in unsigned long numButtons, in unsigned long numAxes); */
NS_IMETHODIMP
GamepadServiceTest::AddGamepad(const char* aID,
uint32_t aMapping,
uint32_t aNumButtons,
uint32_t aNumAxes,
uint32_t* aGamepadIndex)
{
*aGamepadIndex = GamepadFunctions::AddGamepad(aID,
static_cast<GamepadMappingType>(aMapping),
aNumButtons,
aNumAxes);
return NS_OK;
}
/* void removeGamepad (in uint32_t index); */
NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
{
GamepadFunctions::RemoveGamepad(aIndex);
return NS_OK;
}
/* void newButtonEvent (in uint32_t index, in uint32_t button,
in boolean pressed); */
NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
uint32_t aButton,
bool aPressed)
{
GamepadFunctions::NewButtonEvent(aIndex, aButton, aPressed);
return NS_OK;
}
/* void newButtonEvent (in uint32_t index, in uint32_t button,
in boolean pressed, double value); */
NS_IMETHODIMP GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
uint32_t aButton,
bool aPressed,
double aValue)
{
GamepadFunctions::NewButtonEvent(aIndex, aButton, aPressed, aValue);
return NS_OK;
}
/* void newAxisMoveEvent (in uint32_t index, in uint32_t axis,
in double value); */
NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
uint32_t aAxis,
double aValue)
{
GamepadFunctions::NewAxisMoveEvent(aIndex, aAxis, aValue);
return NS_OK;
}

View File

@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef mozilla_dom_GamepadServiceTest_h_
#define mozilla_dom_GamepadServiceTest_h_
#include "nsIGamepadServiceTest.h"
namespace mozilla {
namespace dom {
class GamepadService;
// Service for testing purposes
class GamepadServiceTest : public nsIGamepadServiceTest
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIGAMEPADSERVICETEST
GamepadServiceTest();
static already_AddRefed<GamepadServiceTest> CreateService();
private:
static GamepadServiceTest* sSingleton;
// Hold a reference to the gamepad service so we don't have to worry about
// execution order in tests.
nsRefPtr<GamepadService> mService;
virtual ~GamepadServiceTest();
};
} // namespace dom
} // namespace mozilla
#define NS_GAMEPAD_TEST_CID \
{ 0xfb1fcb57, 0xebab, 0x4cf4, \
{ 0x96, 0x3b, 0x1e, 0x4d, 0xb8, 0x52, 0x16, 0x96 } }
#define NS_GAMEPAD_TEST_CONTRACTID "@mozilla.org/gamepad-test;1"
#endif

View File

@ -3,25 +3,20 @@
* 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/. */
#include "Hal.h"
#include "AndroidBridge.h"
using namespace mozilla::hal;
namespace mozilla {
namespace hal_impl {
namespace dom {
void
StartMonitoringGamepadStatus()
void StartGamepadMonitoring()
{
widget::GeckoAppShell::StartMonitoringGamepad();
}
void
StopMonitoringGamepadStatus()
void StopGamepadMonitoring()
{
widget::GeckoAppShell::StopMonitoringGamepad();
}
} // hal_impl
} // mozilla
} // namespace dom
} // namespace mozilla

View File

@ -6,7 +6,8 @@
// mostly derived from the Allegro source code at:
// http://alleg.svn.sourceforge.net/viewvc/alleg/allegro/branches/4.9/src/macosx/hidjoy.m?revision=13760&view=markup
#include "mozilla/dom/GamepadService.h"
#include "mozilla/dom/GamepadFunctions.h"
#include "mozilla/ArrayUtils.h"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDBase.h>
#include <IOKit/hid/IOHIDKeys.h>
@ -17,8 +18,8 @@
namespace {
using mozilla::dom::GamepadService;
using namespace mozilla;
using namespace mozilla::dom::GamepadFunctions;
using std::vector;
struct Button {
@ -248,20 +249,18 @@ DarwinGamepadService::DeviceAdded(IOHIDDeviceRef device)
sizeof(product_name), kCFStringEncodingASCII);
char buffer[256];
sprintf(buffer, "%x-%x-%s", vendorId, productId, product_name);
nsRefPtr<GamepadService> service(GamepadService::GetService());
mGamepads[slot].mSuperIndex = service->AddGamepad(buffer,
mozilla::dom::GamepadMappingType::_empty,
(int)mGamepads[slot].numButtons(),
(int)mGamepads[slot].numAxes());
mGamepads[slot].mSuperIndex = AddGamepad(buffer,
mozilla::dom::GamepadMappingType::_empty,
(int)mGamepads[slot].numButtons(),
(int)mGamepads[slot].numAxes());
}
void
DarwinGamepadService::DeviceRemoved(IOHIDDeviceRef device)
{
nsRefPtr<GamepadService> service(GamepadService::GetService());
for (size_t i = 0; i < mGamepads.size(); i++) {
if (mGamepads[i] == device) {
service->RemoveGamepad(mGamepads[i].mSuperIndex);
RemoveGamepad(mGamepads[i].mSuperIndex);
mGamepads[i].clear();
return;
}
@ -316,7 +315,6 @@ DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
// massive (30+ byte) values and crash IOHIDValueGetIntegerValue
return;
}
nsRefPtr<GamepadService> service(GamepadService::GetService());
IOHIDElementRef element = IOHIDValueGetElement(value);
IOHIDDeviceRef device = IOHIDElementGetDevice(element);
for (unsigned i = 0; i < mGamepads.size(); i++) {
@ -332,7 +330,7 @@ DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
const int numButtons = gamepad.numButtons();
for (unsigned b = 0; b < ArrayLength(newState); b++) {
if (newState[b] != oldState[b]) {
service->NewButtonEvent(i, numButtons - 4 + b, newState[b]);
NewButtonEvent(i, numButtons - 4 + b, newState[b]);
}
}
gamepad.setDpadState(newState);
@ -340,7 +338,7 @@ DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
double d = IOHIDValueGetIntegerValue(value);
double v = 2.0f * (d - axis->min) /
(double)(axis->max - axis->min) - 1.0f;
service->NewAxisMoveEvent(i, axis->id, v);
NewAxisMoveEvent(i, axis->id, v);
} else if (const Button* button = gamepad.lookupButton(element)) {
int iv = IOHIDValueGetIntegerValue(value);
bool pressed = iv != 0;
@ -351,7 +349,7 @@ DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
} else {
v = pressed ? 1.0 : 0.0;
}
service->NewButtonEvent(i, button->id, pressed, v);
NewButtonEvent(i, button->id, pressed, v);
}
return;
}
@ -495,28 +493,30 @@ void DarwinGamepadService::Shutdown()
} // namespace
namespace mozilla {
namespace hal_impl {
namespace dom {
DarwinGamepadService* gService = nullptr;
void StartMonitoringGamepadStatus()
void StartGamepadMonitoring()
{
if (gService)
if (gService) {
return;
}
gService = new DarwinGamepadService();
gService->Startup();
}
void StopMonitoringGamepadStatus()
void StopGamepadMonitoring()
{
if (!gService)
if (!gService) {
return;
}
gService->Shutdown();
delete gService;
gService = nullptr;
}
} // namespace hal_impl
} // namespace dom
} // namespace mozilla

View File

@ -4,16 +4,17 @@
* 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/. */
#include "Hal.h"
namespace mozilla {
namespace hal_impl {
namespace dom {
void StartMonitoringGamepadStatus()
{}
void StartGamepadMonitoring()
{
}
void StopMonitoringGamepadStatus()
{}
void StopGamepadMonitoring()
{
}
} // hal_impl
} // namespace dom
} // namespace mozilla

View File

@ -16,14 +16,13 @@
#include <stdint.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "nscore.h"
#include "mozilla/dom/GamepadService.h"
#include "mozilla/dom/GamepadFunctions.h"
#include "udev.h"
namespace {
using mozilla::dom::GamepadService;
using namespace mozilla::dom::GamepadFunctions;
using mozilla::udev_lib;
using mozilla::udev_device;
using mozilla::udev_list_entry;
@ -138,11 +137,10 @@ LinuxGamepadService::AddDevice(struct udev_device* dev)
ioctl(fd, JSIOCGBUTTONS, &numButtons);
gamepad.numButtons = numButtons;
nsRefPtr<GamepadService> service(GamepadService::GetService());
gamepad.index = service->AddGamepad(gamepad.idstring,
mozilla::dom::GamepadMappingType::_empty,
gamepad.numButtons,
gamepad.numAxes);
gamepad.index = AddGamepad(gamepad.idstring,
mozilla::dom::GamepadMappingType::_empty,
gamepad.numButtons,
gamepad.numAxes);
gamepad.source_id =
g_io_add_watch(channel,
@ -162,11 +160,10 @@ LinuxGamepadService::RemoveDevice(struct udev_device* dev)
return;
}
nsRefPtr<GamepadService> service(GamepadService::GetService());
for (unsigned int i = 0; i < mGamepads.Length(); i++) {
if (strcmp(mGamepads[i].devpath, devpath) == 0) {
g_source_remove(mGamepads[i].source_id);
service->RemoveGamepad(mGamepads[i].index);
RemoveGamepad(mGamepads[i].index);
mGamepads.RemoveElementAt(i);
break;
}
@ -319,14 +316,13 @@ LinuxGamepadService::OnGamepadData(GIOChannel* source,
continue;
}
nsRefPtr<GamepadService> service(GamepadService::GetService());
switch (event.type) {
case JS_EVENT_BUTTON:
service->NewButtonEvent(index, event.number, !!event.value);
NewButtonEvent(index, event.number, !!event.value);
break;
case JS_EVENT_AXIS:
service->NewAxisMoveEvent(index, event.number,
((float)event.value) / kMaxAxisValue);
NewAxisMoveEvent(index, event.number,
((float)event.value) / kMaxAxisValue);
break;
}
}
@ -350,24 +346,26 @@ LinuxGamepadService::OnUdevMonitor(GIOChannel* source,
} // namespace
namespace mozilla {
namespace hal_impl {
namespace dom {
void StartMonitoringGamepadStatus()
{
if (!gService) {
gService = new LinuxGamepadService();
gService->Startup();
}
}
void StopMonitoringGamepadStatus()
void StartGamepadMonitoring()
{
if (gService) {
gService->Shutdown();
delete gService;
gService = nullptr;
return;
}
gService = new LinuxGamepadService();
gService->Startup();
}
} // namespace hal_impl
void StopGamepadMonitoring()
{
if (!gService) {
return;
}
gService->Shutdown();
delete gService;
gService = nullptr;
}
} // namespace dom
} // namespace mozilla

View File

@ -7,13 +7,40 @@
EXPORTS.mozilla.dom += [
'Gamepad.h',
'GamepadButton.h',
'GamepadFunctions.h',
'GamepadMonitoring.h',
'GamepadService.h',
'GamepadServiceTest.h'
]
UNIFIED_SOURCES = [
'Gamepad.cpp',
'GamepadButton.cpp',
'GamepadFunctions.cpp',
'GamepadMonitoring.cpp',
'GamepadService.cpp',
'GamepadServiceTest.cpp'
]
if CONFIG['MOZ_GAMEPAD_BACKEND'] == 'stub':
UNIFIED_SOURCES += [
'fallback/FallbackGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'cocoa':
UNIFIED_SOURCES += [
'cocoa/CocoaGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'windows':
UNIFIED_SOURCES += [
'windows/WindowsGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'linux':
UNIFIED_SOURCES += [
'linux/LinuxGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'android':
UNIFIED_SOURCES += [
'android/AndroidGamepad.cpp'
]
FAIL_ON_WARNINGS = True
@ -25,3 +52,7 @@ LOCAL_INCLUDES += [
'/dom/base',
]
CFLAGS += CONFIG['GLIB_CFLAGS']
CFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
CXXFLAGS += CONFIG['GLIB_CFLAGS']
CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']

View File

@ -20,12 +20,13 @@
#include "nsITimer.h"
#include "nsTArray.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/dom/GamepadService.h"
#include "mozilla/dom/GamepadFunctions.h"
#include "mozilla/Services.h"
namespace {
using namespace mozilla::dom;
using namespace mozilla::dom::GamepadFunctions;
using mozilla::ArrayLength;
// USB HID usage tables, page 1 (Hat switch)
@ -455,11 +456,6 @@ WindowsGamepadService::ScanForXInputDevices()
{
MOZ_ASSERT(mXInput, "XInput should be present!");
nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
if (!gamepadsvc) {
return false;
}
bool found = false;
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
XINPUT_STATE state = {};
@ -480,10 +476,10 @@ WindowsGamepadService::ScanForXInputDevices()
gamepad.userIndex = i;
gamepad.numButtons = kStandardGamepadButtons;
gamepad.numAxes = kStandardGamepadAxes;
gamepad.id = gamepadsvc->AddGamepad("xinput",
GamepadMappingType::Standard,
kStandardGamepadButtons,
kStandardGamepadAxes);
gamepad.id = AddGamepad("xinput",
GamepadMappingType::Standard,
kStandardGamepadButtons,
kStandardGamepadAxes);
mGamepads.AppendElement(gamepad);
}
@ -510,14 +506,10 @@ WindowsGamepadService::ScanForDevices()
}
}
nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
if (!gamepadsvc) {
return;
}
// Look for devices that are no longer present and remove them.
for (int i = mGamepads.Length() - 1; i >= 0; i--) {
if (!mGamepads[i].present) {
gamepadsvc->RemoveGamepad(mGamepads[i].id);
RemoveGamepad(mGamepads[i].id);
mGamepads.RemoveElementAt(i);
}
}
@ -552,17 +544,16 @@ WindowsGamepadService::PollXInput()
void WindowsGamepadService::CheckXInputChanges(Gamepad& gamepad,
XINPUT_STATE& state) {
nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
// Handle digital buttons first
for (size_t b = 0; b < kNumMappings; b++) {
if (state.Gamepad.wButtons & kXIButtonMap[b].button &&
!(gamepad.state.Gamepad.wButtons & kXIButtonMap[b].button)) {
// Button pressed
gamepadsvc->NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, true);
NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, true);
} else if (!(state.Gamepad.wButtons & kXIButtonMap[b].button) &&
gamepad.state.Gamepad.wButtons & kXIButtonMap[b].button) {
// Button released
gamepadsvc->NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, false);
NewButtonEvent(gamepad.id, kXIButtonMap[b].mapped, false);
}
}
@ -570,33 +561,33 @@ void WindowsGamepadService::CheckXInputChanges(Gamepad& gamepad,
if (state.Gamepad.bLeftTrigger != gamepad.state.Gamepad.bLeftTrigger) {
bool pressed =
state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
gamepadsvc->NewButtonEvent(gamepad.id, kButtonLeftTrigger,
pressed, state.Gamepad.bLeftTrigger / 255.0);
NewButtonEvent(gamepad.id, kButtonLeftTrigger,
pressed, state.Gamepad.bLeftTrigger / 255.0);
}
if (state.Gamepad.bRightTrigger != gamepad.state.Gamepad.bRightTrigger) {
bool pressed =
state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
gamepadsvc->NewButtonEvent(gamepad.id, kButtonRightTrigger,
pressed, state.Gamepad.bRightTrigger / 255.0);
NewButtonEvent(gamepad.id, kButtonRightTrigger,
pressed, state.Gamepad.bRightTrigger / 255.0);
}
// Finally deal with analog sticks
// TODO: bug 1001955 - Support deadzones.
if (state.Gamepad.sThumbLX != gamepad.state.Gamepad.sThumbLX) {
gamepadsvc->NewAxisMoveEvent(gamepad.id, kLeftStickXAxis,
state.Gamepad.sThumbLX / 32767.0);
NewAxisMoveEvent(gamepad.id, kLeftStickXAxis,
state.Gamepad.sThumbLX / 32767.0);
}
if (state.Gamepad.sThumbLY != gamepad.state.Gamepad.sThumbLY) {
gamepadsvc->NewAxisMoveEvent(gamepad.id, kLeftStickYAxis,
-1.0 * state.Gamepad.sThumbLY / 32767.0);
NewAxisMoveEvent(gamepad.id, kLeftStickYAxis,
-1.0 * state.Gamepad.sThumbLY / 32767.0);
}
if (state.Gamepad.sThumbRX != gamepad.state.Gamepad.sThumbRX) {
gamepadsvc->NewAxisMoveEvent(gamepad.id, kRightStickXAxis,
state.Gamepad.sThumbRX / 32767.0);
NewAxisMoveEvent(gamepad.id, kRightStickXAxis,
state.Gamepad.sThumbRX / 32767.0);
}
if (state.Gamepad.sThumbRY != gamepad.state.Gamepad.sThumbRY) {
gamepadsvc->NewAxisMoveEvent(gamepad.id, kRightStickYAxis,
-1.0 * state.Gamepad.sThumbRY / 32767.0);
NewAxisMoveEvent(gamepad.id, kRightStickYAxis,
-1.0 * state.Gamepad.sThumbRY / 32767.0);
}
gamepad.state = state;
}
@ -757,15 +748,10 @@ WindowsGamepadService::GetRawGamepad(HANDLE handle)
gamepad.handle = handle;
gamepad.present = true;
nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
if (!gamepadsvc) {
return false;
}
gamepad.id = gamepadsvc->AddGamepad(gamepad_id,
GamepadMappingType::_empty,
gamepad.numButtons,
gamepad.numAxes);
gamepad.id = GamepadFunctions::AddGamepad(gamepad_id,
GamepadMappingType::_empty,
gamepad.numButtons,
gamepad.numAxes);
mGamepads.AppendElement(gamepad);
return true;
}
@ -776,10 +762,6 @@ WindowsGamepadService::HandleRawInput(HRAWINPUT handle)
if (!mHID) {
return false;
}
nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
if (!gamepadsvc) {
return false;
}
// First, get data from the handle
UINT size;
@ -838,7 +820,7 @@ WindowsGamepadService::HandleRawInput(HRAWINPUT handle)
for (unsigned i = 0; i < gamepad->numButtons; i++) {
if (gamepad->buttons[i] != buttons[i]) {
gamepadsvc->NewButtonEvent(gamepad->id, i, buttons[i]);
NewButtonEvent(gamepad->id, i, buttons[i]);
gamepad->buttons[i] = buttons[i];
}
}
@ -872,7 +854,7 @@ LONG value;
gamepad->axes[i].caps.LogicalMax);
}
if (gamepad->axes[i].value != new_value) {
gamepadsvc->NewAxisMoveEvent(gamepad->id, i, new_value);
NewAxisMoveEvent(gamepad->id, i, new_value);
gamepad->axes[i].value = new_value;
}
}
@ -977,9 +959,9 @@ GamepadWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
} // namespace
namespace mozilla {
namespace hal_impl {
namespace dom {
void StartMonitoringGamepadStatus()
void StartGamepadMonitoring()
{
if (gService) {
return;
@ -1007,7 +989,7 @@ void StartMonitoringGamepadStatus()
}
}
void StopMonitoringGamepadStatus()
void StopGamepadMonitoring()
{
if (!gService) {
return;
@ -1024,6 +1006,6 @@ void StopMonitoringGamepadStatus()
gService = nullptr;
}
} // namespace hal_impl
} // namespace dom
} // namespace mozilla

View File

@ -9,18 +9,21 @@ interface nsIVariant;
/*
* This interface is intended only for use in tests.
*/
[scriptable, uuid(b6ed093c-6ea0-4141-a8eb-f99645162651)]
[scriptable, uuid(c03ec4ed-8a7e-40e7-99da-c609f1760d0c)]
interface nsIGamepadServiceTest : nsISupports
{
const unsigned long NO_MAPPING = 0;
const unsigned long STANDARD_MAPPING = 1;
unsigned long addGamepad(in string id, in unsigned long mapping,
unsigned long addGamepad(in string id,
in unsigned long mapping,
in unsigned long numButtons,
in unsigned long numAxes);
void removeGamepad(in unsigned long index);
void newButtonEvent(in unsigned long index, in unsigned long button,
in boolean pressed);
void newButtonValueEvent(in unsigned long index, in unsigned long button,
in boolean pressed, in double value);
void newAxisMoveEvent(in unsigned long index, in unsigned long axis,
in double value);
};

View File

@ -155,6 +155,10 @@
#include "ipc/Nuwa.h"
#endif
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadService.h"
#endif
#include "mozilla/dom/File.h"
#include "mozilla/dom/cellbroadcast/CellBroadcastIPCService.h"
#include "mozilla/dom/icc/IccChild.h"
@ -2775,6 +2779,18 @@ ContentChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChi
return true;
}
bool
ContentChild::RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent)
{
#ifdef MOZ_GAMEPAD
nsRefPtr<GamepadService> svc(GamepadService::GetService());
if (svc) {
svc->Update(aGamepadEvent);
}
#endif
return true;
}
// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp
// can't include ContentChild.h since it includes windows.h.

View File

@ -464,6 +464,8 @@ public:
virtual bool
DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) override;
virtual bool RecvGamepadUpdate(const GamepadChangeEvent& aGamepadEvent) override;
private:
virtual void ActorDestroy(ActorDestroyReason why) override;

View File

@ -226,6 +226,10 @@ using namespace mozilla::system;
#include "nsIProfileSaveEvent.h"
#endif
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadMonitoring.h"
#endif
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
using base::ChildPrivileges;
@ -2157,6 +2161,7 @@ ContentParent::ContentParent(mozIApplication* aApp,
, mOpener(aOpener)
, mIsForBrowser(aIsForBrowser)
, mIsNuwaProcess(aIsNuwaProcess)
, mHasGamepadListener(false)
{
InitializeMembers(); // Perform common initialization.
@ -4990,6 +4995,34 @@ ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestP
return true;
}
bool
ContentParent::RecvGamepadListenerAdded()
{
#ifdef MOZ_GAMEPAD
if (mHasGamepadListener) {
NS_WARNING("Gamepad listener already started, cannot start again!");
return false;
}
mHasGamepadListener = true;
StartGamepadMonitoring();
#endif
return true;
}
bool
ContentParent::RecvGamepadListenerRemoved()
{
#ifdef MOZ_GAMEPAD
if (!mHasGamepadListener) {
NS_WARNING("Gamepad listener already stopped, cannot stop again!");
return false;
}
mHasGamepadListener = false;
MaybeStopGamepadMonitoring();
#endif
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -377,6 +377,8 @@ public:
virtual bool
DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
bool HasGamepadListener() const { return mHasGamepadListener; }
protected:
void OnChannelConnected(int32_t pid) override;
virtual void ActorDestroy(ActorDestroyReason why) override;
@ -840,6 +842,10 @@ private:
virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction,
const uint32_t& aDropEffect) override;
virtual bool RecvGamepadListenerAdded() override;
virtual bool RecvGamepadListenerRemoved() override;
// If you add strong pointers to cycle collected objects here, be sure to
// release these objects in ShutDownProcess. See the comment there for more
// details.
@ -884,6 +890,7 @@ private:
bool mSendDataStoreInfos;
bool mIsForBrowser;
bool mIsNuwaProcess;
bool mHasGamepadListener;
// These variables track whether we've called Close(), CloseWithError()
// and KillHard() on our channel.

View File

@ -368,6 +368,38 @@ struct DomainPolicyClone
URIParams[] superWhitelist;
};
struct GamepadAdded {
nsString id;
uint32_t index;
uint32_t mapping;
uint32_t num_buttons;
uint32_t num_axes;
};
struct GamepadRemoved {
uint32_t index;
};
struct GamepadAxisInformation {
uint32_t index;
uint32_t axis;
double value;
};
struct GamepadButtonInformation {
uint32_t index;
uint32_t button;
bool pressed;
double value;
};
union GamepadChangeEvent {
GamepadAdded;
GamepadRemoved;
GamepadAxisInformation;
GamepadButtonInformation;
};
prio(normal upto urgent) sync protocol PContent
{
parent spawns PPluginModule;
@ -605,6 +637,10 @@ child:
*/
async UpdateWindow(uintptr_t aChildId);
/**
* Send gamepad status update to child.
*/
GamepadUpdate(GamepadChangeEvent aGamepadEvent);
parent:
/**
* Tell the parent process a new accessible document has been created.
@ -992,6 +1028,16 @@ parent:
PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal,
TabId tabId);
/*
* Tells the parent to start the gamepad listening service if it hasn't already.
*/
GamepadListenerAdded();
/**
* Tells the parent to stop the gamepad listening service if it hasn't already.
*/
GamepadListenerRemoved();
both:
AsyncMessage(nsString aMessage, ClonedMessageData aData,
CpowEntry[] aCpows, Principal aPrincipal);

View File

@ -1,18 +1,13 @@
[DEFAULT]
skip-if=e10s || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
support-files =
gamepad_frame.html
gamepad_frame_state.html
mock_gamepad.js
[test_check_timestamp.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_gamepad.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_gamepad_connect_events.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_gamepad_frame_state_sync.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_gamepad_hidden_frame.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_navigator_gamepads.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage

View File

@ -11,6 +11,7 @@
<script type="text/javascript" src="mock_gamepad.js"></script>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var index = GamepadService.addGamepad("test gamepad", // id
SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
4, // buttons

View File

@ -17,19 +17,29 @@ var index = GamepadService.addGamepad("test gamepad", // id
SpecialPowers.Ci.nsIGamepadServiceTest.STANDARD_MAPPING,
4, // buttons
2);// axes
// Press a button
GamepadService.newButtonEvent(index, 0, true);
function connecthandler(e) {
ok(e.gamepad.timestamp <= 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.buttons.length, 4, "correct number of buttons");
is(e.gamepad.axes.length, 2, "correct number of axes");
// Press a button
GamepadService.newButtonEvent(index, 0, true);
gamepads = navigator.getGamepads();
is(gamepads[0].buttons[0].pressed, true, "gamepad button should register as pressed")
GamepadService.newButtonValueEvent(index, 1, true, 0.5);
gamepads = navigator.getGamepads();
is(gamepads[0].buttons[1].pressed, true, "gamepad button should register as pressed")
is(gamepads[0].buttons[1].value, 0.5, "gamepad button value should be 0.5")
SimpleTest.executeSoon(function() {
GamepadService.removeGamepad(index);
SimpleTest.finish();
});
}
</script>
</body>
</html>

View File

@ -37,49 +37,61 @@ function disconnecthandler(e) {
window.addEventListener("gamepadconnected", connecthandler);
window.addEventListener("gamepaddisconnected", disconnecthandler);
// Add a gamepad
var index1 = GamepadService.addGamepad("test gamepad 1", // id
var internal_index1 = GamepadService.addGamepad("test gamepad 1", // id
SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
4, // buttons
2);// axes
var index2;
var content_index1 = 0;
var internal_index2;
var content_index2 = 1;
// Press a button to make the gamepad visible to the page.
GamepadService.newButtonEvent(index1, 0, true);
GamepadService.newButtonEvent(internal_index1, 0, true);
function check_first_gamepad(e) {
ok(true, "Checking first gamepad");
// First gamepad gets added.
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.
index2 = GamepadService.addGamepad("test gamepad 2", // id
internal_index2 = GamepadService.addGamepad("test gamepad 2", // id
SpecialPowers.Ci.nsIGamepadServiceTest.NO_MAPPING,
4, // buttons
2);// axes
ok(true, "Done checking first gamepad");
}
function check_second_gamepad(e) {
ok(true, "Checking seceond gamepad");
// Second gamepad gets added.
is(e.gamepad.index, 1, "gamepad index should be 1")
is(e.gamepad.id, "test gamepad 2", "correct gamepad name");
var gamepads = navigator.getGamepads();
is(gamepads.length, 2, "should have two gamepads exposed");
is(gamepads[e.gamepad.index], e.gamepad, "right gamepad exposed at index");
is(gamepads[content_index2], e.gamepad, "gamepad counter working correctly");
// Now remove the first one.
GamepadService.removeGamepad(index1);
GamepadService.removeGamepad(internal_index1);
ok(true, "Done checking second gamepad");
}
function check_gamepad_hole(e) {
ok(true, "Checking gamepad hole");
// First gamepad gets removed.
var gamepads = navigator.getGamepads();
is(gamepads.length, 2, "gamepads should have two entries");
is(gamepads[index1], null, "should be a hole in the gamepad list");
isnot(gamepads[index2], null, "second gamepad should exist");
is(gamepads[content_index1], null, "should be a hole in the gamepad list");
isnot(gamepads[content_index2], null, "second gamepad should exist");
// Now remove the second one.
GamepadService.removeGamepad(index2);
GamepadService.removeGamepad(internal_index2);
ok(true, "Done checking gamepad hole");
}
function check_no_gamepads(e) {
ok(true, "Checking no gamepads");
// Second gamepad gets removed.
var gamepads = navigator.getGamepads();
is(gamepads.length, 0, "gamepads should be empty");

View File

@ -636,16 +636,6 @@ void StartForceQuitWatchdog(ShutdownMode aMode, int32_t aTimeoutSecs)
PROXY_IF_SANDBOXED(StartForceQuitWatchdog(aMode, aTimeoutSecs));
}
void StartMonitoringGamepadStatus()
{
PROXY_IF_SANDBOXED(StartMonitoringGamepadStatus());
}
void StopMonitoringGamepadStatus()
{
PROXY_IF_SANDBOXED(StopMonitoringGamepadStatus());
}
void
RegisterWakeLockObserver(WakeLockObserver* aObserver)
{

View File

@ -17,7 +17,6 @@
#include "mozilla/dom/battery/Types.h"
#include "mozilla/dom/network/Types.h"
#include "mozilla/dom/power/Types.h"
#include "mozilla/hal_sandbox/PHal.h"
#include "mozilla/dom/ScreenOrientation.h"
#include "mozilla/HalScreenConfiguration.h"
@ -598,16 +597,6 @@ void StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs);
*/
void FactoryReset(mozilla::dom::FactoryResetReason& aReason);
/**
* Start monitoring the status of gamepads attached to the system.
*/
void StartMonitoringGamepadStatus();
/**
* Stop monitoring the status of gamepads attached to the system.
*/
void StopMonitoringGamepadStatus();
/**
* Start monitoring disk space for low space situations.
*

View File

@ -32,27 +32,6 @@ SOURCES += [
'Hal.cpp',
]
if CONFIG['MOZ_GAMEPAD_BACKEND'] == 'stub':
UNIFIED_SOURCES += [
'fallback/FallbackGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'cocoa':
UNIFIED_SOURCES += [
'cocoa/CocoaGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'windows':
UNIFIED_SOURCES += [
'windows/WindowsGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'linux':
UNIFIED_SOURCES += [
'linux/LinuxGamepad.cpp'
]
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'android':
UNIFIED_SOURCES += [
'android/AndroidGamepad.cpp'
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
UNIFIED_SOURCES += [
'android/AndroidSensor.cpp',

View File

@ -271,13 +271,6 @@ DisableSensorNotifications(SensorType aSensor) {
Hal()->SendDisableSensorNotifications(aSensor);
}
//TODO: bug 852944 - IPC implementations of these
void StartMonitoringGamepadStatus()
{}
void StopMonitoringGamepadStatus()
{}
void
EnableWakeLockNotifications()
{

View File

@ -209,7 +209,7 @@ static void Shutdown();
#include "nsGeolocation.h"
#include "nsDeviceSensors.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadService.h"
#include "mozilla/dom/GamepadServiceTest.h"
#endif
#include "mozilla/dom/nsCSPService.h"
#include "mozilla/dom/nsCSPContext.h"

View File

@ -41,7 +41,8 @@
#include "mozilla/dom/ScreenOrientation.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadService.h"
#include "mozilla/dom/GamepadFunctions.h"
#include "mozilla/dom/Gamepad.h"
#endif
#include "GeckoProfiler.h"
@ -656,19 +657,15 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
case AndroidGeckoEvent::GAMEPAD_ADDREMOVE: {
#ifdef MOZ_GAMEPAD
nsRefPtr<mozilla::dom::GamepadService> svc =
mozilla::dom::GamepadService::GetService();
if (svc) {
if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_ADDED) {
int svc_id = svc->AddGamepad("android",
mozilla::dom::GamepadMappingType::Standard,
mozilla::dom::kStandardGamepadButtons,
mozilla::dom::kStandardGamepadAxes);
int svc_id = dom::GamepadFunctions::AddGamepad("android",
dom::GamepadMappingType::Standard,
dom::kStandardGamepadButtons,
dom::kStandardGamepadAxes);
widget::GeckoAppShell::GamepadAdded(curEvent->ID(),
svc_id);
} else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_REMOVED) {
svc->RemoveGamepad(curEvent->ID());
}
dom::GamepadFunctions::RemoveGamepad(curEvent->ID());
}
#endif
break;
@ -676,12 +673,9 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
case AndroidGeckoEvent::GAMEPAD_DATA: {
#ifdef MOZ_GAMEPAD
nsRefPtr<mozilla::dom::GamepadService> svc =
mozilla::dom::GamepadService::GetService();
if (svc) {
int id = curEvent->ID();
if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_BUTTON) {
svc->NewButtonEvent(id, curEvent->GamepadButton(),
dom::GamepadFunctions::NewButtonEvent(id, curEvent->GamepadButton(),
curEvent->GamepadButtonPressed(),
curEvent->GamepadButtonValue());
} else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_AXES) {
@ -689,8 +683,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
const nsTArray<float>& values = curEvent->GamepadValues();
for (unsigned i = 0; i < values.Length(); i++) {
if (valid & (1<<i)) {
svc->NewAxisMoveEvent(id, i, values[i]);
}
dom::GamepadFunctions::NewAxisMoveEvent(id, i, values[i]);
}
}
}