diff options
author | jiayl@chromium.org <jiayl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-14 21:28:11 +0000 |
---|---|---|
committer | jiayl@chromium.org <jiayl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-14 21:28:11 +0000 |
commit | a8399162b23412192248fbb6895d93f09eabc440 (patch) | |
tree | 59d494fc77bd8de0a3f532be64a65e92f5935c33 /media | |
parent | 3693a5125ee590c9d6324c6306ec65d48e39feed (diff) | |
download | chromium_src-a8399162b23412192248fbb6895d93f09eabc440.zip chromium_src-a8399162b23412192248fbb6895d93f09eabc440.tar.gz chromium_src-a8399162b23412192248fbb6895d93f09eabc440.tar.bz2 |
Adds the UserInputMonitor implementation for Windows.
BUG=274623
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=223223
Review URL: https://chromiumcodereview.appspot.com/23702008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@223260 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/keyboard_event_counter.h | 3 | ||||
-rw-r--r-- | media/base/user_input_monitor.cc | 46 | ||||
-rw-r--r-- | media/base/user_input_monitor.h | 26 | ||||
-rw-r--r-- | media/base/user_input_monitor_linux.cc | 214 | ||||
-rw-r--r-- | media/base/user_input_monitor_mac.cc | 12 | ||||
-rw-r--r-- | media/base/user_input_monitor_unittest.cc | 78 | ||||
-rw-r--r-- | media/base/user_input_monitor_win.cc | 288 | ||||
-rw-r--r-- | media/media.gyp | 18 |
8 files changed, 534 insertions, 151 deletions
diff --git a/media/base/keyboard_event_counter.h b/media/base/keyboard_event_counter.h index c82ca59..d8e76b0 100644 --- a/media/base/keyboard_event_counter.h +++ b/media/base/keyboard_event_counter.h @@ -8,6 +8,7 @@ #include <set> #include "base/synchronization/lock.h" +#include "media/base/media_export.h" #include "ui/base/events/event_constants.h" #include "ui/base/keycodes/keyboard_codes.h" @@ -17,7 +18,7 @@ namespace media { // calls it receives from the client. // Multiple key down events for the same key are counted as one keypress until // the same key is released. -class KeyboardEventCounter { +class MEDIA_EXPORT KeyboardEventCounter { public: KeyboardEventCounter(); ~KeyboardEventCounter(); diff --git a/media/base/user_input_monitor.cc b/media/base/user_input_monitor.cc index 34e07d8..e43cd62 100644 --- a/media/base/user_input_monitor.cc +++ b/media/base/user_input_monitor.cc @@ -4,6 +4,7 @@ #include "media/base/user_input_monitor.h" +#include "base/logging.h" #include "third_party/skia/include/core/SkPoint.h" namespace media { @@ -17,30 +18,37 @@ scoped_ptr<UserInputMonitor> UserInputMonitor::Create( #endif // DISABLE_USER_INPUT_MONITOR UserInputMonitor::UserInputMonitor() - : monitoring_mouse_(false), key_press_counter_references_(0) {} + : key_press_counter_references_(0), + mouse_listeners_count_(0), + mouse_listeners_(new MouseListenerList()) {} UserInputMonitor::~UserInputMonitor() { - DCHECK(!monitoring_mouse_); - DCHECK(!key_press_counter_references_); + DCHECK_EQ(0u, key_press_counter_references_); + mouse_listeners_->AssertEmpty(); } void UserInputMonitor::AddMouseListener(MouseEventListener* listener) { - base::AutoLock auto_lock(lock_); - mouse_listeners_.AddObserver(listener); - if (!monitoring_mouse_) { - StartMouseMonitoring(); - monitoring_mouse_ = true; - DVLOG(2) << "Started mouse monitoring."; + mouse_listeners_->AddObserver(listener); + { + base::AutoLock auto_lock(lock_); + mouse_listeners_count_++; + if (mouse_listeners_count_ == 1) { + StartMouseMonitoring(); + DVLOG(2) << "Started mouse monitoring."; + } } } void UserInputMonitor::RemoveMouseListener(MouseEventListener* listener) { - base::AutoLock auto_lock(lock_); - mouse_listeners_.RemoveObserver(listener); - if (!mouse_listeners_.might_have_observers()) { - StopMouseMonitoring(); - monitoring_mouse_ = false; - DVLOG(2) << "Stopped mouse monitoring."; + mouse_listeners_->RemoveObserver(listener); + { + base::AutoLock auto_lock(lock_); + DCHECK_NE(mouse_listeners_count_, 0u); + mouse_listeners_count_--; + if (mouse_listeners_count_ == 0) { + StopMouseMonitoring(); + DVLOG(2) << "Stopped mouse monitoring."; + } } } @@ -63,12 +71,4 @@ void UserInputMonitor::DisableKeyPressMonitoring() { } } -void UserInputMonitor::OnMouseEvent(const SkIPoint& position) { - base::AutoLock auto_lock(lock_); - if (!monitoring_mouse_) - return; - FOR_EACH_OBSERVER( - MouseEventListener, mouse_listeners_, OnMouseMoved(position)); -} - } // namespace media diff --git a/media/base/user_input_monitor.h b/media/base/user_input_monitor.h index 567907c..ab572694 100644 --- a/media/base/user_input_monitor.h +++ b/media/base/user_input_monitor.h @@ -5,8 +5,9 @@ #ifndef MEDIA_BASE_USER_INPUT_MONITOR_H_ #define MEDIA_BASE_USER_INPUT_MONITOR_H_ +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/observer_list.h" +#include "base/observer_list_threadsafe.h" #include "base/synchronization/lock.h" #include "media/base/media_export.h" @@ -19,11 +20,8 @@ class SingleThreadTaskRunner; namespace media { // Monitors and notifies about mouse movements and keyboard events. -// Thread safe. The thread on which the listenters are called is not guaranteed. -// The callers should not perform expensive/blocking tasks in the callback since -// it might be called on the browser UI/IO threads. -// The object must outlive the browser UI/IO threads to make sure the callbacks -// will not access a deleted object. +// Thread safe. The listeners are called on the thread where the listeners are +// added. class MEDIA_EXPORT UserInputMonitor { public: // The interface to receive mouse movement events. @@ -35,6 +33,8 @@ class MEDIA_EXPORT UserInputMonitor { protected: virtual ~MouseEventListener() {} }; + typedef ObserverListThreadSafe<UserInputMonitor::MouseEventListener> + MouseListenerList; UserInputMonitor(); virtual ~UserInputMonitor(); @@ -66,20 +66,20 @@ class MEDIA_EXPORT UserInputMonitor { virtual size_t GetKeyPressCount() const = 0; protected: - // Called by the platform-specific sub-classes to propagate the events to the - // listeners. - void OnMouseEvent(const SkIPoint& position); + scoped_refptr<MouseListenerList> mouse_listeners() { + return mouse_listeners_; + } private: - virtual void StartMouseMonitoring() = 0; - virtual void StopMouseMonitoring() = 0; virtual void StartKeyboardMonitoring() = 0; virtual void StopKeyboardMonitoring() = 0; + virtual void StartMouseMonitoring() = 0; + virtual void StopMouseMonitoring() = 0; base::Lock lock_; - ObserverList<MouseEventListener, true> mouse_listeners_; - bool monitoring_mouse_; size_t key_press_counter_references_; + size_t mouse_listeners_count_; + scoped_refptr<MouseListenerList> mouse_listeners_; DISALLOW_COPY_AND_ASSIGN(UserInputMonitor); }; diff --git a/media/base/user_input_monitor_linux.cc b/media/base/user_input_monitor_linux.cc index 196b4e7..138de0a 100644 --- a/media/base/user_input_monitor_linux.cc +++ b/media/base/user_input_monitor_linux.cc @@ -15,10 +15,12 @@ #include "base/compiler_specific.h" #include "base/location.h" #include "base/logging.h" +#include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_pump_libevent.h" #include "base/posix/eintr_wrapper.h" #include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.h" #include "media/base/keyboard_event_counter.h" #include "third_party/skia/include/core/SkPoint.h" #include "ui/base/keycodes/keyboard_code_conversion_x.h" @@ -32,32 +34,28 @@ namespace media { namespace { -class UserInputMonitorLinux : public UserInputMonitor , - public base::MessagePumpLibevent::Watcher { +// This is the actual implementation of event monitoring. It's separated from +// UserInputMonitorLinux since it needs to be deleted on the IO thread. +class UserInputMonitorLinuxCore + : public base::MessagePumpLibevent::Watcher, + public base::SupportsWeakPtr<UserInputMonitorLinuxCore> { public: - explicit UserInputMonitorLinux( - const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner); - virtual ~UserInputMonitorLinux(); - - virtual size_t GetKeyPressCount() const OVERRIDE; - - private: enum EventType { MOUSE_EVENT, KEYBOARD_EVENT }; - virtual void StartMouseMonitoring() OVERRIDE; - virtual void StopMouseMonitoring() OVERRIDE; - virtual void StartKeyboardMonitoring() OVERRIDE; - virtual void StopKeyboardMonitoring() OVERRIDE; + explicit UserInputMonitorLinuxCore( + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, + const scoped_refptr<UserInputMonitor::MouseListenerList>& + mouse_listeners); + virtual ~UserInputMonitorLinuxCore(); - // - // The following methods must be called on the IO thread. - // + size_t GetKeyPressCount() const; void StartMonitor(EventType type); void StopMonitor(EventType type); + private: // base::MessagePumpLibevent::Watcher interface. virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; @@ -66,93 +64,70 @@ class UserInputMonitorLinux : public UserInputMonitor , void ProcessXEvent(xEvent* event); static void ProcessReply(XPointer self, XRecordInterceptData* data); - // Task runner on which X Window events are received. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; + scoped_refptr<ObserverListThreadSafe<UserInputMonitor::MouseEventListener> > + mouse_listeners_; // // The following members should only be accessed on the IO thread. // base::MessagePumpLibevent::FileDescriptorWatcher controller_; - Display* display_; + Display* x_control_display_; Display* x_record_display_; XRecordRange* x_record_range_[2]; XRecordContext x_record_context_; KeyboardEventCounter counter_; + DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinuxCore); +}; + +class UserInputMonitorLinux : public UserInputMonitor { + public: + explicit UserInputMonitorLinux( + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner); + virtual ~UserInputMonitorLinux(); + + // Public UserInputMonitor overrides. + virtual size_t GetKeyPressCount() const OVERRIDE; + + private: + // Private UserInputMonitor overrides. + virtual void StartKeyboardMonitoring() OVERRIDE; + virtual void StopKeyboardMonitoring() OVERRIDE; + virtual void StartMouseMonitoring() OVERRIDE; + virtual void StopMouseMonitoring() OVERRIDE; + + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; + UserInputMonitorLinuxCore* core_; + DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinux); }; -UserInputMonitorLinux::UserInputMonitorLinux( - const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) +UserInputMonitorLinuxCore::UserInputMonitorLinuxCore( + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, + const scoped_refptr<UserInputMonitor::MouseListenerList>& mouse_listeners) : io_task_runner_(io_task_runner), - display_(NULL), + mouse_listeners_(mouse_listeners), + x_control_display_(NULL), x_record_display_(NULL), x_record_context_(0) { x_record_range_[0] = NULL; x_record_range_[1] = NULL; } -UserInputMonitorLinux::~UserInputMonitorLinux() { - DCHECK(!display_); +UserInputMonitorLinuxCore::~UserInputMonitorLinuxCore() { + DCHECK(!x_control_display_); DCHECK(!x_record_display_); DCHECK(!x_record_range_[0]); DCHECK(!x_record_range_[1]); DCHECK(!x_record_context_); } -size_t UserInputMonitorLinux::GetKeyPressCount() const { +size_t UserInputMonitorLinuxCore::GetKeyPressCount() const { return counter_.GetKeyPressCount(); } -void UserInputMonitorLinux::StartMouseMonitoring() { - if (!io_task_runner_->BelongsToCurrentThread()) { - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&UserInputMonitorLinux::StartMonitor, - base::Unretained(this), - MOUSE_EVENT)); - return; - } - StartMonitor(MOUSE_EVENT); -} - -void UserInputMonitorLinux::StopMouseMonitoring() { - if (!io_task_runner_->BelongsToCurrentThread()) { - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&UserInputMonitorLinux::StopMonitor, - base::Unretained(this), - MOUSE_EVENT)); - return; - } - StopMonitor(MOUSE_EVENT); -} - -void UserInputMonitorLinux::StartKeyboardMonitoring() { - if (!io_task_runner_->BelongsToCurrentThread()) { - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&UserInputMonitorLinux::StartMonitor, - base::Unretained(this), - KEYBOARD_EVENT)); - return; - } - StartMonitor(KEYBOARD_EVENT); -} - -void UserInputMonitorLinux::StopKeyboardMonitoring() { - if (!io_task_runner_->BelongsToCurrentThread()) { - io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&UserInputMonitorLinux::StopMonitor, - base::Unretained(this), - KEYBOARD_EVENT)); - return; - } - StopMonitor(KEYBOARD_EVENT); -} - -void UserInputMonitorLinux::StartMonitor(EventType type) { +void UserInputMonitorLinuxCore::StartMonitor(EventType type) { DCHECK(io_task_runner_->BelongsToCurrentThread()); if (type == KEYBOARD_EVENT) @@ -163,19 +138,20 @@ void UserInputMonitorLinux::StartMonitor(EventType type) { // and both channels are used from a separate thread, we'll need to duplicate // them with something like the following: // XOpenDisplay(DisplayString(display)); - if (!display_) - display_ = XOpenDisplay(NULL); + if (!x_control_display_) + x_control_display_ = XOpenDisplay(NULL); if (!x_record_display_) x_record_display_ = XOpenDisplay(NULL); - if (!display_ || !x_record_display_) { + if (!x_control_display_ || !x_record_display_) { LOG(ERROR) << "Couldn't open X display"; return; } int xr_opcode, xr_event, xr_error; - if (!XQueryExtension(display_, "RECORD", &xr_opcode, &xr_event, &xr_error)) { + if (!XQueryExtension( + x_control_display_, "RECORD", &xr_opcode, &xr_event, &xr_error)) { LOG(ERROR) << "X Record extension not available."; return; } @@ -198,8 +174,8 @@ void UserInputMonitorLinux::StartMonitor(EventType type) { } if (x_record_context_) { - XRecordDisableContext(display_, x_record_context_); - XFlush(display_); + XRecordDisableContext(x_control_display_, x_record_context_); + XFlush(x_control_display_); XRecordFreeContext(x_record_display_, x_record_context_); x_record_context_ = 0; } @@ -222,7 +198,7 @@ void UserInputMonitorLinux::StartMonitor(EventType type) { if (!XRecordEnableContextAsync(x_record_display_, x_record_context_, - &UserInputMonitorLinux::ProcessReply, + &UserInputMonitorLinuxCore::ProcessReply, reinterpret_cast<XPointer>(this))) { LOG(ERROR) << "XRecordEnableContextAsync failed."; return; @@ -248,7 +224,7 @@ void UserInputMonitorLinux::StartMonitor(EventType type) { OnFileCanReadWithoutBlocking(ConnectionNumber(x_record_display_)); } -void UserInputMonitorLinux::StopMonitor(EventType type) { +void UserInputMonitorLinuxCore::StopMonitor(EventType type) { DCHECK(io_task_runner_->BelongsToCurrentThread()); if (x_record_range_[type]) { @@ -261,8 +237,8 @@ void UserInputMonitorLinux::StopMonitor(EventType type) { // Context must be disabled via the control channel because we can't send // any X protocol traffic over the data channel while it's recording. if (x_record_context_) { - XRecordDisableContext(display_, x_record_context_); - XFlush(display_); + XRecordDisableContext(x_control_display_, x_record_context_); + XFlush(x_control_display_); XRecordFreeContext(x_record_display_, x_record_context_); x_record_context_ = 0; @@ -271,14 +247,14 @@ void UserInputMonitorLinux::StopMonitor(EventType type) { XCloseDisplay(x_record_display_); x_record_display_ = NULL; } - if (display_) { - XCloseDisplay(display_); - display_ = NULL; + if (x_control_display_) { + XCloseDisplay(x_control_display_); + x_control_display_ = NULL; } } } -void UserInputMonitorLinux::OnFileCanReadWithoutBlocking(int fd) { +void UserInputMonitorLinuxCore::OnFileCanReadWithoutBlocking(int fd) { DCHECK(io_task_runner_->BelongsToCurrentThread()); XEvent event; // Fetch pending events if any. @@ -287,16 +263,17 @@ void UserInputMonitorLinux::OnFileCanReadWithoutBlocking(int fd) { } } -void UserInputMonitorLinux::OnFileCanWriteWithoutBlocking(int fd) { +void UserInputMonitorLinuxCore::OnFileCanWriteWithoutBlocking(int fd) { NOTREACHED(); } -void UserInputMonitorLinux::ProcessXEvent(xEvent* event) { +void UserInputMonitorLinuxCore::ProcessXEvent(xEvent* event) { DCHECK(io_task_runner_->BelongsToCurrentThread()); if (event->u.u.type == MotionNotify) { SkIPoint position(SkIPoint::Make(event->u.keyButtonPointer.rootX, event->u.keyButtonPointer.rootY)); - OnMouseEvent(position); + mouse_listeners_->Notify( + &UserInputMonitor::MouseEventListener::OnMouseMoved, position); } else { ui::EventType type; if (event->u.u.type == KeyPress) { @@ -308,22 +285,71 @@ void UserInputMonitorLinux::ProcessXEvent(xEvent* event) { return; } - KeySym key_sym = XkbKeycodeToKeysym(display_, event->u.u.detail, 0, 0); + KeySym key_sym = + XkbKeycodeToKeysym(x_control_display_, event->u.u.detail, 0, 0); ui::KeyboardCode key_code = ui::KeyboardCodeFromXKeysym(key_sym); counter_.OnKeyboardEvent(type, key_code); } } // static -void UserInputMonitorLinux::ProcessReply(XPointer self, - XRecordInterceptData* data) { +void UserInputMonitorLinuxCore::ProcessReply(XPointer self, + XRecordInterceptData* data) { if (data->category == XRecordFromServer) { xEvent* event = reinterpret_cast<xEvent*>(data->data); - reinterpret_cast<UserInputMonitorLinux*>(self)->ProcessXEvent(event); + reinterpret_cast<UserInputMonitorLinuxCore*>(self)->ProcessXEvent(event); } XRecordFreeData(data); } +// +// Implementation of UserInputMonitorLinux. +// + +UserInputMonitorLinux::UserInputMonitorLinux( + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner) + : io_task_runner_(io_task_runner), + core_(new UserInputMonitorLinuxCore(io_task_runner, mouse_listeners())) {} + +UserInputMonitorLinux::~UserInputMonitorLinux() { + if (!io_task_runner_->DeleteSoon(FROM_HERE, core_)) + delete core_; +} + +size_t UserInputMonitorLinux::GetKeyPressCount() const { + return core_->GetKeyPressCount(); +} + +void UserInputMonitorLinux::StartKeyboardMonitoring() { + io_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserInputMonitorLinuxCore::StartMonitor, + core_->AsWeakPtr(), + UserInputMonitorLinuxCore::KEYBOARD_EVENT)); +} + +void UserInputMonitorLinux::StopKeyboardMonitoring() { + io_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserInputMonitorLinuxCore::StopMonitor, + core_->AsWeakPtr(), + UserInputMonitorLinuxCore::KEYBOARD_EVENT)); +} + +void UserInputMonitorLinux::StartMouseMonitoring() { + io_task_runner_->PostTask(FROM_HERE, + base::Bind(&UserInputMonitorLinuxCore::StartMonitor, + core_->AsWeakPtr(), + UserInputMonitorLinuxCore::MOUSE_EVENT)); +} + +void UserInputMonitorLinux::StopMouseMonitoring() { + io_task_runner_->PostTask(FROM_HERE, + base::Bind(&UserInputMonitorLinuxCore::StopMonitor, + core_->AsWeakPtr(), + UserInputMonitorLinuxCore::MOUSE_EVENT)); +} + } // namespace scoped_ptr<UserInputMonitor> UserInputMonitor::Create( diff --git a/media/base/user_input_monitor_mac.cc b/media/base/user_input_monitor_mac.cc index 3d19134..f70ee4d 100644 --- a/media/base/user_input_monitor_mac.cc +++ b/media/base/user_input_monitor_mac.cc @@ -17,10 +17,10 @@ class UserInputMonitorMac : public UserInputMonitor { virtual size_t GetKeyPressCount() const OVERRIDE; private: - virtual void StartMouseMonitoring() OVERRIDE; - virtual void StopMouseMonitoring() OVERRIDE; virtual void StartKeyboardMonitoring() OVERRIDE; virtual void StopKeyboardMonitoring() OVERRIDE; + virtual void StartMouseMonitoring() OVERRIDE; + virtual void StopMouseMonitoring() OVERRIDE; DISALLOW_COPY_AND_ASSIGN(UserInputMonitorMac); }; @@ -36,16 +36,16 @@ size_t UserInputMonitorMac::GetKeyPressCount() const { kCGEventKeyDown); } +void UserInputMonitorMac::StartKeyboardMonitoring() {} + +void UserInputMonitorMac::StopKeyboardMonitoring() {} + // TODO(jiayl): add the impl. void UserInputMonitorMac::StartMouseMonitoring() { NOTIMPLEMENTED(); } // TODO(jiayl): add the impl. void UserInputMonitorMac::StopMouseMonitoring() { NOTIMPLEMENTED(); } -void UserInputMonitorMac::StartKeyboardMonitoring() {} - -void UserInputMonitorMac::StopKeyboardMonitoring() {} - } // namespace scoped_ptr<UserInputMonitor> UserInputMonitor::Create( diff --git a/media/base/user_input_monitor_unittest.cc b/media/base/user_input_monitor_unittest.cc new file mode 100644 index 0000000..4874a10 --- /dev/null +++ b/media/base/user_input_monitor_unittest.cc @@ -0,0 +1,78 @@ +// Copyright 2013 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/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "media/base/keyboard_event_counter.h" +#include "media/base/user_input_monitor.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkPoint.h" + +namespace media { + +class MockMouseListener : public UserInputMonitor::MouseEventListener { + public: + MOCK_METHOD1(OnMouseMoved, void(const SkIPoint& position)); + + virtual ~MockMouseListener() {} +}; + +#if defined(OS_LINUX) || defined(OS_WIN) +TEST(UserInputMonitorTest, KeyPressCounter) { + KeyboardEventCounter counter; + + EXPECT_EQ(0u, counter.GetKeyPressCount()); + + counter.OnKeyboardEvent(ui::ET_KEY_PRESSED, ui::VKEY_0); + EXPECT_EQ(1u, counter.GetKeyPressCount()); + + // Holding the same key without releasing it does not increase the count. + counter.OnKeyboardEvent(ui::ET_KEY_PRESSED, ui::VKEY_0); + EXPECT_EQ(1u, counter.GetKeyPressCount()); + + // Releasing the key does not affect the total count. + counter.OnKeyboardEvent(ui::ET_KEY_RELEASED, ui::VKEY_0); + EXPECT_EQ(1u, counter.GetKeyPressCount()); + + counter.OnKeyboardEvent(ui::ET_KEY_PRESSED, ui::VKEY_0); + counter.OnKeyboardEvent(ui::ET_KEY_RELEASED, ui::VKEY_0); + EXPECT_EQ(2u, counter.GetKeyPressCount()); +} +#endif // defined(OS_LINUX) || defined(OS_WIN) + +TEST(UserInputMonitorTest, CreatePlatformSpecific) { +#if defined(OS_LINUX) + base::MessageLoop message_loop(base::MessageLoop::TYPE_IO); +#else + base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); +#endif // defined(OS_LINUX) + + base::RunLoop run_loop; + scoped_ptr<UserInputMonitor> monitor = UserInputMonitor::Create( + message_loop.message_loop_proxy(), message_loop.message_loop_proxy()); + + if (!monitor) + return; + + MockMouseListener listener; + // Ignore any callbacks. + EXPECT_CALL(listener, OnMouseMoved(testing::_)).Times(testing::AnyNumber()); + +#if !defined(OS_MACOSX) + monitor->AddMouseListener(&listener); + monitor->RemoveMouseListener(&listener); +#endif // !define(OS_MACOSX) + + monitor->EnableKeyPressMonitoring(); + monitor->DisableKeyPressMonitoring(); + + monitor.reset(); + run_loop.RunUntilIdle(); +} + +} // namespace media diff --git a/media/base/user_input_monitor_win.cc b/media/base/user_input_monitor_win.cc index 4ffad42..bf923c7 100644 --- a/media/base/user_input_monitor_win.cc +++ b/media/base/user_input_monitor_win.cc @@ -4,13 +4,295 @@ #include "media/base/user_input_monitor.h" +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/stringprintf.h" +#include "base/synchronization/lock.h" +#include "base/win/message_window.h" +#include "media/base/keyboard_event_counter.h" +#include "third_party/skia/include/core/SkPoint.h" +#include "ui/base/keycodes/keyboard_code_conversion_win.h" + namespace media { +namespace { + +// From the HID Usage Tables specification. +const USHORT kGenericDesktopPage = 1; +const USHORT kMouseUsage = 2; +const USHORT kKeyboardUsage = 6; + +// This is the actual implementation of event monitoring. It's separated from +// UserInputMonitorWin since it needs to be deleted on the UI thread. +class UserInputMonitorWinCore + : public base::SupportsWeakPtr<UserInputMonitorWinCore> { + public: + enum EventBitMask { + MOUSE_EVENT_MASK = 1, + KEYBOARD_EVENT_MASK = 2, + }; + + explicit UserInputMonitorWinCore( + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, + const scoped_refptr<UserInputMonitor::MouseListenerList>& + mouse_listeners); + ~UserInputMonitorWinCore(); + + size_t GetKeyPressCount() const; + void StartMonitor(EventBitMask type); + void StopMonitor(EventBitMask type); + + private: + // Handles WM_INPUT messages. + LRESULT OnInput(HRAWINPUT input_handle); + // Handles messages received by |window_|. + bool HandleMessage(UINT message, + WPARAM wparam, + LPARAM lparam, + LRESULT* result); + RAWINPUTDEVICE* GetRawInputDevices(EventBitMask event, DWORD flags); + + // Task runner on which |window_| is created. + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; + scoped_refptr<ObserverListThreadSafe<UserInputMonitor::MouseEventListener> > + mouse_listeners_; + + // These members are only accessed on the UI thread. + scoped_ptr<base::win::MessageWindow> window_; + uint8 events_monitored_; + KeyboardEventCounter counter_; + + DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWinCore); +}; + +class UserInputMonitorWin : public UserInputMonitor { + public: + explicit UserInputMonitorWin( + const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner); + virtual ~UserInputMonitorWin(); + + // Public UserInputMonitor overrides. + virtual size_t GetKeyPressCount() const OVERRIDE; + + private: + // Private UserInputMonitor overrides. + virtual void StartKeyboardMonitoring() OVERRIDE; + virtual void StopKeyboardMonitoring() OVERRIDE; + virtual void StartMouseMonitoring() OVERRIDE; + virtual void StopMouseMonitoring() OVERRIDE; + + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; + UserInputMonitorWinCore* core_; + + DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWin); +}; + +UserInputMonitorWinCore::UserInputMonitorWinCore( + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, + const scoped_refptr<UserInputMonitor::MouseListenerList>& mouse_listeners) + : ui_task_runner_(ui_task_runner), + mouse_listeners_(mouse_listeners), + events_monitored_(0) {} + +UserInputMonitorWinCore::~UserInputMonitorWinCore() { + DCHECK(!window_); + DCHECK(!events_monitored_); +} + +size_t UserInputMonitorWinCore::GetKeyPressCount() const { + return counter_.GetKeyPressCount(); +} + +void UserInputMonitorWinCore::StartMonitor(EventBitMask type) { + DCHECK(ui_task_runner_->BelongsToCurrentThread()); + + if (events_monitored_ & type) + return; + + if (type == KEYBOARD_EVENT_MASK) + counter_.Reset(); + + if (!window_) { + window_.reset(new base::win::MessageWindow()); + if (!window_->Create(base::Bind(&UserInputMonitorWinCore::HandleMessage, + base::Unretained(this)))) { + LOG_GETLASTERROR(ERROR) << "Failed to create the raw input window"; + window_.reset(); + return; + } + } + + // Register to receive raw mouse and/or keyboard input. + scoped_ptr<RAWINPUTDEVICE> device(GetRawInputDevices(type, RIDEV_INPUTSINK)); + if (!RegisterRawInputDevices(device.get(), 1, sizeof(*device))) { + LOG_GETLASTERROR(ERROR) + << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK"; + return; + } + events_monitored_ |= type; +} + +void UserInputMonitorWinCore::StopMonitor(EventBitMask type) { + DCHECK(ui_task_runner_->BelongsToCurrentThread()); + + if (!(events_monitored_ & type)) + return; + + // Stop receiving raw input. + DCHECK(window_); + scoped_ptr<RAWINPUTDEVICE> device(GetRawInputDevices(type, RIDEV_REMOVE)); + + if (!RegisterRawInputDevices(device.get(), 1, sizeof(*device))) { + LOG_GETLASTERROR(INFO) + << "RegisterRawInputDevices() failed for RIDEV_REMOVE"; + } + + events_monitored_ &= ~type; + if (events_monitored_ == 0) + window_.reset(); +} + +LRESULT UserInputMonitorWinCore::OnInput(HRAWINPUT input_handle) { + DCHECK(ui_task_runner_->BelongsToCurrentThread()); + + // 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; + } + DCHECK_EQ(0u, result); + + // Retrieve the input record itself. + scoped_ptr<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; + } + DCHECK_EQ(size, result); + + // Notify the observer about events generated locally. + if (input->header.dwType == RIM_TYPEMOUSE && input->header.hDevice != NULL) { + POINT position; + if (!GetCursorPos(&position)) { + position.x = 0; + position.y = 0; + } + mouse_listeners_->Notify( + &UserInputMonitor::MouseEventListener::OnMouseMoved, + SkIPoint::Make(position.x, position.y)); + } else if (input->header.dwType == RIM_TYPEKEYBOARD && + input->header.hDevice != NULL) { + ui::EventType event = (input->data.keyboard.Flags & RI_KEY_BREAK) + ? ui::ET_KEY_RELEASED + : ui::ET_KEY_PRESSED; + ui::KeyboardCode key_code = + ui::KeyboardCodeForWindowsKeyCode(input->data.keyboard.VKey); + counter_.OnKeyboardEvent(event, key_code); + } + + return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER)); +} + +bool UserInputMonitorWinCore::HandleMessage(UINT message, + WPARAM wparam, + LPARAM lparam, + LRESULT* result) { + DCHECK(ui_task_runner_->BelongsToCurrentThread()); + + switch (message) { + case WM_INPUT: + *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam)); + return true; + + default: + return false; + } +} + +RAWINPUTDEVICE* UserInputMonitorWinCore::GetRawInputDevices(EventBitMask event, + DWORD flags) { + DCHECK(ui_task_runner_->BelongsToCurrentThread()); + + scoped_ptr<RAWINPUTDEVICE> device(new RAWINPUTDEVICE()); + if (event == MOUSE_EVENT_MASK) { + device->dwFlags = flags; + device->usUsagePage = kGenericDesktopPage; + device->usUsage = kMouseUsage; + device->hwndTarget = window_->hwnd(); + } else { + DCHECK_EQ(KEYBOARD_EVENT_MASK, event); + device->dwFlags = flags; + device->usUsagePage = kGenericDesktopPage; + device->usUsage = kKeyboardUsage; + device->hwndTarget = window_->hwnd(); + } + return device.release(); +} + +// +// Implementation of UserInputMonitorWin. +// + +UserInputMonitorWin::UserInputMonitorWin( + const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) + : ui_task_runner_(ui_task_runner), + core_(new UserInputMonitorWinCore(ui_task_runner, mouse_listeners())) {} + +UserInputMonitorWin::~UserInputMonitorWin() { + if (!ui_task_runner_->DeleteSoon(FROM_HERE, core_)) + delete core_; +} + +size_t UserInputMonitorWin::GetKeyPressCount() const { + return core_->GetKeyPressCount(); +} + +void UserInputMonitorWin::StartKeyboardMonitoring() { + ui_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserInputMonitorWinCore::StartMonitor, + core_->AsWeakPtr(), + UserInputMonitorWinCore::KEYBOARD_EVENT_MASK)); +} + +void UserInputMonitorWin::StopKeyboardMonitoring() { + ui_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserInputMonitorWinCore::StopMonitor, + core_->AsWeakPtr(), + UserInputMonitorWinCore::KEYBOARD_EVENT_MASK)); +} + +void UserInputMonitorWin::StartMouseMonitoring() { + ui_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserInputMonitorWinCore::StartMonitor, + core_->AsWeakPtr(), + UserInputMonitorWinCore::MOUSE_EVENT_MASK)); +} + +void UserInputMonitorWin::StopMouseMonitoring() { + ui_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UserInputMonitorWinCore::StopMonitor, + core_->AsWeakPtr(), + UserInputMonitorWinCore::MOUSE_EVENT_MASK)); +} + +} // namespace -// TODO(jiayl): add the implementation. scoped_ptr<UserInputMonitor> UserInputMonitor::Create( - const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner, + const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) { - return scoped_ptr<UserInputMonitor>(); + return scoped_ptr<UserInputMonitor>(new UserInputMonitorWin(ui_task_runner)); } } // namespace media diff --git a/media/media.gyp b/media/media.gyp index 1bdcbca..eda33b6 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -548,15 +548,6 @@ 'webm/chromeos/webm_encoder.cc', 'webm/chromeos/webm_encoder.h', ], - 'defines': [ - # TODO(jiayl): figure out why MediaStreamInfoBarTest. - # DenyingCameraDoesNotCauseStickyDenyForMics fails on ChromeOS and - # remove this. - 'DISABLE_USER_INPUT_MONITOR', - ], - 'sources!': [ - 'base/user_input_monitor_linux.cc', - ], }], ['use_alsa==1', { 'link_settings': { @@ -635,8 +626,6 @@ 'audio/cras/cras_input.h', 'audio/cras/cras_unified.cc', 'audio/cras/cras_unified.h', - 'base/keyboard_event_counter.cc', - 'base/keyboard_event_counter.h', ], }], ['use_pulseaudio==1', { @@ -830,6 +819,12 @@ 'ENABLE_EAC3_PLAYBACK', ], }], + ['OS!="linux" and OS!="win"', { + 'sources!': [ + 'base/keyboard_event_counter.cc', + 'base/keyboard_event_counter.h', + ], + }], ], }, { @@ -912,6 +907,7 @@ 'base/sinc_resampler_unittest.cc', 'base/test_data_util.cc', 'base/test_data_util.h', + 'base/user_input_monitor_unittest.cc', 'base/vector_math_testing.h', 'base/vector_math_unittest.cc', 'base/video_frame_unittest.cc', |