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