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 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 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 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 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 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<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 194SessionInputInjectorWin::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 203SessionInputInjectorWin::~SessionInputInjectorWin() { 204} 205 206void SessionInputInjectorWin::Start( 207 scoped_ptr<protocol::ClipboardStub> client_clipboard) { 208 core_->Start(client_clipboard.Pass()); 209} 210 211void SessionInputInjectorWin::InjectClipboardEvent( 212 const protocol::ClipboardEvent& event) { 213 core_->InjectClipboardEvent(event); 214} 215 216void SessionInputInjectorWin::InjectKeyEvent(const protocol::KeyEvent& event) { 217 core_->InjectKeyEvent(event); 218} 219 220void SessionInputInjectorWin::InjectMouseEvent( 221 const protocol::MouseEvent& event) { 222 core_->InjectMouseEvent(event); 223} 224 225} // namespace remoting 226