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 +}