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 "components/exo/arc/arc_input_bridge.h" | |
6 | |
7 #include <linux/input.h> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/posix/eintr_wrapper.h" | |
11 #include "base/time/time.h" | |
12 #include "components/arc/arc_bridge_service.h" | |
13 #include "ui/events/keycodes/dom/keycode_converter.h" | |
14 #include "ui/events/ozone/evdev/keyboard_util_evdev.h" | |
15 | |
16 namespace { | |
17 // input_event values for keyboard events | |
18 static const int kKeyReleased = 0; | |
19 static const int kKeyPressed = 1; | |
20 static const int kKeyRepeated = 2; | |
21 | |
22 // maximum number of supported multi-touch slots (simultaneous fingers) | |
23 static const int kMaxSlots = 64; | |
24 | |
25 // tracking id of an empty slot | |
26 static const int kEmptySlot = -1; | |
27 | |
28 // maximum possible pressure as defined in EventHubARC. | |
29 // todo(denniskempin): communicate maximum during initialization. | |
30 static const int kMaxPressure = 65536; | |
31 | |
32 struct MouseButtonMapping { | |
33 int ui_flag; | |
34 int evdev_code; | |
35 }; | |
36 MouseButtonMapping kMouseButtonMap[] = { | |
37 {ui::EF_LEFT_MOUSE_BUTTON, BTN_LEFT}, | |
38 {ui::EF_RIGHT_MOUSE_BUTTON, BTN_RIGHT}, | |
39 {ui::EF_MIDDLE_MOUSE_BUTTON, BTN_MIDDLE}, | |
40 }; | |
41 | |
42 // Weak pointer. This class is owned by ChromeBrowserMainPartsChromeos. | |
43 arc::ArcInputBridge* g_arc_input_bridge = nullptr; | |
44 } | |
45 | |
46 namespace arc { | |
47 | |
48 ArcInputBridge::ArcInputBridge( | |
49 base::WeakPtr<ArcBridgeService> arc_bridge_service) | |
50 : arc_bridge_service_(arc_bridge_service), | |
51 current_slot_(-1), | |
52 current_slot_tracking_ids_(kMaxSlots, kEmptySlot) { | |
53 if (arc_bridge_service) { | |
54 DCHECK(arc_bridge_service->state() == ArcBridgeService::State::STOPPED); | |
55 arc_bridge_service->AddObserver(this); | |
56 } | |
57 g_arc_input_bridge = this; | |
58 } | |
59 | |
60 ArcInputBridge::~ArcInputBridge() { | |
61 if (arc_bridge_service_) { | |
62 arc_bridge_service_->RemoveObserver(this); | |
63 } | |
64 } | |
65 | |
66 // static | |
67 ArcInputBridge* ArcInputBridge::Get() { | |
68 DCHECK(g_arc_input_bridge); | |
69 return g_arc_input_bridge; | |
70 } | |
71 | |
72 void ArcInputBridge::OnInstanceBootPhase(InstanceBootPhase phase) { | |
73 // todo(denniskempin): Investigate how to turn around the communication | |
74 // and initiate the creation of devices from the instance-side. This would | |
75 // reduce the amount of state-tracking necessary. | |
76 if (phase != InstanceBootPhase::SYSTEM_SERVICES_READY) { | |
denniskempin
2015/11/20 00:18:24
According to lloyd this won't work at SYSTEM_SERVI
| |
77 return; | |
78 } | |
79 | |
80 keyboard_fd_ = CreateBridgeInputDevice("ChromeOS Keyboard", "keyboard"); | |
81 mouse_fd_ = CreateBridgeInputDevice("ChromeOS Mouse", "mouse"); | |
82 touchscreen_fd_ = | |
83 CreateBridgeInputDevice("ChromeOS Touchscreen", "touchscreen"); | |
84 } | |
85 | |
86 void ArcInputBridge::SendInputEvent(ui::Event* event) { | |
87 if (event->IsKeyEvent()) { | |
88 SendKeyEvent(static_cast<ui::KeyEvent*>(event)); | |
89 } else if (event->IsMouseEvent()) { | |
90 SendMouseEvent(static_cast<ui::MouseEvent*>(event)); | |
91 } else if (event->IsTouchEvent()) { | |
92 SendTouchEvent(static_cast<ui::TouchEvent*>(event)); | |
93 } | |
94 } | |
95 | |
96 void ArcInputBridge::SendKeyEvent(ui::KeyEvent* event) { | |
97 if (keyboard_fd_.get() < 0) { | |
98 LOG(WARNING) << "No keyboard bridge device available."; | |
99 return; | |
100 } | |
101 | |
102 int native_code = ui::KeycodeConverter::DomCodeToNativeKeycode(event->code()); | |
103 | |
104 unsigned short evdev_code = ui::NativeCodeToEvdevCode(native_code); | |
105 int evdev_value; | |
106 if (event->type() == ui::ET_KEY_PRESSED) { | |
107 if (event->flags() & ui::EF_IS_REPEAT) { | |
108 evdev_value = kKeyRepeated; | |
109 } else { | |
110 evdev_value = kKeyPressed; | |
111 } | |
112 } else if (event->type() == ui::ET_KEY_RELEASED) { | |
113 evdev_value = kKeyReleased; | |
114 } | |
115 | |
116 base::TimeDelta time_stamp = event->time_stamp(); | |
117 SendKernelEvent(keyboard_fd_, time_stamp, EV_KEY, evdev_code, evdev_value); | |
118 SendSynReport(keyboard_fd_, time_stamp); | |
119 } | |
120 | |
121 void ArcInputBridge::SendTouchEvent(ui::TouchEvent* event) { | |
122 if (touchscreen_fd_.get() < 0) { | |
123 LOG(WARNING) << "No touchscreen bridge device available."; | |
124 return; | |
125 } | |
126 | |
127 ui::PointerDetails details = event->pointer_details(); | |
128 base::TimeDelta time_stamp = event->time_stamp(); | |
129 | |
130 // find or assing a slot for this tracking id | |
131 int slot_id = AcquireTouchSlot(event); | |
132 if (slot_id < 0) { | |
133 LOG(ERROR) << "Ran out of slot IDs."; | |
134 return; | |
135 } | |
136 | |
137 // we only need to send the slot ID when it has changed. | |
138 if (slot_id != current_slot_) { | |
139 current_slot_ = slot_id; | |
140 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_SLOT, | |
141 current_slot_); | |
142 } | |
143 | |
144 // update tracking id | |
145 if (event->type() == ui::ET_TOUCH_PRESSED) { | |
146 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TRACKING_ID, | |
147 event->touch_id()); | |
148 } else if (event->type() == ui::ET_TOUCH_RELEASED) { | |
149 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TRACKING_ID, | |
150 kEmptySlot); | |
151 } | |
152 | |
153 // update touch information | |
154 if (event->type() == ui::ET_TOUCH_MOVED || | |
155 event->type() == ui::ET_TOUCH_PRESSED) { | |
156 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_POSITION_X, | |
157 event->x()); | |
158 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_POSITION_Y, | |
159 event->y()); | |
160 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TOUCH_MAJOR, | |
161 details.radius_x()); | |
162 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TOUCH_MINOR, | |
163 details.radius_y()); | |
164 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_PRESSURE, | |
165 details.force() * kMaxPressure); | |
166 } | |
167 SendSynReport(touchscreen_fd_, time_stamp); | |
168 } | |
169 | |
170 void ArcInputBridge::SendMouseEvent(ui::MouseEvent* event) { | |
171 if (mouse_fd_.get() < 0) { | |
172 LOG(WARNING) << "No mouse bridge device available."; | |
173 return; | |
174 } | |
175 | |
176 base::TimeDelta time_stamp = event->time_stamp(); | |
177 | |
178 // update location | |
179 if (event->type() == ui::ET_MOUSE_MOVED) { | |
180 SendKernelEvent(mouse_fd_, time_stamp, EV_ABS, ABS_X, event->x()); | |
181 SendKernelEvent(mouse_fd_, time_stamp, EV_ABS, ABS_Y, event->y()); | |
182 } | |
183 | |
184 // update buttons | |
185 if (event->type() == ui::ET_MOUSE_PRESSED || | |
186 event->type() == ui::ET_MOUSE_RELEASED) { | |
187 int evdev_value = static_cast<int>(event->type() == ui::ET_MOUSE_PRESSED); | |
188 for (MouseButtonMapping mapping : kMouseButtonMap) { | |
189 if (event->changed_button_flags() & mapping.ui_flag) { | |
190 SendKernelEvent(mouse_fd_, time_stamp, EV_KEY, mapping.evdev_code, | |
191 evdev_value); | |
192 } | |
193 } | |
194 } | |
195 | |
196 // update scroll wheel | |
197 if (event->IsMouseWheelEvent()) { | |
198 ui::MouseWheelEvent* wheel_event = static_cast<ui::MouseWheelEvent*>(event); | |
199 SendKernelEvent(mouse_fd_, time_stamp, EV_REL, REL_WHEEL, | |
200 wheel_event->y_offset()); | |
201 SendKernelEvent(mouse_fd_, time_stamp, EV_REL, REL_HWHEEL, | |
202 wheel_event->x_offset()); | |
203 } | |
204 | |
205 SendSynReport(mouse_fd_, time_stamp); | |
206 } | |
207 | |
208 void ArcInputBridge::SendKernelEvent(const base::ScopedFD& fd, | |
209 base::TimeDelta time_stamp, | |
210 unsigned short type, | |
211 unsigned short code, | |
212 int value) { | |
213 if (fd.get() < 0) { | |
214 LOG(ERROR) << "Invalid file descriptor"; | |
215 return; | |
216 } | |
217 // Luckily Chrome on POSIX and the receiver both use monotonic time for | |
218 // input events, so we can just fill in the same timestamp. | |
219 // todo(denniskempin): Nevertheless there are rare issues with stale events on | |
220 // the instance side which need more investigation. | |
221 struct input_event event; | |
222 event.time.tv_sec = time_stamp.InSeconds(); | |
223 base::TimeDelta remainder = | |
224 time_stamp - base::TimeDelta::FromSeconds(event.time.tv_sec); | |
225 event.time.tv_usec = remainder.InMicroseconds(); | |
226 event.type = type; | |
227 event.code = code; | |
228 event.value = value; | |
229 | |
230 // Write event to file descriptor | |
231 int num_written = write(fd.get(), reinterpret_cast<void*>(&event), | |
232 sizeof(struct input_event)); | |
233 if (num_written != sizeof(struct input_event)) { | |
234 LOG(ERROR) << "Can't write to file descriptor"; | |
235 } | |
236 } | |
237 | |
238 void ArcInputBridge::SendSynReport(const base::ScopedFD& fd, | |
239 base::TimeDelta time) { | |
240 SendKernelEvent(fd, time, EV_SYN, SYN_REPORT, 0); | |
241 } | |
242 | |
243 int ArcInputBridge::AcquireTouchSlot(ui::TouchEvent* event) { | |
244 int slot_id; | |
245 if (event->type() == ui::ET_TOUCH_PRESSED) { | |
246 slot_id = FindTouchSlot(kEmptySlot); | |
247 } else { | |
248 slot_id = FindTouchSlot(event->touch_id()); | |
249 } | |
250 if (slot_id < 0) { | |
251 return -1; | |
252 } | |
253 | |
254 if (event->type() == ui::ET_TOUCH_RELEASED) { | |
255 current_slot_tracking_ids_[slot_id] = kEmptySlot; | |
256 } else if (event->type() == ui::ET_TOUCH_PRESSED) { | |
257 current_slot_tracking_ids_[slot_id] = event->touch_id(); | |
258 } | |
259 return slot_id; | |
260 } | |
261 | |
262 int ArcInputBridge::FindTouchSlot(int tracking_id) { | |
263 for (int i = 0; i < kMaxSlots; ++i) { | |
264 if (current_slot_tracking_ids_[i] == tracking_id) { | |
265 return i; | |
266 } | |
267 } | |
268 return -1; | |
269 } | |
270 | |
271 base::ScopedFD ArcInputBridge::CreateBridgeInputDevice( | |
272 const std::string& name, | |
273 const std::string& device_type) { | |
274 if (!arc_bridge_service_) { | |
275 LOG(ERROR) << "ArcBridgeService disappeared."; | |
276 return base::ScopedFD(); | |
277 } | |
278 | |
279 // Create file descriptor pair for communication | |
280 int fd[2]; | |
281 int res = HANDLE_EINTR(pipe(fd)); | |
282 if (res < 0) { | |
283 PLOG(ERROR) << "Cannot create pipe"; | |
284 } | |
285 | |
286 // The read end is sent to the instance, ownership of fd transfers. | |
287 if (!arc_bridge_service_->RegisterInputDevice(name, device_type, | |
288 base::ScopedFD(fd[0]))) { | |
289 close(fd[1]); // close write end. | |
290 LOG(ERROR) << "Cannot create bridge input device"; | |
291 return base::ScopedFD(); | |
292 } | |
293 | |
294 // return the write end | |
295 return base::ScopedFD(fd[1]).Pass(); | |
296 } | |
297 | |
298 } // namespace arc | |
OLD | NEW |