Fix mouse warp with 2x displays
[chromium-blink-merge.git] / remoting / host / local_input_monitor_win.cc
blob1b92e03ff3f957df1167e215c1931143e342124e
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.
5 #include "remoting/host/local_input_monitor.h"
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"
18 namespace remoting {
20 namespace {
22 // From the HID Usage Tables specification.
23 const USHORT kGenericDesktopPage = 1;
24 const USHORT kMouseUsage = 2;
26 class LocalInputMonitorWin : public base::NonThreadSafe,
27 public LocalInputMonitor {
28 public:
29 LocalInputMonitorWin(
30 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
31 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
32 base::WeakPtr<ClientSessionControl> client_session_control);
33 ~LocalInputMonitorWin() override;
35 private:
36 // The actual implementation resides in LocalInputMonitorWin::Core class.
37 class Core : public base::RefCountedThreadSafe<Core> {
38 public:
39 Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
40 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
41 base::WeakPtr<ClientSessionControl> client_session_control);
43 void Start();
44 void Stop();
46 private:
47 friend class base::RefCountedThreadSafe<Core>;
48 virtual ~Core();
50 void StartOnUiThread();
51 void StopOnUiThread();
53 // Handles WM_INPUT messages.
54 LRESULT OnInput(HRAWINPUT input_handle);
56 // Handles messages received by |window_|.
57 bool HandleMessage(UINT message,
58 WPARAM wparam,
59 LPARAM lparam,
60 LRESULT* result);
62 // Task runner on which public methods of this class must be called.
63 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
65 // Task runner on which |window_| is created.
66 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
68 // Used to receive raw input.
69 scoped_ptr<base::win::MessageWindow> window_;
71 // Points to the object receiving mouse event notifications.
72 base::WeakPtr<ClientSessionControl> client_session_control_;
74 DISALLOW_COPY_AND_ASSIGN(Core);
77 scoped_refptr<Core> core_;
79 DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorWin);
82 LocalInputMonitorWin::LocalInputMonitorWin(
83 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
84 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
85 base::WeakPtr<ClientSessionControl> client_session_control)
86 : core_(new Core(caller_task_runner,
87 ui_task_runner,
88 client_session_control)) {
89 core_->Start();
92 LocalInputMonitorWin::~LocalInputMonitorWin() {
93 core_->Stop();
96 LocalInputMonitorWin::Core::Core(
97 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
98 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
99 base::WeakPtr<ClientSessionControl> client_session_control)
100 : caller_task_runner_(caller_task_runner),
101 ui_task_runner_(ui_task_runner),
102 client_session_control_(client_session_control) {
103 DCHECK(client_session_control_);
106 void LocalInputMonitorWin::Core::Start() {
107 DCHECK(caller_task_runner_->BelongsToCurrentThread());
109 ui_task_runner_->PostTask(FROM_HERE,
110 base::Bind(&Core::StartOnUiThread, this));
113 void LocalInputMonitorWin::Core::Stop() {
114 DCHECK(caller_task_runner_->BelongsToCurrentThread());
116 ui_task_runner_->PostTask(FROM_HERE, base::Bind(&Core::StopOnUiThread, this));
119 LocalInputMonitorWin::Core::~Core() {
120 DCHECK(!window_);
123 void LocalInputMonitorWin::Core::StartOnUiThread() {
124 DCHECK(ui_task_runner_->BelongsToCurrentThread());
126 window_.reset(new base::win::MessageWindow());
127 if (!window_->Create(base::Bind(&Core::HandleMessage,
128 base::Unretained(this)))) {
129 PLOG(ERROR) << "Failed to create the raw input window";
130 window_.reset();
132 // If the local input cannot be monitored, the remote user can take over
133 // the session. Disconnect the session now to prevent this.
134 caller_task_runner_->PostTask(
135 FROM_HERE, base::Bind(&ClientSessionControl::DisconnectSession,
136 client_session_control_));
140 void LocalInputMonitorWin::Core::StopOnUiThread() {
141 DCHECK(ui_task_runner_->BelongsToCurrentThread());
143 // Stop receiving raw mouse input.
144 if (window_) {
145 RAWINPUTDEVICE device = {0};
146 device.dwFlags = RIDEV_REMOVE;
147 device.usUsagePage = kGenericDesktopPage;
148 device.usUsage = kMouseUsage;
149 device.hwndTarget = nullptr;
151 // The error is harmless, ignore it.
152 RegisterRawInputDevices(&device, 1, sizeof(device));
155 window_.reset();
158 LRESULT LocalInputMonitorWin::Core::OnInput(HRAWINPUT input_handle) {
159 DCHECK(ui_task_runner_->BelongsToCurrentThread());
161 // Get the size of the input record.
162 UINT size = 0;
163 UINT result = GetRawInputData(input_handle,
164 RID_INPUT,
165 nullptr,
166 &size,
167 sizeof(RAWINPUTHEADER));
168 if (result == -1) {
169 PLOG(ERROR) << "GetRawInputData() failed";
170 return 0;
173 // Retrieve the input record itself.
174 scoped_ptr<uint8[]> buffer(new uint8[size]);
175 RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
176 result = GetRawInputData(input_handle,
177 RID_INPUT,
178 buffer.get(),
179 &size,
180 sizeof(RAWINPUTHEADER));
181 if (result == -1) {
182 PLOG(ERROR) << "GetRawInputData() failed";
183 return 0;
186 // Notify the observer about mouse events generated locally. Remote (injected)
187 // mouse events do not specify a device handle (based on observed behavior).
188 if (input->header.dwType == RIM_TYPEMOUSE &&
189 input->header.hDevice != nullptr) {
190 POINT position;
191 if (!GetCursorPos(&position)) {
192 position.x = 0;
193 position.y = 0;
196 caller_task_runner_->PostTask(
197 FROM_HERE, base::Bind(&ClientSessionControl::OnLocalMouseMoved,
198 client_session_control_,
199 webrtc::DesktopVector(position.x, position.y)));
202 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
205 bool LocalInputMonitorWin::Core::HandleMessage(
206 UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
207 switch (message) {
208 case WM_CREATE: {
209 // Register to receive raw mouse input.
210 RAWINPUTDEVICE device = {0};
211 device.dwFlags = RIDEV_INPUTSINK;
212 device.usUsagePage = kGenericDesktopPage;
213 device.usUsage = kMouseUsage;
214 device.hwndTarget = window_->hwnd();
215 if (RegisterRawInputDevices(&device, 1, sizeof(device))) {
216 *result = 0;
217 } else {
218 PLOG(ERROR) << "RegisterRawInputDevices() failed";
219 *result = -1;
221 return true;
224 case WM_INPUT:
225 *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
226 return true;
228 default:
229 return false;
233 } // namespace
235 scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create(
236 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
237 scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
238 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
239 base::WeakPtr<ClientSessionControl> client_session_control) {
240 return make_scoped_ptr(new LocalInputMonitorWin(
241 caller_task_runner, ui_task_runner, client_session_control));
244 } // namespace remoting