• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/select.h>
8 #include <unistd.h>
9 #define XK_MISCELLANY
10 #include <X11/keysymdef.h>
11 
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/single_thread_task_runner.h"
22 #include "base/synchronization/lock.h"
23 #include "media/base/keyboard_event_counter.h"
24 #include "third_party/skia/include/core/SkPoint.h"
25 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
26 
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>
32 
33 namespace media {
34 namespace {
35 
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 {
42  public:
43   enum EventType {
44     MOUSE_EVENT,
45     KEYBOARD_EVENT
46   };
47 
48   explicit UserInputMonitorLinuxCore(
49       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
50       const scoped_refptr<UserInputMonitor::MouseListenerList>&
51           mouse_listeners);
52   virtual ~UserInputMonitorLinuxCore();
53 
54   // DestructionObserver overrides.
55   virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
56 
57   size_t GetKeyPressCount() const;
58   void StartMonitor(EventType type);
59   void StopMonitor(EventType type);
60 
61  private:
62   // base::MessagePumpLibevent::Watcher interface.
63   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
64   virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
65 
66   // Processes key and mouse events.
67   void ProcessXEvent(xEvent* event);
68   static void ProcessReply(XPointer self, XRecordInterceptData* data);
69 
70   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
71   scoped_refptr<ObserverListThreadSafe<UserInputMonitor::MouseEventListener> >
72       mouse_listeners_;
73 
74   //
75   // The following members should only be accessed on the IO thread.
76   //
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_;
83 
84   DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinuxCore);
85 };
86 
87 class UserInputMonitorLinux : public UserInputMonitor {
88  public:
89   explicit UserInputMonitorLinux(
90       const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner);
91   virtual ~UserInputMonitorLinux();
92 
93   // Public UserInputMonitor overrides.
94   virtual size_t GetKeyPressCount() const OVERRIDE;
95 
96  private:
97   // Private UserInputMonitor overrides.
98   virtual void StartKeyboardMonitoring() OVERRIDE;
99   virtual void StopKeyboardMonitoring() OVERRIDE;
100   virtual void StartMouseMonitoring() OVERRIDE;
101   virtual void StopMouseMonitoring() OVERRIDE;
102 
103   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
104   UserInputMonitorLinuxCore* core_;
105 
106   DISALLOW_COPY_AND_ASSIGN(UserInputMonitorLinux);
107 };
108 
UserInputMonitorLinuxCore(const scoped_refptr<base::SingleThreadTaskRunner> & io_task_runner,const scoped_refptr<UserInputMonitor::MouseListenerList> & mouse_listeners)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;
119 }
120 
~UserInputMonitorLinuxCore()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_);
127 }
128 
WillDestroyCurrentMessageLoop()129 void UserInputMonitorLinuxCore::WillDestroyCurrentMessageLoop() {
130   DCHECK(io_task_runner_->BelongsToCurrentThread());
131   StopMonitor(MOUSE_EVENT);
132   StopMonitor(KEYBOARD_EVENT);
133 }
134 
GetKeyPressCount() const135 size_t UserInputMonitorLinuxCore::GetKeyPressCount() const {
136   return counter_.GetKeyPressCount();
137 }
138 
StartMonitor(EventType type)139 void UserInputMonitorLinuxCore::StartMonitor(EventType type) {
140   DCHECK(io_task_runner_->BelongsToCurrentThread());
141 
142   if (type == KEYBOARD_EVENT)
143     counter_.Reset();
144 
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_ = XOpenDisplay(NULL);
152 
153   if (!x_record_display_)
154     x_record_display_ = XOpenDisplay(NULL);
155 
156   if (!x_control_display_ || !x_record_display_) {
157     LOG(ERROR) << "Couldn't open X display";
158     StopMonitor(type);
159     return;
160   }
161 
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.";
166     StopMonitor(type);
167     return;
168   }
169 
170   if (!x_record_range_[type])
171     x_record_range_[type] = XRecordAllocRange();
172 
173   if (!x_record_range_[type]) {
174     LOG(ERROR) << "XRecordAllocRange failed.";
175     StopMonitor(type);
176     return;
177   }
178 
179   if (type == MOUSE_EVENT) {
180     x_record_range_[type]->device_events.first = MotionNotify;
181     x_record_range_[type]->device_events.last = MotionNotify;
182   } else {
183     DCHECK_EQ(KEYBOARD_EVENT, type);
184     x_record_range_[type]->device_events.first = KeyPress;
185     x_record_range_[type]->device_events.last = KeyRelease;
186   }
187 
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;
193   }
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;
198 
199   XRecordClientSpec client_spec = XRecordAllClients;
200   x_record_context_ = XRecordCreateContext(x_record_display_,
201                                            0,
202                                            &client_spec,
203                                            1,
204                                            record_range_to_use,
205                                            number_of_ranges);
206   if (!x_record_context_) {
207     LOG(ERROR) << "XRecordCreateContext failed.";
208     StopMonitor(type);
209     return;
210   }
211 
212   if (!XRecordEnableContextAsync(x_record_display_,
213                                  x_record_context_,
214                                  &UserInputMonitorLinuxCore::ProcessReply,
215                                  reinterpret_cast<XPointer>(this))) {
216     LOG(ERROR) << "XRecordEnableContextAsync failed.";
217     StopMonitor(type);
218     return;
219   }
220 
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();
225     int result =
226         message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_),
227                                           true,
228                                           base::MessageLoopForIO::WATCH_READ,
229                                           &controller_,
230                                           this);
231     if (!result) {
232       LOG(ERROR) << "Failed to create X record task.";
233       StopMonitor(type);
234       return;
235     }
236 
237     // Start observing message loop destruction if we start monitoring the first
238     // event.
239     base::MessageLoop::current()->AddDestructionObserver(this);
240   }
241 
242   // Fetch pending events if any.
243   OnFileCanReadWithoutBlocking(ConnectionNumber(x_record_display_));
244 }
245 
StopMonitor(EventType type)246 void UserInputMonitorLinuxCore::StopMonitor(EventType type) {
247   DCHECK(io_task_runner_->BelongsToCurrentThread());
248 
249   if (x_record_range_[type]) {
250     XFree(x_record_range_[type]);
251     x_record_range_[type] = NULL;
252   }
253   if (x_record_range_[0] || x_record_range_[1])
254     return;
255 
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;
263 
264     controller_.StopWatchingFileDescriptor();
265   }
266   if (x_record_display_) {
267     XCloseDisplay(x_record_display_);
268     x_record_display_ = NULL;
269   }
270   if (x_control_display_) {
271     XCloseDisplay(x_control_display_);
272     x_control_display_ = NULL;
273   }
274   // Stop observing message loop destruction if no event is being monitored.
275   base::MessageLoop::current()->RemoveDestructionObserver(this);
276 }
277 
OnFileCanReadWithoutBlocking(int fd)278 void UserInputMonitorLinuxCore::OnFileCanReadWithoutBlocking(int fd) {
279   DCHECK(io_task_runner_->BelongsToCurrentThread());
280   XEvent event;
281   // Fetch pending events if any.
282   while (XPending(x_record_display_)) {
283     XNextEvent(x_record_display_, &event);
284   }
285 }
286 
OnFileCanWriteWithoutBlocking(int fd)287 void UserInputMonitorLinuxCore::OnFileCanWriteWithoutBlocking(int fd) {
288   NOTREACHED();
289 }
290 
ProcessXEvent(xEvent * event)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         &UserInputMonitor::MouseEventListener::OnMouseMoved, position);
298   } else {
299     ui::EventType type;
300     if (event->u.u.type == KeyPress) {
301       type = ui::ET_KEY_PRESSED;
302     } else if (event->u.u.type == KeyRelease) {
303       type = ui::ET_KEY_RELEASED;
304     } else {
305       NOTREACHED();
306       return;
307     }
308 
309     KeySym key_sym =
310         XkbKeycodeToKeysym(x_control_display_, event->u.u.detail, 0, 0);
311     ui::KeyboardCode key_code = ui::KeyboardCodeFromXKeysym(key_sym);
312     counter_.OnKeyboardEvent(type, key_code);
313   }
314 }
315 
316 // static
ProcessReply(XPointer self,XRecordInterceptData * data)317 void UserInputMonitorLinuxCore::ProcessReply(XPointer self,
318                                              XRecordInterceptData* data) {
319   if (data->category == XRecordFromServer) {
320     xEvent* event = reinterpret_cast<xEvent*>(data->data);
321     reinterpret_cast<UserInputMonitorLinuxCore*>(self)->ProcessXEvent(event);
322   }
323   XRecordFreeData(data);
324 }
325 
326 //
327 // Implementation of UserInputMonitorLinux.
328 //
329 
UserInputMonitorLinux(const scoped_refptr<base::SingleThreadTaskRunner> & io_task_runner)330 UserInputMonitorLinux::UserInputMonitorLinux(
331     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner)
332     : io_task_runner_(io_task_runner),
333       core_(new UserInputMonitorLinuxCore(io_task_runner, mouse_listeners())) {}
334 
~UserInputMonitorLinux()335 UserInputMonitorLinux::~UserInputMonitorLinux() {
336   if (!io_task_runner_->DeleteSoon(FROM_HERE, core_))
337     delete core_;
338 }
339 
GetKeyPressCount() const340 size_t UserInputMonitorLinux::GetKeyPressCount() const {
341   return core_->GetKeyPressCount();
342 }
343 
StartKeyboardMonitoring()344 void UserInputMonitorLinux::StartKeyboardMonitoring() {
345   io_task_runner_->PostTask(
346       FROM_HERE,
347       base::Bind(&UserInputMonitorLinuxCore::StartMonitor,
348                  core_->AsWeakPtr(),
349                  UserInputMonitorLinuxCore::KEYBOARD_EVENT));
350 }
351 
StopKeyboardMonitoring()352 void UserInputMonitorLinux::StopKeyboardMonitoring() {
353   io_task_runner_->PostTask(
354       FROM_HERE,
355       base::Bind(&UserInputMonitorLinuxCore::StopMonitor,
356                  core_->AsWeakPtr(),
357                  UserInputMonitorLinuxCore::KEYBOARD_EVENT));
358 }
359 
StartMouseMonitoring()360 void UserInputMonitorLinux::StartMouseMonitoring() {
361   io_task_runner_->PostTask(FROM_HERE,
362                             base::Bind(&UserInputMonitorLinuxCore::StartMonitor,
363                                        core_->AsWeakPtr(),
364                                        UserInputMonitorLinuxCore::MOUSE_EVENT));
365 }
366 
StopMouseMonitoring()367 void UserInputMonitorLinux::StopMouseMonitoring() {
368   io_task_runner_->PostTask(FROM_HERE,
369                             base::Bind(&UserInputMonitorLinuxCore::StopMonitor,
370                                        core_->AsWeakPtr(),
371                                        UserInputMonitorLinuxCore::MOUSE_EVENT));
372 }
373 
374 }  // namespace
375 
Create(const scoped_refptr<base::SingleThreadTaskRunner> & io_task_runner,const scoped_refptr<base::SingleThreadTaskRunner> & ui_task_runner)376 scoped_ptr<UserInputMonitor> UserInputMonitor::Create(
377     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
378     const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner) {
379   return scoped_ptr<UserInputMonitor>(
380       new UserInputMonitorLinux(io_task_runner));
381 }
382 
383 }  // namespace media
384