diff options
Diffstat (limited to 'dom/gamepad/GamepadPlatformService.cpp')
-rw-r--r-- | dom/gamepad/GamepadPlatformService.cpp | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/dom/gamepad/GamepadPlatformService.cpp b/dom/gamepad/GamepadPlatformService.cpp new file mode 100644 index 000000000..3df967aec --- /dev/null +++ b/dom/gamepad/GamepadPlatformService.cpp @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/GamepadPlatformService.h" + +#include "mozilla/dom/GamepadEventChannelParent.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/Mutex.h" +#include "mozilla/Unused.h" + +#include "nsCOMPtr.h" +#include "nsHashKeys.h" +#include "nsIThread.h" + +using namespace mozilla::ipc; + +namespace mozilla { +namespace dom { + +namespace { + +// This is the singleton instance of GamepadPlatformService, can be called +// by both background and monitor thread. +StaticRefPtr<GamepadPlatformService> gGamepadPlatformServiceSingleton; + +} //namepsace + +GamepadPlatformService::GamepadPlatformService() + : mGamepadIndex(0), + mMutex("mozilla::dom::GamepadPlatformService") +{} + +GamepadPlatformService::~GamepadPlatformService() +{ + Cleanup(); +} + +// static +already_AddRefed<GamepadPlatformService> +GamepadPlatformService::GetParentService() +{ + //GamepadPlatformService can only be accessed in parent process + MOZ_ASSERT(XRE_IsParentProcess()); + if (!gGamepadPlatformServiceSingleton) { + // Only Background Thread can create new GamepadPlatformService instance. + if (IsOnBackgroundThread()) { + gGamepadPlatformServiceSingleton = new GamepadPlatformService(); + } else { + return nullptr; + } + } + RefPtr<GamepadPlatformService> service(gGamepadPlatformServiceSingleton); + return service.forget(); +} + +template<class T> +void +GamepadPlatformService::NotifyGamepadChange(const T& aInfo) +{ + // This method is called by monitor populated in + // platform-dependent backends + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + + GamepadChangeEvent e(aInfo); + + // mChannelParents may be accessed by background thread in the + // same time, we use mutex to prevent possible race condtion + MutexAutoLock autoLock(mMutex); + + // Buffer all events if we have no Channel to dispatch, which + // may happen when performing Mochitest. + if (mChannelParents.IsEmpty()) { + mPendingEvents.AppendElement(e); + return; + } + + for(uint32_t i = 0; i < mChannelParents.Length(); ++i) { + mChannelParents[i]->DispatchUpdateEvent(e); + } +} + +uint32_t +GamepadPlatformService::AddGamepad(const char* aID, + GamepadMappingType aMapping, + uint32_t aNumButtons, uint32_t aNumAxes) +{ + // This method is called by monitor thread populated in + // platform-dependent backends + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + + uint32_t index = ++mGamepadIndex; + GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), index, + static_cast<uint32_t>(aMapping), GamepadServiceType::Standard, aNumButtons, aNumAxes); + NotifyGamepadChange<GamepadAdded>(a); + return index; +} + +void +GamepadPlatformService::RemoveGamepad(uint32_t aIndex) +{ + // This method is called by monitor thread populated in + // platform-dependent backends + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + GamepadRemoved a(aIndex, GamepadServiceType::Standard); + NotifyGamepadChange<GamepadRemoved>(a); +} + +void +GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, + bool aPressed, double aValue) +{ + // This method is called by monitor thread populated in + // platform-dependent backends + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + GamepadButtonInformation a(aIndex, GamepadServiceType::Standard, + aButton, aPressed, aValue); + NotifyGamepadChange<GamepadButtonInformation>(a); +} + +void +GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, + bool aPressed) +{ + // This method is called by monitor thread populated in + // platform-dependent backends + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + // When only a digital button is available the value will be synthesized. + NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L); +} + +void +GamepadPlatformService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, + double aValue) +{ + // This method is called by monitor thread populated in + // platform-dependent backends + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + GamepadAxisInformation a(aIndex, GamepadServiceType::Standard, + aAxis, aValue); + NotifyGamepadChange<GamepadAxisInformation>(a); +} + +void +GamepadPlatformService::ResetGamepadIndexes() +{ + // This method is called by monitor thread populated in + // platform-dependent backends + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!NS_IsMainThread()); + mGamepadIndex = 0; +} + +void +GamepadPlatformService::AddChannelParent(GamepadEventChannelParent* aParent) +{ + // mChannelParents can only be modified once GamepadEventChannelParent + // is created or removed in Background thread + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParent); + MOZ_ASSERT(!mChannelParents.Contains(aParent)); + + // We use mutex here to prevent race condition with monitor thread + MutexAutoLock autoLock(mMutex); + mChannelParents.AppendElement(aParent); + FlushPendingEvents(); +} + +void +GamepadPlatformService::FlushPendingEvents() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mChannelParents.IsEmpty()); + + if (mPendingEvents.IsEmpty()) { + return; + } + + // NOTE: This method must be called with mMutex held because it accesses + // mChannelParents. + for (uint32_t i=0; i<mChannelParents.Length(); ++i) { + for (uint32_t j=0; j<mPendingEvents.Length();++j) { + mChannelParents[i]->DispatchUpdateEvent(mPendingEvents[j]); + } + } + mPendingEvents.Clear(); +} + +void +GamepadPlatformService::RemoveChannelParent(GamepadEventChannelParent* aParent) +{ + // mChannelParents can only be modified once GamepadEventChannelParent + // is created or removed in Background thread + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParent); + MOZ_ASSERT(mChannelParents.Contains(aParent)); + + // We use mutex here to prevent race condition with monitor thread + MutexAutoLock autoLock(mMutex); + mChannelParents.RemoveElement(aParent); +} + +bool +GamepadPlatformService::HasGamepadListeners() +{ + // mChannelParents may be accessed by background thread in the + // same time, we use mutex to prevent possible race condtion + AssertIsOnBackgroundThread(); + + // We use mutex here to prevent race condition with monitor thread + MutexAutoLock autoLock(mMutex); + for (uint32_t i = 0; i < mChannelParents.Length(); i++) { + if(mChannelParents[i]->HasGamepadListener()) { + return true; + } + } + return false; +} + +void +GamepadPlatformService::MaybeShutdown() +{ + // This method is invoked in MaybeStopGamepadMonitoring when + // an IPDL channel is going to be destroyed + AssertIsOnBackgroundThread(); + + // We have to release gGamepadPlatformServiceSingleton ouside + // the mutex as well as making upcoming GetParentService() call + // recreate new singleton, so we use this RefPtr to temporarily + // hold the reference, postponing the release process until this + // method ends. + RefPtr<GamepadPlatformService> kungFuDeathGrip; + + bool isChannelParentEmpty; + { + MutexAutoLock autoLock(mMutex); + isChannelParentEmpty = mChannelParents.IsEmpty(); + if(isChannelParentEmpty) { + kungFuDeathGrip = gGamepadPlatformServiceSingleton; + gGamepadPlatformServiceSingleton = nullptr; + } + } +} + +void +GamepadPlatformService::Cleanup() +{ + // This method is called when GamepadPlatformService is + // successfully distructed in background thread + AssertIsOnBackgroundThread(); + + MutexAutoLock autoLock(mMutex); + mChannelParents.Clear(); +} + +} // namespace dom +} // namespace mozilla |