Home | History | Annotate | Download | only in base
      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 
    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 
    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 
    129 void UserInputMonitorLinuxCore::WillDestroyCurrentMessageLoop() {
    130   DCHECK(io_task_runner_->BelongsToCurrentThread());
    131   StopMonitor(MOUSE_EVENT);
    132   StopMonitor(KEYBOARD_EVENT);
    133 }
    134 
    135 size_t UserInputMonitorLinuxCore::GetKeyPressCount() const {
    136   return counter_.GetKeyPressCount();
    137 }
    138 
    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 
    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 
    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 
    287 void UserInputMonitorLinuxCore::OnFileCanWriteWithoutBlocking(int fd) {
    288   NOTREACHED();
    289 }
    290 
    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
    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 
    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 
    335 UserInputMonitorLinux::~UserInputMonitorLinux() {
    336   if (!io_task_runner_->DeleteSoon(FROM_HERE, core_))
    337     delete core_;
    338 }
    339 
    340 size_t UserInputMonitorLinux::GetKeyPressCount() const {
    341   return core_->GetKeyPressCount();
    342 }
    343 
    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 
    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 
    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 
    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 
    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