1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "media/base/user_input_monitor.h" 6 7 #include "base/bind.h" 8 #include "base/location.h" 9 #include "base/logging.h" 10 #include "base/memory/weak_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/single_thread_task_runner.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/synchronization/lock.h" 15 #include "base/win/message_window.h" 16 #include "media/base/keyboard_event_counter.h" 17 #include "third_party/skia/include/core/SkPoint.h" 18 #include "ui/events/keycodes/keyboard_code_conversion_win.h" 19 20 namespace media { 21 namespace { 22 23 // From the HID Usage Tables specification. 24 const USHORT kGenericDesktopPage = 1; 25 const USHORT kMouseUsage = 2; 26 const USHORT kKeyboardUsage = 6; 27 28 // This is the actual implementation of event monitoring. It's separated from 29 // UserInputMonitorWin since it needs to be deleted on the UI thread. 30 class UserInputMonitorWinCore 31 : public base::SupportsWeakPtr<UserInputMonitorWinCore>, 32 public base::MessageLoop::DestructionObserver { 33 public: 34 enum EventBitMask { 35 MOUSE_EVENT_MASK = 1, 36 KEYBOARD_EVENT_MASK = 2, 37 }; 38 39 explicit UserInputMonitorWinCore( 40 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, 41 const scoped_refptr<UserInputMonitor::MouseListenerList>& 42 mouse_listeners); 43 ~UserInputMonitorWinCore(); 44 45 // DestructionObserver overrides. 46 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; 47 48 size_t GetKeyPressCount() const; 49 void StartMonitor(EventBitMask type); 50 void StopMonitor(EventBitMask type); 51 52 private: 53 // Handles WM_INPUT messages. 54 LRESULT OnInput(HRAWINPUT input_handle); 55 // Handles messages received by |window_|. 56 bool HandleMessage(UINT message, 57 WPARAM wparam, 58 LPARAM lparam, 59 LRESULT* result); 60 RAWINPUTDEVICE* GetRawInputDevices(EventBitMask event, DWORD flags); 61 62 // Task runner on which |window_| is created. 63 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; 64 scoped_refptr<ObserverListThreadSafe<UserInputMonitor::MouseEventListener> > 65 mouse_listeners_; 66 67 // These members are only accessed on the UI thread. 68 scoped_ptr<base::win::MessageWindow> window_; 69 uint8 events_monitored_; 70 KeyboardEventCounter counter_; 71 72 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWinCore); 73 }; 74 75 class UserInputMonitorWin : public UserInputMonitor { 76 public: 77 explicit UserInputMonitorWin( 78 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner); 79 virtual ~UserInputMonitorWin(); 80 81 // Public UserInputMonitor overrides. 82 virtual size_t GetKeyPressCount() const OVERRIDE; 83 84 private: 85 // Private UserInputMonitor overrides. 86 virtual void StartKeyboardMonitoring() OVERRIDE; 87 virtual void StopKeyboardMonitoring() OVERRIDE; 88 virtual void StartMouseMonitoring() OVERRIDE; 89 virtual void StopMouseMonitoring() OVERRIDE; 90 91 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; 92 UserInputMonitorWinCore* core_; 93 94 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWin); 95 }; 96 97 UserInputMonitorWinCore::UserInputMonitorWinCore( 98 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, 99 const scoped_refptr<UserInputMonitor::MouseListenerList>& mouse_listeners) 100 : ui_task_runner_(ui_task_runner), 101 mouse_listeners_(mouse_listeners), 102 events_monitored_(0) {} 103 104 UserInputMonitorWinCore::~UserInputMonitorWinCore() { 105 DCHECK(!window_); 106 DCHECK(!events_monitored_); 107 } 108 109 void UserInputMonitorWinCore::WillDestroyCurrentMessageLoop() { 110 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 111 StopMonitor(MOUSE_EVENT_MASK); 112 StopMonitor(KEYBOARD_EVENT_MASK); 113 } 114 115 size_t UserInputMonitorWinCore::GetKeyPressCount() const { 116 return counter_.GetKeyPressCount(); 117 } 118 119 void UserInputMonitorWinCore::StartMonitor(EventBitMask type) { 120 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 121 122 if (events_monitored_ & type) 123 return; 124 125 if (type == KEYBOARD_EVENT_MASK) 126 counter_.Reset(); 127 128 if (!window_) { 129 window_.reset(new base::win::MessageWindow()); 130 if (!window_->Create(base::Bind(&UserInputMonitorWinCore::HandleMessage, 131 base::Unretained(this)))) { 132 LOG_GETLASTERROR(ERROR) << "Failed to create the raw input window"; 133 window_.reset(); 134 return; 135 } 136 } 137 138 // Register to receive raw mouse and/or keyboard input. 139 scoped_ptr<RAWINPUTDEVICE> device(GetRawInputDevices(type, RIDEV_INPUTSINK)); 140 if (!RegisterRawInputDevices(device.get(), 1, sizeof(*device))) { 141 LOG_GETLASTERROR(ERROR) 142 << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK"; 143 window_.reset(); 144 return; 145 } 146 147 // Start observing message loop destruction if we start monitoring the first 148 // event. 149 if (!events_monitored_) 150 base::MessageLoop::current()->AddDestructionObserver(this); 151 152 events_monitored_ |= type; 153 } 154 155 void UserInputMonitorWinCore::StopMonitor(EventBitMask type) { 156 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 157 158 if (!(events_monitored_ & type)) 159 return; 160 161 // Stop receiving raw input. 162 DCHECK(window_); 163 scoped_ptr<RAWINPUTDEVICE> device(GetRawInputDevices(type, RIDEV_REMOVE)); 164 165 if (!RegisterRawInputDevices(device.get(), 1, sizeof(*device))) { 166 LOG_GETLASTERROR(INFO) 167 << "RegisterRawInputDevices() failed for RIDEV_REMOVE"; 168 } 169 170 events_monitored_ &= ~type; 171 if (events_monitored_ == 0) { 172 window_.reset(); 173 174 // Stop observing message loop destruction if no event is being monitored. 175 base::MessageLoop::current()->RemoveDestructionObserver(this); 176 } 177 } 178 179 LRESULT UserInputMonitorWinCore::OnInput(HRAWINPUT input_handle) { 180 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 181 182 // Get the size of the input record. 183 UINT size = 0; 184 UINT result = GetRawInputData( 185 input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); 186 if (result == -1) { 187 LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed"; 188 return 0; 189 } 190 DCHECK_EQ(0u, result); 191 192 // Retrieve the input record itself. 193 scoped_ptr<uint8[]> buffer(new uint8[size]); 194 RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get()); 195 result = GetRawInputData( 196 input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER)); 197 if (result == -1) { 198 LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed"; 199 return 0; 200 } 201 DCHECK_EQ(size, result); 202 203 // Notify the observer about events generated locally. 204 if (input->header.dwType == RIM_TYPEMOUSE && input->header.hDevice != NULL) { 205 POINT position; 206 if (!GetCursorPos(&position)) { 207 position.x = 0; 208 position.y = 0; 209 } 210 mouse_listeners_->Notify( 211 &UserInputMonitor::MouseEventListener::OnMouseMoved, 212 SkIPoint::Make(position.x, position.y)); 213 } else if (input->header.dwType == RIM_TYPEKEYBOARD && 214 input->header.hDevice != NULL) { 215 ui::EventType event = (input->data.keyboard.Flags & RI_KEY_BREAK) 216 ? ui::ET_KEY_RELEASED 217 : ui::ET_KEY_PRESSED; 218 ui::KeyboardCode key_code = 219 ui::KeyboardCodeForWindowsKeyCode(input->data.keyboard.VKey); 220 counter_.OnKeyboardEvent(event, key_code); 221 } 222 223 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER)); 224 } 225 226 bool UserInputMonitorWinCore::HandleMessage(UINT message, 227 WPARAM wparam, 228 LPARAM lparam, 229 LRESULT* result) { 230 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 231 232 switch (message) { 233 case WM_INPUT: 234 *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam)); 235 return true; 236 237 default: 238 return false; 239 } 240 } 241 242 RAWINPUTDEVICE* UserInputMonitorWinCore::GetRawInputDevices(EventBitMask event, 243 DWORD flags) { 244 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 245 246 scoped_ptr<RAWINPUTDEVICE> device(new RAWINPUTDEVICE()); 247 if (event == MOUSE_EVENT_MASK) { 248 device->dwFlags = flags; 249 device->usUsagePage = kGenericDesktopPage; 250 device->usUsage = kMouseUsage; 251 device->hwndTarget = window_->hwnd(); 252 } else { 253 DCHECK_EQ(KEYBOARD_EVENT_MASK, event); 254 device->dwFlags = flags; 255 device->usUsagePage = kGenericDesktopPage; 256 device->usUsage = kKeyboardUsage; 257 device->hwndTarget = window_->hwnd(); 258 } 259 return device.release(); 260 } 261 262 // 263 // Implementation of UserInputMonitorWin. 264 // 265 266 UserInputMonitorWin::UserInputMonitorWin( 267 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) 268 : ui_task_runner_(ui_task_runner), 269 core_(new UserInputMonitorWinCore(ui_task_runner, mouse_listeners())) {} 270 271 UserInputMonitorWin::~UserInputMonitorWin() { 272 if (!ui_task_runner_->DeleteSoon(FROM_HERE, core_)) 273 delete core_; 274 } 275 276 size_t UserInputMonitorWin::GetKeyPressCount() const { 277 return core_->GetKeyPressCount(); 278 } 279 280 void UserInputMonitorWin::StartKeyboardMonitoring() { 281 ui_task_runner_->PostTask( 282 FROM_HERE, 283 base::Bind(&UserInputMonitorWinCore::StartMonitor, 284 core_->AsWeakPtr(), 285 UserInputMonitorWinCore::KEYBOARD_EVENT_MASK)); 286 } 287 288 void UserInputMonitorWin::StopKeyboardMonitoring() { 289 ui_task_runner_->PostTask( 290 FROM_HERE, 291 base::Bind(&UserInputMonitorWinCore::StopMonitor, 292 core_->AsWeakPtr(), 293 UserInputMonitorWinCore::KEYBOARD_EVENT_MASK)); 294 } 295 296 void UserInputMonitorWin::StartMouseMonitoring() { 297 ui_task_runner_->PostTask( 298 FROM_HERE, 299 base::Bind(&UserInputMonitorWinCore::StartMonitor, 300 core_->AsWeakPtr(), 301 UserInputMonitorWinCore::MOUSE_EVENT_MASK)); 302 } 303 304 void UserInputMonitorWin::StopMouseMonitoring() { 305 ui_task_runner_->PostTask( 306 FROM_HERE, 307 base::Bind(&UserInputMonitorWinCore::StopMonitor, 308 core_->AsWeakPtr(), 309 UserInputMonitorWinCore::MOUSE_EVENT_MASK)); 310 } 311 312 } // namespace 313 314 scoped_ptr<UserInputMonitor> UserInputMonitor::Create( 315 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner, 316 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) { 317 return scoped_ptr<UserInputMonitor>(new UserInputMonitorWin(ui_task_runner)); 318 } 319 320 } // namespace media 321