Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

bug 604039 - Add DOM Gamepad APIs. r=smaug

--HG--
extra : rebase_source : ffffdc4549da1b25ea263b623c05ae1afb3d46a0
  • Loading branch information...
commit 0de8f1bef8e2fb7c6268f89186265eed62c4e964 1 parent 741598a
Ted Mielczarek authored
Showing with 1,684 additions and 6 deletions.
  1. +3 −0  browser/installer/package-manifest.in
  2. +31 −0 configure.in
  3. +7 −0 content/base/src/nsGkAtomList.h
  4. +23 −0 content/events/public/nsEventNameList.h
  5. +7 −0 content/events/src/Makefile.in
  6. +171 −0 content/events/src/nsDOMGamepad.cpp
  7. +49 −0 content/events/src/nsDOMGamepad.h
  8. +8 −0 content/events/src/nsEventListenerManager.cpp
  9. +14 −0 dom/base/nsDOMClassInfo.cpp
  10. +4 −0 dom/base/nsDOMClassInfoClasses.h
  11. +123 −0 dom/base/nsGlobalWindow.cpp
  12. +29 −0 dom/base/nsGlobalWindow.h
  13. +7 −2 dom/base/nsPIDOMWindow.h
  14. +3 −0  dom/interfaces/events/moz.build
  15. +33 −0 dom/interfaces/events/nsIDOMGamepadAxisMoveEvent.idl
  16. +26 −0 dom/interfaces/events/nsIDOMGamepadButtonEvent.idl
  17. +27 −0 dom/interfaces/events/nsIDOMGamepadEvent.idl
  18. +12 −0 dom/interfaces/gamepad/Makefile.in
  19. +11 −0 dom/interfaces/gamepad/moz.build
  20. +39 −0 dom/interfaces/gamepad/nsIDOMGamepad.idl
  21. +22 −0 dom/interfaces/gamepad/nsIGamepadServiceTest.idl
  22. +1 −0  dom/moz.build
  23. +530 −0 dom/system/GamepadService.cpp
  24. +126 −0 dom/system/GamepadService.h
  25. +10 −2 dom/system/Makefile.in
  26. +23 −0 dom/tests/mochitest/gamepad/Makefile.in
  27. +16 −0 dom/tests/mochitest/gamepad/gamepad_frame.html
  28. +16 −0 dom/tests/mochitest/gamepad/gamepad_frame_state.html
  29. +5 −0 dom/tests/mochitest/gamepad/mock_gamepad.js
  30. +4 −0 dom/tests/mochitest/gamepad/moz.build
  31. +33 −0 dom/tests/mochitest/gamepad/test_gamepad.html
  32. +22 −0 dom/tests/mochitest/gamepad/test_gamepad_basic.html
  33. +85 −0 dom/tests/mochitest/gamepad/test_gamepad_frame_state_sync.html
  34. +71 −0 dom/tests/mochitest/gamepad/test_gamepad_hidden_frame.html
  35. +5 −1 dom/tests/mochitest/general/test_interfaces.html
  36. +3 −0  dom/tests/mochitest/moz.build
  37. +10 −0 hal/Hal.cpp
  38. +10 −0 hal/Hal.h
  39. +4 −0 hal/Makefile.in
  40. +19 −0 hal/fallback/FallbackGamepad.cpp
  41. +7 −0 hal/sandbox/SandboxHal.cpp
  42. +6 −1 js/xpconnect/src/event_impl_gen.conf.in
  43. +17 −0 layout/build/nsLayoutModule.cpp
  44. +12 −0 widget/nsGUIEvent.h
