Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Unified Diff: media/base/user_input_monitor_win.cc

Issue 23702008: Adds the UserInputMonitor implementation for Windows. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/base/user_input_monitor_unittest.cc ('k') | media/media.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/base/user_input_monitor_win.cc
diff --git a/media/base/user_input_monitor_win.cc b/media/base/user_input_monitor_win.cc
index 4ffad42a72d9b479d1611e4a10fea96ae41e0e5d..be3a92394525d95da3e9b39270cfc26d10ce27e5 100644
--- a/media/base/user_input_monitor_win.cc
+++ b/media/base/user_input_monitor_win.cc
@@ -4,13 +4,278 @@
#include "media/base/user_input_monitor.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.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;
+
+class UserInputMonitorWin : public UserInputMonitor {
+ public:
+ explicit UserInputMonitorWin(
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+ ~UserInputMonitorWin();
+
+ virtual size_t GetKeyPressCount() const OVERRIDE;
Wez 2013/09/03 20:07:04 nit: Suggest comment "UserInputMonitor public inte
jiayl 2013/09/03 23:44:12 Done.
+
+ private:
+ enum EventBitMask {
+ MOUSE_EVENT_MASK = 1,
+ KEYBOARD_EVENT_MASK = 2,
+ };
+ static int NumberOfRawInputDevices(uint8 target_events);
Wez 2013/09/03 20:07:04 nit: |target_events| -> |event_mask| to make it cl
jiayl 2013/09/03 23:44:12 Done.
+
+ virtual void StartMouseMonitoring() OVERRIDE;
Wez 2013/09/03 20:07:04 nit: Suggest comment "UserInputMonitor private int
jiayl 2013/09/03 23:44:12 Done.
jiayl 2013/09/03 23:44:12 Done.
+ virtual void StopMouseMonitoring() OVERRIDE;
+ virtual void StartKeyboardMonitoring() OVERRIDE;
+ virtual void StopKeyboardMonitoring() OVERRIDE;
+
+ //
+ // The following methods are only called on the UI message loop.
+ //
+
+ void StartMonitor(EventBitMask type);
+ void StopMonitor(EventBitMask type);
+
+ // 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(uint8 target_events, DWORD flags);
Wez 2013/09/03 20:07:04 If you make this return std::vector<RAWINPUTDEVICE
jiayl 2013/09/03 23:44:12 Done.
+
+ // Task runner on which |window_| is created.
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+
+ //
+ // Members only accessed on the UI thread.
Wez 2013/09/03 20:07:04 If all the methods above are called only on the UI
jiayl 2013/09/03 23:44:12 Done.
+ //
+ // Used to receive raw input.
+ scoped_ptr<base::win::MessageWindow> window_;
+ uint8 events_monitored_;
+ KeyboardEventCounter counter_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWin);
+};
+
+UserInputMonitorWin::UserInputMonitorWin(
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
+ : ui_task_runner_(ui_task_runner), events_monitored_(0) {}
+
+UserInputMonitorWin::~UserInputMonitorWin() {
+ DCHECK(!window_);
+ DCHECK(!events_monitored_);
+}
+
+size_t UserInputMonitorWin::GetKeyPressCount() const {
+ return counter_.GetKeyPressCount();
+}
+
+int UserInputMonitorWin::NumberOfRawInputDevices(uint8 target_events) {
+ int number = 0;
+ if (target_events & MOUSE_EVENT_MASK)
+ number++;
+ if (target_events & KEYBOARD_EVENT_MASK)
+ number++;
+ return number;
+}
+
+void UserInputMonitorWin::StartMouseMonitoring() {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&UserInputMonitorWin::StartMonitor,
+ base::Unretained(this),
Wez 2013/09/03 20:07:04 This will use-after-free if the StartMouseMonitori
jiayl 2013/09/03 21:41:28 You are right. I was assuming the caller should ma
DaleCurtis 2013/09/03 22:59:32 Isn't it invalid for a client to call StartMouseMo
jiayl 2013/09/03 23:44:12 Done.
+ MOUSE_EVENT_MASK));
+ return;
+ }
+ StartMonitor(MOUSE_EVENT_MASK);
Wez 2013/09/03 20:07:04 This path will never be hit unless the caller is a
jiayl 2013/09/03 23:44:12 Done.
+}
+
+void UserInputMonitorWin::StopMouseMonitoring() {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&UserInputMonitorWin::StopMonitor,
+ base::Unretained(this),
+ MOUSE_EVENT_MASK));
+ return;
+ }
+ StopMonitor(MOUSE_EVENT_MASK);
+}
+
+void UserInputMonitorWin::StartKeyboardMonitoring() {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&UserInputMonitorWin::StartMonitor,
+ base::Unretained(this),
+ KEYBOARD_EVENT_MASK));
+ return;
+ }
+ StartMonitor(KEYBOARD_EVENT_MASK);
+}
+
+void UserInputMonitorWin::StopKeyboardMonitoring() {
+ if (!ui_task_runner_->BelongsToCurrentThread()) {
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&UserInputMonitorWin::StopMonitor,
+ base::Unretained(this),
+ KEYBOARD_EVENT_MASK));
+ return;
+ }
+ StopMonitor(KEYBOARD_EVENT_MASK);
+}
+
+void UserInputMonitorWin::StartMonitor(EventBitMask type) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
Wez 2013/09/03 20:07:04 nit: I prefer pre-condition DCHECKs to be followed
jiayl 2013/09/03 23:44:12 Done.
+ if (events_monitored_ & type)
+ return;
+
+ if (type == KEYBOARD_EVENT_MASK)
+ counter_.Reset();
+
+ events_monitored_ |= type;
+
+ window_.reset(new base::win::MessageWindow());
Wez 2013/09/03 20:07:04 Why re-create the window every time a new event is
jiayl 2013/09/03 23:44:12 Done.
+ if (!window_->Create(base::Bind(&UserInputMonitorWin::HandleMessage,
+ base::Unretained(this)))) {
+ LOG_GETLASTERROR(ERROR) << "Failed to create the raw input window";
+ window_.reset();
+ }
+}
+
+void UserInputMonitorWin::StopMonitor(EventBitMask type) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ if (!(events_monitored_ & type))
+ return;
+
+ // Stop receiving raw input.
+ if (window_) {
Wez 2013/09/03 20:07:04 Clarify that |window_| may be NULL if we failed to
jiayl 2013/09/03 23:44:12 Done.
+ scoped_ptr<RAWINPUTDEVICE[]> devices(
+ GetRawInputDevices(type, RIDEV_REMOVE));
+
+ // The error is harmless, ignore it.
Wez 2013/09/03 20:07:04 Which error? Why not trap & at least LOG_SYSERR(WA
jiayl 2013/09/03 23:44:12 Done.
+ RegisterRawInputDevices(devices.get(), 1, sizeof(devices[0]));
+
+ events_monitored_ &= ~type;
+ if (events_monitored_ == 0)
+ window_.reset();
+ }
+}
+
+LRESULT UserInputMonitorWin::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));
Wez 2013/09/03 20:07:04 nit: Verify that |result| is not greater than zero
jiayl 2013/09/03 23:44:12 Done.
+ if (result == -1) {
+ LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed";
+ return 0;
+ }
+
+ // 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) {
Wez 2013/09/03 20:07:04 nit: See above re verifying |result|
jiayl 2013/09/03 23:44:12 Done.
+ LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed";
+ return 0;
+ }
+
+ // 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;
+ }
+ OnMouseEvent(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 UserInputMonitorWin::HandleMessage(UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT* result) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ switch (message) {
+ case WM_CREATE: {
+ // Register to receive raw mouse and/or keyboard input.
+ scoped_ptr<RAWINPUTDEVICE[]> devices(
+ GetRawInputDevices(events_monitored_, RIDEV_INPUTSINK));
+ if (RegisterRawInputDevices(devices.get(),
Wez 2013/09/03 20:07:04 Have you checked the semantics if e.g. two separat
jiayl 2013/09/03 23:44:12 Hmm...I verified that only one window will receive
+ NumberOfRawInputDevices(events_monitored_),
+ sizeof(devices[0]))) {
+ *result = 0;
+ } else {
+ LOG_GETLASTERROR(ERROR) << "RegisterRawInputDevices() failed";
+ *result = -1;
+ }
+ return true;
+ }
+
+ case WM_INPUT:
+ *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+RAWINPUTDEVICE* UserInputMonitorWin::GetRawInputDevices(uint8 target_events,
+ DWORD flags) {
+ DCHECK(ui_task_runner_->BelongsToCurrentThread());
+ RAWINPUTDEVICE* devices =
+ new RAWINPUTDEVICE[NumberOfRawInputDevices(target_events)];
+ int index = 0;
+ if (target_events & MOUSE_EVENT_MASK) {
+ devices[index].dwFlags = flags;
+ devices[index].usUsagePage = kGenericDesktopPage;
+ devices[index].usUsage = kMouseUsage;
+ devices[index].hwndTarget = window_->hwnd();
+ index++;
+ }
+ if (target_events & KEYBOARD_EVENT_MASK) {
+ devices[index].dwFlags = flags;
+ devices[index].usUsagePage = kGenericDesktopPage;
+ devices[index].usUsage = kKeyboardUsage;
+ devices[index].hwndTarget = window_->hwnd();
+ index++;
+ }
+ return devices;
+}
+
+} // 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
« no previous file with comments | « media/base/user_input_monitor_unittest.cc ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698