Chromium Code Reviews| 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. |
|
DaleCurtis
2013/08/29 19:02:42
You'll need to find mark@'s equivalent for the Win
|
| +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; |
| + |
| + private: |
| + enum EventBitMask { |
| + MOUSE_EVENT_MASK = 1, |
| + KEYBOARD_EVENT_MASK = 2, |
| + }; |
| + static int NumberOfRawInputDevices(uint8 target_events); |
| + |
| + virtual void StartMouseMonitoring() OVERRIDE; |
| + 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); |
| + |
| + // Task runner on which |window_| is created. |
| + scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; |
| + |
| + // |
| + // Members only accessed on the UI thread. |
| + // |
| + // 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), |
| + MOUSE_EVENT_MASK)); |
| + return; |
| + } |
| + StartMonitor(MOUSE_EVENT_MASK); |
| +} |
| + |
| +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()); |
| + if (events_monitored_ & type) |
| + return; |
| + |
| + if (type == KEYBOARD_EVENT_MASK) |
| + counter_.Reset(); |
| + |
| + events_monitored_ |= type; |
| + |
| + window_.reset(new base::win::MessageWindow()); |
| + 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_) { |
| + scoped_ptr<RAWINPUTDEVICE[]> devices( |
| + GetRawInputDevices(type, RIDEV_REMOVE)); |
| + |
| + // The error is harmless, ignore it. |
| + 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)); |
| + 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) { |
| + 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(), |
| + 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 |