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
3 // found in the LICENSE file.
5 #include "athena/screen/public/screen_manager.h"
6 #include "athena/system/orientation_controller.h"
8 #include "base/file_util.h"
9 #include "base/files/file_path_watcher.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/task_runner.h"
17 // Path of the socket which the sensor daemon creates.
18 const char kSocketPath[] = "/dev/sensors/orientation";
20 // Threshold after which to rotate in a given direction.
21 const int kGravityThreshold = 6.0f;
23 // Minimum delay before triggering another orientation change.
24 const int kOrientationChangeDelayNS = 500000000;
32 // A sensor event from the device.
33 struct DeviceSensorEvent {
34 // The type of event from the SensorType enum above.
37 // The time in nanoseconds at which the event happened.
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/.
46 // Ambient (room) temperature in degrees Celcius.
49 // Proximity sensor distance in centimeters.
52 // Ambient light level in SI lux units.
59 OrientationController::OrientationController(
60 scoped_refptr<base::TaskRunner> io_task_runner)
61 : DeviceSocketListener(kSocketPath, sizeof(DeviceSensorEvent)),
62 last_orientation_change_time_(0),
64 CHECK(base::MessageLoopForUI::current());
65 ui_task_runner_ = base::MessageLoopForUI::current()->task_runner();
66 io_task_runner->PostTask(FROM_HERE, base::Bind(
67 &OrientationController::WatchForSocketPathOnIO,
68 make_scoped_refptr<OrientationController>(this)));
71 OrientationController::~OrientationController() {
74 void OrientationController::WatchForSocketPathOnIO() {
75 CHECK(base::MessageLoopForIO::current());
76 if (base::PathExists(base::FilePath(kSocketPath))) {
77 ui_task_runner_->PostTask(FROM_HERE,
78 base::Bind(&OrientationController::StartListening,
79 make_scoped_refptr<OrientationController>(this)));
81 watcher_.reset(new base::FilePathWatcher);
83 base::FilePath(kSocketPath), false,
84 base::Bind(&OrientationController::OnFilePathChangedOnIO,
85 make_scoped_refptr<OrientationController>(this)));
89 void OrientationController::OnFilePathChangedOnIO(const base::FilePath& path,
95 ui_task_runner_->PostTask(FROM_HERE,
96 base::Bind(&OrientationController::StartListening,
97 make_scoped_refptr<OrientationController>(this)));
100 void OrientationController::OnDataAvailableOnIO(const void* data) {
101 const DeviceSensorEvent* event =
102 static_cast<const DeviceSensorEvent*>(data);
103 if (event->type != SENSOR_ACCELEROMETER)
106 float gravity_x = event->data[0];
107 float gravity_y = event->data[1];
108 gfx::Display::Rotation rotation;
109 if (gravity_x < -kGravityThreshold) {
110 rotation = gfx::Display::ROTATE_270;
111 } else if (gravity_x > kGravityThreshold) {
112 rotation = gfx::Display::ROTATE_90;
113 } else if (gravity_y < -kGravityThreshold) {
114 rotation = gfx::Display::ROTATE_180;
115 } else if (gravity_y > kGravityThreshold) {
116 rotation = gfx::Display::ROTATE_0;
118 // No rotation as gravity threshold was not hit.
122 if (rotation == current_rotation_ ||
123 event->timestamp - last_orientation_change_time_ <
124 kOrientationChangeDelayNS) {
128 last_orientation_change_time_ = event->timestamp;
129 current_rotation_ = rotation;
130 ui_task_runner_->PostTask(FROM_HERE,
131 base::Bind(&OrientationController::RotateOnUI,
132 make_scoped_refptr<OrientationController>(this), rotation));
135 void OrientationController::RotateOnUI(gfx::Display::Rotation rotation) {
136 ScreenManager* screen_manager = ScreenManager::Get();
137 // Since this is called from the IO thread, the screen manager may no longer
140 screen_manager->SetRotation(rotation);
143 } // namespace athena