View
3  browser/installer/package-manifest.in
@@ -214,6 +214,9 @@
@BINPATH@/components/dom_xbl.xpt
@BINPATH@/components/dom_xpath.xpt
@BINPATH@/components/dom_xul.xpt
+#ifdef MOZ_GAMEPAD
+@BINPATH@/components/dom_gamepad.xpt
+#endif
@BINPATH@/components/downloads.xpt
@BINPATH@/components/editor.xpt
@BINPATH@/components/embed_base.xpt
View
31 configure.in
@@ -5928,6 +5928,37 @@ if test -n "$MOZ_ANGLE_RENDERER" -a -z "$CROSS_COMPILE"; then
fi
dnl ========================================================
+dnl Gamepad support
+dnl ========================================================
+MOZ_GAMEPAD=
+MOZ_GAMEPAD_BACKEND=stub
+
+# Gamepad DOM is built on supported platforms by default.
+case "$OS_TARGET" in
+ Darwin|WINNT|Linux)
+ MOZ_GAMEPAD=1
+ ;;
+ *)
+ ;;
+esac
+
+MOZ_ARG_DISABLE_BOOL(gamepad,
+[ --disable-gamepad Disable gamepad support],
+ MOZ_GAMEPAD=,
+ MOZ_GAMEPAD=1)
+
+if test "$MOZ_GAMEPAD"; then
+ case "$OS_TARGET" in
+ *)
+ ;;
+ esac
+
+ AC_DEFINE(MOZ_GAMEPAD)
+fi
+AC_SUBST(MOZ_GAMEPAD)
+AC_SUBST(MOZ_GAMEPAD_BACKEND)
+
+dnl ========================================================
dnl = Breakpad crash reporting (on by default on supported platforms)
dnl ========================================================
View
7 content/base/src/nsGkAtomList.h
@@ -1895,6 +1895,13 @@ GK_ATOM(canplaythrough, "canplaythrough")
GK_ATOM(ratechange, "ratechange")
GK_ATOM(durationchange, "durationchange")
GK_ATOM(volumechange, "volumechange")
+#ifdef MOZ_GAMEPAD
+GK_ATOM(ongamepadbuttondown, "ongamepadbuttondown")
+GK_ATOM(ongamepadbuttonup, "ongamepadbuttonup")
+GK_ATOM(ongamepadaxismove, "ongamepadaxismove")
+GK_ATOM(ongamepadconnected, "ongamepadconnected")
+GK_ATOM(ongamepaddisconnected, "ongamepaddisconnected")
+#endif
// Content property names
GK_ATOM(animationsProperty, "AnimationsProperty") // FrameAnimations*
View
23 content/events/public/nsEventNameList.h
@@ -754,6 +754,29 @@ NON_IDL_EVENT(MozScrolledAreaChanged,
EventNameType_None,
NS_SCROLLAREA_EVENT)
+#ifdef MOZ_GAMEPAD
+NON_IDL_EVENT(gamepadbuttondown,
+ NS_GAMEPAD_BUTTONDOWN,
+ EventNameType_None,
+ NS_EVENT_NULL)
+NON_IDL_EVENT(gamepadbuttonup,
+ NS_GAMEPAD_BUTTONUP,
+ EventNameType_None,
+ NS_EVENT_NULL)
+NON_IDL_EVENT(gamepadaxismove,
+ NS_GAMEPAD_AXISMOVE,
+ EventNameType_None,
+ NS_EVENT_NULL)
+NON_IDL_EVENT(gamepadconnected,
+ NS_GAMEPAD_CONNECTED,
+ EventNameType_None,
+ NS_EVENT_NULL)
+NON_IDL_EVENT(gamepaddisconnected,
+ NS_GAMEPAD_DISCONNECTED,
+ EventNameType_None,
+ NS_EVENT_NULL)
+#endif
+
// Simple gesture events
NON_IDL_EVENT(MozSwipeGesture,
NS_SIMPLE_GESTURE_SWIPE,
View
7 content/events/src/Makefile.in
@@ -21,6 +21,7 @@ EXPORTS = \
nsEventListenerManager.h \
nsDOMEventTargetHelper.h \
nsDOMEvent.h \
+ nsDOMGamepad.h \
nsDOMTouchEvent.h \
nsDOMUIEvent.h \
$(NULL)
@@ -64,6 +65,12 @@ CPPSRCS = \
TextComposition.cpp \
$(NULL)
+ifdef MOZ_GAMEPAD
+CPPSRCS += \
+ nsDOMGamepad.cpp \
+ $(NULL)
+endif
+
# we don't want the shared lib, but we want to force the creation of a static lib.
FORCE_STATIC_LIB = 1
View
171 content/events/src/nsDOMGamepad.cpp
@@ -0,0 +1,171 @@
+/* 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 "nsDOMGamepad.h"
+#include "nsDOMClassInfoID.h"
+#include "nsIClassInfo.h"
+#include "nsIXPCScriptable.h"
+#include "nsTArray.h"
+#include "nsContentUtils.h"
+#include "nsVariant.h"
+
+DOMCI_DATA(Gamepad, nsDOMGamepad)
+
+NS_IMPL_ADDREF(nsDOMGamepad)
+NS_IMPL_RELEASE(nsDOMGamepad)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMGamepad)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMGamepad)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Gamepad)
+NS_INTERFACE_MAP_END
+
+nsDOMGamepad::nsDOMGamepad(const nsAString& aID, uint32_t aIndex,
+ uint32_t aNumButtons, uint32_t aNumAxes)
+ : mID(aID),
+ mIndex(aIndex),
+ mConnected(true)
+{
+ mButtons.InsertElementsAt(0, aNumButtons, 0);
+ mAxes.InsertElementsAt(0, aNumAxes, 0.0f);
+}
+
+/* readonly attribute DOMString id; */
+NS_IMETHODIMP
+nsDOMGamepad::GetId(nsAString& aID)
+{
+ aID = mID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMGamepad::GetIndex(uint32_t* aIndex)
+{
+ *aIndex = mIndex;
+ return NS_OK;
+}
+
+void
+nsDOMGamepad::SetIndex(uint32_t aIndex)
+{
+ mIndex = aIndex;
+}
+
+void
+nsDOMGamepad::SetConnected(bool aConnected)
+{
+ mConnected = aConnected;
+}
+
+void
+nsDOMGamepad::SetButton(uint32_t aButton, double aValue)
+{
+ MOZ_ASSERT(aButton < mButtons.Length());
+ mButtons[aButton] = aValue;
+}
+
+void
+nsDOMGamepad::SetAxis(uint32_t aAxis, double aValue)
+{
+ MOZ_ASSERT(aAxis < mAxes.Length());
+ mAxes[aAxis] = aValue;
+}
+
+/* readonly attribute boolean connected; */
+NS_IMETHODIMP
+nsDOMGamepad::GetConnected(bool* aConnected)
+{
+ *aConnected = mConnected;
+ return NS_OK;
+}
+
+/* readonly attribute nsIVariant buttons; */
+NS_IMETHODIMP
+nsDOMGamepad::GetButtons(nsIVariant** aButtons)
+{
+ nsRefPtr<nsVariant> out = new nsVariant();
+ NS_ENSURE_STATE(out);
+
+ if (mButtons.Length() == 0) {
+ nsresult rv = out->SetAsEmptyArray();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ // Note: The resulting nsIVariant dupes both the array and its elements.
+ double* array = reinterpret_cast<double*>
+ (NS_Alloc(mButtons.Length() * sizeof(double)));
+ NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
+
+ for (uint32_t i = 0; i < mButtons.Length(); ++i) {
+ array[i] = mButtons[i];
+ }
+
+ nsresult rv = out->SetAsArray(nsIDataType::VTYPE_DOUBLE,
+ nullptr,
+ mButtons.Length(),
+ reinterpret_cast<void*>(array));
+ NS_Free(array);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aButtons = out.forget().get();
+ return NS_OK;
+}
+
+/* readonly attribute nsIVariant axes; */
+NS_IMETHODIMP
+nsDOMGamepad::GetAxes(nsIVariant** aAxes)
+{
+ nsRefPtr<nsVariant> out = new nsVariant();
+ NS_ENSURE_STATE(out);
+
+ if (mAxes.Length() == 0) {
+ nsresult rv = out->SetAsEmptyArray();
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ // Note: The resulting nsIVariant dupes both the array and its elements.
+ double* array = reinterpret_cast<double*>
+ (NS_Alloc(mAxes.Length() * sizeof(double)));
+ NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
+
+ for (uint32_t i = 0; i < mAxes.Length(); ++i) {
+ array[i] = mAxes[i];
+ }
+
+ nsresult rv = out->SetAsArray(nsIDataType::VTYPE_DOUBLE,
+ nullptr,
+ mAxes.Length(),
+ reinterpret_cast<void*>(array));
+ NS_Free(array);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ *aAxes = out.forget().get();
+ return NS_OK;
+}
+
+void
+nsDOMGamepad::SyncState(nsDOMGamepad* aOther)
+{
+ if (mButtons.Length() != aOther->mButtons.Length() ||
+ mAxes.Length() != aOther->mAxes.Length()) {
+ return;
+ }
+
+ mConnected = aOther->mConnected;
+ for (uint32_t i = 0; i < mButtons.Length(); ++i) {
+ mButtons[i] = aOther->mButtons[i];
+ }
+ for (uint32_t i = 0; i < mAxes.Length(); ++i) {
+ mAxes[i] = aOther->mAxes[i];
+ }
+}
+
+already_AddRefed<nsDOMGamepad>
+nsDOMGamepad::Clone()
+{
+ nsRefPtr<nsDOMGamepad> out =
+ new nsDOMGamepad(mID, mIndex, mButtons.Length(), mAxes.Length());
+ out->SyncState(this);
+ return out.forget();
+}
View
49 content/events/src/nsDOMGamepad.h
@@ -0,0 +1,49 @@
+/* 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 nsDomGamepad_h
+#define nsDomGamepad_h
+
+#include "mozilla/StandardInteger.h"
+#include "nsIDOMGamepad.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+class nsDOMGamepad : public nsIDOMGamepad
+{
+public:
+ nsDOMGamepad(const nsAString& aID, uint32_t aIndex,
+ uint32_t aNumButtons, uint32_t aNumAxes);
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMGAMEPAD
+
+ nsDOMGamepad();
+ void SetConnected(bool aConnected);
+ void SetButton(uint32_t aButton, double aValue);
+ void SetAxis(uint32_t aAxis, double aValue);
+ void SetIndex(uint32_t aIndex);
+
+ // Make the state of this gamepad equivalent to other.
+ void SyncState(nsDOMGamepad* other);
+
+ // Return a new nsDOMGamepad containing the same data as this object.
+ already_AddRefed<nsDOMGamepad> Clone();
+
+private:
+ virtual ~nsDOMGamepad() {}
+
+protected:
+ nsString mID;
+ uint32_t mIndex;
+
+ // true if this gamepad is currently connected.
+ bool mConnected;
+
+ // Current state of buttons, axes.
+ nsTArray<double> mButtons;
+ nsTArray<double> mAxes;
+};
+
+#endif // nsDomGamepad_h
View
8 content/events/src/nsEventListenerManager.cpp
@@ -339,6 +339,14 @@ nsEventListenerManager::AddEventListenerInternal(
#endif
window->SetHasMouseEnterLeaveEventListeners();
}
+#ifdef MOZ_GAMEPAD
+ } else if (aType >= NS_GAMEPAD_START &&
+ aType <= NS_GAMEPAD_END) {
+ nsPIDOMWindow* window = GetInnerWindowForTarget();
+ if (window) {
+ window->SetHasGamepadEventListener();
+ }
+#endif
}
}
View
14 dom/base/nsDOMClassInfo.cpp
@@ -230,6 +230,9 @@
#include "nsIControllers.h"
#include "nsISelection.h"
#include "nsIBoxObject.h"
+#ifdef MOZ_GAMEPAD
+#include "nsIDOMGamepad.h"
+#endif
#ifdef MOZ_XUL
#include "nsITreeSelection.h"
#include "nsITreeContentView.h"
@@ -1057,6 +1060,11 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(TouchEvent, nsEventSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
+#ifdef MOZ_GAMEPAD
+ NS_DEFINE_CLASSINFO_DATA(Gamepad, nsDOMGenericSH,
+ DOM_DEFAULT_SCRIPTABLE_FLAGS)
+#endif
+
NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframeRule, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozCSSKeyframesRule, nsDOMGenericSH,
@@ -2662,6 +2670,12 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
+#ifdef MOZ_GAMEPAD
+ DOM_CLASSINFO_MAP_BEGIN(Gamepad, nsIDOMGamepad)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMGamepad)
+ DOM_CLASSINFO_MAP_END
+#endif
+
DOM_CLASSINFO_MAP_BEGIN(MozCSSKeyframeRule, nsIDOMMozCSSKeyframeRule)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozCSSKeyframeRule)
DOM_CLASSINFO_MAP_END
View
4 dom/base/nsDOMClassInfoClasses.h
@@ -263,6 +263,10 @@ DOMCI_CLASS(Touch)
DOMCI_CLASS(TouchList)
DOMCI_CLASS(TouchEvent)
+#ifdef MOZ_GAMEPAD
+DOMCI_CLASS(Gamepad)
+#endif
+
DOMCI_CLASS(MozCSSKeyframeRule)
DOMCI_CLASS(MozCSSKeyframesRule)
View
123 dom/base/nsGlobalWindow.cpp
@@ -211,6 +211,10 @@
#include "mozilla/dom/StructuredCloneTags.h"
+#ifdef MOZ_GAMEPAD
+#include "mozilla/dom/GamepadService.h"
+#endif
+
#include "nsRefreshDriver.h"
#include "mozAutoDocUpdate.h"
@@ -921,6 +925,10 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
#endif
mShowFocusRingForContent(false),
mFocusByKeyOccurred(false),
+ mHasGamepad(false),
+#ifdef MOZ_GAMEPAD
+ mHasSeenGamepadInput(false),
+#endif
mNotifiedIDDestroyed(false),
mAllowScriptsToClose(false),
mTimeoutInsertionPoint(nullptr),
@@ -941,6 +949,10 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
{
nsLayoutStatics::AddRef();
+#ifdef MOZ_GAMEPAD
+ mGamepads.Init();
+#endif
+
// Initialize the PRCList (this).
PR_INIT_CLIST(this);
@@ -1288,6 +1300,9 @@ nsGlobalWindow::CleanUp(bool aIgnoreModalDialog)
inner->CleanUp(aIgnoreModalDialog);
}
+ DisableGamepadUpdates();
+ mHasGamepad = false;
+
if (mCleanMessageManager) {
NS_ABORT_IF_FALSE(mIsChrome, "only chrome should have msg manager cleaned");
nsGlobalChromeWindow *asChrome = static_cast<nsGlobalChromeWindow*>(this);
@@ -8020,6 +8035,14 @@ void nsGlobalWindow::SetIsBackground(bool aIsBackground)
if (resetTimers) {
ResetTimersForNonBackgroundWindow();
}
+#ifdef MOZ_GAMEPAD
+ if (!aIsBackground) {
+ nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
+ if (inner) {
+ inner->SyncGamepadState();
+ }
+ }
+#endif
}
void nsGlobalWindow::MaybeUpdateTouchState()
@@ -8064,6 +8087,34 @@ void nsGlobalWindow::UpdateTouchState()
}
void
+nsGlobalWindow::EnableGamepadUpdates()
+{
+ FORWARD_TO_INNER_VOID(EnableGamepadUpdates, ());
+ if (mHasGamepad) {
+#ifdef MOZ_GAMEPAD
+ nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
+ if (gamepadsvc) {
+ gamepadsvc->AddListener(this);
+ }
+#endif
+ }
+}
+
+void
+nsGlobalWindow::DisableGamepadUpdates()
+{
+ FORWARD_TO_INNER_VOID(DisableGamepadUpdates, ());
+ if (mHasGamepad) {
+#ifdef MOZ_GAMEPAD
+ nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
+ if (gamepadsvc) {
+ gamepadsvc->RemoveListener(this);
+ }
+#endif
+ }
+}
+
+void
nsGlobalWindow::SetChromeEventHandler(nsIDOMEventTarget* aChromeEventHandler)
{
SetChromeEventHandlerInternal(aChromeEventHandler);
@@ -10764,6 +10815,7 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
ac->RemoveWindowListener(mEnabledSensors[i], this);
}
+ DisableGamepadUpdates();
// Suspend all of the workers for this window.
nsIScriptContext *scx = GetContextInternal();
@@ -10844,6 +10896,7 @@ nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
ac->AddWindowListener(mEnabledSensors[i], this);
}
+ EnableGamepadUpdates();
// Resume all of the workers for this window.
nsIScriptContext *scx = GetContextInternal();
@@ -10995,6 +11048,16 @@ nsGlobalWindow::DisableDeviceSensor(uint32_t aType)
}
void
+nsGlobalWindow::SetHasGamepadEventListener(bool aHasGamepad/* = true*/)
+{
+ FORWARD_TO_INNER_VOID(SetHasGamepadEventListener, (aHasGamepad));
+ mHasGamepad = aHasGamepad;
+ if (aHasGamepad) {
+ EnableGamepadUpdates();
+ }
+}
+
+void
nsGlobalWindow::EnableTimeChangeNotifications()
{
nsSystemTimeChangeObserver::AddWindowListener(this);
@@ -11058,6 +11121,66 @@ nsGlobalWindow::SizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
aWindowSizes->mMallocSizeOf);
}
+
+#ifdef MOZ_GAMEPAD
+void
+nsGlobalWindow::AddGamepad(PRUint32 aIndex, nsDOMGamepad* aGamepad)
+{
+ FORWARD_TO_INNER_VOID(AddGamepad, (aIndex, aGamepad));
+ mGamepads.Put(aIndex, aGamepad);
+}
+
+void
+nsGlobalWindow::RemoveGamepad(PRUint32 aIndex)
+{
+ FORWARD_TO_INNER_VOID(RemoveGamepad, (aIndex));
+ mGamepads.Remove(aIndex);
+}
+
+already_AddRefed<nsDOMGamepad>
+nsGlobalWindow::GetGamepad(PRUint32 aIndex)
+{
+ FORWARD_TO_INNER(GetGamepad, (aIndex), nullptr);
+ nsRefPtr<nsDOMGamepad> gamepad;
+ if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
+ return gamepad.forget();
+ }
+
+ return nullptr;
+}
+
+void
+nsGlobalWindow::SetHasSeenGamepadInput(bool aHasSeen)
+{
+ FORWARD_TO_INNER_VOID(SetHasSeenGamepadInput, (aHasSeen));
+ mHasSeenGamepadInput = aHasSeen;
+}
+
+bool
+nsGlobalWindow::HasSeenGamepadInput()
+{
+ FORWARD_TO_INNER(HasSeenGamepadInput, (), false);
+ return mHasSeenGamepadInput;
+}
+
+// static
+PLDHashOperator
+nsGlobalWindow::EnumGamepadsForSync(const PRUint32& aKey, nsDOMGamepad* aData, void* userArg)
+{
+ nsRefPtr<GamepadService> gamepadsvc(GamepadService::GetService());
+ gamepadsvc->SyncGamepadState(aKey, aData);
+ return PL_DHASH_NEXT;
+}
+
+void
+nsGlobalWindow::SyncGamepadState()
+{
+ FORWARD_TO_INNER_VOID(SyncGamepadState, ());
+ if (mHasSeenGamepadInput) {
+ mGamepads.EnumerateRead(EnumGamepadsForSync, NULL);
+ }
+}
+#endif
// nsGlobalChromeWindow implementation
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
View
29 dom/base/nsGlobalWindow.h
@@ -62,6 +62,9 @@
#include "nsWrapperCacheInlines.h"
#include "nsIIdleObserver.h"
#include "nsIDOMWakeLock.h"
+#ifdef MOZ_GAMEPAD
+#include "nsDOMGamepad.h"
+#endif
#include "mozilla/dom/EventTarget.h"
@@ -378,6 +381,8 @@ class nsGlobalWindow : public mozilla::dom::EventTarget,
virtual NS_HIDDEN_(void) RefreshCompartmentPrincipal();
virtual NS_HIDDEN_(nsresult) SetFullScreenInternal(bool aIsFullScreen, bool aRequireTrust);
+ virtual NS_HIDDEN_(void) SetHasGamepadEventListener(bool aHasGamepad = true);
+
// nsIDOMStorageIndexedDB
NS_DECL_NSIDOMSTORAGEINDEXEDDB
@@ -609,6 +614,23 @@ class nsGlobalWindow : public mozilla::dom::EventTarget,
mAllowScriptsToClose = true;
}
+#ifdef MOZ_GAMEPAD
+ void AddGamepad(PRUint32 aIndex, nsDOMGamepad* aGamepad);
+ void RemoveGamepad(PRUint32 aIndex);
+ already_AddRefed<nsDOMGamepad> GetGamepad(PRUint32 aIndex);
+ void SetHasSeenGamepadInput(bool aHasSeen);
+ bool HasSeenGamepadInput();
+ void SyncGamepadState();
+ static PLDHashOperator EnumGamepadsForSync(const PRUint32& aKey,
+ nsDOMGamepad* aData,
+ void* userArg);
+#endif
+
+ // Enable/disable updates for gamepad input.
+ void EnableGamepadUpdates();
+ void DisableGamepadUpdates();
+
+
#define EVENT(name_, id_, type_, struct_) \
mozilla::dom::EventHandlerNonNull* GetOn##name_() \
{ \
@@ -1044,6 +1066,13 @@ class nsGlobalWindow : public mozilla::dom::EventTarget,
// should be displayed.
bool mFocusByKeyOccurred : 1;
+ // Indicates whether this window wants gamepad input events
+ bool mHasGamepad : 1;
+#ifdef MOZ_GAMEPAD
+ nsRefPtrHashtable<nsUint32HashKey, nsDOMGamepad> mGamepads;
+ bool mHasSeenGamepadInput;
+#endif
+
// whether we've sent the destroy notification for our window id
bool mNotifiedIDDestroyed : 1;
// whether scripts may close the window,
View
9 dom/base/nsPIDOMWindow.h
@@ -57,8 +57,8 @@ class AudioContext;
}
#define NS_PIDOMWINDOW_IID \
-{ 0xf5af1c3c, 0xebad, 0x4d00, \
- { 0xa2, 0xa4, 0x12, 0x2e, 0x27, 0x16, 0x59, 0x01 } }
+{0xf30405c2, 0x5da8, 0x4339, \
+ {0x87, 0xe2, 0xfa, 0xb2, 0x51, 0x26, 0x8a, 0xe8}}
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@@ -600,6 +600,11 @@ class nsPIDOMWindow : public nsIDOMWindowInternal
#endif // MOZ_B2G
/**
+ * Tell this window that there is an observer for gamepad input
+ */
+ virtual void SetHasGamepadEventListener(bool aHasGamepad = true) = 0;
+
+ /**
* Set a arguments for this window. This will be set on the window
* right away (if there's an existing document) and it will also be
* installed on the window when the next document is loaded. Each
View
3  dom/interfaces/events/moz.build
@@ -47,6 +47,9 @@ XPIDL_SOURCES += [
'nsIDOMUIEvent.idl',
'nsIDOMUserProximityEvent.idl',
'nsIDOMWheelEvent.idl',
+ 'nsIDOMGamepadButtonEvent.idl',
+ 'nsIDOMGamepadAxisMoveEvent.idl',
+ 'nsIDOMGamepadEvent.idl',
]
XPIDL_MODULE = 'dom_events'
View
33 dom/interfaces/events/nsIDOMGamepadAxisMoveEvent.idl
@@ -0,0 +1,33 @@
+/* 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 "nsIDOMGamepadEvent.idl"
+
+[builtinclass, scriptable, uuid(bd09eef8-8e07-4baf-8d39-4f92003dbca8)]
+interface nsIDOMGamepadAxisMoveEvent : nsIDOMGamepadEvent
+{
+ /**
+ * Index in gamepad.axes of the axis that was moved.
+ */
+ readonly attribute unsigned long axis;
+
+ /**
+ * Position of the axis in the range -1.0...1.0.
+ */
+ readonly attribute double value;
+
+ [noscript]
+ void initGamepadAxisMoveEvent(in DOMString typeArg,
+ in boolean canBubbleArg,
+ in boolean cancelableArg,
+ in nsIDOMGamepad gamepad,
+ in unsigned long axis,
+ in double value);
+};
+
+dictionary GamepadAxisMoveEventInit : GamepadEventInit
+{
+ unsigned long axis;
+ double value;
+};
View
26 dom/interfaces/events/nsIDOMGamepadButtonEvent.idl
@@ -0,0 +1,26 @@
+/* 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 "nsIDOMGamepadEvent.idl"
+
+[builtinclass, scriptable, uuid(d75d4d2b-e7b4-4b93-ac35-2e70b57d9b28)]
+interface nsIDOMGamepadButtonEvent : nsIDOMGamepadEvent
+{
+ /**
+ * Index in gamepad.buttons of the button that was pressed or released.
+ */
+ readonly attribute unsigned long button;
+
+ [noscript]
+ void initGamepadButtonEvent(in DOMString typeArg,
+ in boolean canBubbleArg,
+ in boolean cancelableArg,
+ in nsIDOMGamepad gamepad,
+ in unsigned long button);
+};
+
+dictionary GamepadButtonEventInit : GamepadEventInit
+{
+ unsigned long button;
+};
View
27 dom/interfaces/events/nsIDOMGamepadEvent.idl
@@ -0,0 +1,27 @@
+/* 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 "nsIDOMEvent.idl"
+
+interface nsIDOMGamepad;
+
+[builtinclass, scriptable, uuid(93b048d6-2aef-46a9-b0f4-06d7f00d8ef2)]
+interface nsIDOMGamepadEvent : nsIDOMEvent
+{
+ /**
+ * The device that generated this event.
+ */
+ readonly attribute nsIDOMGamepad gamepad;
+
+ [noscript]
+ void initGamepadEvent(in DOMString typeArg,
+ in boolean canBubbleArg,
+ in boolean cancelableArg,
+ in nsIDOMGamepad gamepad);
+};
+
+dictionary GamepadEventInit : EventInit
+{
+ nsIDOMGamepad gamepad;
+};
View
12 dom/interfaces/gamepad/Makefile.in
@@ -0,0 +1,12 @@
+# 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/.
+
+DEPTH = ../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+include $(topsrcdir)/config/rules.mk
View
11 dom/interfaces/gamepad/moz.build
@@ -0,0 +1,11 @@
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_MODULE = 'dom_gamepad'
+
+XPIDL_SOURCES = [
+ 'nsIDOMGamepad.idl',
+ 'nsIGamepadServiceTest.idl',
+ ]
View
39 dom/interfaces/gamepad/nsIDOMGamepad.idl
@@ -0,0 +1,39 @@
+/* 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 "nsISupports.idl"
+
+interface nsIVariant;
+
+[builtinclass, scriptable, uuid(ff13acd9-11da-4817-8f2a-4a5700dfd13e)]
+interface nsIDOMGamepad : nsISupports
+{
+ /**
+ * An identifier, unique per type of device.
+ */
+ readonly attribute DOMString id;
+
+ /**
+ * The game port index for the device. Unique per device
+ * attached to this system.
+ */
+ readonly attribute unsigned long index;
+
+ /**
+ * true if this gamepad is currently connected to the system.
+ */
+ readonly attribute boolean connected;
+
+ /**
+ * The current state of all buttons on the device, an
+ * array of doubles.
+ */
+ readonly attribute nsIVariant buttons;
+
+ /**
+ * The current position of all axes on the device, an
+ * array of doubles.
+ */
+ readonly attribute nsIVariant axes;
+};
View
22 dom/interfaces/gamepad/nsIGamepadServiceTest.idl
@@ -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/. */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+/*
+ * This interface is intended only for use in tests.
+ */
+[scriptable, uuid(7edf77a2-6b3e-4bbb-9100-4452d425feaa)]
+interface nsIGamepadServiceTest : nsISupports
+{
+ unsigned long addGamepad(in string id, 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 newAxisMoveEvent(in unsigned long index, in unsigned long axis,
+ in double value);
+};
View
1  dom/moz.build
@@ -29,6 +29,7 @@ interfaces = [
'svg',
'smil',
'apps',
+ 'gamepad',
]
PARALLEL_DIRS += ['interfaces/' + i for i in interfaces]
View
530 dom/system/GamepadService.cpp
@@ -0,0 +1,530 @@
+/* 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/Hal.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+
+#include "GamepadService.h"
+#include "nsAutoPtr.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMEventTarget.h"
+#include "nsDOMGamepad.h"
+#include "nsIDOMGamepadButtonEvent.h"
+#include "nsIDOMGamepadAxisMoveEvent.h"
+#include "nsIDOMGamepadEvent.h"
+#include "GeneratedEvents.h"
+#include "nsIDOMWindow.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIServiceManager.h"
+#include "nsITimer.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Services.h"
+
+#include <cstddef>
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+// Amount of time to wait before cleaning up gamepad resources
+// when no pages are listening for events.
+const int kCleanupDelayMS = 2000;
+const nsTArray<nsRefPtr<nsGlobalWindow> >::index_type NoIndex =
+ nsTArray<nsRefPtr<nsGlobalWindow> >::NoIndex;
+
+StaticRefPtr<GamepadService> gGamepadServiceSingleton;
+
+} // namespace
+
+bool GamepadService::sShutdown = false;
+
+NS_IMPL_ISUPPORTS0(GamepadService)
+
+GamepadService::GamepadService()
+ : mStarted(false),
+ mShuttingDown(false)
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->AddObserver(this,
+ NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
+ false);
+}
+
+NS_IMETHODIMP
+GamepadService::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const PRUnichar* aData)
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ nsCOMPtr<nsIObserver> observer = do_QueryInterface(this);
+ observerService->RemoveObserver(observer, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+
+ BeginShutdown();
+ return NS_OK;
+}
+
+void
+GamepadService::BeginShutdown()
+{
+ mShuttingDown = true;
+ if (mTimer) {
+ mTimer->Cancel();
+ }
+ mozilla::hal::StopMonitoringGamepadStatus();
+ mStarted = false;
+ // Don't let windows call back to unregister during shutdown
+ for (uint32_t i = 0; i < mListeners.Length(); i++) {
+ mListeners[i]->SetHasGamepadEventListener(false);
+ }
+ mListeners.Clear();
+ mGamepads.Clear();
+ sShutdown = true;
+}
+
+void
+GamepadService::AddListener(nsGlobalWindow* aWindow)
+{
+ if (mShuttingDown) {
+ return;
+ }
+
+ if (mListeners.IndexOf(aWindow) != NoIndex) {
+ return; // already exists
+ }
+
+ if (!mStarted) {
+ mozilla::hal::StartMonitoringGamepadStatus();
+ mStarted = true;
+ }
+
+ mListeners.AppendElement(aWindow);
+}
+
+void
+GamepadService::RemoveListener(nsGlobalWindow* aWindow)
+{
+ if (mShuttingDown) {
+ // Doesn't matter at this point. It's possible we're being called
+ // as a result of our own destructor here, so just bail out.
+ return;
+ }
+
+ if (mListeners.IndexOf(aWindow) == NoIndex) {
+ return; // doesn't exist
+ }
+
+ mListeners.RemoveElement(aWindow);
+
+ if (mListeners.Length() == 0 && !mShuttingDown) {
+ StartCleanupTimer();
+ }
+}
+
+uint32_t
+GamepadService::AddGamepad(const char* aId,
+ uint32_t aNumButtons,
+ uint32_t aNumAxes)
+{
+ //TODO: bug 852258: get initial button/axis state
+ nsRefPtr<nsDOMGamepad> gamepad =
+ new nsDOMGamepad(NS_ConvertUTF8toUTF16(nsDependentCString(aId)),
+ 0,
+ 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;
+}
+
+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;
+ }
+ }
+}
+
+void
+GamepadService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed)
+{
+ if (mShuttingDown || aIndex >= mGamepads.Length()) {
+ return;
+ }
+
+ double value = aPressed ? 1.0L : 0.0L;
+ mGamepads[aIndex]->SetButton(aButton, value);
+
+ // Hold on to listeners in a separate array because firing events
+ // can mutate the mListeners array.
+ nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
+
+ for (uint32_t i = listeners.Length(); i > 0 ; ) {
+ --i;
+
+ // Only send events to non-background windows
+ if (!listeners[i]->GetOuterWindow() ||
+ listeners[i]->GetOuterWindow()->IsBackground()) {
+ continue;
+ }
+
+ if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
+ SetWindowHasSeenGamepad(listeners[i], aIndex);
+ // This window hasn't seen this gamepad before, so
+ // send a connection event first.
+ NewConnectionEvent(aIndex, true);
+ }
+
+ nsRefPtr<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+ if (gamepad) {
+ gamepad->SetButton(aButton, value);
+ // Fire event
+ FireButtonEvent(listeners[i], gamepad, aButton, value);
+ }
+ }
+}
+
+void
+GamepadService::FireButtonEvent(EventTarget* aTarget,
+ nsDOMGamepad* aGamepad,
+ uint32_t aButton,
+ double aValue)
+{
+ nsCOMPtr<nsIDOMEvent> event;
+ bool defaultActionEnabled = true;
+ NS_NewDOMGamepadButtonEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
+ nsCOMPtr<nsIDOMGamepadButtonEvent> je = do_QueryInterface(event);
+ MOZ_ASSERT(je, "QI should not fail");
+
+
+ nsString name = aValue == 1.0L ? NS_LITERAL_STRING("gamepadbuttondown") :
+ NS_LITERAL_STRING("gamepadbuttonup");
+ je->InitGamepadButtonEvent(name, false, false, aGamepad, aButton);
+ je->SetTrusted(true);
+
+ aTarget->DispatchEvent(event, &defaultActionEnabled);
+}
+
+void
+GamepadService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue)
+{
+ if (mShuttingDown || aIndex >= mGamepads.Length()) {
+ return;
+ }
+ mGamepads[aIndex]->SetAxis(aAxis, aValue);
+
+ // Hold on to listeners in a separate array because firing events
+ // can mutate the mListeners array.
+ nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
+
+ for (uint32_t i = listeners.Length(); i > 0 ; ) {
+ --i;
+
+ // Only send events to non-background windows
+ if (!listeners[i]->GetOuterWindow() ||
+ listeners[i]->GetOuterWindow()->IsBackground()) {
+ continue;
+ }
+
+ if (!WindowHasSeenGamepad(listeners[i], aIndex)) {
+ SetWindowHasSeenGamepad(listeners[i], aIndex);
+ // This window hasn't seen this gamepad before, so
+ // send a connection event first.
+ NewConnectionEvent(aIndex, true);
+ }
+
+ nsRefPtr<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+ if (gamepad) {
+ gamepad->SetAxis(aAxis, aValue);
+ // Fire event
+ FireAxisMoveEvent(listeners[i], gamepad, aAxis, aValue);
+ }
+ }
+}
+
+void
+GamepadService::FireAxisMoveEvent(EventTarget* aTarget,
+ nsDOMGamepad* aGamepad,
+ uint32_t aAxis,
+ double aValue)
+{
+ nsCOMPtr<nsIDOMEvent> event;
+ bool defaultActionEnabled = true;
+ NS_NewDOMGamepadAxisMoveEvent(getter_AddRefs(event), aTarget, nullptr,
+ nullptr);
+ nsCOMPtr<nsIDOMGamepadAxisMoveEvent> je = do_QueryInterface(event);
+ MOZ_ASSERT(je, "QI should not fail");
+
+ je->InitGamepadAxisMoveEvent(NS_LITERAL_STRING("gamepadaxismove"),
+ false, false, aGamepad, aAxis, aValue);
+ je->SetTrusted(true);
+
+ aTarget->DispatchEvent(event, &defaultActionEnabled);
+}
+
+void
+GamepadService::NewConnectionEvent(uint32_t aIndex, bool aConnected)
+{
+ if (mShuttingDown || aIndex >= mGamepads.Length()) {
+ return;
+ }
+
+ // Hold on to listeners in a separate array because firing events
+ // can mutate the mListeners array.
+ nsTArray<nsRefPtr<nsGlobalWindow> > listeners(mListeners);
+
+ if (aConnected) {
+ for (uint32_t i = listeners.Length(); i > 0 ; ) {
+ --i;
+
+ // Only send events to non-background windows
+ if (!listeners[i]->GetOuterWindow() ||
+ listeners[i]->GetOuterWindow()->IsBackground())
+ continue;
+
+ // We don't fire a connected event here unless the window
+ // has seen input from at least one device.
+ if (aConnected && !listeners[i]->HasSeenGamepadInput()) {
+ return;
+ }
+
+ SetWindowHasSeenGamepad(listeners[i], aIndex);
+
+ nsRefPtr<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+ if (gamepad) {
+ // Fire event
+ FireConnectionEvent(listeners[i], gamepad, aConnected);
+ }
+ }
+ } else {
+ // For disconnection events, fire one at every window that has received
+ // data from this gamepad.
+ for (uint32_t i = listeners.Length(); i > 0 ; ) {
+ --i;
+
+ // Even background windows get these events, so we don't have to
+ // deal with the hassle of syncing the state of removed gamepads.
+
+ if (WindowHasSeenGamepad(listeners[i], aIndex)) {
+ nsRefPtr<nsDOMGamepad> gamepad = listeners[i]->GetGamepad(aIndex);
+ if (gamepad) {
+ gamepad->SetConnected(false);
+ // Fire event
+ FireConnectionEvent(listeners[i], gamepad, false);
+ }
+
+ if (gamepad) {
+ listeners[i]->RemoveGamepad(aIndex);
+ }
+ }
+ }
+ }
+}
+
+void
+GamepadService::FireConnectionEvent(EventTarget* aTarget,
+ nsDOMGamepad* aGamepad,
+ bool aConnected)
+{
+ nsCOMPtr<nsIDOMEvent> event;
+ bool defaultActionEnabled = true;
+ NS_NewDOMGamepadEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
+ nsCOMPtr<nsIDOMGamepadEvent> je = do_QueryInterface(event);
+ MOZ_ASSERT(je, "QI should not fail");
+
+ nsString name = aConnected ? NS_LITERAL_STRING("gamepadconnected") :
+ NS_LITERAL_STRING("gamepaddisconnected");
+ je->InitGamepadEvent(name, false, false, aGamepad);
+ je->SetTrusted(true);
+
+ aTarget->DispatchEvent(event, &defaultActionEnabled);
+}
+
+void
+GamepadService::SyncGamepadState(uint32_t aIndex, nsDOMGamepad* aGamepad)
+{
+ if (mShuttingDown || aIndex > mGamepads.Length()) {
+ return;
+ }
+
+ aGamepad->SyncState(mGamepads[aIndex]);
+}
+
+// static
+already_AddRefed<GamepadService>
+GamepadService::GetService()
+{
+ if (sShutdown) {
+ return nullptr;
+ }
+
+ if (!gGamepadServiceSingleton) {
+ gGamepadServiceSingleton = new GamepadService();
+ ClearOnShutdown(&gGamepadServiceSingleton);
+ }
+ nsRefPtr<GamepadService> service(gGamepadServiceSingleton);
+ return service.forget();
+}
+
+bool
+GamepadService::WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex)
+{
+ nsRefPtr<nsDOMGamepad> gamepad = aWindow->GetGamepad(aIndex);
+ return gamepad != nullptr;
+}
+
+void
+GamepadService::SetWindowHasSeenGamepad(nsGlobalWindow* aWindow,
+ uint32_t aIndex,
+ bool aHasSeen)
+{
+ if (mListeners.IndexOf(aWindow) == NoIndex) {
+ // This window isn't even listening for gamepad events.
+ return;
+ }
+
+ if (aHasSeen) {
+ aWindow->SetHasSeenGamepadInput(true);
+ nsRefPtr<nsDOMGamepad> gamepad = mGamepads[aIndex]->Clone();
+ aWindow->AddGamepad(aIndex, gamepad);
+ } else {
+ aWindow->RemoveGamepad(aIndex);
+ }
+}
+
+// static
+void
+GamepadService::TimeoutHandler(nsITimer* aTimer, void* aClosure)
+{
+ // the reason that we use self, instead of just using nsITimerCallback or nsIObserver
+ // is so that subclasses are free to use timers without worry about the base classes's
+ // usage.
+ GamepadService* self = reinterpret_cast<GamepadService*>(aClosure);
+ if (!self) {
+ NS_ERROR("no self");
+ return;
+ }
+
+ if (self->mShuttingDown) {
+ return;
+ }
+
+ if (self->mListeners.Length() == 0) {
+ mozilla::hal::StopMonitoringGamepadStatus();
+ self->mStarted = false;
+ if (!self->mGamepads.IsEmpty()) {
+ self->mGamepads.Clear();
+ }
+ }
+}
+
+void
+GamepadService::StartCleanupTimer()
+{
+ if (mTimer) {
+ mTimer->Cancel();
+ }
+
+ mTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (mTimer) {
+ mTimer->InitWithFuncCallback(TimeoutHandler,
+ this,
+ kCleanupDelayMS,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+/*
+ * 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_ISUPPORTS1(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()
+{
+ /* member initializers and constructor code */
+}
+
+/* uint32_t addGamepad (in string id, in uint32_t numButtons, in uint32_t numAxes); */
+NS_IMETHODIMP GamepadServiceTest::AddGamepad(const char* aID,
+ uint32_t aNumButtons,
+ uint32_t aNumAxes,
+ uint32_t* aRetval)
+{
+ *aRetval = gGamepadServiceSingleton->AddGamepad(aID,
+ 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
+} // namespace mozilla
View
126 dom/system/GamepadService.h
@@ -0,0 +1,126 @@
+/* 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_GamepadService_h_
+#define mozilla_dom_GamepadService_h_
+
+#include "mozilla/StandardInteger.h"
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsDOMGamepad.h"
+#include "nsIGamepadServiceTest.h"
+#include "nsGlobalWindow.h"
+#include "nsIFocusManager.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsTArray.h"
+
+class nsIDOMDocument;
+
+namespace mozilla {
+namespace dom {
+
+class EventTarget;
+
+class GamepadService : public nsIObserver
+{
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ // Get the singleton service
+ static already_AddRefed<GamepadService> GetService();
+
+ void BeginShutdown();
+
+ // Indicate that |aWindow| wants to receive gamepad events.
+ void AddListener(nsGlobalWindow* aWindow);
+ // 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, uint32_t aNumButtons, uint32_t aNumAxes);
+ // Remove the gamepad at |aIndex| from the list of known gamepads.
+ void RemoveGamepad(uint32_t aIndex);
+
+ //TODO: the spec uses double values for buttons, to allow for analog
+ // buttons.
+ void NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed);
+ void NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue);
+
+ // Synchronize the state of |aGamepad| to match the gamepad stored at |aIndex|
+ void SyncGamepadState(uint32_t aIndex, nsDOMGamepad* aGamepad);
+
+ protected:
+ GamepadService();
+ virtual ~GamepadService() {};
+ void StartCleanupTimer();
+
+ void NewConnectionEvent(uint32_t aIndex, bool aConnected);
+ void FireAxisMoveEvent(EventTarget* aTarget,
+ nsDOMGamepad* aGamepad,
+ uint32_t axis,
+ double value);
+ void FireButtonEvent(EventTarget* aTarget,
+ nsDOMGamepad* aGamepad,
+ uint32_t aButton,
+ double aValue);
+ void FireConnectionEvent(EventTarget* aTarget,
+ nsDOMGamepad* aGamepad,
+ bool aConnected);
+
+ // true if the platform-specific backend has started work
+ bool mStarted;
+ // true when shutdown has begun
+ bool mShuttingDown;
+
+ private:
+ // Returns true if we have already sent data from this gamepad
+ // to this window. This should only return true if the user
+ // explicitly interacted with a gamepad while this window
+ // was focused, by pressing buttons or similar actions.
+ bool WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex);
+ // Indicate that a window has recieved data from a gamepad.
+ void SetWindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex,
+ bool aHasSeen = true);
+
+ static void TimeoutHandler(nsITimer* aTimer, void* aClosure);
+ static bool sShutdown;
+
+ // Gamepads connected to the system. Copies of these are handed out
+ // to each window.
+ nsTArray<nsRefPtr<nsDOMGamepad> > mGamepads;
+ // nsGlobalWindows 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
12 dom/system/Makefile.in
@@ -20,6 +20,10 @@ CPPSRCS = \
OSFileConstants.cpp \
$(NULL)
+ifdef MOZ_GAMEPAD
+CPPSRCS += GamepadService.cpp
+endif
+
# We fire the nsDOMDeviceAcceleration
LOCAL_INCLUDES += \
-I$(topsrcdir)/content/events/src \
@@ -55,11 +59,16 @@ EXPORTS_mozilla = \
OSFileConstants.h \
$(NULL)
-# We fire the nsDOMDeviceAcceleration
LOCAL_INCLUDES += \
-I$(topsrcdir)/content/events/src \
-I$(topsrcdir)/js/xpconnect/loader \
$(NULL)
+ifdef MOZ_GAMEPAD
+EXPORTS_NAMESPACES += mozilla/dom
+EXPORTS_mozilla/dom = \
+ GamepadService.h \
+ $(NULL)
+endif
include $(topsrcdir)/config/config.mk
@@ -69,4 +78,3 @@ FORCE_STATIC_LIB = 1
EXPORT_LIBRARY = 1
include $(topsrcdir)/ipc/chromium/chromium-config.mk
include $(topsrcdir)/config/rules.mk
-
View
23 dom/tests/mochitest/gamepad/Makefile.in
@@ -0,0 +1,23 @@
+# 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/.
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = dom/tests/mochitest/gamepad
+
+include $(DEPTH)/config/autoconf.mk
+
+MOCHITEST_FILES = \
+ test_gamepad_basic.html \
+ test_gamepad.html \
+ test_gamepad_frame_state_sync.html \
+ gamepad_frame_state.html \
+ test_gamepad_hidden_frame.html \
+ gamepad_frame.html \
+ mock_gamepad.js \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
View
16 dom/tests/mochitest/gamepad/gamepad_frame.html
@@ -0,0 +1,16 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>frame</title>
+ <script type="text/javascript">
+ var buttonPresses = 0;
+ window.addEventListener("gamepadbuttondown", function() {
+ buttonPresses++;
+});
+ </script>
+</head>
+<body>
+</body>
+</html>
View
16 dom/tests/mochitest/gamepad/gamepad_frame_state.html
@@ -0,0 +1,16 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>frame</title>
+ <script type="text/javascript">
+ var gamepad;
+ window.addEventListener("gamepadconnected", function(e) {
+ gamepad = e.gamepad;
+});
+ </script>
+</head>
+<body>
+</body>
+</html>
View
5 dom/tests/mochitest/gamepad/mock_gamepad.js
@@ -0,0 +1,5 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+var GamepadService = (function() {
+ return SpecialPowers.Cc["@mozilla.org/gamepad-test;1"].getService(SpecialPowers.Ci.nsIGamepadServiceTest);
+})();
View
4 dom/tests/mochitest/gamepad/moz.build
@@ -0,0 +1,4 @@
+# vim: set filetype=python:
+# 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/.
View
33 dom/tests/mochitest/gamepad/test_gamepad.html
@@ -0,0 +1,33 @@
+<!-- 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();
+window.addEventListener("gamepadconnected", connecthandler);
+// Add a gamepad
+var index = GamepadService.addGamepad("test gamepad", // id
+ 4, // buttons
+ 2);// axes
+// Press a button
+GamepadService.newButtonEvent(index, 0, true);
+function connecthandler(e) {
+ is(e.gamepad.id, "test gamepad", "correct gamepad name");
+ is(e.gamepad.buttons.length, 4, "correct number of buttons");
+ is(e.gamepad.axes.length, 2, "correct number of axes");
+ SimpleTest.executeSoon(function() {
+ GamepadService.removeGamepad(index);
+ SimpleTest.finish();
+ });
+}
+</script>
+</body>
+</html>
+
View
22 dom/tests/mochitest/gamepad/test_gamepad_basic.html
@@ -0,0 +1,22 @@
+<!-- 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 class="testbody" type="text/javascript">
+
+// Not much we can test here, but this is enough to get the
+// gamepad service up and running, which should test that it
+// doesn't leak anything.
+window.addEventListener("gamepadconnected", function() {});
+SimpleTest.ok(true, "dummy check");
+
+</script>
+</body>
+</html>
+
View
85 dom/tests/mochitest/gamepad/test_gamepad_frame_state_sync.html
@@ -0,0 +1,85 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test hidden frames</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 index = GamepadService.addGamepad("test gamepad", // id
+ 4, // buttons
+ 2);// axes
+
+function setFrameVisible(f, visible) {
+ var Ci = SpecialPowers.wrap(Components.interfaces);
+ var docshell = SpecialPowers.wrap(f.contentWindow).QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
+ docshell.isActive = visible;
+}
+
+var frames_loaded = 0;
+var f1, f2;
+function frame_loaded() {
+ frames_loaded++;
+ if (frames_loaded == 2) {
+ f1 = document.getElementById('f1');
+ f2 = document.getElementById('f2');
+ // Now press the button, but don't release it.
+ GamepadService.newButtonEvent(index, 0, true);
+ }
+}
+
+window.addEventListener("gamepadbuttondown", function() {
+ // Wait to ensure that all frames received the button press as well.
+ SpecialPowers.executeSoon(tests[testNum++]);
+});
+
+var testNum = 0;
+var tests = [
+ check_button_pressed,
+ check_second_frame_no_button_press,
+];
+
+function check_button_pressed() {
+ // At this point the both frames should see the button as pressed.
+ is(f1.contentWindow.gamepad.buttons[0], 1, "frame 1 sees button pressed");
+ is(f2.contentWindow.gamepad.buttons[0], 1, "frame 2 sees button pressed");
+
+ // Now release the button, then hide the second frame.
+ GamepadService.newButtonEvent(index, 0, false);
+ setFrameVisible(f2, false);
+ SpecialPowers.executeSoon(function() {
+ // Now press the button, but don't release it.
+ GamepadService.newButtonEvent(index, 0, true);
+ });
+}
+
+function check_second_frame_no_button_press () {
+ /*
+ * At this point the first frame should see the button as pressed,
+ * but the second frame should not, since it's hidden.
+ */
+ is(f1.contentWindow.gamepad.buttons[0], 1, "frame 1 sees button pressed");
+ is(f2.contentWindow.gamepad.buttons[0], 0, "frame 2 should not see button pressed");
+
+ // Now unhide the second frame.
+ setFrameVisible(f2, true);
+ SpecialPowers.executeSoon(function() {
+ // Now that the frame is visible again, it should see the button
+ // that was pressed.
+ is(f2.contentWindow.gamepad.buttons[0], 1, "frame 2 sees button pressed");
+ // cleanup
+ GamepadService.removeGamepad(index);
+ SimpleTest.finish();
+ });
+}
+
+</script>
+<iframe id="f1" src="gamepad_frame_state.html" onload="frame_loaded()"></iframe>
+<iframe id="f2" src="gamepad_frame_state.html" onload="frame_loaded()"></iframe>
+</body>
+</html>
View
71 dom/tests/mochitest/gamepad/test_gamepad_hidden_frame.html
@@ -0,0 +1,71 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test hidden frames</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 index = GamepadService.addGamepad("test gamepad", // id
+ 4, // buttons
+ 2);// axes
+
+function pressButton() {
+ GamepadService.newButtonEvent(index, 0, true);
+ GamepadService.newButtonEvent(index, 0, false);
+}
+
+function setFrameVisible(f, visible) {
+ var Ci = SpecialPowers.wrap(Components.interfaces);
+ var docshell = SpecialPowers.wrap(f.contentWindow).QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShell);
+ docshell.isActive = visible;
+}
+
+var frames_loaded = 0;
+var f1, f2;
+function frame_loaded() {
+ frames_loaded++;
+ if (frames_loaded == 2) {
+ f1 = document.getElementById('f1');
+ f2 = document.getElementById('f2');
+ pressButton();
+ }
+}
+
+window.addEventListener("gamepadbuttondown", function() {
+ // Wait to ensure that all frames received the button press as well.
+ SpecialPowers.executeSoon(tests[testNum++]);
+});
+
+var testNum = 0;
+var tests = [
+ test1,
+ test2,
+];
+
+function test1() {
+ is(f1.contentWindow.buttonPresses, 1, "right number of button presses in frame 1");
+ is(f2.contentWindow.buttonPresses, 1, "right number of button presses in frame 2");
+
+ // Now hide the second frame and send another button press.
+ setFrameVisible(f2, false);
+ SpecialPowers.executeSoon(function() { pressButton(); });
+}
+
+function test2() {
+ is(f1.contentWindow.buttonPresses, 2, "right number of button presses in frame 1");
+ is(f2.contentWindow.buttonPresses, 1, "right number of button presses in frame 2");
+ GamepadService.removeGamepad(index);
+ SimpleTest.finish();
+}
+
+</script>
+<iframe id="f1" src="gamepad_frame.html" onload="frame_loaded()"></iframe>
+<iframe id="f2" src="gamepad_frame.html" onload="frame_loaded()"></iframe>
+</body>
+</html>
View
6 dom/tests/mochitest/general/test_interfaces.html
@@ -540,7 +540,11 @@
"AsyncScrollEventDetail",
"MozSmsSegmentInfo",
"DOMCursor",
- "BlobEvent"
+ "BlobEvent",
+ "Gamepad",
+ "GamepadEvent",
+ "GamepadButtonEvent",
+ "GamepadAxisMoveEvent"
]
for (var i in SpecialPowers.Components.interfaces) {
View
3  dom/tests/mochitest/moz.build
@@ -25,6 +25,9 @@ DIRS += [
'webcomponents',
]
+if CONFIG['MOZ_GAMEPAD']:
+ DIRS += ['gamepad']
+
#needs IPC support, also tests do not run successfully in Firefox for now
#if CONFIG['MOZ_BUILD_APP'] != 'mobile':
# DIRS += ['notification']
View
10 hal/Hal.cpp
@@ -629,6 +629,16 @@ 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
10 hal/Hal.h
@@ -559,6 +559,16 @@ void StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs);
*/
void FactoryReset();
+/**
+ * Start monitoring the status of gamepads attached to the system.
+ */
+void StartMonitoringGamepadStatus();
+
+/**
+ * Stop monitoring the status of gamepads attached to the system.
+ */
+void StopMonitoringGamepadStatus();
+
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla
View
4 hal/Makefile.in
@@ -42,6 +42,10 @@ CPPSRCS = \
HalWakeLock.cpp \
$(NULL)
+ifeq (stub,$(MOZ_GAMEPAD_BACKEND))
+CPPSRCS += FallbackGamepad.cpp
+endif
+
ifeq (android,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
AndroidHal.cpp \
View
19 hal/fallback/FallbackGamepad.cpp
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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 "Hal.h"
+
+namespace mozilla {
+namespace hal_impl {
+
+void StartMonitoringGamepadStatus()
+{}
+
+void StopMonitoringGamepadStatus()
+{}
+
+} // hal_impl
+} // namespace mozilla
View
7 hal/sandbox/SandboxHal.cpp
@@ -263,6 +263,13 @@ DisableSensorNotifications(SensorType aSensor) {
Hal()->SendDisableSensorNotifications(aSensor);
}
+//TODO: bug 852944 - IPC implementations of these
+void StartMonitoringGamepadStatus()
+{}
+
+void StopMonitoringGamepadStatus()
+{}
+
void
EnableWakeLockNotifications()
{
View
7 js/xpconnect/src/event_impl_gen.conf.in
@@ -44,7 +44,12 @@ simple_events = [
'MozMmsEvent',
'DeviceStorageChangeEvent',
'PopupBlockedEvent',
- 'BlobEvent'
+ 'BlobEvent',
+#ifdef MOZ_GAMEPAD
+ 'GamepadEvent',
+ 'GamepadButtonEvent',
+ 'GamepadAxisMoveEvent',
+#endif
]
""" include file names """
View
17 layout/build/nsLayoutModule.cpp
@@ -217,6 +217,9 @@ static void Shutdown();
#include "nsGeolocation.h"
#include "nsDeviceSensors.h"
+#ifdef MOZ_GAMEPAD
+#include "mozilla/dom/GamepadService.h"
+#endif
#include "nsCSPService.h"
#include "nsISmsService.h"
#include "nsIMobileMessageService.h"
@@ -316,6 +319,11 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIAlarmHalService,
AlarmHalService::GetInstance)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITimeService,
TimeService::GetInstance)
+#ifdef MOZ_GAMEPAD
+using mozilla::dom::GamepadServiceTest;
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(GamepadServiceTest,
+ GamepadServiceTest::CreateService)
+#endif
#ifdef MOZ_WIDGET_GONK
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIGeolocationProvider,
@@ -839,6 +847,9 @@ NS_DEFINE_NAMED_CID(NS_TIMESERVICE_CID);
NS_DEFINE_NAMED_CID(GONK_GPS_GEOLOCATION_PROVIDER_CID);
#endif
NS_DEFINE_NAMED_CID(NS_MEDIAMANAGERSERVICE_CID);
+#ifdef MOZ_GAMEPAD
+NS_DEFINE_NAMED_CID(NS_GAMEPAD_TEST_CID);
+#endif
static nsresult
CreateWindowCommandTableConstructor(nsISupports *aOuter,
@@ -1118,6 +1129,9 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = {
{ &kGONK_GPS_GEOLOCATION_PROVIDER_CID, false, NULL, nsIGeolocationProviderConstructor },
#endif
{ &kNS_MEDIAMANAGERSERVICE_CID, false, NULL, nsIMediaManagerServiceConstructor },
+#ifdef MOZ_GAMEPAD
+ { &kNS_GAMEPAD_TEST_CID, false, NULL, GamepadServiceTestConstructor },
+#endif
{ NULL }
};
@@ -1260,6 +1274,9 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
#ifdef MOZ_WIDGET_GONK
{ GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID, &kGONK_GPS_GEOLOCATION_PROVIDER_CID },
#endif
+#ifdef MOZ_GAMEPAD
+ { NS_GAMEPAD_TEST_CONTRACTID, &kNS_GAMEPAD_TEST_CID },
+#endif
{ MEDIAMANAGERSERVICE_CONTRACTID, &kNS_MEDIAMANAGERSERVICE_CID },
{ NULL }
};
View
12 widget/nsGUIEvent.h
@@ -452,6 +452,18 @@ enum nsEventStructType {
#define NS_NETWORK_UPLOAD_EVENT (NS_NETWORK_EVENT_START + 1)
#define NS_NETWORK_DOWNLOAD_EVENT (NS_NETWORK_EVENT_START + 2)
+#ifdef MOZ_GAMEPAD
+// Gamepad input events
+#define NS_GAMEPAD_START 6000
+#define NS_GAMEPAD_BUTTONDOWN (NS_GAMEPAD_START)
+#define NS_GAMEPAD_BUTTONUP (NS_GAMEPAD_START+1)
+#define NS_GAMEPAD_AXISMOVE (NS_GAMEPAD_START+2)
+#define NS_GAMEPAD_CONNECTED (NS_GAMEPAD_START+3)
+#define NS_GAMEPAD_DISCONNECTED (NS_GAMEPAD_START+4)
+// Keep this defined to the same value as the event above
+#define NS_GAMEPAD_END (NS_GAMEPAD_START+4)
+#endif
+
/**
* Return status for event processors, nsEventStatus, is defined in
* nsEvent.h.
Please sign in to comment.
Something went wrong with that request. Please try again.