summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorjiayl@chromium.org <jiayl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-14 21:28:11 +0000
committerjiayl@chromium.org <jiayl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-14 21:28:11 +0000
commita8399162b23412192248fbb6895d93f09eabc440 (patch)
tree59d494fc77bd8de0a3f532be64a65e92f5935c33 /media
parent3693a5125ee590c9d6324c6306ec65d48e39feed (diff)
downloadchromium_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.h3
-rw-r--r--media/base/user_input_monitor.cc46
-rw-r--r--media/base/user_input_monitor.h26
-rw-r--r--media/base/user_input_monitor_linux.cc214
-rw-r--r--media/base/user_input_monitor_mac.cc12
-rw-r--r--media/base/user_input_monitor_unittest.cc78
-rw-r--r--media/base/user_input_monitor_win.cc288
-rw-r--r--media/media.gyp18
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',