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 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::KeyEvent; 44 using protocol::MouseEvent; 45 using protocol::TextEvent; 46 47 class SessionInputInjectorWin::Core 48 : public base::RefCountedThreadSafe<SessionInputInjectorWin::Core>, 49 public InputInjector { 50 public: 51 Core( 52 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 53 scoped_ptr<InputInjector> nested_executor, 54 scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner, 55 const base::Closure& inject_sas); 56 57 // InputInjector implementation. 58 virtual void Start( 59 scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE; 60 61 // protocol::ClipboardStub implementation. 62 virtual void InjectClipboardEvent( 63 const protocol::ClipboardEvent& event) OVERRIDE; 64 65 // protocol::InputStub implementation. 66 virtual void InjectKeyEvent(const protocol::KeyEvent& event) OVERRIDE; 67 virtual void InjectTextEvent(const protocol::TextEvent& event) OVERRIDE; 68 virtual void InjectMouseEvent(const protocol::MouseEvent& event) OVERRIDE; 69 70 private: 71 friend class base::RefCountedThreadSafe<Core>; 72 virtual ~Core(); 73 74 // Switches to the desktop receiving a user input if different from 75 // the current one. 76 void SwitchToInputDesktop(); 77 78 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_; 79 80 // Pointer to the next event executor. 81 scoped_ptr<InputInjector> nested_executor_; 82 83 scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner_; 84 85 webrtc::ScopedThreadDesktop desktop_; 86 87 // Used to inject Secure Attention Sequence on Vista+. 88 base::Closure inject_sas_; 89 90 // Used to inject Secure Attention Sequence on XP. 91 scoped_ptr<SasInjector> sas_injector_; 92 93 // Keys currently pressed by the client, used to detect Ctrl-Alt-Del. 94 std::set<uint32> pressed_keys_; 95 96 DISALLOW_COPY_AND_ASSIGN(Core); 97 }; 98 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) { 108 } 109 110 void SessionInputInjectorWin::Core::Start( 111 scoped_ptr<protocol::ClipboardStub> client_clipboard) { 112 if (!input_task_runner_->BelongsToCurrentThread()) { 113 input_task_runner_->PostTask( 114 FROM_HERE, 115 base::Bind(&Core::Start, this, base::Passed(&client_clipboard))); 116 return; 117 } 118 119 nested_executor_->Start(client_clipboard.Pass()); 120 } 121 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)); 127 return; 128 } 129 130 nested_executor_->InjectClipboardEvent(event); 131 } 132 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)); 137 return; 138 } 139 140 // HostEventDispatcher should drop events lacking the pressed field. 141 DCHECK(event.has_pressed()); 142 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"; 149 150 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 151 if (!sas_injector_) 152 sas_injector_ = SasInjector::Create(); 153 if (!sas_injector_->InjectSas()) 154 LOG(ERROR) << "Failed to inject Secure Attention Sequence."; 155 } else { 156 inject_sas_task_runner_->PostTask(FROM_HERE, inject_sas_); 157 } 158 } 159 160 pressed_keys_.insert(event.usb_keycode()); 161 } else { 162 pressed_keys_.erase(event.usb_keycode()); 163 } 164 } 165 166 SwitchToInputDesktop(); 167 nested_executor_->InjectKeyEvent(event); 168 } 169 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)); 174 return; 175 } 176 177 SwitchToInputDesktop(); 178 nested_executor_->InjectTextEvent(event); 179 } 180 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)); 185 return; 186 } 187 188 SwitchToInputDesktop(); 189 nested_executor_->InjectMouseEvent(event); 190 } 191 192 SessionInputInjectorWin::Core::~Core() { 193 } 194 195 void SessionInputInjectorWin::Core::SwitchToInputDesktop() { 196 // Switch to the desktop receiving user input if different from the current 197 // one. 198 scoped_ptr<webrtc::Desktop> input_desktop( 199 webrtc::Desktop::GetInputDesktop()); 200 if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) { 201 // If SetThreadDesktop() fails, the thread is still assigned a desktop. 202 // So we can continue capture screen bits, just from a diffected desktop. 203 desktop_.SetThreadDesktop(input_desktop.release()); 204 } 205 } 206 207 SessionInputInjectorWin::SessionInputInjectorWin( 208 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner, 209 scoped_ptr<InputInjector> nested_executor, 210 scoped_refptr<base::SingleThreadTaskRunner> inject_sas_task_runner, 211 const base::Closure& inject_sas) { 212 core_ = new Core(input_task_runner, nested_executor.Pass(), 213 inject_sas_task_runner, inject_sas); 214 } 215 216 SessionInputInjectorWin::~SessionInputInjectorWin() { 217 } 218 219 void SessionInputInjectorWin::Start( 220 scoped_ptr<protocol::ClipboardStub> client_clipboard) { 221 core_->Start(client_clipboard.Pass()); 222 } 223 224 void SessionInputInjectorWin::InjectClipboardEvent( 225 const protocol::ClipboardEvent& event) { 226 core_->InjectClipboardEvent(event); 227 } 228 229 void SessionInputInjectorWin::InjectKeyEvent(const protocol::KeyEvent& event) { 230 core_->InjectKeyEvent(event); 231 } 232 233 void SessionInputInjectorWin::InjectTextEvent( 234 const protocol::TextEvent& event) { 235 core_->InjectTextEvent(event); 236 } 237 238 void SessionInputInjectorWin::InjectMouseEvent( 239 const protocol::MouseEvent& event) { 240 core_->InjectMouseEvent(event); 241 } 242 243 } // namespace remoting 244