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
18namespace remoting {
19
20namespace {
21
22const wchar_t kWindowClassFormat[] = L"Chromoting_LocalInputMonitorWin_%p";
23
24// From the HID Usage Tables specification.
25const USHORT kGenericDesktopPage = 1;
26const USHORT kMouseUsage = 2;
27
28class 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
84LocalInputMonitorWin::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
94LocalInputMonitorWin::~LocalInputMonitorWin() {
95  core_->Stop();
96}
97
98LocalInputMonitorWin::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
108void 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
115void LocalInputMonitorWin::Core::Stop() {
116  DCHECK(caller_task_runner_->BelongsToCurrentThread());
117
118  ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::StopOnUiThread, this));
119}
120
121LocalInputMonitorWin::Core::~Core() {
122  DCHECK(!window_);
123}
124
125void 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
142void 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
160LRESULT 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
207bool 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
237scoped_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