Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Side by Side Diff: components/exo/arc/arc_input_bridge.cc

Issue 1408263006: chromeos: Add ArcInputBridge to components/arc (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@arcxx
Patch Set: fixed scopedfd Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/exo/arc/arc_input_bridge.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « components/exo/arc/arc_input_bridge.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698