session_input_injector.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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
5#include "remoting/host/win/session_input_injector.h"
6
7#include <set>
8#include <string>
9
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 "media/video/capture/screen/win/desktop.h"
17#include "media/video/capture/screen/win/scoped_thread_desktop.h"
18#include "remoting/host/sas_injector.h"
19#include "remoting/proto/event.pb.h"
20
21namespace {
22
23const uint32 kUsbLeftControl =  0x0700e0;
24const uint32 kUsbRightControl = 0x0700e4;
25const uint32 kUsbLeftAlt =  0x0700e2;
26const uint32 kUsbRightAlt = 0x0700e6;
27const uint32 kUsbDelete = 0x07004c;
28
29bool 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());
36}
37
38} // namespace
39
40namespace remoting {
41
42using protocol::ClipboardEvent;
43using protocol::MouseEvent;
44using protocol::KeyEvent;
45
46class SessionInputInjectorWin::Core
47    : public base::RefCountedThreadSafe<SessionInputInjectorWin::Core>,
48      public InputInjector {
49 public:
50  Core(
51      scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
52      scoped_ptr<InputInjector> nested_executor,
53      scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner,
54      const base::Closure& inject_sas);
55
56  // InputInjector implementation.
57  virtual void Start(
58      scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
59
60  // protocol::ClipboardStub implementation.
61  virtual void InjectClipboardEvent(
62      const protocol::ClipboardEvent& event) OVERRIDE;
63
64  // protocol::InputStub implementation.
65  virtual void InjectKeyEvent(const protocol::KeyEvent& event) OVERRIDE;
66  virtual void InjectMouseEvent(const protocol::MouseEvent& event) OVERRIDE;
67
68 private:
69  friend class base::RefCountedThreadSafe<Core>;
70  virtual ~Core();
71
72  // Switches to the desktop receiving a user input if different from
73  // the current one.
74  void SwitchToInputDesktop();
75
76  scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_;
77
78  // Pointer to the next event executor.
79  scoped_ptr<InputInjector> nested_executor_;
80
81  scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner_;
82
83  media::ScopedThreadDesktop desktop_;
84
85  // Used to inject Secure Attention Sequence on Vista+.
86  base::Closure inject_sas_;
87
88  // Used to inject Secure Attention Sequence on XP.
89  scoped_ptr<SasInjector> sas_injector_;
90
91  // Keys currently pressed by the client, used to detect Ctrl-Alt-Del.
92  std::set<uint32> pressed_keys_;
93
94  DISALLOW_COPY_AND_ASSIGN(Core);
95};
96
97SessionInputInjectorWin::Core::Core(
98    scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
99    scoped_ptr<InputInjector> nested_executor,
100    scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner,
101    const base::Closure& inject_sas)
102    : input_task_runner_(input_task_runner),
103      nested_executor_(nested_executor.Pass()),
104      inject_sas_task_runner_(inject_sas_task_runner),
105      inject_sas_(inject_sas) {
106}
107
108void SessionInputInjectorWin::Core::Start(
109    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
110  if (!input_task_runner_->BelongsToCurrentThread()) {
111    input_task_runner_->PostTask(
112        FROM_HERE,
113        base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
114    return;
115  }
116
117  nested_executor_->Start(client_clipboard.Pass());
118}
119
120void SessionInputInjectorWin::Core::InjectClipboardEvent(
121    const ClipboardEvent& event) {
122  if (!input_task_runner_->BelongsToCurrentThread()) {
123    input_task_runner_->PostTask(
124        FROM_HERE, base::Bind(&Core::InjectClipboardEvent, this, event));
125    return;
126  }
127
128  nested_executor_->InjectClipboardEvent(event);
129}
130
131void SessionInputInjectorWin::Core::InjectKeyEvent(const KeyEvent& event) {
132  if (!input_task_runner_->BelongsToCurrentThread()) {
133    input_task_runner_->PostTask(
134        FROM_HERE, base::Bind(&Core::InjectKeyEvent, this, event));
135    return;
136  }
137
138  // HostEventDispatcher should drop events lacking the pressed field.
139  DCHECK(event.has_pressed());
140
141  if (event.has_usb_keycode()) {
142    if (event.pressed()) {
143      // Simulate secure attention sequence if Ctrl-Alt-Del was just pressed.
144      if (event.usb_keycode() == kUsbDelete &&
145          CheckCtrlAndAltArePressed(pressed_keys_)) {
146        VLOG(3) << "Sending Secure Attention Sequence to console";
147
148        if (base::win::GetVersion() < base::win::VERSION_VISTA) {
149          if (!sas_injector_)
150            sas_injector_ = SasInjector::Create();
151          if (!sas_injector_->InjectSas())
152            LOG(ERROR) << "Failed to inject Secure Attention Sequence.";
153        } else {
154          inject_sas_task_runner_->PostTask(FROM_HERE, inject_sas_);
155        }
156      }
157
158      pressed_keys_.insert(event.usb_keycode());
159    } else {
160      pressed_keys_.erase(event.usb_keycode());
161    }
162  }
163
164  SwitchToInputDesktop();
165  nested_executor_->InjectKeyEvent(event);
166}
167
168void SessionInputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) {
169  if (!input_task_runner_->BelongsToCurrentThread()) {
170    input_task_runner_->PostTask(
171        FROM_HERE, base::Bind(&Core::InjectMouseEvent, this, event));
172    return;
173  }
174
175  SwitchToInputDesktop();
176  nested_executor_->InjectMouseEvent(event);
177}
178
179SessionInputInjectorWin::Core::~Core() {
180}
181
182void SessionInputInjectorWin::Core::SwitchToInputDesktop() {
183  // Switch to the desktop receiving user input if different from the current
184  // one.
185  scoped_ptr<media::Desktop> input_desktop = media::Desktop::GetInputDesktop();
186  if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
187    // If SetThreadDesktop() fails, the thread is still assigned a desktop.
188    // So we can continue capture screen bits, just from a diffected desktop.
189    desktop_.SetThreadDesktop(input_desktop.Pass());
190  }
191}
192
193SessionInputInjectorWin::SessionInputInjectorWin(
194    scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
195    scoped_ptr<InputInjector> nested_executor,
196    scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner,
197    const base::Closure& inject_sas) {
198  core_ = new Core(input_task_runner, nested_executor.Pass(),
199                   inject_sas_task_runner, inject_sas);
200}
201
202SessionInputInjectorWin::~SessionInputInjectorWin() {
203}
204
205void SessionInputInjectorWin::Start(
206    scoped_ptr<protocol::ClipboardStub> client_clipboard) {
207  core_->Start(client_clipboard.Pass());
208}
209
210void SessionInputInjectorWin::InjectClipboardEvent(
211    const protocol::ClipboardEvent& event) {
212  core_->InjectClipboardEvent(event);
213}
214
215void SessionInputInjectorWin::InjectKeyEvent(const protocol::KeyEvent& event) {
216  core_->InjectKeyEvent(event);
217}
218
219void SessionInputInjectorWin::InjectMouseEvent(
220    const protocol::MouseEvent& event) {
221  core_->InjectMouseEvent(event);
222}
223
224}  // namespace remoting
225