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.
5 #include "remoting/host/input_injector_chromeos.h"
10 #include "base/bind_helpers.h"
11 #include "base/location.h"
12 #include "remoting/host/chromeos/point_transformer.h"
13 #include "remoting/host/clipboard.h"
14 #include "remoting/proto/internal.pb.h"
15 #include "ui/events/keycodes/dom3/dom_code.h"
16 #include "ui/events/keycodes/dom4/keycode_converter.h"
17 #include "ui/ozone/public/input_controller.h"
18 #include "ui/ozone/public/ozone_platform.h"
19 #include "ui/ozone/public/system_input_injector.h"
23 using protocol::ClipboardEvent
;
24 using protocol::KeyEvent
;
25 using protocol::MouseEvent
;
26 using protocol::TextEvent
;
27 using protocol::TouchEvent
;
31 ui::EventFlags
MouseButtonToUIFlags(MouseEvent::MouseButton button
) {
33 case MouseEvent::BUTTON_LEFT
:
34 return ui::EF_LEFT_MOUSE_BUTTON
;
35 case MouseEvent::BUTTON_RIGHT
:
36 return ui::EF_RIGHT_MOUSE_BUTTON
;
37 case MouseEvent::BUTTON_MIDDLE
:
38 return ui::EF_MIDDLE_MOUSE_BUTTON
;
47 // This class is run exclusively on the UI thread of the browser process.
48 class InputInjectorChromeos::Core
{
52 // Mirrors the public InputInjectorChromeos interface.
53 void InjectClipboardEvent(const ClipboardEvent
& event
);
54 void InjectKeyEvent(const KeyEvent
& event
);
55 void InjectTextEvent(const TextEvent
& event
);
56 void InjectMouseEvent(const MouseEvent
& event
);
57 void Start(scoped_ptr
<protocol::ClipboardStub
> client_clipboard
);
60 void HandleAutoRepeat(ui::DomCode dom_code
, bool pressed
);
62 scoped_ptr
<ui::SystemInputInjector
> delegate_
;
63 ui::InputController
* input_controller_
;
64 scoped_ptr
<Clipboard
> clipboard_
;
66 // Used to rotate the input coordinates appropriately based on the current
67 // display rotation settings.
68 scoped_ptr
<PointTransformer
> point_transformer_
;
70 // Used by HandleAutoRepeat().
71 std::set
<ui::DomCode
> pressed_keys_
;
72 bool saved_auto_repeat_enabled_
;
74 DISALLOW_COPY_AND_ASSIGN(Core
);
77 InputInjectorChromeos::Core::Core() : saved_auto_repeat_enabled_(false) {
80 void InputInjectorChromeos::Core::InjectClipboardEvent(
81 const ClipboardEvent
& event
) {
82 clipboard_
->InjectClipboardEvent(event
);
85 void InputInjectorChromeos::Core::InjectKeyEvent(const KeyEvent
& event
) {
86 DCHECK(event
.has_pressed());
87 DCHECK(event
.has_usb_keycode());
89 ui::DomCode dom_code
=
90 ui::KeycodeConverter::UsbKeycodeToDomCode(event
.usb_keycode());
92 // Ignore events which can't be mapped.
93 if (dom_code
!= ui::DomCode::NONE
) {
94 HandleAutoRepeat(dom_code
, event
.pressed());
95 delegate_
->InjectKeyPress(dom_code
, event
.pressed());
99 // Disables auto-repeat as long as keys are pressed to avoid duplicated
100 // key-presses if network congestion delays the key-up event from the client.
101 void InputInjectorChromeos::Core::HandleAutoRepeat(ui::DomCode dom_code
,
104 if (pressed_keys_
.find(dom_code
) != pressed_keys_
.end()) {
105 // Key is already held down, so lift the key up to ensure this repeated
106 // press takes effect.
107 // TODO(kelvinp): Fix this code to inject auto-repeated key presses as
108 // the expected behavior of "down down down ... up" as opposed to current
109 // implementation "down up down ... up".
110 delegate_
->InjectKeyPress(dom_code
, false);
113 if (pressed_keys_
.empty()) {
114 // Disable auto-repeat, if necessary, when any key is pressed.
115 saved_auto_repeat_enabled_
= input_controller_
->IsAutoRepeatEnabled();
116 if (saved_auto_repeat_enabled_
) {
117 input_controller_
->SetAutoRepeatEnabled(false);
120 pressed_keys_
.insert(dom_code
);
122 pressed_keys_
.erase(dom_code
);
123 if (pressed_keys_
.empty()) {
124 // Re-enable auto-repeat, if necessary, when all keys are released.
125 if (saved_auto_repeat_enabled_
) {
126 input_controller_
->SetAutoRepeatEnabled(true);
132 void InputInjectorChromeos::Core::InjectTextEvent(const TextEvent
& event
) {
133 // Chrome OS only supports It2Me, which is not supported on mobile clients, so
134 // we don't need to implement text events.
138 void InputInjectorChromeos::Core::InjectMouseEvent(const MouseEvent
& event
) {
139 if (event
.has_button() && event
.has_button_down()) {
140 delegate_
->InjectMouseButton(MouseButtonToUIFlags(event
.button()),
141 event
.button_down());
142 } else if (event
.has_wheel_delta_y() || event
.has_wheel_delta_x()) {
143 delegate_
->InjectMouseWheel(event
.wheel_delta_x(), event
.wheel_delta_y());
145 DCHECK(event
.has_x() && event
.has_y());
146 delegate_
->MoveCursorTo(point_transformer_
->ToScreenCoordinates(
147 gfx::PointF(event
.x(), event
.y())));
151 void InputInjectorChromeos::Core::Start(
152 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
153 ui::OzonePlatform
* ozone_platform
= ui::OzonePlatform::GetInstance();
154 delegate_
= ozone_platform
->CreateSystemInputInjector();
156 input_controller_
= ozone_platform
->GetInputController();
157 DCHECK(input_controller_
);
159 // Implemented by remoting::ClipboardAura.
160 clipboard_
= Clipboard::Create();
161 clipboard_
->Start(client_clipboard
.Pass());
162 point_transformer_
.reset(new PointTransformer());
165 InputInjectorChromeos::InputInjectorChromeos(
166 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
167 : input_task_runner_(task_runner
) {
168 core_
.reset(new Core());
171 InputInjectorChromeos::~InputInjectorChromeos() {
172 input_task_runner_
->DeleteSoon(FROM_HERE
, core_
.release());
175 void InputInjectorChromeos::InjectClipboardEvent(const ClipboardEvent
& event
) {
176 input_task_runner_
->PostTask(
177 FROM_HERE
, base::Bind(&Core::InjectClipboardEvent
,
178 base::Unretained(core_
.get()), event
));
181 void InputInjectorChromeos::InjectKeyEvent(const KeyEvent
& event
) {
182 input_task_runner_
->PostTask(
184 base::Bind(&Core::InjectKeyEvent
, base::Unretained(core_
.get()), event
));
187 void InputInjectorChromeos::InjectTextEvent(const TextEvent
& event
) {
188 input_task_runner_
->PostTask(
190 base::Bind(&Core::InjectTextEvent
, base::Unretained(core_
.get()), event
));
193 void InputInjectorChromeos::InjectMouseEvent(const MouseEvent
& event
) {
194 input_task_runner_
->PostTask(
195 FROM_HERE
, base::Bind(&Core::InjectMouseEvent
,
196 base::Unretained(core_
.get()), event
));
199 void InputInjectorChromeos::InjectTouchEvent(const TouchEvent
& event
) {
200 NOTIMPLEMENTED() << "Raw touch event injection not implemented for ChromeOS.";
203 void InputInjectorChromeos::Start(
204 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
205 input_task_runner_
->PostTask(
206 FROM_HERE
, base::Bind(&Core::Start
, base::Unretained(core_
.get()),
207 base::Passed(&client_clipboard
)));
211 scoped_ptr
<InputInjector
> InputInjector::Create(
212 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
213 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
214 // The Ozone input injector must be called on the UI task runner of the
216 return make_scoped_ptr(new InputInjectorChromeos(ui_task_runner
));
219 } // namespace remoting