summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authoralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-07 08:42:20 +0000
committeralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-07 08:42:20 +0000
commitd1d9bc5bb453c6859c63b3951d7689142a620e3b (patch)
tree8a8b430a3e737dd7b7bf37675fb89b2a0094eaed /remoting
parent519d834b7fc7ce364e4b611d5f24ed3a2e44e845 (diff)
downloadchromium_src-d1d9bc5bb453c6859c63b3951d7689142a620e3b.zip
chromium_src-d1d9bc5bb453c6859c63b3951d7689142a620e3b.tar.gz
chromium_src-d1d9bc5bb453c6859c63b3951d7689142a620e3b.tar.bz2
Use raw input to detect local mouse events.
This CL switches the local input monitor to use raw input instead of hooking low-level mouse input events (WH_MOUSE_LL). The OS can silently remove WH_MOUSE_LL hooks if it is running longer than the timeout specified in [HKCU\Control Panel\Desktop]\LowLevelHooksTimeout. This can result in the local user not being able to take control of the mouse. Other advantades are: - raw input is considered more efficient than low-level hooks; - raw input is received on the UI thread meaning that the host creates one less thread. BUG=148690 Review URL: https://chromiumcodereview.appspot.com/11419216 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@171732 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/host_mock_objects.cc4
-rw-r--r--remoting/host/local_input_monitor_thread_win.cc122
-rw-r--r--remoting/host/local_input_monitor_thread_win.h52
-rw-r--r--remoting/host/local_input_monitor_unittest.cc82
-rw-r--r--remoting/host/local_input_monitor_win.cc187
-rw-r--r--remoting/remoting.gyp3
6 files changed, 257 insertions, 193 deletions
diff --git a/remoting/host/host_mock_objects.cc b/remoting/host/host_mock_objects.cc
index 0c441c5..8b40c6d 100644
--- a/remoting/host/host_mock_objects.cc
+++ b/remoting/host/host_mock_objects.cc
@@ -69,10 +69,6 @@ MockLocalInputMonitor::MockLocalInputMonitor() {}
MockLocalInputMonitor::~MockLocalInputMonitor() {}
-scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create() {
- return scoped_ptr<LocalInputMonitor>(new MockLocalInputMonitor());
-}
-
MockClientSessionEventHandler::MockClientSessionEventHandler() {}
MockClientSessionEventHandler::~MockClientSessionEventHandler() {}
diff --git a/remoting/host/local_input_monitor_thread_win.cc b/remoting/host/local_input_monitor_thread_win.cc
deleted file mode 100644
index 8e08a2b9..0000000
--- a/remoting/host/local_input_monitor_thread_win.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "remoting/host/local_input_monitor_thread_win.h"
-
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/synchronization/waitable_event.h"
-#include "remoting/host/mouse_move_observer.h"
-#include "third_party/skia/include/core/SkPoint.h"
-
-namespace remoting {
-
-namespace {
-LocalInputMonitorThread* g_local_input_monitor_thread = NULL;
-base::LazyInstance<base::Lock>::Leaky g_thread_lock = LAZY_INSTANCE_INITIALIZER;
-} // namespace
-
-LocalInputMonitorThread::LocalInputMonitorThread()
- : base::SimpleThread("LocalInputMonitor") {
-}
-
-LocalInputMonitorThread::~LocalInputMonitorThread() {
- DCHECK(observers_.empty());
-}
-
-void LocalInputMonitorThread::AddObserver(
- MouseMoveObserver* mouse_move_observer) {
- base::AutoLock lock(lock_);
- observers_.insert(mouse_move_observer);
-}
-
-bool LocalInputMonitorThread::RemoveObserver(
- MouseMoveObserver* mouse_move_observer) {
- base::AutoLock lock(lock_);
- observers_.erase(mouse_move_observer);
- return observers_.empty();
-}
-
-void LocalInputMonitorThread::Stop() {
- CHECK(PostThreadMessage(tid(), WM_QUIT, 0, 0));
- Join();
-}
-
-void LocalInputMonitorThread::Run() {
- extern HMODULE g_hModule;
- HHOOK win_event_hook = SetWindowsHookEx(WH_MOUSE_LL, HandleLowLevelMouseEvent,
- g_hModule, 0);
- if (!win_event_hook) {
- DWORD err = GetLastError();
- LOG(ERROR) << "SetWindowHookEx failed: " << err;
- return;
- }
-
- MSG msg;
- BOOL result;
- while ((result = GetMessage(&msg, NULL, 0, 0)) != 0) {
- if (result == -1) {
- DWORD err = GetLastError();
- LOG(ERROR) << "GetMessage failed: " << err;
- break;
- } else {
- DispatchMessage(&msg);
- }
- }
-
- if (win_event_hook) {
- CHECK(UnhookWindowsHookEx(win_event_hook));
- }
-}
-
-void LocalInputMonitorThread::LocalMouseMoved(const SkIPoint& mouse_position) {
- base::AutoLock lock(lock_);
- for (MouseMoveObservers::const_iterator i = observers_.begin();
- i != observers_.end(); ++i) {
- (*i)->OnLocalMouseMoved(mouse_position);
- }
-}
-
-LRESULT WINAPI LocalInputMonitorThread::HandleLowLevelMouseEvent(
- int code, WPARAM event_type, LPARAM event_data) {
- if (code == HC_ACTION) {
- if (event_type == WM_MOUSEMOVE) {
- PMSLLHOOKSTRUCT data = reinterpret_cast<PMSLLHOOKSTRUCT>(event_data);
- if ((data->flags & LLMHF_INJECTED) == 0) {
- SkIPoint mouse_position = SkIPoint::Make(data->pt.x, data->pt.y);
- // |g_local_input_monitor_thread| cannot be NULL because this function
- // is called from within the GetMessage call running on that thread.
- DCHECK(g_local_input_monitor_thread);
- g_local_input_monitor_thread->LocalMouseMoved(mouse_position);
- }
- }
- }
- return CallNextHookEx(NULL, code, event_type, event_data);
-}
-
-
-// static
-void LocalInputMonitorThread::AddMouseMoveObserver(
- MouseMoveObserver* mouse_move_observer) {
- base::AutoLock lock(g_thread_lock.Get());
- if (!g_local_input_monitor_thread) {
- g_local_input_monitor_thread = new LocalInputMonitorThread;
- g_local_input_monitor_thread->Start();
- }
- g_local_input_monitor_thread->AddObserver(mouse_move_observer);
-}
-
-// static
-void LocalInputMonitorThread::RemoveMouseMoveObserver(
- MouseMoveObserver* mouse_move_observer) {
- DCHECK(g_local_input_monitor_thread);
- base::AutoLock lock(g_thread_lock.Get());
- if (g_local_input_monitor_thread->RemoveObserver(mouse_move_observer)) {
- g_local_input_monitor_thread->Stop();
- delete g_local_input_monitor_thread;
- g_local_input_monitor_thread = NULL;
- }
-}
-
-} // namespace remoting
diff --git a/remoting/host/local_input_monitor_thread_win.h b/remoting/host/local_input_monitor_thread_win.h
deleted file mode 100644
index 92687b2..0000000
--- a/remoting/host/local_input_monitor_thread_win.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_HOST_LOCAL_INPUT_MONITOR_THREAD_WIN_H_
-#define REMOTING_HOST_LOCAL_INPUT_MONITOR_THREAD_WIN_H_
-
-#include <set>
-
-#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/simple_thread.h"
-
-struct SkIPoint;
-
-namespace remoting {
-
-class MouseMoveObserver;
-
-class LocalInputMonitorThread : public base::SimpleThread {
- public:
- static void AddMouseMoveObserver(MouseMoveObserver* mouse_move_observer);
- static void RemoveMouseMoveObserver(MouseMoveObserver* mouse_move_observer);
-
- private:
- LocalInputMonitorThread();
- virtual ~LocalInputMonitorThread();
-
- void AddObserver(MouseMoveObserver* mouse_move_observer);
- bool RemoveObserver(MouseMoveObserver* mouse_move_observer);
-
- void Stop();
-
- // Overridden from base::SimpleThread:
- virtual void Run() OVERRIDE;
-
- void LocalMouseMoved(const SkIPoint& mouse_position);
- static LRESULT WINAPI HandleLowLevelMouseEvent(int code,
- WPARAM event_type,
- LPARAM event_data);
-
- base::Lock lock_;
- typedef std::set<MouseMoveObserver*> MouseMoveObservers;
- MouseMoveObservers observers_;
-
- DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorThread);
-};
-
-} // namespace remoting
-
-#endif // REMOTING_HOST_LOCAL_INPUT_MONITOR_THREAD_WIN_H_
diff --git a/remoting/host/local_input_monitor_unittest.cc b/remoting/host/local_input_monitor_unittest.cc
new file mode 100644
index 0000000..590765e
--- /dev/null
+++ b/remoting/host/local_input_monitor_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/run_loop.h"
+#include "remoting/base/auto_thread_task_runner.h"
+#include "remoting/host/local_input_monitor.h"
+#include "remoting/host/mouse_move_observer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkPoint.h"
+
+namespace remoting {
+
+using testing::_;
+using testing::AnyNumber;
+
+namespace {
+
+class FakeMouseMoveObserver : public MouseMoveObserver {
+ public:
+ FakeMouseMoveObserver() {}
+ virtual ~FakeMouseMoveObserver() {}
+
+ // Ignore mouse events to avoid breaking the test of someone moves the mouse.
+ void OnLocalMouseMoved(const SkIPoint&) {}
+ void OnDisconnectCallback() {}
+};
+
+} // namespace
+
+class LocalInputMonitorTest : public testing::Test {
+ public:
+ LocalInputMonitorTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ task_runner_ = new AutoThreadTaskRunner(
+ message_loop_.message_loop_proxy(),
+ base::Bind(&LocalInputMonitorTest::OnStopTaskRunner,
+ base::Unretained(this)));
+ }
+
+ void OnStopTaskRunner() {
+ message_loop_.PostTask(
+ FROM_HERE, base::Bind(&LocalInputMonitorTest::DestroyLocalInputMonitor,
+ base::Unretained(this)));
+ }
+
+ void DestroyLocalInputMonitor() {
+ if (local_input_monitor_) {
+ local_input_monitor_->Stop();
+ local_input_monitor_.reset();
+ }
+
+ message_loop_.PostTask(FROM_HERE, run_loop_.QuitClosure());
+ }
+
+ MessageLoop message_loop_;
+ base::RunLoop run_loop_;
+ scoped_refptr<AutoThreadTaskRunner> task_runner_;
+
+ FakeMouseMoveObserver mouse_move_observer_;
+ scoped_ptr<LocalInputMonitor> local_input_monitor_;
+};
+
+// This test is really to exercise only the creation and destruction code in
+// LocalInputMonitor.
+TEST_F(LocalInputMonitorTest, Basic) {
+ local_input_monitor_ = LocalInputMonitor::Create();
+ local_input_monitor_->Start(
+ &mouse_move_observer_,
+ base::Bind(&FakeMouseMoveObserver::OnDisconnectCallback,
+ base::Unretained(&mouse_move_observer_)));
+
+ task_runner_ = NULL;
+ run_loop_.Run();
+}
+
+} // namespace remoting
diff --git a/remoting/host/local_input_monitor_win.cc b/remoting/host/local_input_monitor_win.cc
index 56c7318..7afb392 100644
--- a/remoting/host/local_input_monitor_win.cc
+++ b/remoting/host/local_input_monitor_win.cc
@@ -6,14 +6,24 @@
#include "base/compiler_specific.h"
#include "base/logging.h"
-#include "remoting/host/chromoting_host.h"
-#include "remoting/host/local_input_monitor_thread_win.h"
+#include "base/stringprintf.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/win/wrapped_window_proc.h"
+#include "remoting/host/mouse_move_observer.h"
+#include "third_party/skia/include/core/SkPoint.h"
namespace remoting {
namespace {
-class LocalInputMonitorWin : public LocalInputMonitor {
+const wchar_t kWindowClassFormat[] = L"Chromoting_LocalInputMonitorWin_%p";
+
+// From the HID Usage Tables specification.
+const USHORT kGenericDesktopPage = 1;
+const USHORT kMouseUsage = 2;
+
+class LocalInputMonitorWin : public base::NonThreadSafe,
+ public LocalInputMonitor {
public:
LocalInputMonitorWin();
~LocalInputMonitorWin();
@@ -23,30 +33,181 @@ class LocalInputMonitorWin : public LocalInputMonitor {
virtual void Stop() OVERRIDE;
private:
- DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorWin);
+ // Handles WM_CREATE messages.
+ LRESULT OnCreate(HWND hwnd);
+
+ // Handles WM_INPUT messages.
+ LRESULT OnInput(HRAWINPUT input_handle);
+
+ // Window procedure invoked by the OS to process window messages.
+ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam);
+
+ // Atom representing the input window class.
+ ATOM atom_;
+
+ // Instance of the module containing the window procedure.
+ HINSTANCE instance_;
+
+ // Handle of the input window.
+ HWND window_;
- MouseMoveObserver* mouse_move_observer_;
+ // Observer to dispatch mouse event notifications to.
+ MouseMoveObserver* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorWin);
};
LocalInputMonitorWin::LocalInputMonitorWin()
- : mouse_move_observer_(NULL) {
+ : atom_(0),
+ instance_(NULL),
+ window_(NULL),
+ observer_(NULL) {
}
LocalInputMonitorWin::~LocalInputMonitorWin() {
- DCHECK(!mouse_move_observer_);
+ DCHECK(CalledOnValidThread());
+ DCHECK(!atom_);
+ DCHECK(!instance_);
+ DCHECK(!window_);
+ DCHECK(!observer_);
}
void LocalInputMonitorWin::Start(MouseMoveObserver* mouse_move_observer,
const base::Closure& disconnect_callback) {
- DCHECK(!mouse_move_observer_);
- mouse_move_observer_ = mouse_move_observer;
- LocalInputMonitorThread::AddMouseMoveObserver(mouse_move_observer_);
+ DCHECK(CalledOnValidThread());
+ DCHECK(!observer_);
+ DCHECK(mouse_move_observer);
+
+ observer_ = mouse_move_observer;
+
+ // Create a window for receiving raw input.
+ string16 window_class_name = base::StringPrintf(kWindowClassFormat, this);
+ WNDCLASSEX window_class;
+ base::win::InitializeWindowClass(
+ window_class_name.c_str(),
+ &base::win::WrappedWindowProc<WindowProc>,
+ 0, 0, 0, NULL, NULL, NULL, NULL, NULL,
+ &window_class);
+ instance_ = window_class.hInstance;
+ atom_ = RegisterClassEx(&window_class);
+ if (atom_ == 0) {
+ LOG_GETLASTERROR(ERROR)
+ << "Failed to register the window class '" << window_class_name << "'";
+ return;
+ }
+
+ window_ = CreateWindow(MAKEINTATOM(atom_), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
+ instance_, this);
+ if (window_ == NULL) {
+ LOG_GETLASTERROR(ERROR) << "Failed to create the raw input window";
+ return;
+ }
}
void LocalInputMonitorWin::Stop() {
- DCHECK(mouse_move_observer_);
- LocalInputMonitorThread::RemoveMouseMoveObserver(mouse_move_observer_);
- mouse_move_observer_ = NULL;
+ DCHECK(CalledOnValidThread());
+ DCHECK(observer_);
+
+ if (window_ != NULL) {
+ DestroyWindow(window_);
+ window_ = NULL;
+ }
+
+ if (atom_ != 0) {
+ UnregisterClass(MAKEINTATOM(atom_), instance_);
+ atom_ = 0;
+ instance_ = NULL;
+ }
+
+ observer_ = NULL;
+}
+
+LRESULT LocalInputMonitorWin::OnCreate(HWND hwnd) {
+ DCHECK(CalledOnValidThread());
+
+ // Register to receive raw mouse input.
+ RAWINPUTDEVICE device = {0};
+ device.dwFlags = RIDEV_INPUTSINK;
+ device.usUsagePage = kGenericDesktopPage;
+ device.usUsage = kMouseUsage;
+ device.hwndTarget = hwnd;
+ if (!RegisterRawInputDevices(&device, 1, sizeof(device))) {
+ LOG_GETLASTERROR(ERROR) << "RegisterRawInputDevices() failed";
+ return -1;
+ }
+
+ return 0;
+}
+
+LRESULT LocalInputMonitorWin::OnInput(HRAWINPUT input_handle) {
+ DCHECK(CalledOnValidThread());
+
+ // Get the size of the input record.
+ UINT size = 0;
+ UINT result = GetRawInputData(input_handle,
+ RID_INPUT,
+ NULL,
+ &size,
+ sizeof(RAWINPUTHEADER));
+ if (result == -1) {
+ LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed";
+ return 0;
+ }
+
+ // Retrieve the input record itself.
+ scoped_array<uint8> buffer(new uint8[size]);
+ RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
+ result = GetRawInputData(input_handle,
+ RID_INPUT,
+ buffer.get(),
+ &size,
+ sizeof(RAWINPUTHEADER));
+ if (result == -1) {
+ LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed";
+ return 0;
+ }
+
+ // Notify the observer about mouse events generated locally. Remote (injected)
+ // mouse events do not specify a device handle (based on observed behavior).
+ if (input->header.dwType == RIM_TYPEMOUSE &&
+ input->header.hDevice != NULL) {
+ POINT position;
+ if (!GetCursorPos(&position)) {
+ position.x = 0;
+ position.y = 0;
+ }
+
+ observer_->OnLocalMouseMoved(SkIPoint::Make(position.x, position.y));
+ }
+
+ return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
+}
+
+// static
+LRESULT CALLBACK LocalInputMonitorWin::WindowProc(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
+ // Store |this| to the window's user data.
+ if (message == WM_CREATE) {
+ CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam);
+ SetLastError(ERROR_SUCCESS);
+ LONG_PTR result = SetWindowLongPtr(
+ hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(cs->lpCreateParams));
+ CHECK(result != 0 || GetLastError() == ERROR_SUCCESS);
+ }
+
+ LocalInputMonitorWin* self = reinterpret_cast<LocalInputMonitorWin*>(
+ GetWindowLongPtr(hwnd, GWLP_USERDATA));
+ switch (message) {
+ case WM_CREATE:
+ return self->OnCreate(hwnd);
+
+ case WM_INPUT:
+ return self->OnInput(reinterpret_cast<HRAWINPUT>(lparam));
+
+ default:
+ return DefWindowProc(hwnd, message, wparam, lparam);
+ }
}
} // namespace
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index ac64826..216a855 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -379,8 +379,6 @@
'host/local_input_monitor_mac.mm',
'host/local_input_monitor_thread_linux.cc',
'host/local_input_monitor_thread_linux.h',
- 'host/local_input_monitor_thread_win.cc',
- 'host/local_input_monitor_thread_win.h',
'host/local_input_monitor_win.cc',
'host/log_to_server.cc',
'host/log_to_server.h',
@@ -2201,6 +2199,7 @@
'host/host_mock_objects.h',
'host/json_host_config_unittest.cc',
'host/linux/x_server_clipboard_unittest.cc',
+ 'host/local_input_monitor_unittest.cc',
'host/log_to_server_unittest.cc',
'host/pin_hash_unittest.cc',
'host/policy_hack/fake_policy_watcher.cc',