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/message_loop/message_loop.h"
19 #include "base/message_loop/message_pump_libevent.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/synchronization/lock.h"
22 #include "media/base/keyboard_event_counter.h"
23 #include "third_party/skia/include/core/SkPoint.h"
24 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
25 #include "ui/gfx/x/x11_types.h"
27 // These includes need to be later than dictated by the style guide due to
28 // Xlib header pollution, specifically the min, max, and Status macros.
29 #include <X11/XKBlib.h>
30 #include <X11/Xlibint.h>
31 #include <X11/extensions/record.h>
36 // This is the actual implementation of event monitoring. It's separated from
37 // UserInputMonitorLinux since it needs to be deleted on the IO thread.
38 class UserInputMonitorLinuxCore
39 : public base::MessagePumpLibevent::Watcher
,
40 public base::SupportsWeakPtr
<UserInputMonitorLinuxCore
>,
41 public base::MessageLoop::DestructionObserver
{
48 explicit UserInputMonitorLinuxCore(
49 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
,
50 const scoped_refptr
<UserInputMonitor::MouseListenerList
>&
52 ~UserInputMonitorLinuxCore() override
;
54 // DestructionObserver overrides.
55 void WillDestroyCurrentMessageLoop() override
;
57 size_t GetKeyPressCount() const;
58 void StartMonitor(EventType type
);
59 void StopMonitor(EventType type
);
62 // base::MessagePumpLibevent::Watcher interface.
63 void OnFileCanReadWithoutBlocking(int fd
) override
;
64 void OnFileCanWriteWithoutBlocking(int fd
) override
;
66 // Processes key and mouse events.
67 void ProcessXEvent(xEvent
* event
);
68 static void ProcessReply(XPointer self
, XRecordInterceptData
* data
);
70 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
71 scoped_refptr
<base::ObserverListThreadSafe
<
72 UserInputMonitor::MouseEventListener
>> mouse_listeners_
;
75 // The following members should only be accessed on the IO thread.
77 base::MessagePumpLibevent::FileDescriptorWatcher controller_
;
78 Display
* x_control_display_
;
79 Display
* x_record_display_
;
80 XRecordRange
* x_record_range_
[2];
81 XRecordContext x_record_context_
;
82 KeyboardEventCounter counter_
;
84 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinuxCore
);
87 class UserInputMonitorLinux
: public UserInputMonitor
{
89 explicit UserInputMonitorLinux(
90 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
);
91 ~UserInputMonitorLinux() override
;
93 // Public UserInputMonitor overrides.
94 size_t GetKeyPressCount() const override
;
97 // Private UserInputMonitor overrides.
98 void StartKeyboardMonitoring() override
;
99 void StopKeyboardMonitoring() override
;
100 void StartMouseMonitoring() override
;
101 void StopMouseMonitoring() override
;
103 scoped_refptr
<base::SingleThreadTaskRunner
> io_task_runner_
;
104 UserInputMonitorLinuxCore
* core_
;
106 DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinux
);
109 UserInputMonitorLinuxCore::UserInputMonitorLinuxCore(
110 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
,
111 const scoped_refptr
<UserInputMonitor::MouseListenerList
>& mouse_listeners
)
112 : io_task_runner_(io_task_runner
),
113 mouse_listeners_(mouse_listeners
),
114 x_control_display_(NULL
),
115 x_record_display_(NULL
),
116 x_record_context_(0) {
117 x_record_range_
[0] = NULL
;
118 x_record_range_
[1] = NULL
;
121 UserInputMonitorLinuxCore::~UserInputMonitorLinuxCore() {
122 DCHECK(!x_control_display_
);
123 DCHECK(!x_record_display_
);
124 DCHECK(!x_record_range_
[0]);
125 DCHECK(!x_record_range_
[1]);
126 DCHECK(!x_record_context_
);
129 void UserInputMonitorLinuxCore::WillDestroyCurrentMessageLoop() {
130 DCHECK(io_task_runner_
->BelongsToCurrentThread());
131 StopMonitor(MOUSE_EVENT
);
132 StopMonitor(KEYBOARD_EVENT
);
135 size_t UserInputMonitorLinuxCore::GetKeyPressCount() const {
136 return counter_
.GetKeyPressCount();
139 void UserInputMonitorLinuxCore::StartMonitor(EventType type
) {
140 DCHECK(io_task_runner_
->BelongsToCurrentThread());
142 if (type
== KEYBOARD_EVENT
)
145 // TODO(jamiewalch): We should pass the display in. At that point, since
146 // XRecord needs a private connection to the X Server for its data channel
147 // and both channels are used from a separate thread, we'll need to duplicate
148 // them with something like the following:
149 // XOpenDisplay(DisplayString(display));
150 if (!x_control_display_
)
151 x_control_display_
= gfx::OpenNewXDisplay();
153 if (!x_record_display_
)
154 x_record_display_
= gfx::OpenNewXDisplay();
156 if (!x_control_display_
|| !x_record_display_
) {
157 LOG(ERROR
) << "Couldn't open X display";
162 int xr_opcode
, xr_event
, xr_error
;
163 if (!XQueryExtension(
164 x_control_display_
, "RECORD", &xr_opcode
, &xr_event
, &xr_error
)) {
165 LOG(ERROR
) << "X Record extension not available.";
170 if (!x_record_range_
[type
])
171 x_record_range_
[type
] = XRecordAllocRange();
173 if (!x_record_range_
[type
]) {
174 LOG(ERROR
) << "XRecordAllocRange failed.";
179 if (type
== MOUSE_EVENT
) {
180 x_record_range_
[type
]->device_events
.first
= MotionNotify
;
181 x_record_range_
[type
]->device_events
.last
= MotionNotify
;
183 DCHECK_EQ(KEYBOARD_EVENT
, type
);
184 x_record_range_
[type
]->device_events
.first
= KeyPress
;
185 x_record_range_
[type
]->device_events
.last
= KeyRelease
;
188 if (x_record_context_
) {
189 XRecordDisableContext(x_control_display_
, x_record_context_
);
190 XFlush(x_control_display_
);
191 XRecordFreeContext(x_record_display_
, x_record_context_
);
192 x_record_context_
= 0;
194 XRecordRange
** record_range_to_use
=
195 (x_record_range_
[0] && x_record_range_
[1]) ? x_record_range_
196 : &x_record_range_
[type
];
197 int number_of_ranges
= (x_record_range_
[0] && x_record_range_
[1]) ? 2 : 1;
199 XRecordClientSpec client_spec
= XRecordAllClients
;
200 x_record_context_
= XRecordCreateContext(x_record_display_
,
206 if (!x_record_context_
) {
207 LOG(ERROR
) << "XRecordCreateContext failed.";
212 if (!XRecordEnableContextAsync(x_record_display_
,
214 &UserInputMonitorLinuxCore::ProcessReply
,
215 reinterpret_cast<XPointer
>(this))) {
216 LOG(ERROR
) << "XRecordEnableContextAsync failed.";
221 if (!x_record_range_
[0] || !x_record_range_
[1]) {
222 // Register OnFileCanReadWithoutBlocking() to be called every time there is
223 // something to read from |x_record_display_|.
224 base::MessageLoopForIO
* message_loop
= base::MessageLoopForIO::current();
226 message_loop
->WatchFileDescriptor(ConnectionNumber(x_record_display_
),
228 base::MessageLoopForIO::WATCH_READ
,
232 LOG(ERROR
) << "Failed to create X record task.";
237 // Start observing message loop destruction if we start monitoring the first
239 base::MessageLoop::current()->AddDestructionObserver(this);
242 // Fetch pending events if any.
243 OnFileCanReadWithoutBlocking(ConnectionNumber(x_record_display_
));
246 void UserInputMonitorLinuxCore::StopMonitor(EventType type
) {
247 DCHECK(io_task_runner_
->BelongsToCurrentThread());
249 if (x_record_range_
[type
]) {
250 XFree(x_record_range_
[type
]);
251 x_record_range_
[type
] = NULL
;
253 if (x_record_range_
[0] || x_record_range_
[1])
256 // Context must be disabled via the control channel because we can't send
257 // any X protocol traffic over the data channel while it's recording.
258 if (x_record_context_
) {
259 XRecordDisableContext(x_control_display_
, x_record_context_
);
260 XFlush(x_control_display_
);
261 XRecordFreeContext(x_record_display_
, x_record_context_
);
262 x_record_context_
= 0;
264 controller_
.StopWatchingFileDescriptor();
266 if (x_record_display_
) {
267 XCloseDisplay(x_record_display_
);
268 x_record_display_
= NULL
;
270 if (x_control_display_
) {
271 XCloseDisplay(x_control_display_
);
272 x_control_display_
= NULL
;
274 // Stop observing message loop destruction if no event is being monitored.
275 base::MessageLoop::current()->RemoveDestructionObserver(this);
278 void UserInputMonitorLinuxCore::OnFileCanReadWithoutBlocking(int fd
) {
279 DCHECK(io_task_runner_
->BelongsToCurrentThread());
281 // Fetch pending events if any.
282 while (XPending(x_record_display_
)) {
283 XNextEvent(x_record_display_
, &event
);
287 void UserInputMonitorLinuxCore::OnFileCanWriteWithoutBlocking(int fd
) {
291 void UserInputMonitorLinuxCore::ProcessXEvent(xEvent
* event
) {
292 DCHECK(io_task_runner_
->BelongsToCurrentThread());
293 if (event
->u
.u
.type
== MotionNotify
) {
294 SkIPoint
position(SkIPoint::Make(event
->u
.keyButtonPointer
.rootX
,
295 event
->u
.keyButtonPointer
.rootY
));
296 mouse_listeners_
->Notify(
297 FROM_HERE
, &UserInputMonitor::MouseEventListener::OnMouseMoved
,
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
));