Home | History | Annotate | Download | only in host
      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/local_input_monitor.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/compiler_specific.h"
      9 #include "base/location.h"
     10 #include "base/logging.h"
     11 #include "base/single_thread_task_runner.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/threading/non_thread_safe.h"
     14 #include "base/win/message_window.h"
     15 #include "remoting/host/client_session_control.h"
     16 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
     17 
     18 namespace remoting {
     19 
     20 namespace {
     21 
     22 const wchar_t kWindowClassFormat[] = L"Chromoting_LocalInputMonitorWin_%p";
     23 
     24 // From the HID Usage Tables specification.
     25 const USHORT kGenericDesktopPage = 1;
     26 const USHORT kMouseUsage = 2;
     27 
     28 class LocalInputMonitorWin : public base::NonThreadSafe,
     29                              public LocalInputMonitor {
     30  public:
     31   LocalInputMonitorWin(
     32       scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     33       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
     34       base::WeakPtr<ClientSessionControl> client_session_control);
     35   ~LocalInputMonitorWin();
     36 
     37  private:
     38   // The actual implementation resides in LocalInputMonitorWin::Core class.
     39   class Core : public base::RefCountedThreadSafe<Core> {
     40    public:
     41     Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     42          scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
     43          base::WeakPtr<ClientSessionControl> client_session_control);
     44 
     45     void Start();
     46     void Stop();
     47 
     48    private:
     49     friend class base::RefCountedThreadSafe<Core>;
     50     virtual ~Core();
     51 
     52     void StartOnUiThread();
     53     void StopOnUiThread();
     54 
     55     // Handles WM_INPUT messages.
     56     LRESULT OnInput(HRAWINPUT input_handle);
     57 
     58     // Handles messages received by |window_|.
     59     bool HandleMessage(UINT message,
     60                        WPARAM wparam,
     61                        LPARAM lparam,
     62                        LRESULT* result);
     63 
     64     // Task runner on which public methods of this class must be called.
     65     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
     66 
     67     // Task runner on which |window_| is created.
     68     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
     69 
     70     // Used to receive raw input.
     71     scoped_ptr<base::win::MessageWindow> window_;
     72 
     73     // Points to the object receiving mouse event notifications.
     74     base::WeakPtr<ClientSessionControl> client_session_control_;
     75 
     76     DISALLOW_COPY_AND_ASSIGN(Core);
     77   };
     78 
     79   scoped_refptr<Core> core_;
     80 
     81   DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorWin);
     82 };
     83 
     84 LocalInputMonitorWin::LocalInputMonitorWin(
     85     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     86     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
     87     base::WeakPtr<ClientSessionControl> client_session_control)
     88     : core_(new Core(caller_task_runner,
     89                      ui_task_runner,
     90                      client_session_control)) {
     91   core_->Start();
     92 }
     93 
     94 LocalInputMonitorWin::~LocalInputMonitorWin() {
     95   core_->Stop();
     96 }
     97 
     98 LocalInputMonitorWin::Core::Core(
     99     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    100     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
    101     base::WeakPtr<ClientSessionControl> client_session_control)
    102     : caller_task_runner_(caller_task_runner),
    103       ui_task_runner_(ui_task_runner),
    104       client_session_control_(client_session_control) {
    105   DCHECK(client_session_control_);
    106 }
    107 
    108 void LocalInputMonitorWin::Core::Start() {
    109   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    110 
    111   ui_task_runner_->PostTask(FROM_HERE,
    112                             base::Bind(&Core::StartOnUiThread, this));
    113 }
    114 
    115 void LocalInputMonitorWin::Core::Stop() {
    116   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    117 
    118   ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::StopOnUiThread, this));
    119 }
    120 
    121 LocalInputMonitorWin::Core::~Core() {
    122   DCHECK(!window_);
    123 }
    124 
    125 void LocalInputMonitorWin::Core::StartOnUiThread() {
    126   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    127 
    128   window_.reset(new base::win::MessageWindow());
    129   if (!window_->Create(base::Bind(&Core::HandleMessage,
    130                                   base::Unretained(this)))) {
    131     PLOG(ERROR) << "Failed to create the raw input window";
    132     window_.reset();
    133 
    134     // If the local input cannot be monitored, the remote user can take over
    135     // the session. Disconnect the session now to prevent this.
    136     caller_task_runner_->PostTask(
    137         FROM_HERE, base::Bind(&ClientSessionControl::DisconnectSession,
    138                               client_session_control_));
    139   }
    140 }
    141 
    142 void LocalInputMonitorWin::Core::StopOnUiThread() {
    143   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    144 
    145   // Stop receiving  raw mouse input.
    146   if (window_) {
    147     RAWINPUTDEVICE device = {0};
    148     device.dwFlags = RIDEV_REMOVE;
    149     device.usUsagePage = kGenericDesktopPage;
    150     device.usUsage = kMouseUsage;
    151     device.hwndTarget = NULL;
    152 
    153     // The error is harmless, ignore it.
    154     RegisterRawInputDevices(&device, 1, sizeof(device));
    155   }
    156 
    157   window_.reset();
    158 }
    159 
    160 LRESULT LocalInputMonitorWin::Core::OnInput(HRAWINPUT input_handle) {
    161   DCHECK(ui_task_runner_->BelongsToCurrentThread());
    162 
    163   // Get the size of the input record.
    164   UINT size = 0;
    165   UINT result = GetRawInputData(input_handle,
    166                                 RID_INPUT,
    167                                 NULL,
    168                                 &size,
    169                                 sizeof(RAWINPUTHEADER));
    170   if (result == -1) {
    171     PLOG(ERROR) << "GetRawInputData() failed";
    172     return 0;
    173   }
    174 
    175   // Retrieve the input record itself.
    176   scoped_ptr<uint8[]> buffer(new uint8[size]);
    177   RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
    178   result = GetRawInputData(input_handle,
    179                            RID_INPUT,
    180                            buffer.get(),
    181                            &size,
    182                            sizeof(RAWINPUTHEADER));
    183   if (result == -1) {
    184     PLOG(ERROR) << "GetRawInputData() failed";
    185     return 0;
    186   }
    187 
    188   // Notify the observer about mouse events generated locally. Remote (injected)
    189   // mouse events do not specify a device handle (based on observed behavior).
    190   if (input->header.dwType == RIM_TYPEMOUSE &&
    191       input->header.hDevice != NULL) {
    192     POINT position;
    193     if (!GetCursorPos(&position)) {
    194       position.x = 0;
    195       position.y = 0;
    196     }
    197 
    198     caller_task_runner_->PostTask(
    199         FROM_HERE, base::Bind(&ClientSessionControl::OnLocalMouseMoved,
    200                               client_session_control_,
    201                               webrtc::DesktopVector(position.x, position.y)));
    202   }
    203 
    204   return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
    205 }
    206 
    207 bool LocalInputMonitorWin::Core::HandleMessage(
    208     UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
    209   switch (message) {
    210     case WM_CREATE: {
    211       // Register to receive raw mouse input.
    212       RAWINPUTDEVICE device = {0};
    213       device.dwFlags = RIDEV_INPUTSINK;
    214       device.usUsagePage = kGenericDesktopPage;
    215       device.usUsage = kMouseUsage;
    216       device.hwndTarget = window_->hwnd();
    217       if (RegisterRawInputDevices(&device, 1, sizeof(device))) {
    218         *result = 0;
    219       } else {
    220         PLOG(ERROR) << "RegisterRawInputDevices() failed";
    221         *result = -1;
    222       }
    223       return true;
    224     }
    225 
    226     case WM_INPUT:
    227       *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
    228       return true;
    229 
    230     default:
    231       return false;
    232   }
    233 }
    234 
    235 }  // namespace
    236 
    237 scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create(
    238     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    239     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
    240     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
    241     base::WeakPtr<ClientSessionControl> client_session_control) {
    242   return scoped_ptr<LocalInputMonitor>(
    243       new LocalInputMonitorWin(caller_task_runner,
    244                                ui_task_runner,
    245                                client_session_control));
    246 }
    247 
    248 }  // namespace remoting
    249