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/gamepad/gamepad_event.h"
15 #include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"
16 
17 namespace ui {
18 
GamepadEventConverterEvdev(base::ScopedFD fd,base::FilePath path,int id,const EventDeviceInfo & devinfo,DeviceEventDispatcherEvdev * dispatcher)19 GamepadEventConverterEvdev::GamepadEventConverterEvdev(
20     base::ScopedFD fd,
21     base::FilePath path,
22     int id,
23     const EventDeviceInfo& devinfo,
24     DeviceEventDispatcherEvdev* dispatcher)
25     : EventConverterEvdev(fd.get(),
26                           path,
27                           id,
28                           devinfo.device_type(),
29                           devinfo.name(),
30                           devinfo.phys(),
31                           devinfo.vendor_id(),
32                           devinfo.product_id(),
33                           devinfo.version()),
34       will_send_frame_(false),
35       input_device_fd_(std::move(fd)),
36       dispatcher_(dispatcher) {
37   input_absinfo abs_info;
38   for (int code = 0; code < ABS_CNT; ++code) {
39     abs_info = devinfo.GetAbsInfoByCode(code);
40     if (devinfo.HasAbsEvent(code)) {
41       ui::GamepadDevice::Axis axis;
42       axis.code = code;
43       axis.min_value = abs_info.minimum;
44       axis.max_value = abs_info.maximum;
45       axis.flat = abs_info.flat;
46       axis.fuzz = abs_info.fuzz;
47       axis.resolution = abs_info.resolution;
48       axes_.push_back(axis);
49     }
50   }
51 }
52 
~GamepadEventConverterEvdev()53 GamepadEventConverterEvdev::~GamepadEventConverterEvdev() {
54   DCHECK(!IsEnabled());
55 }
56 
HasGamepad() const57 bool GamepadEventConverterEvdev::HasGamepad() const {
58   return true;
59 }
60 
OnFileCanReadWithoutBlocking(int fd)61 void GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
62   TRACE_EVENT1("evdev",
63                "GamepadEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
64                fd);
65   while (true) {
66     input_event input;
67     ssize_t read_size = read(fd, &input, sizeof(input));
68     if (read_size != sizeof(input)) {
69       if (errno == EINTR || errno == EAGAIN)
70         return;
71       if (errno != ENODEV)
72         PLOG(ERROR) << "error reading device " << path_.value();
73       Stop();
74       return;
75     }
76 
77     if (!IsEnabled())
78       return;
79 
80     ProcessEvent(input);
81   }
82 }
OnDisabled()83 void GamepadEventConverterEvdev::OnDisabled() {
84   ResetGamepad();
85 }
86 
87 std::vector<ui::GamepadDevice::Axis>
GetGamepadAxes() const88 GamepadEventConverterEvdev::GetGamepadAxes() const {
89   return axes_;
90 }
91 
ProcessEvent(const input_event & evdev_ev)92 void GamepadEventConverterEvdev::ProcessEvent(const input_event& evdev_ev) {
93   base::TimeTicks timestamp = TimeTicksFromInputEvent(evdev_ev);
94   // We may have missed Gamepad releases. Reset everything.
95   // If the event is sync, we send a frame.
96   if (evdev_ev.type == EV_SYN) {
97     if (evdev_ev.code == SYN_DROPPED) {
98       LOG(WARNING) << "kernel dropped input events";
99       ResyncGamepad();
100     } else if (evdev_ev.code == SYN_REPORT) {
101       OnSync(timestamp);
102     }
103   } else if (evdev_ev.type == EV_KEY) {
104     ProcessEvdevKey(evdev_ev.code, evdev_ev.value, timestamp);
105   } else if (evdev_ev.type == EV_ABS) {
106     ProcessEvdevAbs(evdev_ev.code, evdev_ev.value, timestamp);
107   }
108 }
109 
ProcessEvdevKey(uint16_t code,int value,const base::TimeTicks & timestamp)110 void GamepadEventConverterEvdev::ProcessEvdevKey(
111     uint16_t code,
112     int value,
113     const base::TimeTicks& timestamp) {
114   if (value)
115     pressed_buttons_.insert(code);
116   else
117     pressed_buttons_.erase(code);
118   GamepadEvent event(input_device_.id, GamepadEventType::BUTTON, code, value,
119                      timestamp);
120   dispatcher_->DispatchGamepadEvent(event);
121   will_send_frame_ = true;
122 }
123 
ProcessEvdevAbs(uint16_t code,int value,const base::TimeTicks & timestamp)124 void GamepadEventConverterEvdev::ProcessEvdevAbs(
125     uint16_t code,
126     int value,
127     const base::TimeTicks& timestamp) {
128   GamepadEvent event(input_device_.id, GamepadEventType::AXIS, code, value,
129                      timestamp);
130   dispatcher_->DispatchGamepadEvent(event);
131   will_send_frame_ = true;
132 }
133 
ResetGamepad()134 void GamepadEventConverterEvdev::ResetGamepad() {
135   base::TimeTicks timestamp = ui::EventTimeForNow();
136   // Reset all the buttons.
137   for (unsigned int code : pressed_buttons_)
138     ProcessEvdevKey(code, 0, timestamp);
139   // Reset all the axes.
140   for (int code = 0; code < ABS_CNT; ++code)
141     ProcessEvdevAbs(code, 0, timestamp);
142   OnSync(timestamp);
143 }
144 
ResyncGamepad()145 void GamepadEventConverterEvdev::ResyncGamepad() {
146   base::TimeTicks timestamp = ui::EventTimeForNow();
147   // Reset all the buttons.
148   for (unsigned int code : pressed_buttons_)
149     ProcessEvdevKey(code, 0, timestamp);
150   // Read the state of all axis.
151   EventDeviceInfo info;
152   if (!info.Initialize(fd_, path_)) {
153     LOG(ERROR) << "Failed to synchronize state for gamepad device: "
154                << path_.value();
155     Stop();
156     return;
157   }
158   for (int code = 0; code < ABS_CNT; ++code) {
159     if (info.HasAbsEvent(code)) {
160       ProcessEvdevAbs(code, info.GetAbsValue(code), timestamp);
161     }
162   }
163   OnSync(timestamp);
164 }
165 
OnSync(const base::TimeTicks & timestamp)166 void GamepadEventConverterEvdev::OnSync(const base::TimeTicks& timestamp) {
167   if (will_send_frame_) {
168     GamepadEvent event(input_device_.id, GamepadEventType::FRAME, 0, 0,
169                        timestamp);
170     dispatcher_->DispatchGamepadEvent(event);
171     will_send_frame_ = false;
172   }
173 }
174 }  //  namespace ui
175