input_injector_chromeos.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright 2014 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_chromeos.h"
  5. #include <memory>
  6. #include <set>
  7. #include <string>
  8. #include <utility>
  9. #include "ash/display/window_tree_host_manager.h"
  10. #include "ash/shell.h"
  11. #include "base/bind.h"
  12. #include "base/callback_helpers.h"
  13. #include "base/i18n/icu_string_conversions.h"
  14. #include "base/location.h"
  15. #include "base/strings/utf_string_conversions.h"
  16. #include "base/system/sys_info.h"
  17. #include "remoting/host/chromeos/point_transformer.h"
  18. #include "remoting/host/clipboard.h"
  19. #include "remoting/proto/internal.pb.h"
  20. #include "ui/aura/client/cursor_client.h"
  21. #include "ui/aura/window.h"
  22. #include "ui/aura/window_tree_host.h"
  23. #include "ui/base/ime/ash/ime_keyboard.h"
  24. #include "ui/base/ime/ash/input_method_manager.h"
  25. #include "ui/base/ime/input_method.h"
  26. #include "ui/base/ime/text_input_client.h"
  27. #include "ui/events/keycodes/dom/dom_code.h"
  28. #include "ui/events/keycodes/dom/keycode_converter.h"
  29. #include "ui/ozone/public/ozone_platform.h"
  30. #include "ui/ozone/public/system_input_injector.h"
  31. namespace remoting {
  32. using protocol::ClipboardEvent;
  33. using protocol::KeyEvent;
  34. using protocol::MouseEvent;
  35. using protocol::TextEvent;
  36. using protocol::TouchEvent;
  37. namespace {
  38. ui::EventFlags MouseButtonToUIFlags(MouseEvent::MouseButton button) {
  39. switch (button) {
  40. case MouseEvent::BUTTON_LEFT:
  41. return ui::EF_LEFT_MOUSE_BUTTON;
  42. case MouseEvent::BUTTON_RIGHT:
  43. return ui::EF_RIGHT_MOUSE_BUTTON;
  44. case MouseEvent::BUTTON_MIDDLE:
  45. return ui::EF_MIDDLE_MOUSE_BUTTON;
  46. default:
  47. NOTREACHED();
  48. return ui::EF_NONE;
  49. }
  50. }
  51. // Check if the given key could be mapped to caps lock
  52. bool IsLockKey(ui::DomCode dom_code) {
  53. switch (dom_code) {
  54. // Ignores all the keys that could possibly be mapped to Caps Lock in event
  55. // rewriter. Please refer to ui::EventRewriterChromeOS::RewriteModifierKeys.
  56. case ui::DomCode::F16:
  57. case ui::DomCode::CAPS_LOCK:
  58. case ui::DomCode::META_LEFT:
  59. case ui::DomCode::META_RIGHT:
  60. case ui::DomCode::CONTROL_LEFT:
  61. case ui::DomCode::CONTROL_RIGHT:
  62. case ui::DomCode::ALT_LEFT:
  63. case ui::DomCode::ALT_RIGHT:
  64. case ui::DomCode::ESCAPE:
  65. case ui::DomCode::BACKSPACE:
  66. return true;
  67. default:
  68. return false;
  69. }
  70. }
  71. // If caps_lock is specified, sets local keyboard state to match.
  72. void SetCapsLockState(bool caps_lock) {
  73. auto* ime = ash::input_method::InputMethodManager::Get();
  74. ime->GetImeKeyboard()->SetCapsLockEnabled(caps_lock);
  75. }
  76. class SystemInputInjectorStub : public ui::SystemInputInjector {
  77. public:
  78. SystemInputInjectorStub() {
  79. LOG(WARNING)
  80. << "Using stubbed input injector; All CRD user input will be ignored.";
  81. }
  82. SystemInputInjectorStub(const SystemInputInjectorStub&) = delete;
  83. SystemInputInjectorStub& operator=(const SystemInputInjectorStub&) = delete;
  84. ~SystemInputInjectorStub() override = default;
  85. // SystemInputInjector implementation:
  86. void SetDeviceId(int device_id) override {}
  87. void MoveCursorTo(const gfx::PointF& location) override {}
  88. void InjectMouseButton(ui::EventFlags button, bool down) override {}
  89. void InjectMouseWheel(int delta_x, int delta_y) override {}
  90. void InjectKeyEvent(ui::DomCode physical_key,
  91. bool down,
  92. bool suppress_auto_repeat) override {}
  93. };
  94. } // namespace
  95. // This class is run exclusively on the UI thread of the browser process.
  96. class InputInjectorChromeos::Core {
  97. public:
  98. Core();
  99. Core(const Core&) = delete;
  100. Core& operator=(const Core&) = delete;
  101. ~Core();
  102. // Mirrors the public InputInjectorChromeos interface.
  103. void InjectClipboardEvent(const ClipboardEvent& event);
  104. void InjectKeyEvent(const KeyEvent& event);
  105. void InjectTextEvent(const TextEvent& event);
  106. void InjectMouseEvent(const MouseEvent& event);
  107. void Start(std::unique_ptr<protocol::ClipboardStub> client_clipboard);
  108. private:
  109. void SetLockStates(uint32_t states);
  110. void InjectMouseMove(const MouseEvent& event);
  111. bool hide_cursor_on_disconnect_ = false;
  112. std::unique_ptr<ui::SystemInputInjector> delegate_;
  113. std::unique_ptr<Clipboard> clipboard_;
  114. };
  115. InputInjectorChromeos::Core::Core() = default;
  116. InputInjectorChromeos::Core::~Core() {
  117. if (hide_cursor_on_disconnect_) {
  118. aura::client::CursorClient* cursor_client =
  119. aura::client::GetCursorClient(ash::Shell::GetPrimaryRootWindow());
  120. if (cursor_client) {
  121. cursor_client->HideCursor();
  122. }
  123. }
  124. }
  125. void InputInjectorChromeos::Core::InjectClipboardEvent(
  126. const ClipboardEvent& event) {
  127. clipboard_->InjectClipboardEvent(event);
  128. }
  129. void InputInjectorChromeos::Core::InjectKeyEvent(const KeyEvent& event) {
  130. DCHECK(event.has_pressed());
  131. DCHECK(event.has_usb_keycode());
  132. ui::DomCode dom_code =
  133. ui::KeycodeConverter::UsbKeycodeToDomCode(event.usb_keycode());
  134. if (event.pressed() && !IsLockKey(dom_code)) {
  135. if (event.has_caps_lock_state()) {
  136. SetCapsLockState(event.caps_lock_state());
  137. } else if (event.has_lock_states()) {
  138. SetCapsLockState((event.lock_states() &
  139. protocol::KeyEvent::LOCK_STATES_CAPSLOCK) != 0);
  140. }
  141. }
  142. // Ignore events which can't be mapped.
  143. if (dom_code != ui::DomCode::NONE) {
  144. delegate_->InjectKeyEvent(dom_code, event.pressed(),
  145. true /* suppress_auto_repeat */);
  146. }
  147. }
  148. void InputInjectorChromeos::Core::InjectTextEvent(const TextEvent& event) {
  149. DCHECK(event.has_text());
  150. aura::Window* root_window = ash::Shell::GetPrimaryRootWindow();
  151. if (!root_window) {
  152. LOG(ERROR) << "root_window is null, can't inject text.";
  153. return;
  154. }
  155. aura::WindowTreeHost* window_tree_host = root_window->GetHost();
  156. if (!window_tree_host) {
  157. LOG(ERROR) << "window_tree_host is null, can't inject text.";
  158. return;
  159. }
  160. ui::InputMethod* input_method = window_tree_host->GetInputMethod();
  161. if (!input_method) {
  162. LOG(ERROR) << "input_method is null, can't inject text.";
  163. return;
  164. }
  165. ui::TextInputClient* text_input_client = input_method->GetTextInputClient();
  166. if (!text_input_client) {
  167. LOG(ERROR) << "text_input_client is null, can't inject text.";
  168. return;
  169. }
  170. std::string normalized_str;
  171. base::ConvertToUtf8AndNormalize(event.text(), base::kCodepageUTF8,
  172. &normalized_str);
  173. std::u16string utf16_string = base::UTF8ToUTF16(normalized_str);
  174. text_input_client->InsertText(
  175. utf16_string,
  176. ui::TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText);
  177. }
  178. void InputInjectorChromeos::Core::InjectMouseEvent(const MouseEvent& event) {
  179. if (event.has_button() && event.has_button_down()) {
  180. delegate_->InjectMouseButton(MouseButtonToUIFlags(event.button()),
  181. event.button_down());
  182. } else if (event.has_wheel_delta_y() || event.has_wheel_delta_x()) {
  183. delegate_->InjectMouseWheel(event.wheel_delta_x(), event.wheel_delta_y());
  184. } else {
  185. InjectMouseMove(event);
  186. }
  187. }
  188. void InputInjectorChromeos::Core::InjectMouseMove(const MouseEvent& event) {
  189. DCHECK(event.has_x() && event.has_y());
  190. gfx::PointF location_in_screen_in_dip = gfx::PointF(event.x(), event.y());
  191. gfx::PointF location_in_screen_in_pixels =
  192. PointTransformer::ConvertScreenInDipToScreenInPixel(
  193. location_in_screen_in_dip);
  194. delegate_->MoveCursorTo(location_in_screen_in_pixels);
  195. }
  196. void InputInjectorChromeos::Core::Start(
  197. std::unique_ptr<protocol::ClipboardStub> client_clipboard) {
  198. delegate_ = ui::OzonePlatform::GetInstance()->CreateSystemInputInjector();
  199. if (!delegate_ && !base::SysInfo::IsRunningOnChromeOS()) {
  200. // This happens when directly running the Chrome binary on linux.
  201. // We'll simply ignore all input there (instead of crashing).
  202. // Note: it would be nicer to swap this out with input_injector_x11.cc
  203. // on linux instead (and properly handle the input), but that runs into
  204. // dependency issues.
  205. delegate_ = std::make_unique<SystemInputInjectorStub>();
  206. }
  207. DCHECK(delegate_);
  208. delegate_->SetDeviceId(ui::ED_REMOTE_INPUT_DEVICE);
  209. // Implemented by remoting::ClipboardAura.
  210. clipboard_ = Clipboard::Create();
  211. clipboard_->Start(std::move(client_clipboard));
  212. // If the cursor was hidden before we start injecting input then we should try
  213. // to restore its state when the remote user disconnects. The main scenario
  214. // where this is important is for devices in non-interactive Kiosk mode.
  215. // Since no one is interacting with the screen in this mode, we will leave a
  216. // visible cursor after disconnecting which can't be hidden w/o restarting.
  217. aura::client::CursorClient* cursor_client =
  218. aura::client::GetCursorClient(ash::Shell::GetPrimaryRootWindow());
  219. if (cursor_client) {
  220. hide_cursor_on_disconnect_ = !cursor_client->IsCursorVisible();
  221. }
  222. }
  223. InputInjectorChromeos::InputInjectorChromeos(
  224. scoped_refptr<base::SingleThreadTaskRunner> task_runner)
  225. : input_task_runner_(task_runner), core_(std::make_unique<Core>()) {}
  226. InputInjectorChromeos::~InputInjectorChromeos() {
  227. input_task_runner_->DeleteSoon(FROM_HERE, core_.release());
  228. }
  229. void InputInjectorChromeos::InjectClipboardEvent(const ClipboardEvent& event) {
  230. input_task_runner_->PostTask(
  231. FROM_HERE, base::BindOnce(&Core::InjectClipboardEvent,
  232. base::Unretained(core_.get()), event));
  233. }
  234. void InputInjectorChromeos::InjectKeyEvent(const KeyEvent& event) {
  235. input_task_runner_->PostTask(
  236. FROM_HERE, base::BindOnce(&Core::InjectKeyEvent,
  237. base::Unretained(core_.get()), event));
  238. }
  239. void InputInjectorChromeos::InjectTextEvent(const TextEvent& event) {
  240. input_task_runner_->PostTask(
  241. FROM_HERE, base::BindOnce(&Core::InjectTextEvent,
  242. base::Unretained(core_.get()), event));
  243. }
  244. void InputInjectorChromeos::InjectMouseEvent(const MouseEvent& event) {
  245. input_task_runner_->PostTask(
  246. FROM_HERE, base::BindOnce(&Core::InjectMouseEvent,
  247. base::Unretained(core_.get()), event));
  248. }
  249. void InputInjectorChromeos::InjectTouchEvent(const TouchEvent& event) {
  250. NOTIMPLEMENTED() << "Raw touch event injection not implemented for ChromeOS.";
  251. }
  252. void InputInjectorChromeos::Start(
  253. std::unique_ptr<protocol::ClipboardStub> client_clipboard) {
  254. input_task_runner_->PostTask(
  255. FROM_HERE, base::BindOnce(&Core::Start, base::Unretained(core_.get()),
  256. std::move(client_clipboard)));
  257. }
  258. // static
  259. std::unique_ptr<InputInjector> InputInjector::Create(
  260. scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
  261. scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
  262. // The Ozone input injector must be called on the UI task runner of the
  263. // browser process.
  264. return std::make_unique<InputInjectorChromeos>(ui_task_runner);
  265. }
  266. // static
  267. bool InputInjector::SupportsTouchEvents() {
  268. return false;
  269. }
  270. } // namespace remoting