diff options
Diffstat (limited to 'widget/gonk/OrientationObserver.cpp')
-rw-r--r-- | widget/gonk/OrientationObserver.cpp | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/widget/gonk/OrientationObserver.cpp b/widget/gonk/OrientationObserver.cpp new file mode 100644 index 000000000..9096404cf --- /dev/null +++ b/widget/gonk/OrientationObserver.cpp @@ -0,0 +1,332 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et ft=cpp : */ +/* Copyright 2012 Mozilla Foundation and Mozilla contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "base/basictypes.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Hal.h" +#include "nsIScreen.h" +#include "nsIScreenManager.h" +#include "OrientationObserver.h" +#include "mozilla/HalSensor.h" +#include "ProcessOrientation.h" +#include "nsServiceManagerUtils.h" + +using namespace mozilla; +using namespace dom; + +namespace { + +struct OrientationMapping { + uint32_t mScreenRotation; + ScreenOrientationInternal mDomOrientation; +}; + +static OrientationMapping sOrientationMappings[] = { + {nsIScreen::ROTATION_0_DEG, eScreenOrientation_PortraitPrimary}, + {nsIScreen::ROTATION_180_DEG, eScreenOrientation_PortraitSecondary}, + {nsIScreen::ROTATION_90_DEG, eScreenOrientation_LandscapePrimary}, + {nsIScreen::ROTATION_270_DEG, eScreenOrientation_LandscapeSecondary}, +}; + +const static uint32_t sDefaultLandscape = 2; +const static uint32_t sDefaultPortrait = 0; + +static uint32_t sOrientationOffset = 0; + +static already_AddRefed<nsIScreen> +GetPrimaryScreen() +{ + nsCOMPtr<nsIScreenManager> screenMgr = + do_GetService("@mozilla.org/gfx/screenmanager;1"); + NS_ENSURE_TRUE(screenMgr, nullptr); + + nsCOMPtr<nsIScreen> screen; + screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); + return screen.forget(); +} + +static void +DetectDefaultOrientation() +{ + nsCOMPtr<nsIScreen> screen = GetPrimaryScreen(); + if (!screen) { + return; + } + + int32_t left, top, width, height; + if (NS_FAILED(screen->GetRect(&left, &top, &width, &height))) { + return; + } + + uint32_t rotation; + if (NS_FAILED(screen->GetRotation(&rotation))) { + return; + } + + if (width < height) { + if (rotation == nsIScreen::ROTATION_0_DEG || + rotation == nsIScreen::ROTATION_180_DEG) { + sOrientationOffset = sDefaultPortrait; + } else { + sOrientationOffset = sDefaultLandscape; + } + } else { + if (rotation == nsIScreen::ROTATION_0_DEG || + rotation == nsIScreen::ROTATION_180_DEG) { + sOrientationOffset = sDefaultLandscape; + } else { + sOrientationOffset = sDefaultPortrait; + } + } +} + +/** + * Converts DOM orientation to nsIScreen rotation. Portrait and Landscape are + * treated as PortraitPrimary and LandscapePrimary, respectively, during + * conversion. + * + * @param aOrientation DOM orientation e.g. + * dom::eScreenOrientation_PortraitPrimary. + * @param aResult output nsIScreen rotation e.g. nsIScreen::ROTATION_0_DEG. + * @return NS_OK on success. NS_ILLEGAL_VALUE on failure. + */ +static nsresult +ConvertToScreenRotation(ScreenOrientationInternal aOrientation, uint32_t *aResult) +{ + for (uint32_t i = 0; i < ArrayLength(sOrientationMappings); i++) { + if (aOrientation & sOrientationMappings[i].mDomOrientation) { + // Shift the mappings in sOrientationMappings so devices with default + // landscape orientation map landscape-primary to 0 degree and so forth. + int adjusted = (i + sOrientationOffset) % + ArrayLength(sOrientationMappings); + *aResult = sOrientationMappings[adjusted].mScreenRotation; + return NS_OK; + } + } + + *aResult = nsIScreen::ROTATION_0_DEG; + return NS_ERROR_ILLEGAL_VALUE; +} + +/** + * Converts nsIScreen rotation to DOM orientation. + * + * @param aRotation nsIScreen rotation e.g. nsIScreen::ROTATION_0_DEG. + * @param aResult output DOM orientation e.g. + * dom::eScreenOrientation_PortraitPrimary. + * @return NS_OK on success. NS_ILLEGAL_VALUE on failure. + */ +nsresult +ConvertToDomOrientation(uint32_t aRotation, ScreenOrientationInternal *aResult) +{ + for (uint32_t i = 0; i < ArrayLength(sOrientationMappings); i++) { + if (aRotation == sOrientationMappings[i].mScreenRotation) { + // Shift the mappings in sOrientationMappings so devices with default + // landscape orientation map 0 degree to landscape-primary and so forth. + int adjusted = (i + sOrientationOffset) % + ArrayLength(sOrientationMappings); + *aResult = sOrientationMappings[adjusted].mDomOrientation; + return NS_OK; + } + } + + *aResult = eScreenOrientation_None; + return NS_ERROR_ILLEGAL_VALUE; +} + +// Note that all operations with sOrientationSensorObserver +// should be on the main thread. +static StaticAutoPtr<OrientationObserver> sOrientationSensorObserver; + +} // namespace + +OrientationObserver* +OrientationObserver::GetInstance() +{ + if (!sOrientationSensorObserver) { + sOrientationSensorObserver = new OrientationObserver(); + ClearOnShutdown(&sOrientationSensorObserver); + } + + return sOrientationSensorObserver; +} + +OrientationObserver::OrientationObserver() + : mAutoOrientationEnabled(false) + , mAllowedOrientations(sDefaultOrientations) + , mOrientation(new mozilla::ProcessOrientation()) +{ + DetectDefaultOrientation(); + + EnableAutoOrientation(); +} + +OrientationObserver::~OrientationObserver() +{ + if (mAutoOrientationEnabled) { + DisableAutoOrientation(); + } +} + +/* static */ void +OrientationObserver::ShutDown() +{ + if (!sOrientationSensorObserver) { + return; + } + + if (sOrientationSensorObserver->mAutoOrientationEnabled) { + sOrientationSensorObserver->DisableAutoOrientation(); + } +} + +void +OrientationObserver::Notify(const hal::SensorData& aSensorData) +{ + // Sensor will call us on the main thread. + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSensorData.sensor() == hal::SensorType::SENSOR_ACCELERATION); + + nsCOMPtr<nsIScreen> screen = GetPrimaryScreen(); + if (!screen) { + return; + } + + uint32_t currRotation; + if(NS_FAILED(screen->GetRotation(&currRotation))) { + return; + } + + int rotation = mOrientation->OnSensorChanged(aSensorData, static_cast<int>(currRotation)); + if (rotation < 0 || uint32_t(rotation) == currRotation) { + return; + } + + ScreenOrientationInternal orientation; + if (NS_FAILED(ConvertToDomOrientation(rotation, &orientation))) { + return; + } + + if ((mAllowedOrientations & orientation) == eScreenOrientation_None) { + // The orientation from sensor is not allowed. + return; + } + + if (NS_FAILED(screen->SetRotation(static_cast<uint32_t>(rotation)))) { + // Don't notify dom on rotation failure. + return; + } +} + +/** + * Register the observer. Note that the observer shouldn't be registered. + */ +void +OrientationObserver::EnableAutoOrientation() +{ + MOZ_ASSERT(NS_IsMainThread() && !mAutoOrientationEnabled); + + mOrientation->Reset(); + hal::RegisterSensorObserver(hal::SENSOR_ACCELERATION, this); + mAutoOrientationEnabled = true; +} + +/** + * Unregister the observer. Note that the observer should already be registered. + */ +void +OrientationObserver::DisableAutoOrientation() +{ + MOZ_ASSERT(NS_IsMainThread() && mAutoOrientationEnabled); + + hal::UnregisterSensorObserver(hal::SENSOR_ACCELERATION, this); + mAutoOrientationEnabled = false; +} + +bool +OrientationObserver::LockScreenOrientation(ScreenOrientationInternal aOrientation) +{ + MOZ_ASSERT(aOrientation | (eScreenOrientation_PortraitPrimary | + eScreenOrientation_PortraitSecondary | + eScreenOrientation_LandscapePrimary | + eScreenOrientation_LandscapeSecondary | + eScreenOrientation_Default)); + + if (aOrientation == eScreenOrientation_Default) { + aOrientation = (sOrientationOffset == sDefaultPortrait) ? + eScreenOrientation_PortraitPrimary : + eScreenOrientation_LandscapePrimary; + } + + // If there are multiple orientations allowed, we should enable the + // auto-rotation. + if (aOrientation != eScreenOrientation_LandscapePrimary && + aOrientation != eScreenOrientation_LandscapeSecondary && + aOrientation != eScreenOrientation_PortraitPrimary && + aOrientation != eScreenOrientation_PortraitSecondary) { + if (!mAutoOrientationEnabled) { + EnableAutoOrientation(); + } + } else if (mAutoOrientationEnabled) { + DisableAutoOrientation(); + } + + mAllowedOrientations = aOrientation; + + nsCOMPtr<nsIScreen> screen = GetPrimaryScreen(); + NS_ENSURE_TRUE(screen, false); + + uint32_t currRotation; + nsresult rv = screen->GetRotation(&currRotation); + NS_ENSURE_SUCCESS(rv, false); + + ScreenOrientationInternal currOrientation = eScreenOrientation_None; + rv = ConvertToDomOrientation(currRotation, &currOrientation); + NS_ENSURE_SUCCESS(rv, false); + + // Don't rotate if the current orientation matches one of the + // requested orientations. + if (currOrientation & aOrientation) { + return true; + } + + // Return false on invalid orientation value. + uint32_t rotation; + rv = ConvertToScreenRotation(aOrientation, &rotation); + NS_ENSURE_SUCCESS(rv, false); + + rv = screen->SetRotation(rotation); + NS_ENSURE_SUCCESS(rv, false); + + // This conversion will disambiguate aOrientation. + ScreenOrientationInternal orientation; + rv = ConvertToDomOrientation(rotation, &orientation); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +void +OrientationObserver::UnlockScreenOrientation() +{ + if (!mAutoOrientationEnabled) { + EnableAutoOrientation(); + } + + mAllowedOrientations = sDefaultOrientations; +} |