OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "athena/screen/public/screen_manager.h" | 5 #include "athena/screen/public/screen_manager.h" |
6 #include "athena/system/orientation_controller.h" | 6 #include "athena/system/orientation_controller.h" |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/files/file_path_watcher.h" | 8 #include "base/files/file_path_watcher.h" |
9 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
10 #include "base/message_loop/message_loop.h" | 10 #include "base/message_loop/message_loop.h" |
11 #include "base/task_runner.h" | 11 #include "base/task_runner.h" |
12 | 12 |
13 namespace athena { | 13 namespace athena { |
14 | 14 |
15 namespace { | 15 namespace { |
16 | 16 |
| 17 // Path of the socket which the sensor daemon creates. |
| 18 const char kSocketPath[] = "/dev/sensors/orientation"; |
| 19 |
17 // Threshold after which to rotate in a given direction. | 20 // Threshold after which to rotate in a given direction. |
18 const int kGravityThreshold = 6.0f; | 21 const int kGravityThreshold = 6.0f; |
19 | 22 |
| 23 // Minimum delay before triggering another orientation change. |
| 24 const int kOrientationChangeDelayNS = 500000000; |
| 25 |
| 26 enum SensorType { |
| 27 SENSOR_ACCELEROMETER, |
| 28 SENSOR_LIGHT, |
| 29 SENSOR_PROXIMITY |
| 30 }; |
| 31 |
| 32 // A sensor event from the device. |
| 33 struct DeviceSensorEvent { |
| 34 // The type of event from the SensorType enum above. |
| 35 int32_t type; |
| 36 |
| 37 // The time in nanoseconds at which the event happened. |
| 38 int64_t timestamp; |
| 39 |
| 40 union { |
| 41 // Accelerometer X,Y,Z values in SI units (m/s^2) including gravity. |
| 42 // The orientation is described at |
| 43 // http://www.html5rocks.com/en/tutorials/device/orientation/. |
| 44 float data[3]; |
| 45 |
| 46 // Ambient (room) temperature in degrees Celcius. |
| 47 float temperature; |
| 48 |
| 49 // Proximity sensor distance in centimeters. |
| 50 float distance; |
| 51 |
| 52 // Ambient light level in SI lux units. |
| 53 float light; |
| 54 }; |
| 55 }; |
| 56 |
20 } // namespace | 57 } // namespace |
21 | 58 |
22 OrientationController::OrientationController() { | 59 OrientationController::OrientationController() |
| 60 : DeviceSocketListener(kSocketPath, sizeof(DeviceSensorEvent)), |
| 61 last_orientation_change_time_(0), |
| 62 shutdown_(false) { |
| 63 CHECK(base::MessageLoopForUI::current()); |
| 64 ui_task_runner_ = base::MessageLoopForUI::current()->task_runner(); |
23 } | 65 } |
24 | 66 |
25 void OrientationController::InitWith( | 67 void OrientationController::InitWith( |
26 scoped_refptr<base::TaskRunner> blocking_task_runner) { | 68 scoped_refptr<base::TaskRunner> file_task_runner) { |
27 accelerometer_reader_.reset( | 69 file_task_runner_ = file_task_runner; |
28 new chromeos::AccelerometerReader(blocking_task_runner, this)); | 70 file_task_runner->PostTask( |
| 71 FROM_HERE, |
| 72 base::Bind(&OrientationController::WatchForSocketPathOnFILE, this)); |
29 } | 73 } |
30 | 74 |
31 OrientationController::~OrientationController() { | 75 OrientationController::~OrientationController() { |
32 } | 76 } |
33 | 77 |
34 void OrientationController::Shutdown() { | 78 void OrientationController::Shutdown() { |
35 accelerometer_reader_.reset(); | 79 CHECK(file_task_runner_.get()); |
| 80 StopListening(); |
| 81 file_task_runner_->PostTask( |
| 82 FROM_HERE, |
| 83 base::Bind(&OrientationController::ShutdownOnFILE, this)); |
36 } | 84 } |
37 | 85 |
38 void OrientationController::HandleAccelerometerUpdate( | 86 void OrientationController::ShutdownOnFILE() { |
39 const ui::AccelerometerUpdate& update) { | 87 shutdown_ = true; |
40 if (!update.has(ui::ACCELEROMETER_SOURCE_SCREEN)) | 88 watcher_.reset(); |
| 89 } |
| 90 |
| 91 void OrientationController::WatchForSocketPathOnFILE() { |
| 92 CHECK(base::MessageLoopForIO::current()); |
| 93 if (base::PathExists(base::FilePath(kSocketPath))) { |
| 94 ui_task_runner_->PostTask(FROM_HERE, |
| 95 base::Bind(&OrientationController::StartListening, this)); |
| 96 } else { |
| 97 watcher_.reset(new base::FilePathWatcher); |
| 98 watcher_->Watch( |
| 99 base::FilePath(kSocketPath), |
| 100 false, |
| 101 base::Bind(&OrientationController::OnFilePathChangedOnFILE, this)); |
| 102 } |
| 103 } |
| 104 |
| 105 void OrientationController::OnFilePathChangedOnFILE(const base::FilePath& path, |
| 106 bool error) { |
| 107 watcher_.reset(); |
| 108 if (error || shutdown_) |
41 return; | 109 return; |
42 | 110 |
43 float gravity_x = update.get(ui::ACCELEROMETER_SOURCE_SCREEN).x(); | 111 ui_task_runner_->PostTask(FROM_HERE, |
44 float gravity_y = update.get(ui::ACCELEROMETER_SOURCE_SCREEN).y(); | 112 base::Bind(&OrientationController::StartListening, this)); |
| 113 } |
| 114 |
| 115 void OrientationController::OnDataAvailableOnFILE(const void* data) { |
| 116 const DeviceSensorEvent* event = |
| 117 static_cast<const DeviceSensorEvent*>(data); |
| 118 if (event->type != SENSOR_ACCELEROMETER) |
| 119 return; |
| 120 |
| 121 float gravity_x = event->data[0]; |
| 122 float gravity_y = event->data[1]; |
45 gfx::Display::Rotation rotation; | 123 gfx::Display::Rotation rotation; |
46 if (gravity_x < -kGravityThreshold) { | 124 if (gravity_x < -kGravityThreshold) { |
47 rotation = gfx::Display::ROTATE_270; | 125 rotation = gfx::Display::ROTATE_270; |
48 } else if (gravity_x > kGravityThreshold) { | 126 } else if (gravity_x > kGravityThreshold) { |
49 rotation = gfx::Display::ROTATE_90; | 127 rotation = gfx::Display::ROTATE_90; |
50 } else if (gravity_y < -kGravityThreshold) { | 128 } else if (gravity_y < -kGravityThreshold) { |
51 rotation = gfx::Display::ROTATE_180; | 129 rotation = gfx::Display::ROTATE_180; |
52 } else if (gravity_y > kGravityThreshold) { | 130 } else if (gravity_y > kGravityThreshold) { |
53 rotation = gfx::Display::ROTATE_0; | 131 rotation = gfx::Display::ROTATE_0; |
54 } else { | 132 } else { |
55 // No rotation as gravity threshold was not hit. | 133 // No rotation as gravity threshold was not hit. |
56 return; | 134 return; |
57 } | 135 } |
58 | 136 |
59 if (rotation == current_rotation_) | 137 if (rotation == current_rotation_ || |
| 138 event->timestamp - last_orientation_change_time_ < |
| 139 kOrientationChangeDelayNS) { |
60 return; | 140 return; |
| 141 } |
61 | 142 |
| 143 last_orientation_change_time_ = event->timestamp; |
62 current_rotation_ = rotation; | 144 current_rotation_ = rotation; |
63 ScreenManager::Get()->SetRotation(rotation); | 145 ui_task_runner_->PostTask(FROM_HERE, |
| 146 base::Bind(&OrientationController::RotateOnUI, this, rotation)); |
| 147 } |
| 148 |
| 149 void OrientationController::RotateOnUI(gfx::Display::Rotation rotation) { |
| 150 ScreenManager* screen_manager = ScreenManager::Get(); |
| 151 // Since this is called from the FILE thread, the screen manager may no longer |
| 152 // exist. |
| 153 if (screen_manager) |
| 154 screen_manager->SetRotation(rotation); |
64 } | 155 } |
65 | 156 |
66 } // namespace athena | 157 } // namespace athena |
OLD | NEW |