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 "third_party/webrtc/modules/desktop_capture/win/desktop.h"
19 #include "third_party/webrtc/modules/desktop_capture/win/scoped_thread_desktop.h"
23 const uint32 kUsbLeftControl
= 0x0700e0;
24 const uint32 kUsbRightControl
= 0x0700e4;
25 const uint32 kUsbLeftAlt
= 0x0700e2;
26 const uint32 kUsbRightAlt
= 0x0700e6;
27 const uint32 kUsbDelete
= 0x07004c;
29 bool CheckCtrlAndAltArePressed(const std::set
<uint32
>& pressed_keys
) {
30 size_t ctrl_keys
= pressed_keys
.count(kUsbLeftControl
) +
31 pressed_keys
.count(kUsbRightControl
);
32 size_t alt_keys
= pressed_keys
.count(kUsbLeftAlt
) +
33 pressed_keys
.count(kUsbRightAlt
);
34 return ctrl_keys
!= 0 && alt_keys
!= 0 &&
35 (ctrl_keys
+ alt_keys
== pressed_keys
.size());
42 using protocol::ClipboardEvent
;
43 using protocol::KeyEvent
;
44 using protocol::MouseEvent
;
45 using protocol::TextEvent
;
46 using protocol::TouchEvent
;
48 class SessionInputInjectorWin::Core
49 : public base::RefCountedThreadSafe
<SessionInputInjectorWin::Core
>,
50 public InputInjector
{
53 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
54 scoped_ptr
<InputInjector
> nested_executor
,
55 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner
,
56 const base::Closure
& inject_sas
);
58 // InputInjector implementation.
59 virtual void Start(scoped_ptr
<ClipboardStub
> client_clipboard
) override
;
61 // protocol::ClipboardStub implementation.
62 virtual void InjectClipboardEvent(const ClipboardEvent
& event
) override
;
64 // protocol::InputStub implementation.
65 virtual void InjectKeyEvent(const KeyEvent
& event
) override
;
66 virtual void InjectTextEvent(const TextEvent
& event
) override
;
67 virtual void InjectMouseEvent(const MouseEvent
& event
) override
;
68 virtual void InjectTouchEvent(const TouchEvent
& event
) override
;
71 friend class base::RefCountedThreadSafe
<Core
>;
74 // Switches to the desktop receiving a user input if different from
76 void SwitchToInputDesktop();
78 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner_
;
80 // Pointer to the next event executor.
81 scoped_ptr
<InputInjector
> nested_executor_
;
83 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner_
;
85 webrtc::ScopedThreadDesktop desktop_
;
87 // Used to inject Secure Attention Sequence on Vista+.
88 base::Closure inject_sas_
;
90 // Used to inject Secure Attention Sequence on XP.
91 scoped_ptr
<SasInjector
> sas_injector_
;
93 // Keys currently pressed by the client, used to detect Ctrl-Alt-Del.
94 std::set
<uint32
> pressed_keys_
;
96 DISALLOW_COPY_AND_ASSIGN(Core
);
99 SessionInputInjectorWin::Core::Core(
100 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
101 scoped_ptr
<InputInjector
> nested_executor
,
102 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner
,
103 const base::Closure
& inject_sas
)
104 : input_task_runner_(input_task_runner
),
105 nested_executor_(nested_executor
.Pass()),
106 inject_sas_task_runner_(inject_sas_task_runner
),
107 inject_sas_(inject_sas
) {
110 void SessionInputInjectorWin::Core::Start(
111 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
112 if (!input_task_runner_
->BelongsToCurrentThread()) {
113 input_task_runner_
->PostTask(
115 base::Bind(&Core::Start
, this, base::Passed(&client_clipboard
)));
119 nested_executor_
->Start(client_clipboard
.Pass());
122 void SessionInputInjectorWin::Core::InjectClipboardEvent(
123 const ClipboardEvent
& event
) {
124 if (!input_task_runner_
->BelongsToCurrentThread()) {
125 input_task_runner_
->PostTask(
126 FROM_HERE
, base::Bind(&Core::InjectClipboardEvent
, this, event
));
130 nested_executor_
->InjectClipboardEvent(event
);
133 void SessionInputInjectorWin::Core::InjectKeyEvent(const KeyEvent
& event
) {
134 if (!input_task_runner_
->BelongsToCurrentThread()) {
135 input_task_runner_
->PostTask(
136 FROM_HERE
, base::Bind(&Core::InjectKeyEvent
, this, event
));
140 // HostEventDispatcher should drop events lacking the pressed field.
141 DCHECK(event
.has_pressed());
143 if (event
.has_usb_keycode()) {
144 if (event
.pressed()) {
145 // Simulate secure attention sequence if Ctrl-Alt-Del was just pressed.
146 if (event
.usb_keycode() == kUsbDelete
&&
147 CheckCtrlAndAltArePressed(pressed_keys_
)) {
148 VLOG(3) << "Sending Secure Attention Sequence to the session";
150 if (base::win::GetVersion() < base::win::VERSION_VISTA
) {
152 sas_injector_
= SasInjector::Create();
153 if (!sas_injector_
->InjectSas())
154 LOG(ERROR
) << "Failed to inject Secure Attention Sequence.";
156 inject_sas_task_runner_
->PostTask(FROM_HERE
, inject_sas_
);
160 pressed_keys_
.insert(event
.usb_keycode());
162 pressed_keys_
.erase(event
.usb_keycode());
166 SwitchToInputDesktop();
167 nested_executor_
->InjectKeyEvent(event
);
170 void SessionInputInjectorWin::Core::InjectTextEvent(const TextEvent
& event
) {
171 if (!input_task_runner_
->BelongsToCurrentThread()) {
172 input_task_runner_
->PostTask(
173 FROM_HERE
, base::Bind(&Core::InjectTextEvent
, this, event
));
177 SwitchToInputDesktop();
178 nested_executor_
->InjectTextEvent(event
);
181 void SessionInputInjectorWin::Core::InjectMouseEvent(const MouseEvent
& event
) {
182 if (!input_task_runner_
->BelongsToCurrentThread()) {
183 input_task_runner_
->PostTask(
184 FROM_HERE
, base::Bind(&Core::InjectMouseEvent
, this, event
));
188 SwitchToInputDesktop();
189 nested_executor_
->InjectMouseEvent(event
);
192 void SessionInputInjectorWin::Core::InjectTouchEvent(const TouchEvent
& event
) {
196 SessionInputInjectorWin::Core::~Core() {
199 void SessionInputInjectorWin::Core::SwitchToInputDesktop() {
200 // Switch to the desktop receiving user input if different from the current
202 scoped_ptr
<webrtc::Desktop
> input_desktop(
203 webrtc::Desktop::GetInputDesktop());
204 if (input_desktop
.get() != nullptr && !desktop_
.IsSame(*input_desktop
)) {
205 // If SetThreadDesktop() fails, the thread is still assigned a desktop.
206 // So we can continue capture screen bits, just from a diffected desktop.
207 desktop_
.SetThreadDesktop(input_desktop
.release());
211 SessionInputInjectorWin::SessionInputInjectorWin(
212 scoped_refptr
<base::SingleThreadTaskRunner
> input_task_runner
,
213 scoped_ptr
<InputInjector
> nested_executor
,
214 scoped_refptr
<base::SingleThreadTaskRunner
> inject_sas_task_runner
,
215 const base::Closure
& inject_sas
) {
216 core_
= new Core(input_task_runner
, nested_executor
.Pass(),
217 inject_sas_task_runner
, inject_sas
);
220 SessionInputInjectorWin::~SessionInputInjectorWin() {
223 void SessionInputInjectorWin::Start(
224 scoped_ptr
<protocol::ClipboardStub
> client_clipboard
) {
225 core_
->Start(client_clipboard
.Pass());
228 void SessionInputInjectorWin::InjectClipboardEvent(
229 const protocol::ClipboardEvent
& event
) {
230 core_
->InjectClipboardEvent(event
);
233 void SessionInputInjectorWin::InjectKeyEvent(const protocol::KeyEvent
& event
) {
234 core_
->InjectKeyEvent(event
);
237 void SessionInputInjectorWin::InjectTextEvent(
238 const protocol::TextEvent
& event
) {
239 core_
->InjectTextEvent(event
);
242 void SessionInputInjectorWin::InjectMouseEvent(
243 const protocol::MouseEvent
& event
) {
244 core_
->InjectMouseEvent(event
);
247 void SessionInputInjectorWin::InjectTouchEvent(
248 const protocol::TouchEvent
& event
) {
249 core_
->InjectTouchEvent(event
);
252 } // namespace remoting