widget/gonk/OrientationObserver.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/gonk/OrientationObserver.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,332 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set sw=2 ts=8 et ft=cpp : */
     1.6 +/* Copyright 2012 Mozilla Foundation and Mozilla contributors
     1.7 + *
     1.8 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.9 + * you may not use this file except in compliance with the License.
    1.10 + * You may obtain a copy of the License at
    1.11 + *
    1.12 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.13 + *
    1.14 + * Unless required by applicable law or agreed to in writing, software
    1.15 + * distributed under the License is distributed on an "AS IS" BASIS,
    1.16 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.17 + * See the License for the specific language governing permissions and
    1.18 + * limitations under the License.
    1.19 + */
    1.20 +
    1.21 +#include "base/basictypes.h"
    1.22 +#include "mozilla/ClearOnShutdown.h"
    1.23 +#include "mozilla/StaticPtr.h"
    1.24 +#include "mozilla/Hal.h"
    1.25 +#include "nsIScreen.h"
    1.26 +#include "nsIScreenManager.h"
    1.27 +#include "OrientationObserver.h"
    1.28 +#include "mozilla/HalSensor.h"
    1.29 +#include "ProcessOrientation.h"
    1.30 +#include "nsServiceManagerUtils.h"
    1.31 +
    1.32 +using namespace mozilla;
    1.33 +using namespace dom;
    1.34 +
    1.35 +namespace {
    1.36 +
    1.37 +struct OrientationMapping {
    1.38 +  uint32_t mScreenRotation;
    1.39 +  ScreenOrientation mDomOrientation;
    1.40 +};
    1.41 +
    1.42 +static OrientationMapping sOrientationMappings[] = {
    1.43 +  {nsIScreen::ROTATION_0_DEG,   eScreenOrientation_PortraitPrimary},
    1.44 +  {nsIScreen::ROTATION_180_DEG, eScreenOrientation_PortraitSecondary},
    1.45 +  {nsIScreen::ROTATION_90_DEG,  eScreenOrientation_LandscapePrimary},
    1.46 +  {nsIScreen::ROTATION_270_DEG, eScreenOrientation_LandscapeSecondary},
    1.47 +};
    1.48 +
    1.49 +const static int sDefaultLandscape = 2;
    1.50 +const static int sDefaultPortrait = 0;
    1.51 +
    1.52 +static uint32_t sOrientationOffset = 0;
    1.53 +
    1.54 +static already_AddRefed<nsIScreen>
    1.55 +GetPrimaryScreen()
    1.56 +{
    1.57 +  nsCOMPtr<nsIScreenManager> screenMgr =
    1.58 +    do_GetService("@mozilla.org/gfx/screenmanager;1");
    1.59 +  NS_ENSURE_TRUE(screenMgr, nullptr);
    1.60 +
    1.61 +  nsCOMPtr<nsIScreen> screen;
    1.62 +  screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
    1.63 +  return screen.forget();
    1.64 +}
    1.65 +
    1.66 +static void
    1.67 +DetectDefaultOrientation()
    1.68 +{
    1.69 +  nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
    1.70 +  if (!screen) {
    1.71 +    return;
    1.72 +  }
    1.73 +
    1.74 +  int32_t left, top, width, height;
    1.75 +  if (NS_FAILED(screen->GetRect(&left, &top, &width, &height))) {
    1.76 +    return;
    1.77 +  }
    1.78 +
    1.79 +  uint32_t rotation;
    1.80 +  if (NS_FAILED(screen->GetRotation(&rotation))) {
    1.81 +    return;
    1.82 +  }
    1.83 +
    1.84 +  if (width < height) {
    1.85 +    if (rotation == nsIScreen::ROTATION_0_DEG ||
    1.86 +        rotation == nsIScreen::ROTATION_180_DEG) {
    1.87 +      sOrientationOffset = sDefaultPortrait;
    1.88 +    } else {
    1.89 +      sOrientationOffset = sDefaultLandscape;
    1.90 +    }
    1.91 +  } else {
    1.92 +    if (rotation == nsIScreen::ROTATION_0_DEG ||
    1.93 +        rotation == nsIScreen::ROTATION_180_DEG) {
    1.94 +      sOrientationOffset = sDefaultLandscape;
    1.95 +    } else {
    1.96 +      sOrientationOffset = sDefaultPortrait;
    1.97 +    }
    1.98 +  }
    1.99 +}
   1.100 +
   1.101 +/**
   1.102 + * Converts DOM orientation to nsIScreen rotation. Portrait and Landscape are
   1.103 + * treated as PortraitPrimary and LandscapePrimary, respectively, during
   1.104 + * conversion.
   1.105 + *
   1.106 + * @param aOrientation DOM orientation e.g.
   1.107 + *        dom::eScreenOrientation_PortraitPrimary.
   1.108 + * @param aResult output nsIScreen rotation e.g. nsIScreen::ROTATION_0_DEG.
   1.109 + * @return NS_OK on success. NS_ILLEGAL_VALUE on failure.
   1.110 + */
   1.111 +static nsresult
   1.112 +ConvertToScreenRotation(ScreenOrientation aOrientation, uint32_t *aResult)
   1.113 +{
   1.114 +  for (int i = 0; i < ArrayLength(sOrientationMappings); i++) {
   1.115 +    if (aOrientation & sOrientationMappings[i].mDomOrientation) {
   1.116 +      // Shift the mappings in sOrientationMappings so devices with default
   1.117 +      // landscape orientation map landscape-primary to 0 degree and so forth.
   1.118 +      int adjusted = (i + sOrientationOffset) %
   1.119 +                     ArrayLength(sOrientationMappings);
   1.120 +      *aResult = sOrientationMappings[adjusted].mScreenRotation;
   1.121 +      return NS_OK;
   1.122 +    }
   1.123 +  }
   1.124 +
   1.125 +  *aResult = nsIScreen::ROTATION_0_DEG;
   1.126 +  return NS_ERROR_ILLEGAL_VALUE;
   1.127 +}
   1.128 +
   1.129 +/**
   1.130 + * Converts nsIScreen rotation to DOM orientation.
   1.131 + *
   1.132 + * @param aRotation nsIScreen rotation e.g. nsIScreen::ROTATION_0_DEG.
   1.133 + * @param aResult output DOM orientation e.g.
   1.134 + *        dom::eScreenOrientation_PortraitPrimary.
   1.135 + * @return NS_OK on success. NS_ILLEGAL_VALUE on failure.
   1.136 + */
   1.137 +nsresult
   1.138 +ConvertToDomOrientation(uint32_t aRotation, ScreenOrientation *aResult)
   1.139 +{
   1.140 +  for (int i = 0; i < ArrayLength(sOrientationMappings); i++) {
   1.141 +    if (aRotation == sOrientationMappings[i].mScreenRotation) {
   1.142 +      // Shift the mappings in sOrientationMappings so devices with default
   1.143 +      // landscape orientation map 0 degree to landscape-primary and so forth.
   1.144 +      int adjusted = (i + sOrientationOffset) %
   1.145 +                     ArrayLength(sOrientationMappings);
   1.146 +      *aResult = sOrientationMappings[adjusted].mDomOrientation;
   1.147 +      return NS_OK;
   1.148 +    }
   1.149 +  }
   1.150 +
   1.151 +  *aResult = eScreenOrientation_None;
   1.152 +  return NS_ERROR_ILLEGAL_VALUE;
   1.153 +}
   1.154 +
   1.155 +// Note that all operations with sOrientationSensorObserver
   1.156 +// should be on the main thread.
   1.157 +static StaticAutoPtr<OrientationObserver> sOrientationSensorObserver;
   1.158 +
   1.159 +} // Anonymous namespace
   1.160 +
   1.161 +OrientationObserver*
   1.162 +OrientationObserver::GetInstance()
   1.163 +{
   1.164 +  if (!sOrientationSensorObserver) {
   1.165 +    sOrientationSensorObserver = new OrientationObserver();
   1.166 +    ClearOnShutdown(&sOrientationSensorObserver);
   1.167 +  }
   1.168 +
   1.169 +  return sOrientationSensorObserver;
   1.170 +}
   1.171 +
   1.172 +OrientationObserver::OrientationObserver()
   1.173 +  : mAutoOrientationEnabled(false)
   1.174 +  , mAllowedOrientations(sDefaultOrientations)
   1.175 +  , mOrientation(new mozilla::ProcessOrientation())
   1.176 +{
   1.177 +  DetectDefaultOrientation();
   1.178 +
   1.179 +  EnableAutoOrientation();
   1.180 +}
   1.181 +
   1.182 +OrientationObserver::~OrientationObserver()
   1.183 +{
   1.184 +  if (mAutoOrientationEnabled) {
   1.185 +    DisableAutoOrientation();
   1.186 +  }
   1.187 +}
   1.188 +
   1.189 +/* static */ void
   1.190 +OrientationObserver::ShutDown()
   1.191 +{
   1.192 +  if (!sOrientationSensorObserver) {
   1.193 +    return;
   1.194 +  }
   1.195 +
   1.196 +  if (sOrientationSensorObserver->mAutoOrientationEnabled) {
   1.197 +    sOrientationSensorObserver->DisableAutoOrientation();
   1.198 +  }
   1.199 +}
   1.200 +
   1.201 +void
   1.202 +OrientationObserver::Notify(const hal::SensorData& aSensorData)
   1.203 +{
   1.204 +  // Sensor will call us on the main thread.
   1.205 +  MOZ_ASSERT(NS_IsMainThread());
   1.206 +  MOZ_ASSERT(aSensorData.sensor() == hal::SensorType::SENSOR_ACCELERATION);
   1.207 +
   1.208 +  nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
   1.209 +  if (!screen) {
   1.210 +    return;
   1.211 +  }
   1.212 +
   1.213 +  uint32_t currRotation;
   1.214 +  if(NS_FAILED(screen->GetRotation(&currRotation))) {
   1.215 +    return;
   1.216 +  }
   1.217 +
   1.218 +  int rotation = mOrientation->OnSensorChanged(aSensorData, static_cast<int>(currRotation));
   1.219 +  if (rotation < 0 || rotation == currRotation) {
   1.220 +    return;
   1.221 +  }
   1.222 +
   1.223 +  ScreenOrientation orientation;
   1.224 +  if (NS_FAILED(ConvertToDomOrientation(rotation, &orientation))) {
   1.225 +    return;
   1.226 +  }
   1.227 +
   1.228 +  if ((mAllowedOrientations & orientation) == eScreenOrientation_None) {
   1.229 +    // The orientation from sensor is not allowed.
   1.230 +    return;
   1.231 +  }
   1.232 +
   1.233 +  if (NS_FAILED(screen->SetRotation(static_cast<uint32_t>(rotation)))) {
   1.234 +    // Don't notify dom on rotation failure.
   1.235 +    return;
   1.236 +  }
   1.237 +}
   1.238 +
   1.239 +/**
   1.240 + * Register the observer. Note that the observer shouldn't be registered.
   1.241 + */
   1.242 +void
   1.243 +OrientationObserver::EnableAutoOrientation()
   1.244 +{
   1.245 +  MOZ_ASSERT(NS_IsMainThread() && !mAutoOrientationEnabled);
   1.246 +
   1.247 +  mOrientation->Reset();
   1.248 +  hal::RegisterSensorObserver(hal::SENSOR_ACCELERATION, this);
   1.249 +  mAutoOrientationEnabled = true;
   1.250 +}
   1.251 +
   1.252 +/**
   1.253 + * Unregister the observer. Note that the observer should already be registered.
   1.254 + */
   1.255 +void
   1.256 +OrientationObserver::DisableAutoOrientation()
   1.257 +{
   1.258 +  MOZ_ASSERT(NS_IsMainThread() && mAutoOrientationEnabled);
   1.259 +
   1.260 +  hal::UnregisterSensorObserver(hal::SENSOR_ACCELERATION, this);
   1.261 +  mAutoOrientationEnabled = false;
   1.262 +}
   1.263 +
   1.264 +bool
   1.265 +OrientationObserver::LockScreenOrientation(ScreenOrientation aOrientation)
   1.266 +{
   1.267 +  MOZ_ASSERT(aOrientation | (eScreenOrientation_PortraitPrimary |
   1.268 +                             eScreenOrientation_PortraitSecondary |
   1.269 +                             eScreenOrientation_LandscapePrimary |
   1.270 +                             eScreenOrientation_LandscapeSecondary |
   1.271 +                             eScreenOrientation_Default));
   1.272 +
   1.273 +  if (aOrientation == eScreenOrientation_Default) {
   1.274 +    aOrientation = (sOrientationOffset == sDefaultPortrait) ?
   1.275 +                    eScreenOrientation_PortraitPrimary :
   1.276 +                    eScreenOrientation_LandscapePrimary;
   1.277 +  }
   1.278 +
   1.279 +  // If there are multiple orientations allowed, we should enable the
   1.280 +  // auto-rotation.
   1.281 +  if (aOrientation != eScreenOrientation_LandscapePrimary &&
   1.282 +      aOrientation != eScreenOrientation_LandscapeSecondary &&
   1.283 +      aOrientation != eScreenOrientation_PortraitPrimary &&
   1.284 +      aOrientation != eScreenOrientation_PortraitSecondary) {
   1.285 +    if (!mAutoOrientationEnabled) {
   1.286 +      EnableAutoOrientation();
   1.287 +    }
   1.288 +  } else if (mAutoOrientationEnabled) {
   1.289 +    DisableAutoOrientation();
   1.290 +  }
   1.291 +
   1.292 +  mAllowedOrientations = aOrientation;
   1.293 +
   1.294 +  nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
   1.295 +  NS_ENSURE_TRUE(screen, false);
   1.296 +
   1.297 +  uint32_t currRotation;
   1.298 +  nsresult rv = screen->GetRotation(&currRotation);
   1.299 +  NS_ENSURE_SUCCESS(rv, false);
   1.300 +
   1.301 +  ScreenOrientation currOrientation = eScreenOrientation_None;
   1.302 +  rv = ConvertToDomOrientation(currRotation, &currOrientation);
   1.303 +  NS_ENSURE_SUCCESS(rv, false);
   1.304 +
   1.305 +  // Don't rotate if the current orientation matches one of the
   1.306 +  // requested orientations.
   1.307 +  if (currOrientation & aOrientation) {
   1.308 +    return true;
   1.309 +  }
   1.310 +
   1.311 +  // Return false on invalid orientation value.
   1.312 +  uint32_t rotation;
   1.313 +  rv = ConvertToScreenRotation(aOrientation, &rotation);
   1.314 +  NS_ENSURE_SUCCESS(rv, false);
   1.315 +
   1.316 +  rv = screen->SetRotation(rotation);
   1.317 +  NS_ENSURE_SUCCESS(rv, false);
   1.318 +
   1.319 +  // This conversion will disambiguate aOrientation.
   1.320 +  ScreenOrientation orientation;
   1.321 +  rv = ConvertToDomOrientation(rotation, &orientation);
   1.322 +  NS_ENSURE_SUCCESS(rv, false);
   1.323 +
   1.324 +  return true;
   1.325 +}
   1.326 +
   1.327 +void
   1.328 +OrientationObserver::UnlockScreenOrientation()
   1.329 +{
   1.330 +  if (!mAutoOrientationEnabled) {
   1.331 +    EnableAutoOrientation();
   1.332 +  }
   1.333 +
   1.334 +  mAllowedOrientations = sDefaultOrientations;
   1.335 +}

mercurial