Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 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. | |
| 4 | |
| 5 #include "ui/events/ozone/evdev/gamepad_event_converter_evdev.h" | |
| 6 | |
| 7 #include <errno.h> | |
| 8 #include <linux/input.h> | |
| 9 #include <stddef.h> | |
| 10 | |
| 11 #include "base/trace_event/trace_event.h" | |
| 12 #include "ui/events/event_utils.h" | |
| 13 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" | |
| 14 #include "ui/events/ozone/evdev/scoped_input_device.h" | |
| 15 #include "ui/events/ozone/gamepad/gamepad_event.h" | |
| 16 #include "ui/events/ozone/gamepad/gamepad_provider_ozone.h" | |
| 17 #include "ui/events/ozone/gamepad/webgamepad_constants.h" | |
| 18 | |
| 19 namespace { | |
| 20 double kHatThreshold = 0.5; | |
|
spang
2017/05/05 15:37:43
constexpr
jkwang
2017/05/05 21:32:57
Done.
| |
| 21 } | |
| 22 | |
| 23 namespace ui { | |
| 24 | |
| 25 class Axis { | |
| 26 public: | |
| 27 Axis() {} | |
| 28 Axis(const input_absinfo& abs_info, | |
| 29 GamepadEventType mapped_type, | |
| 30 uint16_t mapped_code) | |
| 31 : mapped_type_(mapped_type), mapped_code_(mapped_code) { | |
| 32 double mapped_abs_min = kWebGamepadJoystickMin; | |
| 33 double mapped_abs_max = kWebGamepadJoystickMax; | |
| 34 // If the mapped event is a trigger, we set the min and max to trigger | |
| 35 // min/max. | |
| 36 if (mapped_code == WG_BUTTON_LT || mapped_code == WG_BUTTON_RT) { | |
| 37 mapped_abs_min = kWebGamepadTriggerMin; | |
| 38 mapped_abs_max = kWebGamepadTriggerMax; | |
| 39 } | |
| 40 | |
| 41 scale_ = (mapped_abs_max - mapped_abs_min) / | |
| 42 (abs_info.maximum - abs_info.minimum); | |
| 43 offset_ = (abs_info.maximum + abs_info.minimum) / | |
| 44 (mapped_abs_max - mapped_abs_min) * scale_ * mapped_abs_min; | |
| 45 | |
| 46 scaled_flat_ = abs_info.flat * scale_; | |
| 47 scaled_fuzz_ = abs_info.fuzz * scale_; | |
| 48 double tmp; | |
| 49 // Map the current value and it will be set to value_. | |
| 50 MapValue(abs_info.value, &tmp); | |
| 51 } | |
| 52 | |
| 53 bool MapValue(uint16_t value, double* mapped_value) { | |
| 54 *mapped_value = value * scale_ + offset_; | |
| 55 // As the definition of linux input_absinfo.flat, value within the range of | |
| 56 // flat should be seen as zero. | |
| 57 if (*mapped_value<scaled_flat_&& * mapped_value> - scaled_flat_) { | |
|
spang
2017/05/05 15:37:42
Please git cl format / clang-format to fix the whi
jkwang
2017/05/05 21:32:57
Interesting! I did run git cl format. The whitespa
scottmg
2017/05/05 22:04:35
Weird, I filed b/38039784 for this.
| |
| 58 *mapped_value = 0.0; | |
| 59 // We always send out flat. | |
| 60 last_value_ = 0.0; | |
| 61 return true; | |
| 62 } | |
| 63 return ValueChangeSignificantly(*mapped_value); | |
| 64 } | |
| 65 | |
| 66 GamepadEventType MappedType() { return mapped_type_; } | |
|
spang
2017/05/05 15:37:43
mapped_type()
jkwang
2017/05/05 21:32:57
Done.
| |
| 67 | |
| 68 uint16_t MappedCode() { return mapped_code_; } | |
|
spang
2017/05/05 15:37:42
mapped_code()
jkwang
2017/05/05 21:32:57
Done.
| |
| 69 | |
| 70 private: | |
| 71 bool ValueChangeSignificantly(double new_value) { | |
| 72 // To remove noise, the value must change at least by fuzz. | |
| 73 if (new_value >= last_value_ - scaled_fuzz_ && | |
| 74 new_value <= last_value_ + scaled_fuzz_) { | |
| 75 return false; | |
| 76 } | |
| 77 last_value_ = new_value; | |
| 78 return true; | |
| 79 } | |
| 80 | |
| 81 double last_value_; | |
|
spang
2017/05/05 15:37:42
= 0.
and the rest too
jkwang
2017/05/05 21:32:57
Done.
| |
| 82 | |
| 83 double scale_; | |
| 84 | |
| 85 double offset_; | |
| 86 | |
| 87 double scaled_fuzz_; | |
| 88 | |
| 89 double scaled_flat_; | |
| 90 | |
| 91 GamepadEventType mapped_type_; | |
| 92 | |
| 93 uint16_t mapped_code_; | |
| 94 }; | |
| 95 | |
| 96 class GamepadEventConverterEvdev::AxisInfos { | |
| 97 public: | |
| 98 AxisInfos(const EventDeviceInfo& devinfo) { | |
| 99 GamepadMapper mapper = | |
| 100 GetGamepadMapper(devinfo.vendor_id(), devinfo.product_id()); | |
| 101 input_absinfo abs_info; | |
| 102 GamepadEventType mapped_type; | |
| 103 uint16_t mapped_code; | |
| 104 // In order to map gamepad, we have to save the abs_info from device_info | |
| 105 // and get the gamepad_mapping. | |
| 106 for (int code = 0; code < ABS_CNT; ++code) { | |
| 107 abs_info = devinfo.GetAbsInfoByCode(code); | |
| 108 if (devinfo.HasAbsEvent(code)) { | |
| 109 // If fuzz was reported as zero, it will be set to flat * 0.25f. It is | |
| 110 // the same thing done in Android InputReader.cpp. See: | |
| 111 // frameworks/native/services/inputflinger/InputReader.cpp line 6988 for | |
| 112 // more details. | |
| 113 if (abs_info.fuzz == 0) { | |
| 114 abs_info.fuzz = abs_info.flat * 0.25f; | |
| 115 } | |
| 116 mapper(EV_ABS, code, &mapped_type, &mapped_code); | |
| 117 axises_[code] = Axis(abs_info, mapped_type, mapped_code); | |
| 118 } | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 Axis& GetAxis(uint16_t code) { return axises_[code]; } | |
| 123 | |
| 124 private: | |
| 125 Axis axises_[ABS_CNT]; | |
|
spang
2017/05/05 15:37:43
axes
| |
| 126 }; | |
| 127 | |
| 128 GamepadEventConverterEvdev::GamepadEventConverterEvdev( | |
| 129 ScopedInputDevice fd, | |
| 130 base::FilePath path, | |
| 131 int id, | |
| 132 const EventDeviceInfo& devinfo, | |
| 133 DeviceEventDispatcherEvdev* dispatcher) | |
| 134 : EventConverterEvdev(fd.get(), | |
| 135 path, | |
| 136 id, | |
| 137 devinfo.device_type(), | |
| 138 devinfo.name(), | |
| 139 devinfo.vendor_id(), | |
| 140 devinfo.product_id()), | |
| 141 will_send_frame_(false), | |
| 142 axis_infos_(new AxisInfos(devinfo)), | |
| 143 last_hat_left_press_(false), | |
| 144 last_hat_right_press_(false), | |
| 145 last_hat_up_press_(false), | |
| 146 last_hat_down_press_(false), | |
| 147 mapper_(GetGamepadMapper(devinfo.vendor_id(), devinfo.product_id())), | |
| 148 input_device_fd_(std::move(fd)), | |
| 149 dispatcher_(dispatcher) {} | |
| 150 | |
| 151 GamepadEventConverterEvdev::~GamepadEventConverterEvdev() { | |
| 152 DCHECK(!enabled_); | |
| 153 delete axis_infos_; | |
| 154 } | |
| 155 | |
| 156 bool GamepadEventConverterEvdev::HasGamepad() const { | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 void GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) { | |
| 161 TRACE_EVENT1("evdev", | |
| 162 "GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd", | |
| 163 fd); | |
| 164 while (true) { | |
| 165 input_event input; | |
| 166 ssize_t read_size = read(fd, &input, sizeof(input)); | |
| 167 if (read_size != sizeof(input)) { | |
| 168 if (errno == EINTR || errno == EAGAIN) | |
| 169 return; | |
| 170 if (errno != ENODEV) | |
| 171 PLOG(ERROR) << "error reading device " << path_.value(); | |
| 172 Stop(); | |
| 173 return; | |
| 174 } | |
| 175 | |
| 176 if (!enabled_) | |
| 177 return; | |
| 178 | |
| 179 ProcessEvent(input, TimeTicksFromInputEvent(input)); | |
| 180 } | |
| 181 } | |
| 182 void GamepadEventConverterEvdev::OnDisabled() { | |
| 183 ResetGamepad(); | |
| 184 } | |
| 185 | |
| 186 void GamepadEventConverterEvdev::ProcessEvent( | |
| 187 const input_event& evdev_ev, | |
| 188 const base::TimeTicks& timestamp) { | |
| 189 // We may have missed Gamepad releases. Reset everything. | |
| 190 // If the event is sync, we send a frame. | |
| 191 if (evdev_ev.type == EV_SYN) { | |
| 192 if (evdev_ev.code == SYN_DROPPED) { | |
| 193 LOG(WARNING) << "kernel dropped input events"; | |
| 194 ResyncGamepad(); | |
| 195 } else if (evdev_ev.code == SYN_REPORT) { | |
| 196 OnSync(timestamp); | |
| 197 } | |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 GamepadEventType mapped_type; | |
| 202 uint16_t mapped_code; | |
| 203 | |
| 204 if (evdev_ev.type == EV_KEY) { | |
| 205 bool found_map = | |
|
spang
2017/05/05 15:37:43
Can you put the key handling into its own function
jkwang
2017/05/05 21:32:57
Done.
| |
| 206 mapper_(evdev_ev.type, evdev_ev.code, &mapped_type, &mapped_code); | |
| 207 | |
| 208 // If we cannot find a map for this event, it will be discarded. | |
| 209 if (!found_map) { | |
| 210 return; | |
| 211 } | |
| 212 | |
| 213 // If it's btn -> btn mapping, we can send the event and return now. | |
| 214 OnButtonChange(mapped_code, evdev_ev.value, timestamp); | |
| 215 } | |
| 216 | |
| 217 if (evdev_ev.type != EV_ABS) { | |
| 218 return; | |
| 219 } | |
| 220 | |
|
spang
2017/05/05 15:37:42
Can you put the absolute handling into its own fun
jkwang
2017/05/05 21:32:57
Done.
| |
| 221 double mapped_abs_value; | |
| 222 Axis& axis = axis_infos_->GetAxis(evdev_ev.code); | |
| 223 mapped_type = axis.MappedType(); | |
| 224 mapped_code = axis.MappedCode(); | |
| 225 | |
| 226 if (!axis.MapValue(evdev_ev.value, &mapped_abs_value)) { | |
| 227 return; | |
| 228 } | |
| 229 | |
| 230 // If the mapped type is abs, we can send it now. | |
| 231 if (mapped_type == GamepadEventType::AXIS) { | |
| 232 OnAbsChange(mapped_code, mapped_abs_value, timestamp); | |
| 233 return; | |
| 234 } | |
| 235 | |
| 236 // We need to map HAT to DPAD depend on the state of the axis. | |
| 237 if (mapped_code == kHAT_X) { | |
| 238 bool hat_left_press = (mapped_abs_value < -kHatThreshold); | |
| 239 bool hat_right_press = (mapped_abs_value > kHatThreshold); | |
| 240 if (hat_left_press != last_hat_left_press_) { | |
| 241 OnButtonChange(WG_BUTTON_DPAD_LEFT, hat_left_press, timestamp); | |
| 242 last_hat_left_press_ = hat_left_press; | |
| 243 } | |
| 244 | |
| 245 if (hat_right_press != last_hat_right_press_) { | |
| 246 OnButtonChange(WG_BUTTON_DPAD_RIGHT, hat_right_press, timestamp); | |
| 247 last_hat_right_press_ = hat_right_press; | |
| 248 } | |
| 249 } else if (mapped_code == kHAT_Y) { | |
| 250 bool hat_up_press = (mapped_abs_value < -kHatThreshold); | |
| 251 bool hat_down_press = (mapped_abs_value > kHatThreshold); | |
| 252 if (hat_up_press != last_hat_up_press_) { | |
| 253 OnButtonChange(WG_BUTTON_DPAD_UP, hat_up_press, timestamp); | |
| 254 last_hat_up_press_ = hat_up_press; | |
| 255 } | |
| 256 | |
| 257 if (hat_down_press != last_hat_down_press_) { | |
| 258 OnButtonChange(WG_BUTTON_DPAD_DOWN, hat_down_press, timestamp); | |
| 259 last_hat_down_press_ = hat_down_press; | |
| 260 } | |
| 261 } else { | |
| 262 OnButtonChange(mapped_code, mapped_abs_value, timestamp); | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 void GamepadEventConverterEvdev::ResetGamepad() { | |
| 267 base::TimeTicks timestamp = ui::EventTimeForNow(); | |
| 268 for (int btn_code = 0; btn_code < WG_BUTTON_COUNT; ++btn_code) { | |
| 269 OnButtonChange(btn_code, 0, timestamp); | |
| 270 } | |
| 271 | |
| 272 for (int abs_code = 0; abs_code < WG_ABS_COUNT; ++abs_code) { | |
| 273 OnAbsChange(abs_code, 0, timestamp); | |
| 274 } | |
| 275 OnSync(timestamp); | |
| 276 } | |
| 277 | |
| 278 void GamepadEventConverterEvdev::ResyncGamepad() { | |
| 279 base::TimeTicks timestamp = ui::EventTimeForNow(); | |
| 280 // Reset all the buttons to 0. | |
| 281 for (int btn_code = 0; btn_code < WG_BUTTON_COUNT; ++btn_code) { | |
| 282 OnButtonChange(btn_code, 0, timestamp); | |
| 283 } | |
| 284 // Read the state of all axis. | |
| 285 EventDeviceInfo info; | |
| 286 if (!info.Initialize(fd_, path_)) { | |
| 287 LOG(ERROR) << "Failed to synchronize state for gamepad device: " | |
| 288 << path_.value(); | |
| 289 Stop(); | |
| 290 return; | |
| 291 } | |
| 292 for (int code = 0; code < ABS_CNT; ++code) { | |
| 293 if (info.HasAbsEvent(code)) { | |
| 294 input_event event; | |
| 295 event.type = EV_ABS; | |
| 296 event.code = code; | |
| 297 event.value = info.GetAbsValue(code); | |
| 298 ProcessEvent(event, timestamp); | |
| 299 } | |
| 300 } | |
| 301 OnSync(timestamp); | |
| 302 } | |
| 303 | |
| 304 void GamepadEventConverterEvdev::OnButtonChange( | |
| 305 unsigned int code, | |
| 306 double value, | |
| 307 const base::TimeTicks& timestamp) { | |
| 308 GamepadEvent event(input_device_.id, GamepadEventType::BUTTON, code, value, | |
| 309 timestamp); | |
| 310 dispatcher_->DispatchGamepadEvent(event); | |
| 311 will_send_frame_ = true; | |
| 312 } | |
| 313 | |
| 314 void GamepadEventConverterEvdev::OnAbsChange(unsigned int code, | |
| 315 double value, | |
| 316 const base::TimeTicks& timestamp) { | |
| 317 GamepadEvent event(input_device_.id, GamepadEventType::AXIS, code, value, | |
| 318 timestamp); | |
| 319 dispatcher_->DispatchGamepadEvent(event); | |
| 320 will_send_frame_ = true; | |
| 321 } | |
| 322 | |
| 323 void GamepadEventConverterEvdev::OnSync(const base::TimeTicks& timestamp) { | |
| 324 if (will_send_frame_) { | |
| 325 GamepadEvent event(input_device_.id, GamepadEventType::FRAME, 0, 0, | |
| 326 timestamp); | |
| 327 dispatcher_->DispatchGamepadEvent(event); | |
| 328 will_send_frame_ = false; | |
| 329 } | |
| 330 } | |
| 331 } // namespace ui | |
| OLD | NEW |