widget/gonk/OrientationObserver.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:710987fe92af
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et ft=cpp : */
3 /* Copyright 2012 Mozilla Foundation and Mozilla contributors
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "base/basictypes.h"
19 #include "mozilla/ClearOnShutdown.h"
20 #include "mozilla/StaticPtr.h"
21 #include "mozilla/Hal.h"
22 #include "nsIScreen.h"
23 #include "nsIScreenManager.h"
24 #include "OrientationObserver.h"
25 #include "mozilla/HalSensor.h"
26 #include "ProcessOrientation.h"
27 #include "nsServiceManagerUtils.h"
28
29 using namespace mozilla;
30 using namespace dom;
31
32 namespace {
33
34 struct OrientationMapping {
35 uint32_t mScreenRotation;
36 ScreenOrientation mDomOrientation;
37 };
38
39 static OrientationMapping sOrientationMappings[] = {
40 {nsIScreen::ROTATION_0_DEG, eScreenOrientation_PortraitPrimary},
41 {nsIScreen::ROTATION_180_DEG, eScreenOrientation_PortraitSecondary},
42 {nsIScreen::ROTATION_90_DEG, eScreenOrientation_LandscapePrimary},
43 {nsIScreen::ROTATION_270_DEG, eScreenOrientation_LandscapeSecondary},
44 };
45
46 const static int sDefaultLandscape = 2;
47 const static int sDefaultPortrait = 0;
48
49 static uint32_t sOrientationOffset = 0;
50
51 static already_AddRefed<nsIScreen>
52 GetPrimaryScreen()
53 {
54 nsCOMPtr<nsIScreenManager> screenMgr =
55 do_GetService("@mozilla.org/gfx/screenmanager;1");
56 NS_ENSURE_TRUE(screenMgr, nullptr);
57
58 nsCOMPtr<nsIScreen> screen;
59 screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
60 return screen.forget();
61 }
62
63 static void
64 DetectDefaultOrientation()
65 {
66 nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
67 if (!screen) {
68 return;
69 }
70
71 int32_t left, top, width, height;
72 if (NS_FAILED(screen->GetRect(&left, &top, &width, &height))) {
73 return;
74 }
75
76 uint32_t rotation;
77 if (NS_FAILED(screen->GetRotation(&rotation))) {
78 return;
79 }
80
81 if (width < height) {
82 if (rotation == nsIScreen::ROTATION_0_DEG ||
83 rotation == nsIScreen::ROTATION_180_DEG) {
84 sOrientationOffset = sDefaultPortrait;
85 } else {
86 sOrientationOffset = sDefaultLandscape;
87 }
88 } else {
89 if (rotation == nsIScreen::ROTATION_0_DEG ||
90 rotation == nsIScreen::ROTATION_180_DEG) {
91 sOrientationOffset = sDefaultLandscape;
92 } else {
93 sOrientationOffset = sDefaultPortrait;
94 }
95 }
96 }
97
98 /**
99 * Converts DOM orientation to nsIScreen rotation. Portrait and Landscape are
100 * treated as PortraitPrimary and LandscapePrimary, respectively, during
101 * conversion.
102 *
103 * @param aOrientation DOM orientation e.g.
104 * dom::eScreenOrientation_PortraitPrimary.
105 * @param aResult output nsIScreen rotation e.g. nsIScreen::ROTATION_0_DEG.
106 * @return NS_OK on success. NS_ILLEGAL_VALUE on failure.
107 */
108 static nsresult
109 ConvertToScreenRotation(ScreenOrientation aOrientation, uint32_t *aResult)
110 {
111 for (int i = 0; i < ArrayLength(sOrientationMappings); i++) {
112 if (aOrientation & sOrientationMappings[i].mDomOrientation) {
113 // Shift the mappings in sOrientationMappings so devices with default
114 // landscape orientation map landscape-primary to 0 degree and so forth.
115 int adjusted = (i + sOrientationOffset) %
116 ArrayLength(sOrientationMappings);
117 *aResult = sOrientationMappings[adjusted].mScreenRotation;
118 return NS_OK;
119 }
120 }
121
122 *aResult = nsIScreen::ROTATION_0_DEG;
123 return NS_ERROR_ILLEGAL_VALUE;
124 }
125
126 /**
127 * Converts nsIScreen rotation to DOM orientation.
128 *
129 * @param aRotation nsIScreen rotation e.g. nsIScreen::ROTATION_0_DEG.
130 * @param aResult output DOM orientation e.g.
131 * dom::eScreenOrientation_PortraitPrimary.
132 * @return NS_OK on success. NS_ILLEGAL_VALUE on failure.
133 */
134 nsresult
135 ConvertToDomOrientation(uint32_t aRotation, ScreenOrientation *aResult)
136 {
137 for (int i = 0; i < ArrayLength(sOrientationMappings); i++) {
138 if (aRotation == sOrientationMappings[i].mScreenRotation) {
139 // Shift the mappings in sOrientationMappings so devices with default
140 // landscape orientation map 0 degree to landscape-primary and so forth.
141 int adjusted = (i + sOrientationOffset) %
142 ArrayLength(sOrientationMappings);
143 *aResult = sOrientationMappings[adjusted].mDomOrientation;
144 return NS_OK;
145 }
146 }
147
148 *aResult = eScreenOrientation_None;
149 return NS_ERROR_ILLEGAL_VALUE;
150 }
151
152 // Note that all operations with sOrientationSensorObserver
153 // should be on the main thread.
154 static StaticAutoPtr<OrientationObserver> sOrientationSensorObserver;
155
156 } // Anonymous namespace
157
158 OrientationObserver*
159 OrientationObserver::GetInstance()
160 {
161 if (!sOrientationSensorObserver) {
162 sOrientationSensorObserver = new OrientationObserver();
163 ClearOnShutdown(&sOrientationSensorObserver);
164 }
165
166 return sOrientationSensorObserver;
167 }
168
169 OrientationObserver::OrientationObserver()
170 : mAutoOrientationEnabled(false)
171 , mAllowedOrientations(sDefaultOrientations)
172 , mOrientation(new mozilla::ProcessOrientation())
173 {
174 DetectDefaultOrientation();
175
176 EnableAutoOrientation();
177 }
178
179 OrientationObserver::~OrientationObserver()
180 {
181 if (mAutoOrientationEnabled) {
182 DisableAutoOrientation();
183 }
184 }
185
186 /* static */ void
187 OrientationObserver::ShutDown()
188 {
189 if (!sOrientationSensorObserver) {
190 return;
191 }
192
193 if (sOrientationSensorObserver->mAutoOrientationEnabled) {
194 sOrientationSensorObserver->DisableAutoOrientation();
195 }
196 }
197
198 void
199 OrientationObserver::Notify(const hal::SensorData& aSensorData)
200 {
201 // Sensor will call us on the main thread.
202 MOZ_ASSERT(NS_IsMainThread());
203 MOZ_ASSERT(aSensorData.sensor() == hal::SensorType::SENSOR_ACCELERATION);
204
205 nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
206 if (!screen) {
207 return;
208 }
209
210 uint32_t currRotation;
211 if(NS_FAILED(screen->GetRotation(&currRotation))) {
212 return;
213 }
214
215 int rotation = mOrientation->OnSensorChanged(aSensorData, static_cast<int>(currRotation));
216 if (rotation < 0 || rotation == currRotation) {
217 return;
218 }
219
220 ScreenOrientation orientation;
221 if (NS_FAILED(ConvertToDomOrientation(rotation, &orientation))) {
222 return;
223 }
224
225 if ((mAllowedOrientations & orientation) == eScreenOrientation_None) {
226 // The orientation from sensor is not allowed.
227 return;
228 }
229
230 if (NS_FAILED(screen->SetRotation(static_cast<uint32_t>(rotation)))) {
231 // Don't notify dom on rotation failure.
232 return;
233 }
234 }
235
236 /**
237 * Register the observer. Note that the observer shouldn't be registered.
238 */
239 void
240 OrientationObserver::EnableAutoOrientation()
241 {
242 MOZ_ASSERT(NS_IsMainThread() && !mAutoOrientationEnabled);
243
244 mOrientation->Reset();
245 hal::RegisterSensorObserver(hal::SENSOR_ACCELERATION, this);
246 mAutoOrientationEnabled = true;
247 }
248
249 /**
250 * Unregister the observer. Note that the observer should already be registered.
251 */
252 void
253 OrientationObserver::DisableAutoOrientation()
254 {
255 MOZ_ASSERT(NS_IsMainThread() && mAutoOrientationEnabled);
256
257 hal::UnregisterSensorObserver(hal::SENSOR_ACCELERATION, this);
258 mAutoOrientationEnabled = false;
259 }
260
261 bool
262 OrientationObserver::LockScreenOrientation(ScreenOrientation aOrientation)
263 {
264 MOZ_ASSERT(aOrientation | (eScreenOrientation_PortraitPrimary |
265 eScreenOrientation_PortraitSecondary |
266 eScreenOrientation_LandscapePrimary |
267 eScreenOrientation_LandscapeSecondary |
268 eScreenOrientation_Default));
269
270 if (aOrientation == eScreenOrientation_Default) {
271 aOrientation = (sOrientationOffset == sDefaultPortrait) ?
272 eScreenOrientation_PortraitPrimary :
273 eScreenOrientation_LandscapePrimary;
274 }
275
276 // If there are multiple orientations allowed, we should enable the
277 // auto-rotation.
278 if (aOrientation != eScreenOrientation_LandscapePrimary &&
279 aOrientation != eScreenOrientation_LandscapeSecondary &&
280 aOrientation != eScreenOrientation_PortraitPrimary &&
281 aOrientation != eScreenOrientation_PortraitSecondary) {
282 if (!mAutoOrientationEnabled) {
283 EnableAutoOrientation();
284 }
285 } else if (mAutoOrientationEnabled) {
286 DisableAutoOrientation();
287 }
288
289 mAllowedOrientations = aOrientation;
290
291 nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
292 NS_ENSURE_TRUE(screen, false);
293
294 uint32_t currRotation;
295 nsresult rv = screen->GetRotation(&currRotation);
296 NS_ENSURE_SUCCESS(rv, false);
297
298 ScreenOrientation currOrientation = eScreenOrientation_None;
299 rv = ConvertToDomOrientation(currRotation, &currOrientation);
300 NS_ENSURE_SUCCESS(rv, false);
301
302 // Don't rotate if the current orientation matches one of the
303 // requested orientations.
304 if (currOrientation & aOrientation) {
305 return true;
306 }
307
308 // Return false on invalid orientation value.
309 uint32_t rotation;
310 rv = ConvertToScreenRotation(aOrientation, &rotation);
311 NS_ENSURE_SUCCESS(rv, false);
312
313 rv = screen->SetRotation(rotation);
314 NS_ENSURE_SUCCESS(rv, false);
315
316 // This conversion will disambiguate aOrientation.
317 ScreenOrientation orientation;
318 rv = ConvertToDomOrientation(rotation, &orientation);
319 NS_ENSURE_SUCCESS(rv, false);
320
321 return true;
322 }
323
324 void
325 OrientationObserver::UnlockScreenOrientation()
326 {
327 if (!mAutoOrientationEnabled) {
328 EnableAutoOrientation();
329 }
330
331 mAllowedOrientations = sDefaultOrientations;
332 }

mercurial