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

Side by Side 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, 3 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 unified diff | Download patch
« no previous file with comments | « media/base/user_input_monitor_unittest.cc ('k') | media/media.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/base/user_input_monitor.h" 5 #include "media/base/user_input_monitor.h"
6 6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/win/message_window.h"
13 #include "media/base/keyboard_event_counter.h"
14 #include "third_party/skia/include/core/SkPoint.h"
15 #include "ui/base/keycodes/keyboard_code_conversion_win.h"
16
7 namespace media { 17 namespace media {
8 18 namespace {
9 // TODO(jiayl): add the implementation. 19
20 // From the HID Usage Tables specification.
21 const USHORT kGenericDesktopPage = 1;
22 const USHORT kMouseUsage = 2;
23 const USHORT kKeyboardUsage = 6;
24
25 class UserInputMonitorWin : public UserInputMonitor {
26 public:
27 explicit UserInputMonitorWin(
28 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
29 ~UserInputMonitorWin();
30
31 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.
32
33 private:
34 enum EventBitMask {
35 MOUSE_EVENT_MASK = 1,
36 KEYBOARD_EVENT_MASK = 2,
37 };
38 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.
39
40 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.
41 virtual void StopMouseMonitoring() OVERRIDE;
42 virtual void StartKeyboardMonitoring() OVERRIDE;
43 virtual void StopKeyboardMonitoring() OVERRIDE;
44
45 //
46 // The following methods are only called on the UI message loop.
47 //
48
49 void StartMonitor(EventBitMask type);
50 void StopMonitor(EventBitMask type);
51
52 // Handles WM_INPUT messages.
53 LRESULT OnInput(HRAWINPUT input_handle);
54 // Handles messages received by |window_|.
55 bool HandleMessage(UINT message,
56 WPARAM wparam,
57 LPARAM lparam,
58 LRESULT* result);
59 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.
60
61 // Task runner on which |window_| is created.
62 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
63
64 //
65 // 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.
66 //
67 // Used to receive raw input.
68 scoped_ptr<base::win::MessageWindow> window_;
69 uint8 events_monitored_;
70 KeyboardEventCounter counter_;
71
72 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWin);
73 };
74
75 UserInputMonitorWin::UserInputMonitorWin(
76 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
77 : ui_task_runner_(ui_task_runner), events_monitored_(0) {}
78
79 UserInputMonitorWin::~UserInputMonitorWin() {
80 DCHECK(!window_);
81 DCHECK(!events_monitored_);
82 }
83
84 size_t UserInputMonitorWin::GetKeyPressCount() const {
85 return counter_.GetKeyPressCount();
86 }
87
88 int UserInputMonitorWin::NumberOfRawInputDevices(uint8 target_events) {
89 int number = 0;
90 if (target_events & MOUSE_EVENT_MASK)
91 number++;
92 if (target_events & KEYBOARD_EVENT_MASK)
93 number++;
94 return number;
95 }
96
97 void UserInputMonitorWin::StartMouseMonitoring() {
98 if (!ui_task_runner_->BelongsToCurrentThread()) {
99 ui_task_runner_->PostTask(FROM_HERE,
100 base::Bind(&UserInputMonitorWin::StartMonitor,
101 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.
102 MOUSE_EVENT_MASK));
103 return;
104 }
105 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.
106 }
107
108 void UserInputMonitorWin::StopMouseMonitoring() {
109 if (!ui_task_runner_->BelongsToCurrentThread()) {
110 ui_task_runner_->PostTask(FROM_HERE,
111 base::Bind(&UserInputMonitorWin::StopMonitor,
112 base::Unretained(this),
113 MOUSE_EVENT_MASK));
114 return;
115 }
116 StopMonitor(MOUSE_EVENT_MASK);
117 }
118
119 void UserInputMonitorWin::StartKeyboardMonitoring() {
120 if (!ui_task_runner_->BelongsToCurrentThread()) {
121 ui_task_runner_->PostTask(FROM_HERE,
122 base::Bind(&UserInputMonitorWin::StartMonitor,
123 base::Unretained(this),
124 KEYBOARD_EVENT_MASK));
125 return;
126 }
127 StartMonitor(KEYBOARD_EVENT_MASK);
128 }
129
130 void UserInputMonitorWin::StopKeyboardMonitoring() {
131 if (!ui_task_runner_->BelongsToCurrentThread()) {
132 ui_task_runner_->PostTask(FROM_HERE,
133 base::Bind(&UserInputMonitorWin::StopMonitor,
134 base::Unretained(this),
135 KEYBOARD_EVENT_MASK));
136 return;
137 }
138 StopMonitor(KEYBOARD_EVENT_MASK);
139 }
140
141 void UserInputMonitorWin::StartMonitor(EventBitMask type) {
142 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.
143 if (events_monitored_ & type)
144 return;
145
146 if (type == KEYBOARD_EVENT_MASK)
147 counter_.Reset();
148
149 events_monitored_ |= type;
150
151 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.
152 if (!window_->Create(base::Bind(&UserInputMonitorWin::HandleMessage,
153 base::Unretained(this)))) {
154 LOG_GETLASTERROR(ERROR) << "Failed to create the raw input window";
155 window_.reset();
156 }
157 }
158
159 void UserInputMonitorWin::StopMonitor(EventBitMask type) {
160 DCHECK(ui_task_runner_->BelongsToCurrentThread());
161 if (!(events_monitored_ & type))
162 return;
163
164 // Stop receiving raw input.
165 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.
166 scoped_ptr<RAWINPUTDEVICE[]> devices(
167 GetRawInputDevices(type, RIDEV_REMOVE));
168
169 // 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.
170 RegisterRawInputDevices(devices.get(), 1, sizeof(devices[0]));
171
172 events_monitored_ &= ~type;
173 if (events_monitored_ == 0)
174 window_.reset();
175 }
176 }
177
178 LRESULT UserInputMonitorWin::OnInput(HRAWINPUT input_handle) {
179 DCHECK(ui_task_runner_->BelongsToCurrentThread());
180 // Get the size of the input record.
181 UINT size = 0;
182 UINT result = GetRawInputData(
183 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.
184 if (result == -1) {
185 LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed";
186 return 0;
187 }
188
189 // Retrieve the input record itself.
190 scoped_ptr<uint8[]> buffer(new uint8[size]);
191 RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
192 result = GetRawInputData(
193 input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER));
194 if (result == -1) {
Wez 2013/09/03 20:07:04 nit: See above re verifying |result|
jiayl 2013/09/03 23:44:12 Done.
195 LOG_GETLASTERROR(ERROR) << "GetRawInputData() failed";
196 return 0;
197 }
198
199 // Notify the observer about events generated locally.
200 if (input->header.dwType == RIM_TYPEMOUSE && input->header.hDevice != NULL) {
201 POINT position;
202 if (!GetCursorPos(&position)) {
203 position.x = 0;
204 position.y = 0;
205 }
206 OnMouseEvent(SkIPoint::Make(position.x, position.y));
207 } else if (input->header.dwType == RIM_TYPEKEYBOARD &&
208 input->header.hDevice != NULL) {
209 ui::EventType event = (input->data.keyboard.Flags & RI_KEY_BREAK)
210 ? ui::ET_KEY_RELEASED
211 : ui::ET_KEY_PRESSED;
212 ui::KeyboardCode key_code =
213 ui::KeyboardCodeForWindowsKeyCode(input->data.keyboard.VKey);
214 counter_.OnKeyboardEvent(event, key_code);
215 }
216
217 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
218 }
219
220 bool UserInputMonitorWin::HandleMessage(UINT message,
221 WPARAM wparam,
222 LPARAM lparam,
223 LRESULT* result) {
224 DCHECK(ui_task_runner_->BelongsToCurrentThread());
225 switch (message) {
226 case WM_CREATE: {
227 // Register to receive raw mouse and/or keyboard input.
228 scoped_ptr<RAWINPUTDEVICE[]> devices(
229 GetRawInputDevices(events_monitored_, RIDEV_INPUTSINK));
230 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
231 NumberOfRawInputDevices(events_monitored_),
232 sizeof(devices[0]))) {
233 *result = 0;
234 } else {
235 LOG_GETLASTERROR(ERROR) << "RegisterRawInputDevices() failed";
236 *result = -1;
237 }
238 return true;
239 }
240
241 case WM_INPUT:
242 *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
243 return true;
244
245 default:
246 return false;
247 }
248 }
249
250 RAWINPUTDEVICE* UserInputMonitorWin::GetRawInputDevices(uint8 target_events,
251 DWORD flags) {
252 DCHECK(ui_task_runner_->BelongsToCurrentThread());
253 RAWINPUTDEVICE* devices =
254 new RAWINPUTDEVICE[NumberOfRawInputDevices(target_events)];
255 int index = 0;
256 if (target_events & MOUSE_EVENT_MASK) {
257 devices[index].dwFlags = flags;
258 devices[index].usUsagePage = kGenericDesktopPage;
259 devices[index].usUsage = kMouseUsage;
260 devices[index].hwndTarget = window_->hwnd();
261 index++;
262 }
263 if (target_events & KEYBOARD_EVENT_MASK) {
264 devices[index].dwFlags = flags;
265 devices[index].usUsagePage = kGenericDesktopPage;
266 devices[index].usUsage = kKeyboardUsage;
267 devices[index].hwndTarget = window_->hwnd();
268 index++;
269 }
270 return devices;
271 }
272
273 } // namespace
274
10 scoped_ptr<UserInputMonitor> UserInputMonitor::Create( 275 scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
11 const scoped_refptr<base::SingleThreadTaskRunner>& input_task_runner, 276 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
12 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) { 277 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
13 return scoped_ptr<UserInputMonitor>(); 278 return scoped_ptr<UserInputMonitor>(new UserInputMonitorWin(ui_task_runner));
14 } 279 }
15 280
16 } // namespace media 281 } // namespace media
OLDNEW
« 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