Home | History | Annotate | Download | only in evdev
      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.
      4 
      5 #include "ui/events/ozone/evdev/event_factory_evdev.h"
      6 
      7 #include <fcntl.h>
      8 #include <linux/input.h>
      9 
     10 #include "base/debug/trace_event.h"
     11 #include "base/stl_util.h"
     12 #include "base/task_runner.h"
     13 #include "base/thread_task_runner_handle.h"
     14 #include "base/threading/worker_pool.h"
     15 #include "ui/events/ozone/device/device_event.h"
     16 #include "ui/events/ozone/device/device_manager.h"
     17 #include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
     18 #include "ui/events/ozone/evdev/event_device_info.h"
     19 #include "ui/events/ozone/evdev/key_event_converter_evdev.h"
     20 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
     21 
     22 #if defined(USE_EVDEV_GESTURES)
     23 #include "ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.h"
     24 #include "ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.h"
     25 #endif
     26 
     27 #ifndef EVIOCSCLOCKID
     28 #define EVIOCSCLOCKID  _IOW('E', 0xa0, int)
     29 #endif
     30 
     31 namespace ui {
     32 
     33 namespace {
     34 
     35 #if defined(USE_EVDEV_GESTURES)
     36 bool UseGesturesLibraryForDevice(const EventDeviceInfo& devinfo) {
     37   if (devinfo.HasAbsXY() && !devinfo.IsMappedToScreen())
     38     return true;  // touchpad
     39 
     40   if (devinfo.HasRelXY())
     41     return true;  // mouse
     42 
     43   return false;
     44 }
     45 #endif
     46 
     47 scoped_ptr<EventConverterEvdev> CreateConverter(
     48     int fd,
     49     const base::FilePath& path,
     50     const EventDeviceInfo& devinfo,
     51     const EventDispatchCallback& dispatch,
     52     EventModifiersEvdev* modifiers,
     53     CursorDelegateEvdev* cursor) {
     54 #if defined(USE_EVDEV_GESTURES)
     55   // Touchpad or mouse: use gestures library.
     56   // EventReaderLibevdevCros -> GestureInterpreterLibevdevCros -> DispatchEvent
     57   if (UseGesturesLibraryForDevice(devinfo)) {
     58     scoped_ptr<GestureInterpreterLibevdevCros> gesture_interp = make_scoped_ptr(
     59         new GestureInterpreterLibevdevCros(modifiers, cursor, dispatch));
     60     scoped_ptr<EventReaderLibevdevCros> libevdev_reader =
     61         make_scoped_ptr(new EventReaderLibevdevCros(
     62             fd,
     63             path,
     64             gesture_interp.PassAs<EventReaderLibevdevCros::Delegate>()));
     65     return libevdev_reader.PassAs<EventConverterEvdev>();
     66   }
     67 #endif
     68 
     69   // Touchscreen: use TouchEventConverterEvdev.
     70   scoped_ptr<EventConverterEvdev> converter;
     71   if (devinfo.HasAbsXY())
     72     return make_scoped_ptr<EventConverterEvdev>(
     73         new TouchEventConverterEvdev(fd, path, devinfo, dispatch));
     74 
     75   // Everything else: use KeyEventConverterEvdev.
     76   return make_scoped_ptr<EventConverterEvdev>(
     77       new KeyEventConverterEvdev(fd, path, modifiers, dispatch));
     78 }
     79 
     80 // Open an input device. Opening may put the calling thread to sleep, and
     81 // therefore should be run on a thread where latency is not critical. We
     82 // run it on a thread from the worker pool.
     83 //
     84 // This takes a TaskRunner and runs the reply on that thread, so that we
     85 // can hop threads if necessary (back to the UI thread).
     86 void OpenInputDevice(
     87     const base::FilePath& path,
     88     EventModifiersEvdev* modifiers,
     89     CursorDelegateEvdev* cursor,
     90     scoped_refptr<base::TaskRunner> reply_runner,
     91     const EventDispatchCallback& dispatch,
     92     base::Callback<void(scoped_ptr<EventConverterEvdev>)> reply_callback) {
     93   TRACE_EVENT1("ozone", "OpenInputDevice", "path", path.value());
     94 
     95   int fd = open(path.value().c_str(), O_RDONLY | O_NONBLOCK);
     96   if (fd < 0) {
     97     PLOG(ERROR) << "Cannot open '" << path.value();
     98     return;
     99   }
    100 
    101   // Use monotonic timestamps for events. The touch code in particular
    102   // expects event timestamps to correlate to the monotonic clock
    103   // (base::TimeTicks).
    104   unsigned int clk = CLOCK_MONOTONIC;
    105   if (ioctl(fd, EVIOCSCLOCKID, &clk))
    106     PLOG(ERROR) << "failed to set CLOCK_MONOTONIC";
    107 
    108   EventDeviceInfo devinfo;
    109   if (!devinfo.Initialize(fd)) {
    110     LOG(ERROR) << "failed to get device information for " << path.value();
    111     close(fd);
    112     return;
    113   }
    114 
    115   scoped_ptr<EventConverterEvdev> converter =
    116       CreateConverter(fd, path, devinfo, dispatch, modifiers, cursor);
    117 
    118   // Reply with the constructed converter.
    119   reply_runner->PostTask(FROM_HERE,
    120                          base::Bind(reply_callback, base::Passed(&converter)));
    121 }
    122 
    123 // Close an input device. Closing may put the calling thread to sleep, and
    124 // therefore should be run on a thread where latency is not critical. We
    125 // run it on the FILE thread.
    126 void CloseInputDevice(const base::FilePath& path,
    127                       scoped_ptr<EventConverterEvdev> converter) {
    128   TRACE_EVENT1("ozone", "CloseInputDevice", "path", path.value());
    129   converter.reset();
    130 }
    131 
    132 }  // namespace
    133 
    134 EventFactoryEvdev::EventFactoryEvdev(
    135     CursorDelegateEvdev* cursor,
    136     DeviceManager* device_manager)
    137     : device_manager_(device_manager),
    138       cursor_(cursor),
    139       dispatch_callback_(
    140           base::Bind(base::IgnoreResult(&EventFactoryEvdev::DispatchUiEvent),
    141                      base::Unretained(this))),
    142       weak_ptr_factory_(this) {
    143   DCHECK(device_manager_);
    144 }
    145 
    146 EventFactoryEvdev::~EventFactoryEvdev() { STLDeleteValues(&converters_); }
    147 
    148 void EventFactoryEvdev::DispatchUiEvent(Event* event) {
    149   DispatchEvent(event);
    150 }
    151 
    152 void EventFactoryEvdev::AttachInputDevice(
    153     const base::FilePath& path,
    154     scoped_ptr<EventConverterEvdev> converter) {
    155   TRACE_EVENT1("ozone", "AttachInputDevice", "path", path.value());
    156   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    157 
    158   // If we have an existing device, detach it. We don't want two
    159   // devices with the same name open at the same time.
    160   if (converters_[path])
    161     DetachInputDevice(path);
    162 
    163   // Add initialized device to map.
    164   converters_[path] = converter.release();
    165   converters_[path]->Start();
    166 }
    167 
    168 void EventFactoryEvdev::OnDeviceEvent(const DeviceEvent& event) {
    169   if (event.device_type() != DeviceEvent::INPUT)
    170     return;
    171 
    172   switch (event.action_type()) {
    173     case DeviceEvent::ADD:
    174     case DeviceEvent::CHANGE: {
    175       TRACE_EVENT1("ozone", "OnDeviceAdded", "path", event.path().value());
    176 
    177       // Dispatch task to open from the worker pool, since open may block.
    178       base::WorkerPool::PostTask(
    179           FROM_HERE,
    180           base::Bind(&OpenInputDevice,
    181                      event.path(),
    182                      &modifiers_,
    183                      cursor_,
    184                      ui_task_runner_,
    185                      dispatch_callback_,
    186                      base::Bind(&EventFactoryEvdev::AttachInputDevice,
    187                                 weak_ptr_factory_.GetWeakPtr(),
    188                                 event.path())),
    189           true);
    190     }
    191       break;
    192     case DeviceEvent::REMOVE: {
    193       TRACE_EVENT1("ozone", "OnDeviceRemoved", "path", event.path().value());
    194       DetachInputDevice(event.path());
    195     }
    196       break;
    197   }
    198 }
    199 
    200 void EventFactoryEvdev::OnDispatcherListChanged() {
    201   if (!ui_task_runner_.get()) {
    202     ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
    203     // Scan & monitor devices.
    204     device_manager_->AddObserver(this);
    205     device_manager_->ScanDevices(this);
    206   }
    207 }
    208 
    209 void EventFactoryEvdev::DetachInputDevice(const base::FilePath& path) {
    210   TRACE_EVENT1("ozone", "DetachInputDevice", "path", path.value());
    211   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
    212 
    213   // Remove device from map.
    214   scoped_ptr<EventConverterEvdev> converter(converters_[path]);
    215   converters_.erase(path);
    216 
    217   if (converter) {
    218     // Cancel libevent notifications from this converter. This part must be
    219     // on UI since the polling happens on UI.
    220     converter->Stop();
    221 
    222     // Dispatch task to close from the worker pool, since close may block.
    223     base::WorkerPool::PostTask(
    224         FROM_HERE,
    225         base::Bind(&CloseInputDevice, path, base::Passed(&converter)),
    226         true);
    227   }
    228 }
    229 
    230 void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget,
    231                                      const gfx::PointF& location) {
    232   if (cursor_) {
    233     cursor_->MoveCursorTo(widget, location);
    234     MouseEvent mouse_event(ET_MOUSE_MOVED,
    235                            cursor_->location(),
    236                            cursor_->location(),
    237                            modifiers_.GetModifierFlags(),
    238                            /* changed_button_flags */ 0);
    239     DispatchEvent(&mouse_event);
    240   }
    241 }
    242 
    243 }  // namespace ui
    244