• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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"
20 
21 namespace {
22 
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;
28 
CheckCtrlAndAltArePressed(const std::set<uint32> & pressed_keys)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());
36 }
37 
38 } // namespace
39 
40 namespace remoting {
41 
42 using protocol::ClipboardEvent;
43 using protocol::MouseEvent;
44 using protocol::KeyEvent;
45 
46 class 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   webrtc::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 
Core(scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,scoped_ptr<InputInjector> nested_executor,scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner,const base::Closure & inject_sas)97 SessionInputInjectorWin::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 
Start(scoped_ptr<protocol::ClipboardStub> client_clipboard)108 void 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 
InjectClipboardEvent(const ClipboardEvent & event)120 void 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 
InjectKeyEvent(const KeyEvent & event)131 void 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 the session";
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 
InjectMouseEvent(const MouseEvent & event)168 void 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 
~Core()179 SessionInputInjectorWin::Core::~Core() {
180 }
181 
SwitchToInputDesktop()182 void SessionInputInjectorWin::Core::SwitchToInputDesktop() {
183   // Switch to the desktop receiving user input if different from the current
184   // one.
185   scoped_ptr<webrtc::Desktop> input_desktop(
186       webrtc::Desktop::GetInputDesktop());
187   if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
188     // If SetThreadDesktop() fails, the thread is still assigned a desktop.
189     // So we can continue capture screen bits, just from a diffected desktop.
190     desktop_.SetThreadDesktop(input_desktop.release());
191   }
192 }
193 
SessionInputInjectorWin(scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,scoped_ptr<InputInjector> nested_executor,scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner,const base::Closure & inject_sas)194 SessionInputInjectorWin::SessionInputInjectorWin(
195     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
196     scoped_ptr<InputInjector> nested_executor,
197     scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner,
198     const base::Closure& inject_sas) {
199   core_ = new Core(input_task_runner, nested_executor.Pass(),
200                    inject_sas_task_runner, inject_sas);
201 }
202 
~SessionInputInjectorWin()203 SessionInputInjectorWin::~SessionInputInjectorWin() {
204 }
205 
Start(scoped_ptr<protocol::ClipboardStub> client_clipboard)206 void SessionInputInjectorWin::Start(
207     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
208   core_->Start(client_clipboard.Pass());
209 }
210 
InjectClipboardEvent(const protocol::ClipboardEvent & event)211 void SessionInputInjectorWin::InjectClipboardEvent(
212     const protocol::ClipboardEvent& event) {
213   core_->InjectClipboardEvent(event);
214 }
215 
InjectKeyEvent(const protocol::KeyEvent & event)216 void SessionInputInjectorWin::InjectKeyEvent(const protocol::KeyEvent& event) {
217   core_->InjectKeyEvent(event);
218 }
219 
InjectMouseEvent(const protocol::MouseEvent & event)220 void SessionInputInjectorWin::InjectMouseEvent(
221     const protocol::MouseEvent& event) {
222   core_->InjectMouseEvent(event);
223 }
224 
225 }  // namespace remoting
226