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

Side by Side Diff: components/arc/input/arc_input_bridge_impl.cc

Issue 1408263006: chromeos: Add ArcInputBridge to components/arc (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@arcxx
Patch Set: nit fix Created 5 years 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
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/arc/input/arc_input_bridge_impl.h"
6
7 #include <linux/input.h>
8 #include <fcntl.h>
9
10 #include "base/logging.h"
11 #include "base/posix/eintr_wrapper.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "components/arc/arc_bridge_service.h"
15 #include "ui/aura/env.h"
16 #include "ui/aura/env_observer.h"
17 #include "ui/aura/window.h"
18 #include "ui/aura/window_observer.h"
19 #include "ui/events/event.h"
20 #include "ui/events/event_handler.h"
21 #include "ui/events/event_utils.h"
22 #include "ui/events/keycodes/dom/keycode_converter.h"
23 #include "ui/events/ozone/evdev/keyboard_util_evdev.h"
24
25 namespace {
26
27 // input_event values for keyboard events.
28 const int kKeyReleased = 0;
29 const int kKeyPressed = 1;
30 const int kKeyRepeated = 2;
31
32 // maximum number of supported multi-touch slots (simultaneous fingers).
33 const int kMaxSlots = 64;
34
35 // tracking id of an empty slot.
36 const int kEmptySlot = -1;
37
38 // maximum possible pressure as defined in EventHubARC.
39 // TODO(denniskempin): communicate maximum during initialization.
40 const int kMaxPressure = 65536;
41
42 // speed of the scroll emulation. Scroll events are reported at about 100 times
43 // the speed of a scroll wheel.
44 const float kScrollEmulationSpeed = 100.0f;
45
46 struct MouseButtonMapping {
47 int ui_flag;
48 int evdev_code;
49 } kMouseButtonMap[] = {
50 {ui::EF_LEFT_MOUSE_BUTTON, BTN_LEFT},
51 {ui::EF_RIGHT_MOUSE_BUTTON, BTN_RIGHT},
52 {ui::EF_MIDDLE_MOUSE_BUTTON, BTN_MIDDLE},
53 };
54
55 } // namespace
56
57 namespace arc {
58
59 ArcInputBridgeImpl::ArcInputBridgeImpl(ArcBridgeService* arc_bridge_service)
60 : arc_bridge_service_(arc_bridge_service),
61 offset_x_acc_(0.5f),
62 offset_y_acc_(0.5f),
63 current_slot_(-1),
64 current_slot_tracking_ids_(kMaxSlots, kEmptySlot),
65 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()),
66 weak_factory_(this) {
67 DCHECK(arc_bridge_service->state() == ArcBridgeService::State::STOPPED);
68 arc_bridge_service->AddObserver(this);
69
70 DCHECK(aura::Env::GetInstance());
71 aura::Env::GetInstance()->AddObserver(this);
72 }
73
74 ArcInputBridgeImpl::~ArcInputBridgeImpl() {
reveman 2015/12/05 00:48:45 nit: aura::Env::GetInstance()->RemoveObserver(this
denniskempin 2015/12/05 01:17:56 Done.
75 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
76 arc_bridge_service_->RemoveObserver(this);
77 }
78
79 void ArcInputBridgeImpl::OnInstanceBootPhase(InstanceBootPhase phase) {
80 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
81 if (phase != InstanceBootPhase::SYSTEM_SERVICES_READY)
82 return;
83
84 keyboard_fd_ = CreateBridgeInputDevice("ChromeOS Keyboard", "keyboard");
85 mouse_fd_ = CreateBridgeInputDevice("ChromeOS Mouse", "mouse");
86 touchscreen_fd_ =
87 CreateBridgeInputDevice("ChromeOS Touchscreen", "touchscreen");
88 }
89
90 // Translates and sends a ui::Event to the appropriate bridge device of the
91 // ARC instance. If the devices have not yet been initialized, the event
92 // will be ignored.
93 void ArcInputBridgeImpl::OnEvent(ui::Event* event) {
94 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
95 if (event->IsKeyEvent()) {
96 SendKeyEvent(static_cast<ui::KeyEvent*>(event));
97 } else if (event->IsMouseEvent() || event->IsScrollEvent()) {
98 SendMouseEvent(static_cast<ui::MouseEvent*>(event));
99 } else if (event->IsTouchEvent()) {
100 SendTouchEvent(static_cast<ui::TouchEvent*>(event));
101 }
102 }
103
104 // Attaches the input bridge to the window if it is marked as an ARC window.
105 void ArcInputBridgeImpl::OnWindowInitialized(aura::Window* new_window) {
106 if (new_window->name() == "ExoSurface")
107 new_window->AddPreTargetHandler(this);
reveman 2015/12/05 00:48:45 This assumes that we outlive all windows. I would
108 }
109
110 void ArcInputBridgeImpl::SendKeyEvent(ui::KeyEvent* event) {
111 if (keyboard_fd_.get() < 0) {
112 VLOG(2) << "No keyboard bridge device available.";
113 return;
114 }
115
116 int native_code = ui::KeycodeConverter::DomCodeToNativeKeycode(event->code());
117
118 unsigned short evdev_code = ui::NativeCodeToEvdevCode(native_code);
119 int evdev_value;
120 if (event->type() == ui::ET_KEY_PRESSED) {
121 if (event->flags() & ui::EF_IS_REPEAT) {
122 evdev_value = kKeyRepeated;
123 } else {
124 evdev_value = kKeyPressed;
125 }
126 } else if (event->type() == ui::ET_KEY_RELEASED) {
127 evdev_value = kKeyReleased;
128 }
129
130 base::TimeDelta time_stamp = event->time_stamp();
131 SendKernelEvent(keyboard_fd_, time_stamp, EV_KEY, evdev_code, evdev_value);
132 SendSynReport(keyboard_fd_, time_stamp);
133 }
134
135 void ArcInputBridgeImpl::SendTouchEvent(ui::TouchEvent* event) {
136 if (touchscreen_fd_.get() < 0) {
137 VLOG(2) << "No touchscreen bridge device available.";
138 return;
139 }
140
141 ui::PointerDetails details = event->pointer_details();
142 base::TimeDelta time_stamp = event->time_stamp();
143
144 // find or assing a slot for this tracking id
145 int slot_id = AcquireTouchSlot(event);
146 if (slot_id < 0) {
147 VLOG(1) << "Ran out of slot IDs.";
148 return;
149 }
150
151 // we only need to send the slot ID when it has changed.
152 if (slot_id != current_slot_) {
153 current_slot_ = slot_id;
154 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_SLOT,
155 current_slot_);
156 }
157
158 // update tracking id
159 if (event->type() == ui::ET_TOUCH_PRESSED) {
160 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TRACKING_ID,
161 event->touch_id());
162 } else if (event->type() == ui::ET_TOUCH_RELEASED) {
163 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TRACKING_ID,
164 kEmptySlot);
165 }
166
167 // update touch information
168 if (event->type() == ui::ET_TOUCH_MOVED ||
169 event->type() == ui::ET_TOUCH_PRESSED) {
170 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_POSITION_X,
171 event->x());
172 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_POSITION_Y,
173 event->y());
174 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TOUCH_MAJOR,
175 details.radius_x());
176 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_TOUCH_MINOR,
177 details.radius_y());
178 SendKernelEvent(touchscreen_fd_, time_stamp, EV_ABS, ABS_MT_PRESSURE,
179 details.force() * kMaxPressure);
180 }
181 SendSynReport(touchscreen_fd_, time_stamp);
182 }
183
184 void ArcInputBridgeImpl::SendMouseEvent(ui::MouseEvent* event) {
185 if (mouse_fd_.get() < 0) {
186 VLOG(2) << "No mouse bridge device available.";
187 return;
188 }
189
190 base::TimeDelta time_stamp = event->time_stamp();
191
192 // update location
193 if (event->type() == ui::ET_MOUSE_MOVED ||
194 event->type() == ui::ET_MOUSE_DRAGGED) {
195 SendKernelEvent(mouse_fd_, time_stamp, EV_ABS, ABS_X, event->x());
196 SendKernelEvent(mouse_fd_, time_stamp, EV_ABS, ABS_Y, event->y());
197 }
198
199 // update buttons
200 if (event->type() == ui::ET_MOUSE_PRESSED ||
201 event->type() == ui::ET_MOUSE_RELEASED) {
202 int evdev_value = static_cast<int>(event->type() == ui::ET_MOUSE_PRESSED);
203 for (MouseButtonMapping mapping : kMouseButtonMap) {
204 if (event->changed_button_flags() & mapping.ui_flag) {
205 SendKernelEvent(mouse_fd_, time_stamp, EV_KEY, mapping.evdev_code,
206 evdev_value);
207 }
208 }
209 }
210
211 // update scroll wheel
212 if (event->type() == ui::ET_SCROLL) {
213 ui::ScrollEvent* scroll_event = static_cast<ui::ScrollEvent*>(event);
214 // accumulate floating point scroll offset since we can only send full
215 // integer
216 // wheel events.
217 offset_x_acc_ += scroll_event->x_offset_ordinal() / kScrollEmulationSpeed;
218 offset_y_acc_ += scroll_event->y_offset_ordinal() / kScrollEmulationSpeed;
219
220 int wheel = floor(offset_y_acc_);
221 if (wheel != 0) {
222 SendKernelEvent(mouse_fd_, time_stamp, EV_REL, REL_WHEEL, wheel);
223 offset_y_acc_ -= static_cast<float>(wheel);
224 }
225
226 int hwheel = floor(offset_x_acc_);
227 if (hwheel != 0) {
228 SendKernelEvent(mouse_fd_, time_stamp, EV_REL, REL_HWHEEL, hwheel);
229 offset_x_acc_ -= static_cast<float>(hwheel);
230 }
231 }
232
233 SendSynReport(mouse_fd_, time_stamp);
234 }
235
236 void ArcInputBridgeImpl::SendKernelEvent(const base::ScopedFD& fd,
237 base::TimeDelta time_stamp,
238 unsigned short type,
239 unsigned short code,
240 int value) {
241 DCHECK(fd.is_valid());
242
243 // Chrome does not always use the monotonic system time for event times.
244 // For now always force the event time to the current system time.
245 // TODO(denniskempin): To enable performance tracing of events we will want
246 // to use the kernel-provided monotonic time stamps of events.
247 time_stamp = ui::EventTimeForNow();
248
249 struct input_event event;
250 event.time.tv_sec = time_stamp.InSeconds();
251 base::TimeDelta remainder =
252 time_stamp - base::TimeDelta::FromSeconds(event.time.tv_sec);
253 event.time.tv_usec = remainder.InMicroseconds();
254 event.type = type;
255 event.code = code;
256 event.value = value;
257
258 // Write event to file descriptor
259 size_t num_written = write(fd.get(), reinterpret_cast<void*>(&event),
260 sizeof(struct input_event));
261 DCHECK_EQ(num_written, sizeof(struct input_event));
262 }
263
264 void ArcInputBridgeImpl::SendSynReport(const base::ScopedFD& fd,
265 base::TimeDelta time) {
266 DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
267
268 SendKernelEvent(fd, time, EV_SYN, SYN_REPORT, 0);
269 }
270
271 int ArcInputBridgeImpl::AcquireTouchSlot(ui::TouchEvent* event) {
272 int slot_id;
273 if (event->type() == ui::ET_TOUCH_PRESSED) {
274 slot_id = FindTouchSlot(kEmptySlot);
275 } else {
276 slot_id = FindTouchSlot(event->touch_id());
277 }
278 if (slot_id < 0) {
279 return -1;
280 }
281
282 if (event->type() == ui::ET_TOUCH_RELEASED) {
283 current_slot_tracking_ids_[slot_id] = kEmptySlot;
284 } else if (event->type() == ui::ET_TOUCH_PRESSED) {
285 current_slot_tracking_ids_[slot_id] = event->touch_id();
286 }
287 return slot_id;
288 }
289
290 int ArcInputBridgeImpl::FindTouchSlot(int tracking_id) {
291 for (int i = 0; i < kMaxSlots; ++i) {
292 if (current_slot_tracking_ids_[i] == tracking_id) {
293 return i;
294 }
295 }
296 return -1;
297 }
298
299 base::ScopedFD ArcInputBridgeImpl::CreateBridgeInputDevice(
300 const std::string& name,
301 const std::string& device_type) {
302 if (!arc_bridge_service_) {
303 VLOG(1) << "ArcBridgeService disappeared.";
304 return base::ScopedFD();
305 }
306
307 // Create file descriptor pair for communication
308 int fd[2];
309 int res = HANDLE_EINTR(pipe(fd));
310 if (res < 0) {
311 VPLOG(1) << "Cannot create pipe";
312 return base::ScopedFD();
313 }
314 base::ScopedFD read_fd(fd[0]);
315 base::ScopedFD write_fd(fd[1]);
316
317 // The read end is sent to the instance, ownership of fd transfers.
318 if (!arc_bridge_service_->RegisterInputDevice(name, device_type,
319 std::move(read_fd))) {
320 VLOG(1) << "Cannot create bridge input device";
321 return base::ScopedFD();
322 }
323
324 // setup write end as non blocking
325 int flags = HANDLE_EINTR(fcntl(write_fd.get(), F_GETFL, 0));
326 if (res < 0) {
327 VPLOG(1) << "Cannot get file descriptor flags";
328 return base::ScopedFD();
329 }
330
331 res = HANDLE_EINTR(fcntl(write_fd.get(), F_SETFL, flags | O_NONBLOCK));
332 if (res < 0) {
333 VPLOG(1) << "Cannot set file descriptor flags";
334 return base::ScopedFD();
335 }
336 return std::move(write_fd);
337 }
338
339 scoped_ptr<ArcInputBridge> ArcInputBridge::Create(
340 ArcBridgeService* arc_bridge_service) {
341 return make_scoped_ptr(new ArcInputBridgeImpl(arc_bridge_service));
342 }
343
344 } // namespace arc
OLDNEW
« components/arc/input/arc_input_bridge.h ('K') | « components/arc/input/arc_input_bridge_impl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698