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