1 // Copyright (c) 2012 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 "remoting/host/input_injector.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <memory>
10 #include <set>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/compiler_specific.h"
15 #include "base/location.h"
16 #include "base/macros.h"
17 #include "base/optional.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/strings/utf_string_conversion_utils.h"
20 #include "base/time/time.h"
21 #include "build/build_config.h"
22 #include "remoting/base/logging.h"
23 #include "remoting/host/clipboard.h"
24 #include "remoting/host/linux/unicode_to_keysym.h"
25 #include "remoting/host/linux/x11_character_injector.h"
26 #include "remoting/host/linux/x11_keyboard_impl.h"
27 #include "remoting/host/linux/x11_util.h"
28 #include "remoting/proto/internal.pb.h"
29 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
30 #include "ui/events/keycodes/dom/dom_code.h"
31 #include "ui/events/keycodes/dom/keycode_converter.h"
32 #include "ui/gfx/x/keysyms/keysyms.h"
33 #include "ui/gfx/x/xinput.h"
34 #include "ui/gfx/x/xkb.h"
35 #include "ui/gfx/x/xproto.h"
36 #include "ui/gfx/x/xtest.h"
37 
38 #if defined(OS_CHROMEOS)
39 #include "remoting/host/chromeos/point_transformer.h"
40 #endif
41 
42 namespace remoting {
43 
44 namespace {
45 
46 using protocol::ClipboardEvent;
47 using protocol::KeyEvent;
48 using protocol::MouseEvent;
49 using protocol::TextEvent;
50 using protocol::TouchEvent;
51 
52 enum class ScrollDirection {
53   DOWN = -1,
54   UP = 1,
55   NONE = 0,
56 };
57 
WheelDeltaToScrollDirection(float num)58 ScrollDirection WheelDeltaToScrollDirection(float num) {
59   return (num > 0)   ? ScrollDirection::UP
60          : (num < 0) ? ScrollDirection::DOWN
61                      : ScrollDirection::NONE;
62 }
63 
IsDomModifierKey(ui::DomCode dom_code)64 bool IsDomModifierKey(ui::DomCode dom_code) {
65   return dom_code == ui::DomCode::CONTROL_LEFT ||
66          dom_code == ui::DomCode::SHIFT_LEFT ||
67          dom_code == ui::DomCode::ALT_LEFT ||
68          dom_code == ui::DomCode::META_LEFT ||
69          dom_code == ui::DomCode::CONTROL_RIGHT ||
70          dom_code == ui::DomCode::SHIFT_RIGHT ||
71          dom_code == ui::DomCode::ALT_RIGHT ||
72          dom_code == ui::DomCode::META_RIGHT;
73 }
74 
75 // Pixel-to-wheel-ticks conversion ratio used by GTK.
76 // From third_party/WebKit/Source/web/gtk/WebInputEventFactory.cpp .
77 const float kWheelTicksPerPixel = 3.0f / 160.0f;
78 
79 // When the user is scrolling, generate at least one tick per time period.
80 const base::TimeDelta kContinuousScrollTimeout =
81     base::TimeDelta::FromMilliseconds(500);
82 
83 // A class to generate events on X11.
84 class InputInjectorX11 : public InputInjector {
85  public:
86   explicit InputInjectorX11(
87       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
88   ~InputInjectorX11() override;
89 
90   bool Init();
91 
92   // Clipboard stub interface.
93   void InjectClipboardEvent(const ClipboardEvent& event) override;
94 
95   // InputStub interface.
96   void InjectKeyEvent(const KeyEvent& event) override;
97   void InjectTextEvent(const TextEvent& event) override;
98   void InjectMouseEvent(const MouseEvent& event) override;
99   void InjectTouchEvent(const TouchEvent& event) override;
100 
101   // InputInjector interface.
102   void Start(
103       std::unique_ptr<protocol::ClipboardStub> client_clipboard) override;
104 
105  private:
106   // The actual implementation resides in InputInjectorX11::Core class.
107   class Core : public base::RefCountedThreadSafe<Core> {
108    public:
109     explicit Core(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
110 
111     bool Init();
112 
113     // Mirrors the ClipboardStub interface.
114     void InjectClipboardEvent(const ClipboardEvent& event);
115 
116     // Mirrors the InputStub interface.
117     void InjectKeyEvent(const KeyEvent& event);
118     void InjectTextEvent(const TextEvent& event);
119     void InjectMouseEvent(const MouseEvent& event);
120 
121     // Mirrors the InputInjector interface.
122     void Start(std::unique_ptr<protocol::ClipboardStub> client_clipboard);
123 
124     void Stop();
125 
126    private:
127     friend class base::RefCountedThreadSafe<Core>;
128     virtual ~Core();
129 
130     void InitClipboard();
131 
132     // Queries whether keyboard auto-repeat is globally enabled. This is used
133     // to decide whether to temporarily disable then restore this setting. If
134     // auto-repeat has already been disabled, this class should leave it
135     // untouched.
136     bool IsAutoRepeatEnabled();
137 
138     // Enables or disables keyboard auto-repeat globally.
139     void SetAutoRepeatEnabled(bool enabled);
140 
141     // Check if the given scan code is caps lock or num lock.
142     bool IsLockKey(x11::KeyCode keycode);
143 
144     // Sets the keyboard lock states to those provided.
145     void SetLockStates(base::Optional<bool> caps_lock,
146                        base::Optional<bool> num_lock);
147 
148     void InjectScrollWheelClicks(int button, int count);
149     // Compensates for global button mappings and resets the XTest device
150     // mapping.
151     void InitMouseButtonMap();
152     int MouseButtonToX11ButtonNumber(MouseEvent::MouseButton button);
153     int HorizontalScrollWheelToX11ButtonNumber(int dx);
154     int VerticalScrollWheelToX11ButtonNumber(int dy);
155 
156     scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
157 
158     std::set<int> pressed_keys_;
159     webrtc::DesktopVector latest_mouse_position_ =
160         webrtc::DesktopVector(-1, -1);
161     float wheel_ticks_x_ = 0;
162     float wheel_ticks_y_ = 0;
163     base::Time latest_tick_y_event_;
164     // The direction of the last scroll event that resulted in at least one
165     // "tick" being injected.
166     ScrollDirection latest_tick_y_direction_ = ScrollDirection::NONE;
167 
168     // X11 graphics context.
169     x11::Connection connection_;
170 
171     // Number of buttons we support.
172     // Left, Right, Middle, VScroll Up/Down, HScroll Left/Right, back, forward.
173     static const int kNumPointerButtons = 9;
174 
175     int pointer_button_map_[kNumPointerButtons];
176 
177 #if defined(OS_CHROMEOS)
178     PointTransformer point_transformer_;
179 #endif
180 
181     std::unique_ptr<Clipboard> clipboard_;
182 
183     std::unique_ptr<X11CharacterInjector> character_injector_;
184 
185     bool saved_auto_repeat_enabled_ = false;
186 
187     DISALLOW_COPY_AND_ASSIGN(Core);
188   };
189 
190   scoped_refptr<Core> core_;
191 
192   DISALLOW_COPY_AND_ASSIGN(InputInjectorX11);
193 };
194 
InputInjectorX11(scoped_refptr<base::SingleThreadTaskRunner> task_runner)195 InputInjectorX11::InputInjectorX11(
196     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
197   core_ = new Core(task_runner);
198 }
199 
~InputInjectorX11()200 InputInjectorX11::~InputInjectorX11() {
201   core_->Stop();
202 }
203 
Init()204 bool InputInjectorX11::Init() {
205   return core_->Init();
206 }
207 
InjectClipboardEvent(const ClipboardEvent & event)208 void InputInjectorX11::InjectClipboardEvent(const ClipboardEvent& event) {
209   core_->InjectClipboardEvent(event);
210 }
211 
InjectKeyEvent(const KeyEvent & event)212 void InputInjectorX11::InjectKeyEvent(const KeyEvent& event) {
213   core_->InjectKeyEvent(event);
214 }
215 
InjectTextEvent(const TextEvent & event)216 void InputInjectorX11::InjectTextEvent(const TextEvent& event) {
217   core_->InjectTextEvent(event);
218 }
219 
InjectMouseEvent(const MouseEvent & event)220 void InputInjectorX11::InjectMouseEvent(const MouseEvent& event) {
221   core_->InjectMouseEvent(event);
222 }
223 
InjectTouchEvent(const TouchEvent & event)224 void InputInjectorX11::InjectTouchEvent(const TouchEvent& event) {
225   NOTIMPLEMENTED() << "Raw touch event injection not implemented for X11.";
226 }
227 
Start(std::unique_ptr<protocol::ClipboardStub> client_clipboard)228 void InputInjectorX11::Start(
229     std::unique_ptr<protocol::ClipboardStub> client_clipboard) {
230   core_->Start(std::move(client_clipboard));
231 }
232 
Core(scoped_refptr<base::SingleThreadTaskRunner> task_runner)233 InputInjectorX11::Core::Core(
234     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
235     : task_runner_(task_runner) {}
236 
Init()237 bool InputInjectorX11::Core::Init() {
238   CHECK(connection_.Ready());
239 
240   if (!task_runner_->BelongsToCurrentThread())
241     task_runner_->PostTask(FROM_HERE,
242                            base::BindOnce(&Core::InitClipboard, this));
243 
244   if (!IgnoreXServerGrabs(&connection_, true)) {
245     LOG(ERROR) << "Server does not support XTest.";
246     return false;
247   }
248   InitMouseButtonMap();
249   return true;
250 }
251 
InjectClipboardEvent(const ClipboardEvent & event)252 void InputInjectorX11::Core::InjectClipboardEvent(const ClipboardEvent& event) {
253   if (!task_runner_->BelongsToCurrentThread()) {
254     task_runner_->PostTask(
255         FROM_HERE, base::BindOnce(&Core::InjectClipboardEvent, this, event));
256     return;
257   }
258 
259   // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
260   clipboard_->InjectClipboardEvent(event);
261 }
262 
InjectKeyEvent(const KeyEvent & event)263 void InputInjectorX11::Core::InjectKeyEvent(const KeyEvent& event) {
264   // HostEventDispatcher should filter events missing the pressed field.
265   if (!event.has_pressed() || !event.has_usb_keycode())
266     return;
267 
268   if (!task_runner_->BelongsToCurrentThread()) {
269     task_runner_->PostTask(FROM_HERE,
270                            base::BindOnce(&Core::InjectKeyEvent, this, event));
271     return;
272   }
273 
274   int keycode =
275       ui::KeycodeConverter::UsbKeycodeToNativeKeycode(event.usb_keycode());
276 
277   VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
278           << " to keycode: " << keycode << std::dec;
279 
280   // Ignore events which can't be mapped.
281   if (keycode == ui::KeycodeConverter::InvalidNativeKeycode())
282     return;
283 
284   if (event.pressed()) {
285     if (pressed_keys_.find(keycode) != pressed_keys_.end()) {
286       // Ignore repeats for modifier keys.
287       if (IsDomModifierKey(static_cast<ui::DomCode>(event.usb_keycode())))
288         return;
289       // Key is already held down, so lift the key up to ensure this repeated
290       // press takes effect.
291       connection_.xtest().FakeInput({x11::KeyEvent::Release, keycode});
292     }
293 
294     if (!IsLockKey(static_cast<x11::KeyCode>(keycode))) {
295       base::Optional<bool> caps_lock;
296       base::Optional<bool> num_lock;
297 
298       // For caps lock, check both the new caps_lock field and the old
299       // lock_states field.
300       if (event.has_caps_lock_state()) {
301         caps_lock = event.caps_lock_state();
302       } else if (event.has_lock_states()) {
303         caps_lock = (event.lock_states() &
304                      protocol::KeyEvent::LOCK_STATES_CAPSLOCK) != 0;
305       }
306 
307       // Not all clients have a concept of num lock. Since there's no way to
308       // distinguish these clients using the legacy lock_states field, only
309       // update if the new num_lock field is specified.
310       if (event.has_num_lock_state()) {
311         num_lock = event.num_lock_state();
312       }
313 
314       SetLockStates(caps_lock, num_lock);
315     }
316 
317     // We turn autorepeat off when we initially connect, but in can get
318     // re-enabled when, e.g., the desktop environment reapplies its settings.
319     if (IsAutoRepeatEnabled()) {
320       SetAutoRepeatEnabled(false);
321     }
322 
323     pressed_keys_.insert(keycode);
324   } else {
325     pressed_keys_.erase(keycode);
326   }
327 
328   auto opcode = event.pressed() ? x11::KeyEvent::Press : x11::KeyEvent::Release;
329   connection_.xtest().FakeInput({opcode, keycode});
330   connection_.Flush();
331 }
332 
InjectTextEvent(const TextEvent & event)333 void InputInjectorX11::Core::InjectTextEvent(const TextEvent& event) {
334   if (!task_runner_->BelongsToCurrentThread()) {
335     task_runner_->PostTask(FROM_HERE,
336                            base::BindOnce(&Core::InjectTextEvent, this, event));
337     return;
338   }
339 
340   // Release all keys before injecting text event. This is necessary to avoid
341   // any interference with the currently pressed keys. E.g. if Shift is pressed
342   // when TextEvent is received.
343   for (int key : pressed_keys_)
344     connection_.xtest().FakeInput({x11::KeyEvent::Release, key});
345   pressed_keys_.clear();
346 
347   const std::string text = event.text();
348   for (int32_t index = 0; index < static_cast<int32_t>(text.size()); ++index) {
349     uint32_t code_point;
350     if (!base::ReadUnicodeCharacter(text.c_str(), text.size(), &index,
351                                     &code_point)) {
352       continue;
353     }
354     character_injector_->Inject(code_point);
355   }
356 }
357 
~Core()358 InputInjectorX11::Core::~Core() {
359   CHECK(pressed_keys_.empty());
360 }
361 
InitClipboard()362 void InputInjectorX11::Core::InitClipboard() {
363   DCHECK(task_runner_->BelongsToCurrentThread());
364   clipboard_ = Clipboard::Create();
365 }
366 
IsAutoRepeatEnabled()367 bool InputInjectorX11::Core::IsAutoRepeatEnabled() {
368   if (auto reply = connection_.GetKeyboardControl({}).Sync())
369     return reply->global_auto_repeat == x11::AutoRepeatMode::On;
370   LOG(ERROR) << "Failed to get keyboard auto-repeat status, assuming ON.";
371   return true;
372 }
373 
SetAutoRepeatEnabled(bool mode)374 void InputInjectorX11::Core::SetAutoRepeatEnabled(bool mode) {
375   connection_.ChangeKeyboardControl(
376       {.auto_repeat_mode =
377            mode ? x11::AutoRepeatMode::On : x11::AutoRepeatMode::Off});
378   connection_.Flush();
379 }
380 
IsLockKey(x11::KeyCode keycode)381 bool InputInjectorX11::Core::IsLockKey(x11::KeyCode keycode) {
382   auto state = connection_.xkb().GetState({}).Sync();
383   if (!state)
384     return false;
385   auto mods = state->baseMods | state->latchedMods | state->lockedMods;
386   auto keysym =
387       connection_.KeycodeToKeysym(keycode, static_cast<unsigned>(mods));
388   if (state && keysym)
389     return keysym == XK_Caps_Lock || keysym == XK_Num_Lock;
390   else
391     return false;
392 }
393 
SetLockStates(base::Optional<bool> caps_lock,base::Optional<bool> num_lock)394 void InputInjectorX11::Core::SetLockStates(base::Optional<bool> caps_lock,
395                                            base::Optional<bool> num_lock) {
396   // The lock bits associated with each lock key.
397   auto caps_lock_mask = static_cast<unsigned int>(x11::ModMask::Lock);
398   auto num_lock_mask = static_cast<unsigned int>(x11::ModMask::c_2);
399 
400   unsigned int update_mask = 0;  // The lock bits we want to update
401   unsigned int lock_values = 0;  // The value of those bits
402 
403   if (caps_lock) {
404     update_mask |= caps_lock_mask;
405     if (*caps_lock) {
406       lock_values |= caps_lock_mask;
407     }
408   }
409 
410   if (num_lock) {
411     update_mask |= num_lock_mask;
412     if (*num_lock) {
413       lock_values |= num_lock_mask;
414     }
415   }
416 
417   if (update_mask) {
418     connection_.xkb().LatchLockState(
419         {static_cast<x11::Xkb::DeviceSpec>(x11::Xkb::Id::UseCoreKbd),
420          static_cast<x11::ModMask>(update_mask),
421          static_cast<x11::ModMask>(lock_values)});
422   }
423 }
424 
InjectScrollWheelClicks(int button,int count)425 void InputInjectorX11::Core::InjectScrollWheelClicks(int button, int count) {
426   if (button < 0) {
427     LOG(WARNING) << "Ignoring unmapped scroll wheel button";
428     return;
429   }
430   for (int i = 0; i < count; i++) {
431     // Generate a button-down and a button-up to simulate a wheel click.
432     connection_.xtest().FakeInput({x11::ButtonEvent::Press, button});
433     connection_.xtest().FakeInput({x11::ButtonEvent::Release, button});
434   }
435 }
436 
InjectMouseEvent(const MouseEvent & event)437 void InputInjectorX11::Core::InjectMouseEvent(const MouseEvent& event) {
438   if (!task_runner_->BelongsToCurrentThread()) {
439     task_runner_->PostTask(
440         FROM_HERE, base::BindOnce(&Core::InjectMouseEvent, this, event));
441     return;
442   }
443 
444   if (event.has_delta_x() && event.has_delta_y() &&
445       (event.delta_x() != 0 || event.delta_y() != 0)) {
446     latest_mouse_position_.set(-1, -1);
447     VLOG(3) << "Moving mouse by " << event.delta_x() << "," << event.delta_y();
448     connection_.xtest().FakeInput({
449         .type = x11::MotionNotifyEvent::opcode,
450         .detail = true,
451         .rootX = event.delta_x(),
452         .rootY = event.delta_y(),
453     });
454   } else if (event.has_x() && event.has_y()) {
455     // Injecting a motion event immediately before a button release results in
456     // a MotionNotify even if the mouse position hasn't changed, which confuses
457     // apps which assume MotionNotify implies movement. See crbug.com/138075.
458     bool inject_motion = true;
459     webrtc::DesktopVector new_mouse_position(event.x(), event.y());
460 #if defined(OS_CHROMEOS)
461     // Interim hack to handle display rotation on Chrome OS.
462     // TODO(kelvin): Remove this when Chrome OS has completely migrated to
463     // Ozone (crbug.com/439287).
464     gfx::PointF screen_location = point_transformer_.ToScreenCoordinates(
465         gfx::PointF(event.x(), event.y()));
466     new_mouse_position.set(screen_location.x(), screen_location.y());
467 #endif
468     if (event.has_button() && event.has_button_down() && !event.button_down()) {
469       if (new_mouse_position.equals(latest_mouse_position_))
470         inject_motion = false;
471     }
472 
473     if (inject_motion) {
474       latest_mouse_position_.set(std::max(0, new_mouse_position.x()),
475                                  std::max(0, new_mouse_position.y()));
476 
477       VLOG(3) << "Moving mouse to " << latest_mouse_position_.x() << ","
478               << latest_mouse_position_.y();
479       connection_.xtest().FakeInput({
480           .type = x11::MotionNotifyEvent::opcode,
481           .detail = false,
482           .root = connection_.default_root(),
483           .rootX = latest_mouse_position_.x(),
484           .rootY = latest_mouse_position_.y(),
485       });
486     }
487   }
488 
489   if (event.has_button() && event.has_button_down()) {
490     int button_number = MouseButtonToX11ButtonNumber(event.button());
491 
492     if (button_number < 0) {
493       LOG(WARNING) << "Ignoring unknown button type: " << event.button();
494       return;
495     }
496 
497     VLOG(3) << "Button " << event.button() << " received, sending "
498             << (event.button_down() ? "down " : "up ") << button_number;
499     auto opcode = event.button_down() ? x11::ButtonEvent::Press
500                                       : x11::ButtonEvent::Release;
501     connection_.xtest().FakeInput({opcode, button_number});
502   }
503 
504   // remotedesktop.google.com currently sends scroll events in pixels, which
505   // are accumulated host-side.
506   int ticks_y = 0;
507   if (event.has_wheel_ticks_y()) {
508     ticks_y = event.wheel_ticks_y();
509   } else if (event.has_wheel_delta_y()) {
510     wheel_ticks_y_ += event.wheel_delta_y() * kWheelTicksPerPixel;
511     ticks_y = static_cast<int>(wheel_ticks_y_);
512     wheel_ticks_y_ -= ticks_y;
513   }
514   if (ticks_y == 0 && event.has_wheel_delta_y()) {
515     // For the y-direction only (the common case), try to ensure that a tick is
516     // injected when the user would expect one, regardless of how many pixels
517     // the client sends per tick (even if it accelerates wheel events). To do
518     // this, generate a tick if one has not occurred recently in the current
519     // scroll direction. The accumulated pixels are not reset in this case.
520     //
521     // The effect when a physical mouse is used is as follows:
522     //
523     // Client sends slightly too few pixels per tick (e.g. Linux):
524     // * First scroll in either direction synthesizes a tick.
525     // * Subsequent scrolls in the same direction are unaffected (their
526     //   accumulated pixel deltas mostly meet the threshold for a regular
527     //   tick; occasionally a tick will be dropped if the user is scrolling
528     //   quickly).
529     //
530     // Client sends far too few pixels per tick, but applies acceleration
531     // (e.g. macOs, ChromeOS):
532     // * First scroll in either direction synthesizes a tick.
533     // * Slow scrolling will synthesize a tick a few times per second.
534     // * Fast scrolling is unaffected (acceleration means that enough pixels
535     //   are accumulated naturally).
536     //
537     // Client sends too many pixels per tick (e.g. Windows):
538     // * Scrolling is unaffected (most scroll events generate one tick; every
539     //   so often one generates two ticks).
540     //
541     // The effect when a trackpad is used is that the first tick in either
542     // direction occurs sooner; subsequent ticks are mostly unaffected.
543     const ScrollDirection current_tick_y_direction =
544         WheelDeltaToScrollDirection(event.wheel_delta_y());
545     if ((base::Time::Now() - latest_tick_y_event_ > kContinuousScrollTimeout) ||
546         latest_tick_y_direction_ != current_tick_y_direction) {
547       ticks_y = static_cast<int>(current_tick_y_direction);
548     }
549   }
550   if (ticks_y != 0) {
551     latest_tick_y_direction_ = WheelDeltaToScrollDirection(ticks_y);
552     latest_tick_y_event_ = base::Time::Now();
553     InjectScrollWheelClicks(VerticalScrollWheelToX11ButtonNumber(ticks_y),
554                             abs(ticks_y));
555   }
556 
557   int ticks_x = 0;
558   if (event.has_wheel_ticks_x()) {
559     ticks_x = event.wheel_ticks_x();
560   } else if (event.has_wheel_delta_x()) {
561     wheel_ticks_x_ += event.wheel_delta_x() * kWheelTicksPerPixel;
562     ticks_x = static_cast<int>(wheel_ticks_x_);
563     wheel_ticks_x_ -= ticks_x;
564   }
565   if (ticks_x != 0) {
566     InjectScrollWheelClicks(HorizontalScrollWheelToX11ButtonNumber(ticks_x),
567                             abs(ticks_x));
568   }
569 
570   connection_.Flush();
571 }
572 
InitMouseButtonMap()573 void InputInjectorX11::Core::InitMouseButtonMap() {
574   // TODO(rmsousa): Run this on global/device mapping change events.
575 
576   // Do not touch global pointer mapping, since this may affect the local user.
577   // Instead, try to work around it by reversing the mapping.
578   // Note that if a user has a global mapping that completely disables a button
579   // (by assigning 0 to it), we won't be able to inject it.
580   std::vector<uint8_t> pointer_mapping;
581   if (auto reply = connection_.GetPointerMapping({}).Sync())
582     pointer_mapping = std::move(reply->map);
583   for (int& i : pointer_button_map_)
584     i = -1;
585   for (size_t i = 0; i < pointer_mapping.size(); i++) {
586     // Reverse the mapping.
587     if (pointer_mapping[i] > 0 && pointer_mapping[i] <= kNumPointerButtons)
588       pointer_button_map_[pointer_mapping[i] - 1] = i + 1;
589   }
590   for (int i = 0; i < kNumPointerButtons; i++) {
591     if (pointer_button_map_[i] == -1)
592       LOG(ERROR) << "Global pointer mapping does not support button " << i + 1;
593   }
594 
595   if (!connection_.QueryExtension({"XInputExtension"}).Sync()) {
596     // If XInput is not available, we're done. But it would be very unusual to
597     // have a server that supports XTest but not XInput, so log it as an error.
598     LOG(ERROR) << "X Input extension not available";
599     return;
600   }
601 
602   // Make sure the XTEST XInput pointer device mapping is trivial. It should be
603   // safe to reset this mapping, as it won't affect the user's local devices.
604   // In fact, the reason why we do this is because an old gnome-settings-daemon
605   // may have mistakenly applied left-handed preferences to the XTEST device.
606   uint8_t device_id = 0;
607   bool device_found = false;
608   if (auto devices = connection_.xinput().ListInputDevices({}).Sync()) {
609     for (size_t i = 0; i < devices->devices.size(); i++) {
610       const auto& device_info = devices->devices[i];
611       const std::string& name = devices->names[i].name;
612       if (device_info.device_use ==
613               x11::Input::DeviceUse::IsXExtensionPointer &&
614           name == "Virtual core XTEST pointer") {
615         device_id = device_info.device_id;
616         device_found = true;
617         break;
618       }
619     }
620   }
621 
622   if (!device_found) {
623     HOST_LOG << "Cannot find XTest device.";
624     return;
625   }
626 
627   auto device = connection_.xinput().OpenDevice({device_id}).Sync();
628   if (!device) {
629     LOG(ERROR) << "Cannot open XTest device.";
630     return;
631   }
632 
633   if (auto mapping =
634           connection_.xinput().GetDeviceButtonMapping({device_id}).Sync()) {
635     size_t num_device_buttons = mapping->map.size();
636     std::vector<uint8_t> new_mapping;
637     for (size_t i = 0; i < num_device_buttons; i++)
638       new_mapping.push_back(i + 1);
639     if (!connection_.xinput()
640              .SetDeviceButtonMapping({device_id, new_mapping})
641              .Sync()) {
642       LOG(ERROR) << "Failed to set XTest device button mapping";
643     }
644   }
645 
646   connection_.xinput().CloseDevice({device_id});
647   connection_.Flush();
648 }
649 
MouseButtonToX11ButtonNumber(MouseEvent::MouseButton button)650 int InputInjectorX11::Core::MouseButtonToX11ButtonNumber(
651     MouseEvent::MouseButton button) {
652   switch (button) {
653     case MouseEvent::BUTTON_LEFT:
654       return pointer_button_map_[0];
655     case MouseEvent::BUTTON_RIGHT:
656       return pointer_button_map_[2];
657     case MouseEvent::BUTTON_MIDDLE:
658       return pointer_button_map_[1];
659     case MouseEvent::BUTTON_BACK:
660       return pointer_button_map_[7];
661     case MouseEvent::BUTTON_FORWARD:
662       return pointer_button_map_[8];
663     case MouseEvent::BUTTON_UNDEFINED:
664     default:
665       return -1;
666   }
667 }
668 
HorizontalScrollWheelToX11ButtonNumber(int dx)669 int InputInjectorX11::Core::HorizontalScrollWheelToX11ButtonNumber(int dx) {
670   return (dx > 0 ? pointer_button_map_[5] : pointer_button_map_[6]);
671 }
672 
VerticalScrollWheelToX11ButtonNumber(int dy)673 int InputInjectorX11::Core::VerticalScrollWheelToX11ButtonNumber(int dy) {
674   // Positive y-values are wheel scroll-up events (button 4), negative y-values
675   // are wheel scroll-down events (button 5).
676   return (dy > 0 ? pointer_button_map_[3] : pointer_button_map_[4]);
677 }
678 
Start(std::unique_ptr<protocol::ClipboardStub> client_clipboard)679 void InputInjectorX11::Core::Start(
680     std::unique_ptr<protocol::ClipboardStub> client_clipboard) {
681   if (!task_runner_->BelongsToCurrentThread()) {
682     task_runner_->PostTask(
683         FROM_HERE,
684         base::BindOnce(&Core::Start, this, std::move(client_clipboard)));
685     return;
686   }
687 
688   InitMouseButtonMap();
689 
690   clipboard_->Start(std::move(client_clipboard));
691 
692   character_injector_ = std::make_unique<X11CharacterInjector>(
693       std::make_unique<X11KeyboardImpl>(&connection_));
694 
695   // Disable auto-repeat, if necessary, to avoid triggering auto-repeat
696   // if network congestion delays the key-up event from the client. This is
697   // done for the duration of the session because some window managers do
698   // not handle changes to this setting efficiently.
699   saved_auto_repeat_enabled_ = IsAutoRepeatEnabled();
700   if (saved_auto_repeat_enabled_)
701     SetAutoRepeatEnabled(false);
702 }
703 
Stop()704 void InputInjectorX11::Core::Stop() {
705   if (!task_runner_->BelongsToCurrentThread()) {
706     task_runner_->PostTask(FROM_HERE, base::BindOnce(&Core::Stop, this));
707     return;
708   }
709 
710   clipboard_.reset();
711   character_injector_.reset();
712   // Re-enable auto-repeat, if necessary, on disconnect.
713   if (saved_auto_repeat_enabled_)
714     SetAutoRepeatEnabled(true);
715 }
716 
717 }  // namespace
718 
719 // static
Create(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)720 std::unique_ptr<InputInjector> InputInjector::Create(
721     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
722     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
723   std::unique_ptr<InputInjectorX11> injector(
724       new InputInjectorX11(main_task_runner));
725   if (!injector->Init())
726     return nullptr;
727   return std::move(injector);
728 }
729 
730 // static
SupportsTouchEvents()731 bool InputInjector::SupportsTouchEvents() {
732   return false;
733 }
734 
735 }  // namespace remoting
736