OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/input_device_factory_evdev.h" |
| 6 |
| 7 #include <fcntl.h> |
| 8 #include <linux/input.h> |
| 9 |
| 10 #include "base/stl_util.h" |
| 11 #include "base/thread_task_runner_handle.h" |
| 12 #include "base/threading/worker_pool.h" |
| 13 #include "base/time/time.h" |
| 14 #include "base/trace_event/trace_event.h" |
| 15 #include "ui/events/devices/device_data_manager.h" |
| 16 #include "ui/events/devices/device_util_linux.h" |
| 17 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" |
| 18 #include "ui/events/ozone/evdev/event_converter_evdev_impl.h" |
| 19 #include "ui/events/ozone/evdev/event_device_info.h" |
| 20 #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h" |
| 21 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h" |
| 22 |
| 23 #if defined(USE_EVDEV_GESTURES) |
| 24 #include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h" |
| 25 #include "ui/events/ozone/evdev/libgestures_glue/gesture_feedback.h" |
| 26 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cr
os.h" |
| 27 #include "ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h" |
| 28 #endif |
| 29 |
| 30 #ifndef EVIOCSCLOCKID |
| 31 #define EVIOCSCLOCKID _IOW('E', 0xa0, int) |
| 32 #endif |
| 33 |
| 34 namespace ui { |
| 35 |
| 36 namespace { |
| 37 |
| 38 typedef base::Callback<void(scoped_ptr<EventConverterEvdev>)> |
| 39 OpenInputDeviceReplyCallback; |
| 40 |
| 41 struct OpenInputDeviceParams { |
| 42 // Unique identifier for the new device. |
| 43 int id; |
| 44 |
| 45 // Device path to open. |
| 46 base::FilePath path; |
| 47 |
| 48 // Dispatcher for events. Call on UI thread only. |
| 49 DeviceEventDispatcherEvdev* dispatcher; |
| 50 |
| 51 // State shared between devices. Must not be dereferenced on worker thread. |
| 52 CursorDelegateEvdev* cursor; |
| 53 #if defined(USE_EVDEV_GESTURES) |
| 54 GesturePropertyProvider* gesture_property_provider; |
| 55 #endif |
| 56 }; |
| 57 |
| 58 #if defined(USE_EVDEV_GESTURES) |
| 59 void SetGestureIntProperty(GesturePropertyProvider* provider, |
| 60 int id, |
| 61 const std::string& name, |
| 62 int value) { |
| 63 GesturesProp* property = provider->GetProperty(id, name); |
| 64 if (property) { |
| 65 std::vector<int> values(1, value); |
| 66 property->SetIntValue(values); |
| 67 } |
| 68 } |
| 69 |
| 70 void SetGestureBoolProperty(GesturePropertyProvider* provider, |
| 71 int id, |
| 72 const std::string& name, |
| 73 bool value) { |
| 74 GesturesProp* property = provider->GetProperty(id, name); |
| 75 if (property) { |
| 76 std::vector<bool> values(1, value); |
| 77 property->SetBoolValue(values); |
| 78 } |
| 79 } |
| 80 |
| 81 #endif |
| 82 |
| 83 scoped_ptr<EventConverterEvdev> CreateConverter( |
| 84 const OpenInputDeviceParams& params, |
| 85 int fd, |
| 86 InputDeviceType type, |
| 87 const EventDeviceInfo& devinfo) { |
| 88 #if defined(USE_EVDEV_GESTURES) |
| 89 // Touchpad or mouse: use gestures library. |
| 90 // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent |
| 91 if (devinfo.HasTouchpad() || devinfo.HasMouse()) { |
| 92 scoped_ptr<GestureInterpreterLibevdevCros> gesture_interp = |
| 93 make_scoped_ptr(new GestureInterpreterLibevdevCros( |
| 94 params.id, params.cursor, params.gesture_property_provider, |
| 95 params.dispatcher)); |
| 96 return make_scoped_ptr(new EventReaderLibevdevCros( |
| 97 fd, params.path, params.id, type, devinfo, gesture_interp.Pass())); |
| 98 } |
| 99 #endif |
| 100 |
| 101 // Touchscreen: use TouchEventConverterEvdev. |
| 102 if (devinfo.HasTouchscreen()) { |
| 103 scoped_ptr<TouchEventConverterEvdev> converter(new TouchEventConverterEvdev( |
| 104 fd, params.path, params.id, type, devinfo, params.dispatcher)); |
| 105 converter->Initialize(devinfo); |
| 106 return converter.Pass(); |
| 107 } |
| 108 |
| 109 // Graphics tablet |
| 110 if (devinfo.HasTablet()) |
| 111 return make_scoped_ptr<EventConverterEvdev>(new TabletEventConverterEvdev( |
| 112 fd, params.path, params.id, type, params.cursor, devinfo, |
| 113 params.dispatcher)); |
| 114 |
| 115 // Everything else: use EventConverterEvdevImpl. |
| 116 return make_scoped_ptr<EventConverterEvdevImpl>( |
| 117 new EventConverterEvdevImpl(fd, params.path, params.id, type, devinfo, |
| 118 params.cursor, params.dispatcher)); |
| 119 } |
| 120 |
| 121 // Open an input device. Opening may put the calling thread to sleep, and |
| 122 // therefore should be run on a thread where latency is not critical. We |
| 123 // run it on a thread from the worker pool. |
| 124 // |
| 125 // This takes a TaskRunner and runs the reply on that thread, so that we |
| 126 // can hop threads if necessary (back to the UI thread). |
| 127 void OpenInputDevice(scoped_ptr<OpenInputDeviceParams> params, |
| 128 scoped_refptr<base::TaskRunner> reply_runner, |
| 129 const OpenInputDeviceReplyCallback& reply_callback) { |
| 130 const base::FilePath& path = params->path; |
| 131 scoped_ptr<EventConverterEvdev> converter; |
| 132 |
| 133 TRACE_EVENT1("evdev", "OpenInputDevice", "path", path.value()); |
| 134 |
| 135 int fd = open(path.value().c_str(), O_RDWR | O_NONBLOCK); |
| 136 if (fd < 0) { |
| 137 PLOG(ERROR) << "Cannot open '" << path.value(); |
| 138 reply_runner->PostTask( |
| 139 FROM_HERE, base::Bind(reply_callback, base::Passed(&converter))); |
| 140 return; |
| 141 } |
| 142 |
| 143 // Use monotonic timestamps for events. The touch code in particular |
| 144 // expects event timestamps to correlate to the monotonic clock |
| 145 // (base::TimeTicks). |
| 146 unsigned int clk = CLOCK_MONOTONIC; |
| 147 if (ioctl(fd, EVIOCSCLOCKID, &clk)) |
| 148 PLOG(ERROR) << "failed to set CLOCK_MONOTONIC"; |
| 149 |
| 150 EventDeviceInfo devinfo; |
| 151 if (!devinfo.Initialize(fd)) { |
| 152 LOG(ERROR) << "failed to get device information for " << path.value(); |
| 153 close(fd); |
| 154 reply_runner->PostTask( |
| 155 FROM_HERE, base::Bind(reply_callback, base::Passed(&converter))); |
| 156 return; |
| 157 } |
| 158 |
| 159 InputDeviceType type = GetInputDeviceTypeFromPath(path); |
| 160 |
| 161 converter = CreateConverter(*params, fd, type, devinfo); |
| 162 |
| 163 // Reply with the constructed converter. |
| 164 reply_runner->PostTask(FROM_HERE, |
| 165 base::Bind(reply_callback, base::Passed(&converter))); |
| 166 } |
| 167 |
| 168 // Close an input device. Closing may put the calling thread to sleep, and |
| 169 // therefore should be run on a thread where latency is not critical. We |
| 170 // run it on the FILE thread. |
| 171 void CloseInputDevice(const base::FilePath& path, |
| 172 scoped_ptr<EventConverterEvdev> converter) { |
| 173 TRACE_EVENT1("evdev", "CloseInputDevice", "path", path.value()); |
| 174 converter.reset(); |
| 175 } |
| 176 |
| 177 } // namespace |
| 178 |
| 179 InputDeviceFactoryEvdev::InputDeviceFactoryEvdev( |
| 180 scoped_ptr<DeviceEventDispatcherEvdev> dispatcher, |
| 181 CursorDelegateEvdev* cursor) |
| 182 : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 183 cursor_(cursor), |
| 184 #if defined(USE_EVDEV_GESTURES) |
| 185 gesture_property_provider_(new GesturePropertyProvider), |
| 186 #endif |
| 187 dispatcher_(dispatcher.Pass()), |
| 188 weak_ptr_factory_(this) { |
| 189 } |
| 190 |
| 191 InputDeviceFactoryEvdev::~InputDeviceFactoryEvdev() { |
| 192 STLDeleteValues(&converters_); |
| 193 } |
| 194 |
| 195 void InputDeviceFactoryEvdev::AddInputDevice(int id, |
| 196 const base::FilePath& path) { |
| 197 scoped_ptr<OpenInputDeviceParams> params(new OpenInputDeviceParams); |
| 198 params->id = id; |
| 199 params->path = path; |
| 200 params->cursor = cursor_; |
| 201 params->dispatcher = dispatcher_.get(); |
| 202 |
| 203 #if defined(USE_EVDEV_GESTURES) |
| 204 params->gesture_property_provider = gesture_property_provider_.get(); |
| 205 #endif |
| 206 |
| 207 OpenInputDeviceReplyCallback reply_callback = |
| 208 base::Bind(&InputDeviceFactoryEvdev::AttachInputDevice, |
| 209 weak_ptr_factory_.GetWeakPtr()); |
| 210 |
| 211 ++pending_device_changes_; |
| 212 |
| 213 // Dispatch task to open from the worker pool, since open may block. |
| 214 base::WorkerPool::PostTask(FROM_HERE, |
| 215 base::Bind(&OpenInputDevice, base::Passed(¶ms), |
| 216 task_runner_, reply_callback), |
| 217 false /* task_is_slow */); |
| 218 } |
| 219 |
| 220 void InputDeviceFactoryEvdev::RemoveInputDevice(const base::FilePath& path) { |
| 221 DetachInputDevice(path); |
| 222 } |
| 223 |
| 224 void InputDeviceFactoryEvdev::OnStartupScanComplete() { |
| 225 startup_devices_enumerated_ = true; |
| 226 NotifyDevicesUpdated(); |
| 227 } |
| 228 |
| 229 void InputDeviceFactoryEvdev::AttachInputDevice( |
| 230 scoped_ptr<EventConverterEvdev> converter) { |
| 231 if (converter.get()) { |
| 232 const base::FilePath& path = converter->path(); |
| 233 |
| 234 TRACE_EVENT1("evdev", "AttachInputDevice", "path", path.value()); |
| 235 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 236 |
| 237 // If we have an existing device, detach it. We don't want two |
| 238 // devices with the same name open at the same time. |
| 239 if (converters_[path]) |
| 240 DetachInputDevice(path); |
| 241 |
| 242 // Add initialized device to map. |
| 243 converters_[path] = converter.release(); |
| 244 converters_[path]->Start(); |
| 245 UpdateDirtyFlags(converters_[path]); |
| 246 |
| 247 // Sync settings to new device. |
| 248 ApplyInputDeviceSettings(); |
| 249 ApplyCapsLockLed(); |
| 250 } |
| 251 |
| 252 --pending_device_changes_; |
| 253 NotifyDevicesUpdated(); |
| 254 } |
| 255 |
| 256 void InputDeviceFactoryEvdev::DetachInputDevice(const base::FilePath& path) { |
| 257 TRACE_EVENT1("evdev", "DetachInputDevice", "path", path.value()); |
| 258 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 259 |
| 260 // Remove device from map. |
| 261 scoped_ptr<EventConverterEvdev> converter(converters_[path]); |
| 262 converters_.erase(path); |
| 263 |
| 264 if (converter) { |
| 265 // Disable the device (to release keys/buttons/etc). |
| 266 converter->SetEnabled(false); |
| 267 |
| 268 // Cancel libevent notifications from this converter. This part must be |
| 269 // on UI since the polling happens on UI. |
| 270 converter->Stop(); |
| 271 |
| 272 UpdateDirtyFlags(converter.get()); |
| 273 NotifyDevicesUpdated(); |
| 274 |
| 275 // Dispatch task to close from the worker pool, since close may block. |
| 276 base::WorkerPool::PostTask( |
| 277 FROM_HERE, |
| 278 base::Bind(&CloseInputDevice, path, base::Passed(&converter)), true); |
| 279 } |
| 280 } |
| 281 |
| 282 void InputDeviceFactoryEvdev::SetCapsLockLed(bool enabled) { |
| 283 caps_lock_led_enabled_ = enabled; |
| 284 ApplyCapsLockLed(); |
| 285 } |
| 286 |
| 287 void InputDeviceFactoryEvdev::UpdateInputDeviceSettings( |
| 288 const InputDeviceSettingsEvdev& settings) { |
| 289 input_device_settings_ = settings; |
| 290 ApplyInputDeviceSettings(); |
| 291 } |
| 292 |
| 293 void InputDeviceFactoryEvdev::GetTouchDeviceStatus( |
| 294 const GetTouchDeviceStatusReply& reply) { |
| 295 scoped_ptr<std::string> status(new std::string); |
| 296 #if defined(USE_EVDEV_GESTURES) |
| 297 DumpTouchDeviceStatus(gesture_property_provider_.get(), status.get()); |
| 298 #endif |
| 299 reply.Run(status.Pass()); |
| 300 } |
| 301 |
| 302 void InputDeviceFactoryEvdev::GetTouchEventLog( |
| 303 const base::FilePath& out_dir, |
| 304 const GetTouchEventLogReply& reply) { |
| 305 scoped_ptr<std::vector<base::FilePath>> log_paths( |
| 306 new std::vector<base::FilePath>); |
| 307 #if defined(USE_EVDEV_GESTURES) |
| 308 DumpTouchEventLog(gesture_property_provider_.get(), out_dir, log_paths.Pass(), |
| 309 reply); |
| 310 #else |
| 311 reply.Run(log_paths.Pass()); |
| 312 #endif |
| 313 } |
| 314 |
| 315 base::WeakPtr<InputDeviceFactoryEvdev> InputDeviceFactoryEvdev::GetWeakPtr() { |
| 316 return weak_ptr_factory_.GetWeakPtr(); |
| 317 } |
| 318 |
| 319 void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() { |
| 320 TRACE_EVENT0("evdev", "ApplyInputDeviceSettings"); |
| 321 |
| 322 SetIntPropertyForOneType(DT_TOUCHPAD, "Pointer Sensitivity", |
| 323 input_device_settings_.touchpad_sensitivity); |
| 324 SetIntPropertyForOneType(DT_TOUCHPAD, "Scroll Sensitivity", |
| 325 input_device_settings_.touchpad_sensitivity); |
| 326 |
| 327 SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Enable", |
| 328 input_device_settings_.tap_to_click_enabled); |
| 329 SetBoolPropertyForOneType(DT_TOUCHPAD, "T5R2 Three Finger Click Enable", |
| 330 input_device_settings_.three_finger_click_enabled); |
| 331 SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Drag Enable", |
| 332 input_device_settings_.tap_dragging_enabled); |
| 333 |
| 334 SetBoolPropertyForOneType(DT_MULTITOUCH, "Australian Scrolling", |
| 335 input_device_settings_.natural_scroll_enabled); |
| 336 |
| 337 SetIntPropertyForOneType(DT_MOUSE, "Pointer Sensitivity", |
| 338 input_device_settings_.mouse_sensitivity); |
| 339 SetIntPropertyForOneType(DT_MOUSE, "Scroll Sensitivity", |
| 340 input_device_settings_.mouse_sensitivity); |
| 341 |
| 342 SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Paused", |
| 343 input_device_settings_.tap_to_click_paused); |
| 344 |
| 345 for (const auto& it : converters_) { |
| 346 EventConverterEvdev* converter = it.second; |
| 347 converter->SetEnabled(IsDeviceEnabled(converter)); |
| 348 |
| 349 if (converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && |
| 350 converter->HasKeyboard()) { |
| 351 converter->SetKeyFilter( |
| 352 input_device_settings_.enable_internal_keyboard_filter, |
| 353 input_device_settings_.internal_keyboard_allowed_keys); |
| 354 } |
| 355 } |
| 356 } |
| 357 |
| 358 void InputDeviceFactoryEvdev::ApplyCapsLockLed() { |
| 359 for (const auto& it : converters_) { |
| 360 EventConverterEvdev* converter = it.second; |
| 361 converter->SetCapsLockLed(caps_lock_led_enabled_); |
| 362 } |
| 363 } |
| 364 |
| 365 bool InputDeviceFactoryEvdev::IsDeviceEnabled( |
| 366 const EventConverterEvdev* converter) { |
| 367 if (!input_device_settings_.enable_internal_touchpad && |
| 368 converter->type() == InputDeviceType::INPUT_DEVICE_INTERNAL && |
| 369 converter->HasTouchpad()) |
| 370 return false; |
| 371 |
| 372 return input_device_settings_.enable_devices; |
| 373 } |
| 374 |
| 375 void InputDeviceFactoryEvdev::UpdateDirtyFlags( |
| 376 const EventConverterEvdev* converter) { |
| 377 if (converter->HasTouchscreen()) |
| 378 touchscreen_list_dirty_ = true; |
| 379 |
| 380 if (converter->HasKeyboard()) |
| 381 keyboard_list_dirty_ = true; |
| 382 |
| 383 if (converter->HasMouse()) |
| 384 mouse_list_dirty_ = true; |
| 385 |
| 386 if (converter->HasTouchpad()) |
| 387 touchpad_list_dirty_ = true; |
| 388 } |
| 389 |
| 390 void InputDeviceFactoryEvdev::NotifyDevicesUpdated() { |
| 391 if (!startup_devices_enumerated_ || pending_device_changes_) |
| 392 return; // No update until full scan done and no pending operations. |
| 393 if (touchscreen_list_dirty_) |
| 394 NotifyTouchscreensUpdated(); |
| 395 if (keyboard_list_dirty_) |
| 396 NotifyKeyboardsUpdated(); |
| 397 if (mouse_list_dirty_) |
| 398 NotifyMouseDevicesUpdated(); |
| 399 if (touchpad_list_dirty_) |
| 400 NotifyTouchpadDevicesUpdated(); |
| 401 if (!startup_devices_opened_) { |
| 402 dispatcher_->DispatchDeviceListsComplete(); |
| 403 startup_devices_opened_ = true; |
| 404 } |
| 405 touchscreen_list_dirty_ = false; |
| 406 keyboard_list_dirty_ = false; |
| 407 mouse_list_dirty_ = false; |
| 408 touchpad_list_dirty_ = false; |
| 409 } |
| 410 |
| 411 void InputDeviceFactoryEvdev::NotifyTouchscreensUpdated() { |
| 412 std::vector<TouchscreenDevice> touchscreens; |
| 413 for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
| 414 if (it->second->HasTouchscreen()) { |
| 415 touchscreens.push_back(TouchscreenDevice(it->second->input_device(), |
| 416 it->second->GetTouchscreenSize(), it->second->GetTouchPoints())); |
| 417 } |
| 418 } |
| 419 |
| 420 dispatcher_->DispatchTouchscreenDevicesUpdated(touchscreens); |
| 421 } |
| 422 |
| 423 void InputDeviceFactoryEvdev::NotifyKeyboardsUpdated() { |
| 424 std::vector<KeyboardDevice> keyboards; |
| 425 for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
| 426 if (it->second->HasKeyboard()) { |
| 427 keyboards.push_back(KeyboardDevice(it->second->input_device())); |
| 428 } |
| 429 } |
| 430 |
| 431 dispatcher_->DispatchKeyboardDevicesUpdated(keyboards); |
| 432 } |
| 433 |
| 434 void InputDeviceFactoryEvdev::NotifyMouseDevicesUpdated() { |
| 435 std::vector<InputDevice> mice; |
| 436 for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
| 437 if (it->second->HasMouse()) { |
| 438 mice.push_back(it->second->input_device()); |
| 439 } |
| 440 } |
| 441 |
| 442 dispatcher_->DispatchMouseDevicesUpdated(mice); |
| 443 } |
| 444 |
| 445 void InputDeviceFactoryEvdev::NotifyTouchpadDevicesUpdated() { |
| 446 std::vector<InputDevice> touchpads; |
| 447 for (auto it = converters_.begin(); it != converters_.end(); ++it) { |
| 448 if (it->second->HasTouchpad()) { |
| 449 touchpads.push_back(it->second->input_device()); |
| 450 } |
| 451 } |
| 452 |
| 453 dispatcher_->DispatchTouchpadDevicesUpdated(touchpads); |
| 454 } |
| 455 |
| 456 void InputDeviceFactoryEvdev::SetIntPropertyForOneType( |
| 457 const EventDeviceType type, |
| 458 const std::string& name, |
| 459 int value) { |
| 460 #if defined(USE_EVDEV_GESTURES) |
| 461 std::vector<int> ids; |
| 462 gesture_property_provider_->GetDeviceIdsByType(type, &ids); |
| 463 for (size_t i = 0; i < ids.size(); ++i) { |
| 464 SetGestureIntProperty(gesture_property_provider_.get(), ids[i], name, |
| 465 value); |
| 466 } |
| 467 #endif |
| 468 // In the future, we may add property setting codes for other non-gesture |
| 469 // devices. One example would be keyboard settings. |
| 470 // TODO(sheckylin): See http://crbug.com/398518 for example. |
| 471 } |
| 472 |
| 473 void InputDeviceFactoryEvdev::SetBoolPropertyForOneType( |
| 474 const EventDeviceType type, |
| 475 const std::string& name, |
| 476 bool value) { |
| 477 #if defined(USE_EVDEV_GESTURES) |
| 478 std::vector<int> ids; |
| 479 gesture_property_provider_->GetDeviceIdsByType(type, &ids); |
| 480 for (size_t i = 0; i < ids.size(); ++i) { |
| 481 SetGestureBoolProperty(gesture_property_provider_.get(), ids[i], name, |
| 482 value); |
| 483 } |
| 484 #endif |
| 485 } |
| 486 |
| 487 } // namespace ui |
OLD | NEW |