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>
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>
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
{
49 explicit UserInputMonitorLinuxCore(
50 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
,
51 const scoped_refptr
<UserInputMonitor::MouseListenerList
>&
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
);
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
> >
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
{
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
;
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
)
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";
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.";
171 if (!x_record_range_
[type
])
172 x_record_range_
[type
] = XRecordAllocRange();
174 if (!x_record_range_
[type
]) {
175 LOG(ERROR
) << "XRecordAllocRange failed.";
180 if (type
== MOUSE_EVENT
) {
181 x_record_range_
[type
]->device_events
.first
= MotionNotify
;
182 x_record_range_
[type
]->device_events
.last
= MotionNotify
;
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_
,
207 if (!x_record_context_
) {
208 LOG(ERROR
) << "XRecordCreateContext failed.";
213 if (!XRecordEnableContextAsync(x_record_display_
,
215 &UserInputMonitorLinuxCore::ProcessReply
,
216 reinterpret_cast<XPointer
>(this))) {
217 LOG(ERROR
) << "XRecordEnableContextAsync failed.";
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();
227 message_loop
->WatchFileDescriptor(ConnectionNumber(x_record_display_
),
229 base::MessageLoopForIO::WATCH_READ
,
233 LOG(ERROR
) << "Failed to create X record task.";
238 // Start observing message loop destruction if we start monitoring the first
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])
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());
282 // Fetch pending events if any.
283 while (XPending(x_record_display_
)) {
284 XNextEvent(x_record_display_
, &event
);
288 void UserInputMonitorLinuxCore::OnFileCanWriteWithoutBlocking(int fd
) {
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
);
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
;
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
);
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_
))
341 size_t UserInputMonitorLinux::GetKeyPressCount() const {
342 return core_
->GetKeyPressCount();
345 void UserInputMonitorLinux::StartKeyboardMonitoring() {
346 io_task_runner_
->PostTask(
348 base::Bind(&UserInputMonitorLinuxCore::StartMonitor
,
350 UserInputMonitorLinuxCore::KEYBOARD_EVENT
));
353 void UserInputMonitorLinux::StopKeyboardMonitoring() {
354 io_task_runner_
->PostTask(
356 base::Bind(&UserInputMonitorLinuxCore::StopMonitor
,
358 UserInputMonitorLinuxCore::KEYBOARD_EVENT
));
361 void UserInputMonitorLinux::StartMouseMonitoring() {
362 io_task_runner_
->PostTask(FROM_HERE
,
363 base::Bind(&UserInputMonitorLinuxCore::StartMonitor
,
365 UserInputMonitorLinuxCore::MOUSE_EVENT
));
368 void UserInputMonitorLinux::StopMouseMonitoring() {
369 io_task_runner_
->PostTask(FROM_HERE
,
370 base::Bind(&UserInputMonitorLinuxCore::StopMonitor
,
372 UserInputMonitorLinuxCore::MOUSE_EVENT
));
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
));