Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2012 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 "remoting/host/local_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/message_loop/message_loop.h"
     19 #include "base/message_loop/message_pump_libevent.h"
     20 #include "base/posix/eintr_wrapper.h"
     21 #include "base/single_thread_task_runner.h"
     22 #include "base/threading/non_thread_safe.h"
     23 #include "remoting/host/client_session_control.h"
     24 #include "third_party/skia/include/core/SkPoint.h"
     25 
     26 // These includes need to be later than dictated by the style guide due to
     27 // Xlib header pollution, specifically the min, max, and Status macros.
     28 #include <X11/XKBlib.h>
     29 #include <X11/Xlibint.h>
     30 #include <X11/extensions/record.h>
     31 
     32 namespace remoting {
     33 
     34 namespace {
     35 
     36 class LocalInputMonitorLinux : public base::NonThreadSafe,
     37                                public LocalInputMonitor {
     38  public:
     39   LocalInputMonitorLinux(
     40       scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     41       scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
     42       base::WeakPtr<ClientSessionControl> client_session_control);
     43   virtual ~LocalInputMonitorLinux();
     44 
     45  private:
     46   // The actual implementation resides in LocalInputMonitorLinux::Core class.
     47   class Core
     48       : public base::RefCountedThreadSafe<Core>,
     49         public base::MessagePumpLibevent::Watcher {
     50    public:
     51     Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
     52          scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
     53          base::WeakPtr<ClientSessionControl> client_session_control);
     54 
     55     void Start();
     56     void Stop();
     57 
     58    private:
     59     friend class base::RefCountedThreadSafe<Core>;
     60     virtual ~Core();
     61 
     62     void StartOnInputThread();
     63     void StopOnInputThread();
     64 
     65     // base::MessagePumpLibevent::Watcher interface.
     66     virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
     67     virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
     68 
     69     // Processes key and mouse events.
     70     void ProcessXEvent(xEvent* event);
     71 
     72     static void ProcessReply(XPointer self, XRecordInterceptData* data);
     73 
     74     // Task runner on which public methods of this class must be called.
     75     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
     76 
     77     // Task runner on which X Window events are received.
     78     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner_;
     79 
     80     // Points to the object receiving mouse event notifications and session
     81     // disconnect requests.
     82     base::WeakPtr<ClientSessionControl> client_session_control_;
     83 
     84     // Used to receive base::MessagePumpLibevent::Watcher events.
     85     base::MessagePumpLibevent::FileDescriptorWatcher controller_;
     86 
     87     // True when Alt is pressed.
     88     bool alt_pressed_;
     89 
     90     // True when Ctrl is pressed.
     91     bool ctrl_pressed_;
     92 
     93     Display* display_;
     94     Display* x_record_display_;
     95     XRecordRange* x_record_range_[2];
     96     XRecordContext x_record_context_;
     97 
     98     DISALLOW_COPY_AND_ASSIGN(Core);
     99   };
    100 
    101   scoped_refptr<Core> core_;
    102 
    103   DISALLOW_COPY_AND_ASSIGN(LocalInputMonitorLinux);
    104 };
    105 
    106 LocalInputMonitorLinux::LocalInputMonitorLinux(
    107     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    108     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
    109     base::WeakPtr<ClientSessionControl> client_session_control)
    110     : core_(new Core(caller_task_runner,
    111                      input_task_runner,
    112                      client_session_control)) {
    113   core_->Start();
    114 }
    115 
    116 LocalInputMonitorLinux::~LocalInputMonitorLinux() {
    117   core_->Stop();
    118 }
    119 
    120 LocalInputMonitorLinux::Core::Core(
    121     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    122     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
    123     base::WeakPtr<ClientSessionControl> client_session_control)
    124     : caller_task_runner_(caller_task_runner),
    125       input_task_runner_(input_task_runner),
    126       client_session_control_(client_session_control),
    127       alt_pressed_(false),
    128       ctrl_pressed_(false),
    129       display_(NULL),
    130       x_record_display_(NULL),
    131       x_record_context_(0) {
    132   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    133   DCHECK(client_session_control_.get());
    134 
    135   x_record_range_[0] = NULL;
    136   x_record_range_[1] = NULL;
    137 }
    138 
    139 void LocalInputMonitorLinux::Core::Start() {
    140   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    141 
    142   input_task_runner_->PostTask(FROM_HERE,
    143                                base::Bind(&Core::StartOnInputThread, this));
    144 }
    145 
    146 void LocalInputMonitorLinux::Core::Stop() {
    147   DCHECK(caller_task_runner_->BelongsToCurrentThread());
    148 
    149   input_task_runner_->PostTask(FROM_HERE,
    150                                base::Bind(&Core::StopOnInputThread, this));
    151 }
    152 
    153 LocalInputMonitorLinux::Core::~Core() {
    154   DCHECK(!display_);
    155   DCHECK(!x_record_display_);
    156   DCHECK(!x_record_range_[0]);
    157   DCHECK(!x_record_range_[1]);
    158   DCHECK(!x_record_context_);
    159 }
    160 
    161 void LocalInputMonitorLinux::Core::StartOnInputThread() {
    162   DCHECK(input_task_runner_->BelongsToCurrentThread());
    163   DCHECK(!display_);
    164   DCHECK(!x_record_display_);
    165   DCHECK(!x_record_range_[0]);
    166   DCHECK(!x_record_range_[1]);
    167   DCHECK(!x_record_context_);
    168 
    169   // TODO(jamiewalch): We should pass the display in. At that point, since
    170   // XRecord needs a private connection to the X Server for its data channel
    171   // and both channels are used from a separate thread, we'll need to duplicate
    172   // them with something like the following:
    173   //   XOpenDisplay(DisplayString(display));
    174   display_ = XOpenDisplay(NULL);
    175   x_record_display_ = XOpenDisplay(NULL);
    176   if (!display_ || !x_record_display_) {
    177     LOG(ERROR) << "Couldn't open X display";
    178     return;
    179   }
    180 
    181   int xr_opcode, xr_event, xr_error;
    182   if (!XQueryExtension(display_, "RECORD", &xr_opcode, &xr_event, &xr_error)) {
    183     LOG(ERROR) << "X Record extension not available.";
    184     return;
    185   }
    186 
    187   x_record_range_[0] = XRecordAllocRange();
    188   x_record_range_[1] = XRecordAllocRange();
    189   if (!x_record_range_[0] || !x_record_range_[1]) {
    190     LOG(ERROR) << "XRecordAllocRange failed.";
    191     return;
    192   }
    193   x_record_range_[0]->device_events.first = MotionNotify;
    194   x_record_range_[0]->device_events.last = MotionNotify;
    195   x_record_range_[1]->device_events.first = KeyPress;
    196   x_record_range_[1]->device_events.last = KeyRelease;
    197   XRecordClientSpec client_spec = XRecordAllClients;
    198 
    199   x_record_context_ = XRecordCreateContext(
    200       x_record_display_, 0, &client_spec, 1, x_record_range_,
    201       arraysize(x_record_range_));
    202   if (!x_record_context_) {
    203     LOG(ERROR) << "XRecordCreateContext failed.";
    204     return;
    205   }
    206 
    207   if (!XRecordEnableContextAsync(x_record_display_, x_record_context_,
    208                                  &Core::ProcessReply,
    209                                  reinterpret_cast<XPointer>(this))) {
    210     LOG(ERROR) << "XRecordEnableContextAsync failed.";
    211     return;
    212   }
    213 
    214   // Register OnFileCanReadWithoutBlocking() to be called every time there is
    215   // something to read from |x_record_display_|.
    216   base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
    217   int result =
    218       message_loop->WatchFileDescriptor(ConnectionNumber(x_record_display_),
    219                                         true,
    220                                         base::MessageLoopForIO::WATCH_READ,
    221                                         &controller_,
    222                                         this);
    223   if (!result) {
    224     LOG(ERROR) << "Failed to create X record task.";
    225     return;
    226   }
    227 
    228   // Fetch pending events if any.
    229   while (XPending(x_record_display_)) {
    230     XEvent ev;
    231     XNextEvent(x_record_display_, &ev);
    232   }
    233 }
    234 
    235 void LocalInputMonitorLinux::Core::StopOnInputThread() {
    236   DCHECK(input_task_runner_->BelongsToCurrentThread());
    237 
    238   // Context must be disabled via the control channel because we can't send
    239   // any X protocol traffic over the data channel while it's recording.
    240   if (x_record_context_) {
    241     XRecordDisableContext(display_, x_record_context_);
    242     XFlush(display_);
    243   }
    244 
    245   controller_.StopWatchingFileDescriptor();
    246 
    247   if (x_record_range_[0]) {
    248     XFree(x_record_range_[0]);
    249     x_record_range_[0] = NULL;
    250   }
    251   if (x_record_range_[1]) {
    252     XFree(x_record_range_[1]);
    253     x_record_range_[1] = NULL;
    254   }
    255   if (x_record_context_) {
    256     XRecordFreeContext(x_record_display_, x_record_context_);
    257     x_record_context_ = 0;
    258   }
    259   if (x_record_display_) {
    260     XCloseDisplay(x_record_display_);
    261     x_record_display_ = NULL;
    262   }
    263   if (display_) {
    264     XCloseDisplay(display_);
    265     display_ = NULL;
    266   }
    267 }
    268 
    269 void LocalInputMonitorLinux::Core::OnFileCanReadWithoutBlocking(int fd) {
    270   DCHECK(input_task_runner_->BelongsToCurrentThread());
    271 
    272   // Fetch pending events if any.
    273   while (XPending(x_record_display_)) {
    274     XEvent ev;
    275     XNextEvent(x_record_display_, &ev);
    276   }
    277 }
    278 
    279 void LocalInputMonitorLinux::Core::OnFileCanWriteWithoutBlocking(int fd) {
    280   NOTREACHED();
    281 }
    282 
    283 void LocalInputMonitorLinux::Core::ProcessXEvent(xEvent* event) {
    284   DCHECK(input_task_runner_->BelongsToCurrentThread());
    285 
    286   if (event->u.u.type == MotionNotify) {
    287     SkIPoint position(SkIPoint::Make(event->u.keyButtonPointer.rootX,
    288                                      event->u.keyButtonPointer.rootY));
    289     caller_task_runner_->PostTask(
    290         FROM_HERE, base::Bind(&ClientSessionControl::OnLocalMouseMoved,
    291                               client_session_control_,
    292                               position));
    293   } else {
    294     int key_code = event->u.u.detail;
    295     bool down = event->u.u.type == KeyPress;
    296     KeySym key_sym = XkbKeycodeToKeysym(display_, key_code, 0, 0);
    297     if (key_sym == XK_Control_L || key_sym == XK_Control_R) {
    298       ctrl_pressed_ = down;
    299     } else if (key_sym == XK_Alt_L || key_sym == XK_Alt_R) {
    300       alt_pressed_ = down;
    301     } else if (key_sym == XK_Escape && down && alt_pressed_ && ctrl_pressed_) {
    302       caller_task_runner_->PostTask(
    303           FROM_HERE, base::Bind(&ClientSessionControl::DisconnectSession,
    304                                 client_session_control_));
    305     }
    306   }
    307 }
    308 
    309 // static
    310 void LocalInputMonitorLinux::Core::ProcessReply(XPointer self,
    311                                                 XRecordInterceptData* data) {
    312   if (data->category == XRecordFromServer) {
    313     xEvent* event = reinterpret_cast<xEvent*>(data->data);
    314     reinterpret_cast<Core*>(self)->ProcessXEvent(event);
    315   }
    316   XRecordFreeData(data);
    317 }
    318 
    319 }  // namespace
    320 
    321 scoped_ptr<LocalInputMonitor> LocalInputMonitor::Create(
    322     scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
    323     scoped_refptr<base::SingleThreadTaskRunner> input_task_runner,
    324     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
    325     base::WeakPtr<ClientSessionControl> client_session_control) {
    326   return scoped_ptr<LocalInputMonitor>(
    327       new LocalInputMonitorLinux(caller_task_runner,
    328                                  input_task_runner,
    329                                  client_session_control));
    330 }
    331 
    332 }  // namespace remoting
    333