input_injector_x11.cc 25 KB

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