mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 604039 - Add DOM Gamepad APIs. r=smaug
--HG-- extra : rebase_source : ffffdc4549da1b25ea263b623c05ae1afb3d46a0
This commit is contained in:
parent
72d26c093a
commit
bc92001c0b
@ -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
|
||||
|
31
configure.in
31
configure.in
@ -5927,6 +5927,37 @@ if test -n "$MOZ_ANGLE_RENDERER" -a -z "$CROSS_COMPILE"; then
|
||||
MOZ_D3DCOMPILER_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
|
||||
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 ========================================================
|
||||
|
@ -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*
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
171
content/events/src/nsDOMGamepad.cpp
Normal file
171
content/events/src/nsDOMGamepad.cpp
Normal file
@ -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();
|
||||
}
|
49
content/events/src/nsDOMGamepad.h
Normal file
49
content/events/src/nsDOMGamepad.h
Normal file
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
@ -8063,6 +8086,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)
|
||||
{
|
||||
@ -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();
|
||||
@ -10994,6 +11047,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()
|
||||
{
|
||||
@ -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,
|
||||
|
@ -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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ protected:
|
||||
// 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,
|
||||
|
@ -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
|
||||
{
|
||||
@ -599,6 +599,11 @@ public:
|
||||
virtual void DisableNetworkEvent(uint32_t aType) = 0;
|
||||
#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
|
||||
|
@ -47,6 +47,9 @@ XPIDL_SOURCES += [
|
||||
'nsIDOMUIEvent.idl',
|
||||
'nsIDOMUserProximityEvent.idl',
|
||||
'nsIDOMWheelEvent.idl',
|
||||
'nsIDOMGamepadButtonEvent.idl',
|
||||
'nsIDOMGamepadAxisMoveEvent.idl',
|
||||
'nsIDOMGamepadEvent.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_events'
|
||||
|
33
dom/interfaces/events/nsIDOMGamepadAxisMoveEvent.idl
Normal file
33
dom/interfaces/events/nsIDOMGamepadAxisMoveEvent.idl
Normal file
@ -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;
|
||||
};
|
26
dom/interfaces/events/nsIDOMGamepadButtonEvent.idl
Normal file
26
dom/interfaces/events/nsIDOMGamepadButtonEvent.idl
Normal file
@ -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;
|
||||
};
|
27
dom/interfaces/events/nsIDOMGamepadEvent.idl
Normal file
27
dom/interfaces/events/nsIDOMGamepadEvent.idl
Normal file
@ -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;
|
||||
};
|
12
dom/interfaces/gamepad/Makefile.in
Normal file
12
dom/interfaces/gamepad/Makefile.in
Normal file
@ -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
|
11
dom/interfaces/gamepad/moz.build
Normal file
11
dom/interfaces/gamepad/moz.build
Normal file
@ -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',
|
||||
]
|
39
dom/interfaces/gamepad/nsIDOMGamepad.idl
Normal file
39
dom/interfaces/gamepad/nsIDOMGamepad.idl
Normal file
@ -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;
|
||||
};
|
22
dom/interfaces/gamepad/nsIGamepadServiceTest.idl
Normal file
22
dom/interfaces/gamepad/nsIGamepadServiceTest.idl
Normal 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/. */
|
||||
|
||||
#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);
|
||||
};
|
@ -29,6 +29,7 @@ interfaces = [
|
||||
'svg',
|
||||
'smil',
|
||||
'apps',
|
||||
'gamepad',
|
||||
]
|
||||
|
||||
PARALLEL_DIRS += ['interfaces/' + i for i in interfaces]
|
||||
|
530
dom/system/GamepadService.cpp
Normal file
530
dom/system/GamepadService.cpp
Normal file
@ -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
|
126
dom/system/GamepadService.h
Normal file
126
dom/system/GamepadService.h
Normal file
@ -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_
|
@ -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
|
||||
|
||||
|
23
dom/tests/mochitest/gamepad/Makefile.in
Normal file
23
dom/tests/mochitest/gamepad/Makefile.in
Normal file
@ -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
|
16
dom/tests/mochitest/gamepad/gamepad_frame.html
Normal file
16
dom/tests/mochitest/gamepad/gamepad_frame.html
Normal file
@ -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>
|
16
dom/tests/mochitest/gamepad/gamepad_frame_state.html
Normal file
16
dom/tests/mochitest/gamepad/gamepad_frame_state.html
Normal file
@ -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>
|
5
dom/tests/mochitest/gamepad/mock_gamepad.js
Normal file
5
dom/tests/mochitest/gamepad/mock_gamepad.js
Normal file
@ -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);
|
||||
})();
|
4
dom/tests/mochitest/gamepad/moz.build
Normal file
4
dom/tests/mochitest/gamepad/moz.build
Normal file
@ -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/.
|
33
dom/tests/mochitest/gamepad/test_gamepad.html
Normal file
33
dom/tests/mochitest/gamepad/test_gamepad.html
Normal file
@ -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>
|
||||
|
22
dom/tests/mochitest/gamepad/test_gamepad_basic.html
Normal file
22
dom/tests/mochitest/gamepad/test_gamepad_basic.html
Normal file
@ -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>
|
||||
|
@ -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>
|
71
dom/tests/mochitest/gamepad/test_gamepad_hidden_frame.html
Normal file
71
dom/tests/mochitest/gamepad/test_gamepad_hidden_frame.html
Normal file
@ -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>
|
@ -540,7 +540,11 @@ var interfaceNamesInGlobalScope =
|
||||
"AsyncScrollEventDetail",
|
||||
"MozSmsSegmentInfo",
|
||||
"DOMCursor",
|
||||
"BlobEvent"
|
||||
"BlobEvent",
|
||||
"Gamepad",
|
||||
"GamepadEvent",
|
||||
"GamepadButtonEvent",
|
||||
"GamepadAxisMoveEvent"
|
||||
]
|
||||
|
||||
for (var i in SpecialPowers.Components.interfaces) {
|
||||
|
@ -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']
|
||||
|
10
hal/Hal.cpp
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)
|
||||
{
|
||||
|
10
hal/Hal.h
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
|
||||
|
||||
|
@ -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 \
|
||||
|
19
hal/fallback/FallbackGamepad.cpp
Normal file
19
hal/fallback/FallbackGamepad.cpp
Normal file
@ -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
|
@ -263,6 +263,13 @@ DisableSensorNotifications(SensorType aSensor) {
|
||||
Hal()->SendDisableSensorNotifications(aSensor);
|
||||
}
|
||||
|
||||
//TODO: bug 852944 - IPC implementations of these
|
||||
void StartMonitoringGamepadStatus()
|
||||
{}
|
||||
|
||||
void StopMonitoringGamepadStatus()
|
||||
{}
|
||||
|
||||
void
|
||||
EnableWakeLockNotifications()
|
||||
{
|
||||
|
@ -44,7 +44,12 @@ simple_events = [
|
||||
'MozMmsEvent',
|
||||
'DeviceStorageChangeEvent',
|
||||
'PopupBlockedEvent',
|
||||
'BlobEvent'
|
||||
'BlobEvent',
|
||||
#ifdef MOZ_GAMEPAD
|
||||
'GamepadEvent',
|
||||
'GamepadButtonEvent',
|
||||
'GamepadAxisMoveEvent',
|
||||
#endif
|
||||
]
|
||||
|
||||
""" include file names """
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
@ -1259,6 +1273,9 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
|
||||
{ TIMESERVICE_CONTRACTID, &kNS_TIMESERVICE_CID },
|
||||
#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 }
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user