Removed unused VideoCaptureCapability parameters.
[chromium-blink-merge.git] / media / base / user_input_monitor_linux.cc
blob361fd01a90f3d3994eb3f9406c344a2c58cabee0
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.
5 #include "media/base/user_input_monitor.h"
7 #include <sys/select.h>
8 #include <unistd.h>
9 #define XK_MISCELLANY
10 #include <X11/keysymdef.h>
12 #include "base/basictypes.h"
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/compiler_specific.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/message_loop/message_pump_libevent.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/single_thread_task_runner.h"
23 #include "base/synchronization/lock.h"
24 #include "media/base/keyboard_event_counter.h"
25 #include "third_party/skia/include/core/SkPoint.h"
26 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
28 // These includes need to be later than dictated by the style guide due to
29 // Xlib header pollution, specifically the min, max, and Status macros.
30 #include <X11/XKBlib.h>
31 #include <X11/Xlibint.h>
32 #include <X11/extensions/record.h>
34 namespace media {
35 namespace {
37 // This is the actual implementation of event monitoring. It's separated from
38 // UserInputMonitorLinux since it needs to be deleted on the IO thread.
39 class UserInputMonitorLinuxCore
40 : public base::MessagePumpLibevent::Watcher,
41 public base::SupportsWeakPtr<UserInputMonitorLinuxCore>,
42 public base::MessageLoop::DestructionObserver {
43 public:
44 enum EventType {
45 MOUSE_EVENT,
46 KEYBOARD_EVENT
49 explicit UserInputMonitorLinuxCore(
50 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
51 const scoped_refptr<UserInputMonitor::MouseListenerList>&
52 mouse_listeners);
53 virtual ~UserInputMonitorLinuxCore();
55 // DestructionObserver overrides.
56 virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
58 size_t GetKeyPressCount() const;
59 void StartMonitor(EventType type);
60 void StopMonitor(EventType type);
62 private:
63 // base::MessagePumpLibevent::Watcher interface.
64 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
65 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
67 // Processes key and mouse events.
68 void ProcessXEvent(xEvent* event);
69 static void ProcessReply(XPointer self, XRecordInterceptData* data);
71 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
72 scoped_refptr<ObserverListThreadSafe<UserInputMonitor::MouseEventListener> >
73 mouse_listeners_;
76 // The following members should only be accessed on the IO thread.
78 base::MessagePumpLibevent::FileDescriptorWatcher controller_;
79 Display* x_control_display_;
80 Display* x_record_display_;
81 XRecordRange* x_record_range_[2];
82 XRecordContext x_record_context_;
83 KeyboardEventCounter counter_;
85 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinuxCore);
88 class UserInputMonitorLinux : public UserInputMonitor {
89 public:
90 explicit UserInputMonitorLinux(
91 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
92 virtual ~UserInputMonitorLinux();
94 // Public UserInputMonitor overrides.
95 virtual size_t GetKeyPressCount() const OVERRIDE;
97 private:
98 // Private UserInputMonitor overrides.
99 virtual void StartKeyboardMonitoring() OVERRIDE;
100 virtual void StopKeyboardMonitoring() OVERRIDE;
101 virtual void StartMouseMonitoring() OVERRIDE;
102 virtual void StopMouseMonitoring() OVERRIDE;
104 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
105 UserInputMonitorLinuxCore* core_;
107 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinux);
110 UserInputMonitorLinuxCore::UserInputMonitorLinuxCore(
111 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
112 const scoped_refptr<UserInputMonitor::MouseListenerList>& mouse_listeners)
113 : io_task_runner_(io_task_runner),
114 mouse_listeners_(mouse_listeners),
115 x_control_display_(NULL),
116 x_record_display_(NULL),
117 x_record_context_(0) {
118 x_record_range_[0] = NULL;
119 x_record_range_[1] = NULL;
122 UserInputMonitorLinuxCore::~UserInputMonitorLinuxCore() {
123 DCHECK(!x_control_display_);
124 DCHECK(!x_record_display_);
125 DCHECK(!x_record_range_[0]);
126 DCHECK(!x_record_range_[1]);
127 DCHECK(!x_record_context_);
130 void UserInputMonitorLinuxCore::WillDestroyCurrentMessageLoop() {
131 DCHECK(io_task_runner_->BelongsToCurrentThread());
132 StopMonitor(MOUSE_EVENT);
133 StopMonitor(KEYBOARD_EVENT);
136 size_t UserInputMonitorLinuxCore::GetKeyPressCount() const {
137 return counter_.GetKeyPressCount();
140 void UserInputMonitorLinuxCore::StartMonitor(EventType type) {
141 DCHECK(io_task_runner_->BelongsToCurrentThread());
143 if (type == KEYBOARD_EVENT)
144 counter_.Reset();
146 // TODO(jamiewalch): We should pass the display in. At that point, since
147 // XRecord needs a private connection to the X Server for its data channel
148 // and both channels are used from a separate thread, we'll need to duplicate
149 // them with something like the following:
150 // XOpenDisplay(DisplayString(display));
151 if (!x_control_display_)
152 x_control_display_ = XOpenDisplay(NULL);
154 if (!x_record_display_)
155 x_record_display_ = XOpenDisplay(NULL);
157 if (!x_control_display_ || !x_record_display_) {
158 LOG(ERROR) << "Couldn't open X display";
159 StopMonitor(type);
160 return;
163 int xr_opcode, xr_event, xr_error;
164 if (!XQueryExtension(
165 x_control_display_, "RECORD", &xr_opcode, &xr_event, &xr_error)) {
166 LOG(ERROR) << "X Record extension not available.";
167 StopMonitor(type);
168 return;
171 if (!x_record_range_[type])
172 x_record_range_[type] = XRecordAllocRange();
174 if (!x_record_range_[type]) {
175 LOG(ERROR) << "XRecordAllocRange failed.";
176 StopMonitor(type);
177 return;
180 if (type == MOUSE_EVENT) {
181 x_record_range_[type]->device_events.first = MotionNotify;
182 x_record_range_[type]->device_events.last = MotionNotify;
183 } else {
184 DCHECK_EQ(KEYBOARD_EVENT, type);
185 x_record_range_[type]->device_events.first = KeyPress;
186 x_record_range_[type]->device_events.last = KeyRelease;
189 if (x_record_context_) {
190 XRecordDisableContext(x_control_display_, x_record_context_);
191 XFlush(x_control_display_);
192 XRecordFreeContext(x_record_display_, x_record_context_);
193 x_record_context_ = 0;
195 XRecordRange** record_range_to_use =
196 (x_record_range_[0] && x_record_range_[1]) ? x_record_range_
197 : &x_record_range_[type];
198 int number_of_ranges = (x_record_range_[0] && x_record_range_[1]) ? 2 : 1;
200 XRecordClientSpec client_spec = XRecordAllClients;
201 x_record_context_ = XRecordCreateContext(x_record_display_,
203 &client_spec,
205 record_range_to_use,
206 number_of_ranges);
207 if (!x_record_context_) {
208 LOG(ERROR) << "XRecordCreateContext failed.";
209 StopMonitor(type);
210 return;
213 if (!XRecordEnableContextAsync(x_record_display_,
214 x_record_context_,
215 &UserInputMonitorLinuxCore::ProcessReply,
216 reinterpret_cast<XPointer>(this))) {
217 LOG(ERROR) << "XRecordEnableContextAsync failed.";
218 StopMonitor(type);
219 return;
222 if (!x_record_range_[0] || !x_record_range_[1]) {
223 // Register OnFileCanReadWithoutBlocking() to be called every time there is
224 // something to read from |x_record_display_|.
225 base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
226 int result =
227 message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_),
228 true,
229 base::MessageLoopForIO::WATCH_READ,
230 &controller_,
231 this);
232 if (!result) {
233 LOG(ERROR) << "Failed to create X record task.";
234 StopMonitor(type);
235 return;
238 // Start observing message loop destruction if we start monitoring the first
239 // event.
240 base::MessageLoop::current()->AddDestructionObserver(this);
243 // Fetch pending events if any.
244 OnFileCanReadWithoutBlocking(ConnectionNumber(x_record_display_));
247 void UserInputMonitorLinuxCore::StopMonitor(EventType type) {
248 DCHECK(io_task_runner_->BelongsToCurrentThread());
250 if (x_record_range_[type]) {
251 XFree(x_record_range_[type]);
252 x_record_range_[type] = NULL;
254 if (x_record_range_[0] || x_record_range_[1])
255 return;
257 // Context must be disabled via the control channel because we can't send
258 // any X protocol traffic over the data channel while it's recording.
259 if (x_record_context_) {
260 XRecordDisableContext(x_control_display_, x_record_context_);
261 XFlush(x_control_display_);
262 XRecordFreeContext(x_record_display_, x_record_context_);
263 x_record_context_ = 0;
265 controller_.StopWatchingFileDescriptor();
267 if (x_record_display_) {
268 XCloseDisplay(x_record_display_);
269 x_record_display_ = NULL;
271 if (x_control_display_) {
272 XCloseDisplay(x_control_display_);
273 x_control_display_ = NULL;
275 // Stop observing message loop destruction if no event is being monitored.
276 base::MessageLoop::current()->RemoveDestructionObserver(this);
279 void UserInputMonitorLinuxCore::OnFileCanReadWithoutBlocking(int fd) {
280 DCHECK(io_task_runner_->BelongsToCurrentThread());
281 XEvent event;
282 // Fetch pending events if any.
283 while (XPending(x_record_display_)) {
284 XNextEvent(x_record_display_, &event);
288 void UserInputMonitorLinuxCore::OnFileCanWriteWithoutBlocking(int fd) {
289 NOTREACHED();
292 void UserInputMonitorLinuxCore::ProcessXEvent(xEvent* event) {
293 DCHECK(io_task_runner_->BelongsToCurrentThread());
294 if (event->u.u.type == MotionNotify) {
295 SkIPoint position(SkIPoint::Make(event->u.keyButtonPointer.rootX,
296 event->u.keyButtonPointer.rootY));
297 mouse_listeners_->Notify(
298 &UserInputMonitor::MouseEventListener::OnMouseMoved, position);
299 } else {
300 ui::EventType type;
301 if (event->u.u.type == KeyPress) {
302 type = ui::ET_KEY_PRESSED;
303 } else if (event->u.u.type == KeyRelease) {
304 type = ui::ET_KEY_RELEASED;
305 } else {
306 NOTREACHED();
307 return;
310 KeySym key_sym =
311 XkbKeycodeToKeysym(x_control_display_, event->u.u.detail, 0, 0);
312 ui::KeyboardCode key_code = ui::KeyboardCodeFromXKeysym(key_sym);
313 counter_.OnKeyboardEvent(type, key_code);
317 // static
318 void UserInputMonitorLinuxCore::ProcessReply(XPointer self,
319 XRecordInterceptData* data) {
320 if (data->category == XRecordFromServer) {
321 xEvent* event = reinterpret_cast<xEvent*>(data->data);
322 reinterpret_cast<UserInputMonitorLinuxCore*>(self)->ProcessXEvent(event);
324 XRecordFreeData(data);
328 // Implementation of UserInputMonitorLinux.
331 UserInputMonitorLinux::UserInputMonitorLinux(
332 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
333 : io_task_runner_(io_task_runner),
334 core_(new UserInputMonitorLinuxCore(io_task_runner, mouse_listeners())) {}
336 UserInputMonitorLinux::~UserInputMonitorLinux() {
337 if (!io_task_runner_->DeleteSoon(FROM_HERE, core_))
338 delete core_;
341 size_t UserInputMonitorLinux::GetKeyPressCount() const {
342 return core_->GetKeyPressCount();
345 void UserInputMonitorLinux::StartKeyboardMonitoring() {
346 io_task_runner_->PostTask(
347 FROM_HERE,
348 base::Bind(&UserInputMonitorLinuxCore::StartMonitor,
349 core_->AsWeakPtr(),
350 UserInputMonitorLinuxCore::KEYBOARD_EVENT));
353 void UserInputMonitorLinux::StopKeyboardMonitoring() {
354 io_task_runner_->PostTask(
355 FROM_HERE,
356 base::Bind(&UserInputMonitorLinuxCore::StopMonitor,
357 core_->AsWeakPtr(),
358 UserInputMonitorLinuxCore::KEYBOARD_EVENT));
361 void UserInputMonitorLinux::StartMouseMonitoring() {
362 io_task_runner_->PostTask(FROM_HERE,
363 base::Bind(&UserInputMonitorLinuxCore::StartMonitor,
364 core_->AsWeakPtr(),
365 UserInputMonitorLinuxCore::MOUSE_EVENT));
368 void UserInputMonitorLinux::StopMouseMonitoring() {
369 io_task_runner_->PostTask(FROM_HERE,
370 base::Bind(&UserInputMonitorLinuxCore::StopMonitor,
371 core_->AsWeakPtr(),
372 UserInputMonitorLinuxCore::MOUSE_EVENT));
375 } // namespace
377 scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
378 const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
379 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
380 return scoped_ptr<UserInputMonitor>(
381 new UserInputMonitorLinux(io_task_runner));
384 } // namespace media