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::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