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.
5 #include "remoting/host/win/session_input_injector.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/compiler_specific.h"
13 #include "base/location.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/win/windows_version.h"
16 #include "remoting/host/sas_injector.h"
17 #include "remoting/proto/event.pb.h"
18 #include "remoting/protocol/usb_key_codes.h"
19 #include "third_party/webrtc/modules/desktop_capture/win/desktop.h"
20 #include "third_party/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
24 bool CheckCtrlAndAltArePressed(const std::set
<uint32
>& pressed_keys
) {
25 size_t ctrl_keys
= pressed_keys
.count(kUsbLeftControl
) +
26 pressed_keys
.count(kUsbRightControl
);
27 size_t alt_keys
= pressed_keys
.count(kUsbLeftAlt
) +
28 pressed_keys
.count(kUsbRightAlt
);
29 return ctrl_keys
!= 0 && alt_keys
!= 0 &&
30 (ctrl_keys
+ alt_keys
== pressed_keys
.size());
37 using protocol::ClipboardEvent
;
38 using protocol::KeyEvent
;
39 using protocol::MouseEvent
;
40 using protocol::TextEvent
;
41 using protocol::TouchEvent
;
43 class SessionInputInjectorWin::Core
44 : public base::RefCountedThreadSafe
<SessionInputInjectorWin::Core
>,
45 public InputInjector
{
48 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
49 scoped_ptr
<InputInjector
> nested_executor
,
50 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner
,
51 const base::Closure
& inject_sas
);
53 // InputInjector implementation.
54 void Start(scoped_ptr
<ClipboardStub
> client_clipboard
) override
;
56 // protocol::ClipboardStub implementation.
57 void InjectClipboardEvent(const ClipboardEvent
& event
) override
;
59 // protocol::InputStub implementation.
60 void InjectKeyEvent(const KeyEvent
& event
) override
;
61 void InjectTextEvent(const TextEvent
& event
) override
;
62 void InjectMouseEvent(const MouseEvent
& event
) override
;
63 void InjectTouchEvent(const TouchEvent
& event
) override
;
66 friend class base::RefCountedThreadSafe
<Core
>;
69 // Switches to the desktop receiving a user input if different from
71 void SwitchToInputDesktop();
73 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner_
;
75 // Pointer to the next event executor.
76 scoped_ptr
<InputInjector
> nested_executor_
;
78 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner_
;
80 webrtc::ScopedThreadDesktop desktop_
;
82 // Used to inject Secure Attention Sequence on Vista+.
83 base::Closure inject_sas_
;
85 // Used to inject Secure Attention Sequence on XP.
86 scoped_ptr
<SasInjector
> sas_injector_
;
88 // Keys currently pressed by the client, used to detect Ctrl-Alt-Del.
89 std::set
<uint32
> pressed_keys_
;
91 DISALLOW_COPY_AND_ASSIGN(Core
);
94 SessionInputInjectorWin::Core::Core(
95 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
96 scoped_ptr
<InputInjector
> nested_executor
,
97 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner
,
98 const base::Closure
& inject_sas
)
99 : input_task_runner_(input_task_runner
),
100 nested_executor_(nested_executor
.Pass()),
101 inject_sas_task_runner_(inject_sas_task_runner
),
102 inject_sas_(inject_sas
) {
105 void SessionInputInjectorWin::Core::Start(
106 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
107 if (!input_task_runner_
->BelongsToCurrentThread()) {
108 input_task_runner_
->PostTask(
110 base::Bind(&Core::Start
, this, base::Passed(&client_clipboard
)));
114 nested_executor_
->Start(client_clipboard
.Pass());
117 void SessionInputInjectorWin::Core::InjectClipboardEvent(
118 const ClipboardEvent
& event
) {
119 if (!input_task_runner_
->BelongsToCurrentThread()) {
120 input_task_runner_
->PostTask(
121 FROM_HERE
, base::Bind(&Core::InjectClipboardEvent
, this, event
));
125 nested_executor_
->InjectClipboardEvent(event
);
128 void SessionInputInjectorWin::Core::InjectKeyEvent(const KeyEvent
& event
) {
129 if (!input_task_runner_
->BelongsToCurrentThread()) {
130 input_task_runner_
->PostTask(
131 FROM_HERE
, base::Bind(&Core::InjectKeyEvent
, this, event
));
135 // HostEventDispatcher should drop events lacking the pressed field.
136 DCHECK(event
.has_pressed());
138 if (event
.has_usb_keycode()) {
139 if (event
.pressed()) {
140 // Simulate secure attention sequence if Ctrl-Alt-Del was just pressed.
141 if (event
.usb_keycode() == kUsbDelete
&&
142 CheckCtrlAndAltArePressed(pressed_keys_
)) {
143 VLOG(3) << "Sending Secure Attention Sequence to the session";
145 if (base::win::GetVersion() < base::win::VERSION_VISTA
) {
147 sas_injector_
= SasInjector::Create();
148 if (!sas_injector_
->InjectSas())
149 LOG(ERROR
) << "Failed to inject Secure Attention Sequence.";
151 inject_sas_task_runner_
->PostTask(FROM_HERE
, inject_sas_
);
155 pressed_keys_
.insert(event
.usb_keycode());
157 pressed_keys_
.erase(event
.usb_keycode());
161 SwitchToInputDesktop();
162 nested_executor_
->InjectKeyEvent(event
);
165 void SessionInputInjectorWin::Core::InjectTextEvent(const TextEvent
& event
) {
166 if (!input_task_runner_
->BelongsToCurrentThread()) {
167 input_task_runner_
->PostTask(
168 FROM_HERE
, base::Bind(&Core::InjectTextEvent
, this, event
));
172 SwitchToInputDesktop();
173 nested_executor_
->InjectTextEvent(event
);
176 void SessionInputInjectorWin::Core::InjectMouseEvent(const MouseEvent
& event
) {
177 if (!input_task_runner_
->BelongsToCurrentThread()) {
178 input_task_runner_
->PostTask(
179 FROM_HERE
, base::Bind(&Core::InjectMouseEvent
, this, event
));
183 SwitchToInputDesktop();
184 nested_executor_
->InjectMouseEvent(event
);
187 void SessionInputInjectorWin::Core::InjectTouchEvent(const TouchEvent
& event
) {
188 if (!input_task_runner_
->BelongsToCurrentThread()) {
189 input_task_runner_
->PostTask(
190 FROM_HERE
, base::Bind(&Core::InjectTouchEvent
, this, event
));
194 SwitchToInputDesktop();
195 nested_executor_
->InjectTouchEvent(event
);
198 SessionInputInjectorWin::Core::~Core() {
201 void SessionInputInjectorWin::Core::SwitchToInputDesktop() {
202 // Switch to the desktop receiving user input if different from the current
204 scoped_ptr
<webrtc::Desktop
> input_desktop(
205 webrtc::Desktop::GetInputDesktop());
206 if (input_desktop
.get() != nullptr && !desktop_
.IsSame(*input_desktop
)) {
207 // If SetThreadDesktop() fails, the thread is still assigned a desktop.
208 // So we can continue capture screen bits, just from a diffected desktop.
209 desktop_
.SetThreadDesktop(input_desktop
.release());
213 SessionInputInjectorWin::SessionInputInjectorWin(
214 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
215 scoped_ptr
<InputInjector
> nested_executor
,
216 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner
,
217 const base::Closure
& inject_sas
) {
218 core_
= new Core(input_task_runner
, nested_executor
.Pass(),
219 inject_sas_task_runner
, inject_sas
);
222 SessionInputInjectorWin::~SessionInputInjectorWin() {
225 void SessionInputInjectorWin::Start(
226 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
227 core_
->Start(client_clipboard
.Pass());
230 void SessionInputInjectorWin::InjectClipboardEvent(
231 const protocol::ClipboardEvent
& event
) {
232 core_
->InjectClipboardEvent(event
);
235 void SessionInputInjectorWin::InjectKeyEvent(const protocol::KeyEvent
& event
) {
236 core_
->InjectKeyEvent(event
);
239 void SessionInputInjectorWin::InjectTextEvent(
240 const protocol::TextEvent
& event
) {
241 core_
->InjectTextEvent(event
);
244 void SessionInputInjectorWin::InjectMouseEvent(
245 const protocol::MouseEvent
& event
) {
246 core_
->InjectMouseEvent(event
);
249 void SessionInputInjectorWin::InjectTouchEvent(
250 const protocol::TouchEvent
& event
) {
251 core_
->InjectTouchEvent(event
);
254 } // namespace remoting