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/input_injector.h"
      6 
      7 #include <X11/extensions/XInput.h>
      8 #include <X11/extensions/XTest.h>
      9 #include <X11/Xlib.h>
     10 #include <X11/XKBlib.h>
     11 
     12 #include <set>
     13 
     14 #include "base/basictypes.h"
     15 #include "base/bind.h"
     16 #include "base/compiler_specific.h"
     17 #include "base/location.h"
     18 #include "base/single_thread_task_runner.h"
     19 #include "base/strings/utf_string_conversion_utils.h"
     20 #include "remoting/base/logging.h"
     21 #include "remoting/host/clipboard.h"
     22 #include "remoting/host/linux/unicode_to_keysym.h"
     23 #include "remoting/proto/internal.pb.h"
     24 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
     25 #include "ui/events/keycodes/dom4/keycode_converter.h"
     26 
     27 namespace remoting {
     28 
     29 namespace {
     30 
     31 using protocol::ClipboardEvent;
     32 using protocol::KeyEvent;
     33 using protocol::TextEvent;
     34 using protocol::MouseEvent;
     35 
     36 bool FindKeycodeForKeySym(Display* display,
     37                           KeySym key_sym,
     38                           uint32_t* keycode,
     39                           uint32_t* modifiers) {
     40   *keycode = XKeysymToKeycode(display, key_sym);
     41 
     42   const uint32_t kModifiersToTry[] = {
     43     0,
     44     ShiftMask,
     45     Mod2Mask,
     46     Mod3Mask,
     47     Mod4Mask,
     48     ShiftMask | Mod2Mask,
     49     ShiftMask | Mod3Mask,
     50     ShiftMask | Mod4Mask,
     51   };
     52 
     53   // TODO(sergeyu): Is there a better way to find modifiers state?
     54   for (size_t i = 0; i < arraysize(kModifiersToTry); ++i) {
     55     unsigned long key_sym_with_mods;
     56     if (XkbLookupKeySym(
     57             display, *keycode, kModifiersToTry[i], NULL, &key_sym_with_mods) &&
     58         key_sym_with_mods == key_sym) {
     59       *modifiers = kModifiersToTry[i];
     60       return true;
     61     }
     62   }
     63 
     64   return false;
     65 }
     66 
     67 // Finds a keycode and set of modifiers that generate character with the
     68 // specified |code_point|.
     69 bool FindKeycodeForUnicode(Display* display,
     70                           uint32_t code_point,
     71                           uint32_t* keycode,
     72                           uint32_t* modifiers) {
     73   std::vector<uint32_t> keysyms;
     74   GetKeySymsForUnicode(code_point, &keysyms);
     75 
     76   for (std::vector<uint32_t>::iterator it = keysyms.begin();
     77        it != keysyms.end(); ++it) {
     78     if (FindKeycodeForKeySym(display, *it, keycode, modifiers)) {
     79       return true;
     80     }
     81   }
     82 
     83   return false;
     84 }
     85 
     86 // Pixel-to-wheel-ticks conversion ratio used by GTK.
     87 // From third_party/WebKit/Source/web/gtk/WebInputEventFactory.cpp .
     88 const float kWheelTicksPerPixel = 3.0f / 160.0f;
     89 
     90 // A class to generate events on Linux.
     91 class InputInjectorLinux : public InputInjector {
     92  public:
     93   explicit InputInjectorLinux(
     94       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
     95   virtual ~InputInjectorLinux();
     96 
     97   bool Init();
     98 
     99   // Clipboard stub interface.
    100   virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE;
    101 
    102   // InputStub interface.
    103   virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE;
    104   virtual void InjectTextEvent(const TextEvent& event) OVERRIDE;
    105   virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE;
    106 
    107   // InputInjector interface.
    108   virtual void Start(
    109       scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
    110 
    111  private:
    112   // The actual implementation resides in InputInjectorLinux::Core class.
    113   class Core : public base::RefCountedThreadSafe<Core> {
    114    public:
    115     explicit Core(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
    116 
    117     bool Init();
    118 
    119     // Mirrors the ClipboardStub interface.
    120     void InjectClipboardEvent(const ClipboardEvent& event);
    121 
    122     // Mirrors the InputStub interface.
    123     void InjectKeyEvent(const KeyEvent& event);
    124     void InjectTextEvent(const TextEvent& event);
    125     void InjectMouseEvent(const MouseEvent& event);
    126 
    127     // Mirrors the InputInjector interface.
    128     void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
    129 
    130     void Stop();
    131 
    132    private:
    133     friend class base::RefCountedThreadSafe<Core>;
    134     virtual ~Core();
    135 
    136     void InitClipboard();
    137 
    138     // Queries whether keyboard auto-repeat is globally enabled. This is used
    139     // to decide whether to temporarily disable then restore this setting. If
    140     // auto-repeat has already been disabled, this class should leave it
    141     // untouched.
    142     bool IsAutoRepeatEnabled();
    143 
    144     // Enables or disables keyboard auto-repeat globally.
    145     void SetAutoRepeatEnabled(bool enabled);
    146 
    147     void InjectScrollWheelClicks(int button, int count);
    148     // Compensates for global button mappings and resets the XTest device
    149     // mapping.
    150     void InitMouseButtonMap();
    151     int MouseButtonToX11ButtonNumber(MouseEvent::MouseButton button);
    152     int HorizontalScrollWheelToX11ButtonNumber(int dx);
    153     int VerticalScrollWheelToX11ButtonNumber(int dy);
    154 
    155     scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
    156 
    157     std::set<int> pressed_keys_;
    158     webrtc::DesktopVector latest_mouse_position_;
    159     float wheel_ticks_x_;
    160     float wheel_ticks_y_;
    161 
    162     // X11 graphics context.
    163     Display* display_;
    164     Window root_window_;
    165 
    166     int test_event_base_;
    167     int test_error_base_;
    168 
    169     // Number of buttons we support.
    170     // Left, Right, Middle, VScroll Up/Down, HScroll Left/Right.
    171     static const int kNumPointerButtons = 7;
    172 
    173     int pointer_button_map_[kNumPointerButtons];
    174 
    175     scoped_ptr<Clipboard> clipboard_;
    176 
    177     bool saved_auto_repeat_enabled_;
    178 
    179     DISALLOW_COPY_AND_ASSIGN(Core);
    180   };
    181 
    182   scoped_refptr<Core> core_;
    183 
    184   DISALLOW_COPY_AND_ASSIGN(InputInjectorLinux);
    185 };
    186 
    187 InputInjectorLinux::InputInjectorLinux(
    188     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
    189   core_ = new Core(task_runner);
    190 }
    191 
    192 InputInjectorLinux::~InputInjectorLinux() {
    193   core_->Stop();
    194 }
    195 
    196 bool InputInjectorLinux::Init() {
    197   return core_->Init();
    198 }
    199 
    200 void InputInjectorLinux::InjectClipboardEvent(const ClipboardEvent& event) {
    201   core_->InjectClipboardEvent(event);
    202 }
    203 
    204 void InputInjectorLinux::InjectKeyEvent(const KeyEvent& event) {
    205   core_->InjectKeyEvent(event);
    206 }
    207 
    208 void InputInjectorLinux::InjectTextEvent(const TextEvent& event) {
    209   core_->InjectTextEvent(event);
    210 }
    211 
    212 void InputInjectorLinux::InjectMouseEvent(const MouseEvent& event) {
    213   core_->InjectMouseEvent(event);
    214 }
    215 
    216 void InputInjectorLinux::Start(
    217     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
    218   core_->Start(client_clipboard.Pass());
    219 }
    220 
    221 InputInjectorLinux::Core::Core(
    222     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
    223     : task_runner_(task_runner),
    224       latest_mouse_position_(-1, -1),
    225       wheel_ticks_x_(0.0f),
    226       wheel_ticks_y_(0.0f),
    227       display_(XOpenDisplay(NULL)),
    228       root_window_(BadValue),
    229       saved_auto_repeat_enabled_(false) {
    230 }
    231 
    232 bool InputInjectorLinux::Core::Init() {
    233   CHECK(display_);
    234 
    235   if (!task_runner_->BelongsToCurrentThread())
    236     task_runner_->PostTask(FROM_HERE, base::Bind(&Core::InitClipboard, this));
    237 
    238   root_window_ = RootWindow(display_, DefaultScreen(display_));
    239   if (root_window_ == BadValue) {
    240     LOG(ERROR) << "Unable to get the root window";
    241     return false;
    242   }
    243 
    244   // TODO(ajwong): Do we want to check the major/minor version at all for XTest?
    245   int major = 0;
    246   int minor = 0;
    247   if (!XTestQueryExtension(display_, &test_event_base_, &test_error_base_,
    248                            &major, &minor)) {
    249     LOG(ERROR) << "Server does not support XTest.";
    250     return false;
    251   }
    252   InitMouseButtonMap();
    253   return true;
    254 }
    255 
    256 void InputInjectorLinux::Core::InjectClipboardEvent(
    257     const ClipboardEvent& event) {
    258   if (!task_runner_->BelongsToCurrentThread()) {
    259     task_runner_->PostTask(
    260         FROM_HERE, base::Bind(&Core::InjectClipboardEvent, this, event));
    261     return;
    262   }
    263 
    264   // |clipboard_| will ignore unknown MIME-types, and verify the data's format.
    265   clipboard_->InjectClipboardEvent(event);
    266 }
    267 
    268 void InputInjectorLinux::Core::InjectKeyEvent(const KeyEvent& event) {
    269   // HostEventDispatcher should filter events missing the pressed field.
    270   if (!event.has_pressed() || !event.has_usb_keycode())
    271     return;
    272 
    273   if (!task_runner_->BelongsToCurrentThread()) {
    274     task_runner_->PostTask(FROM_HERE,
    275                            base::Bind(&Core::InjectKeyEvent, this, event));
    276     return;
    277   }
    278 
    279   int keycode =
    280       ui::KeycodeConverter::UsbKeycodeToNativeKeycode(event.usb_keycode());
    281 
    282   VLOG(3) << "Converting USB keycode: " << std::hex << event.usb_keycode()
    283           << " to keycode: " << keycode << std::dec;
    284 
    285   // Ignore events which can't be mapped.
    286   if (keycode == ui::KeycodeConverter::InvalidNativeKeycode())
    287     return;
    288 
    289   if (event.pressed()) {
    290     if (pressed_keys_.find(keycode) != pressed_keys_.end()) {
    291       // Key is already held down, so lift the key up to ensure this repeated
    292       // press takes effect.
    293       XTestFakeKeyEvent(display_, keycode, False, CurrentTime);
    294     }
    295 
    296     if (pressed_keys_.empty()) {
    297       // Disable auto-repeat, if necessary, to avoid triggering auto-repeat
    298       // if network congestion delays the key-up event from the client.
    299       saved_auto_repeat_enabled_ = IsAutoRepeatEnabled();
    300       if (saved_auto_repeat_enabled_)
    301         SetAutoRepeatEnabled(false);
    302     }
    303     pressed_keys_.insert(keycode);
    304   } else {
    305     pressed_keys_.erase(keycode);
    306     if (pressed_keys_.empty()) {
    307       // Re-enable auto-repeat, if necessary, when all keys are released.
    308       if (saved_auto_repeat_enabled_)
    309         SetAutoRepeatEnabled(true);
    310     }
    311   }
    312 
    313   XTestFakeKeyEvent(display_, keycode, event.pressed(), CurrentTime);
    314   XFlush(display_);
    315 }
    316 
    317 void InputInjectorLinux::Core::InjectTextEvent(const TextEvent& event) {
    318   if (!task_runner_->BelongsToCurrentThread()) {
    319     task_runner_->PostTask(FROM_HERE,
    320                            base::Bind(&Core::InjectTextEvent, this, event));
    321     return;
    322   }
    323 
    324   const std::string text = event.text();
    325   for (int32 index = 0; index < static_cast<int32>(text.size()); ++index) {
    326     uint32_t code_point;
    327     if (!base::ReadUnicodeCharacter(
    328             text.c_str(), text.size(), &index, &code_point)) {
    329       continue;
    330     }
    331 
    332     uint32_t keycode;
    333     uint32_t modifiers;
    334     if (!FindKeycodeForUnicode(display_, code_point, &keycode, &modifiers))
    335       continue;
    336 
    337     XkbLockModifiers(display_, XkbUseCoreKbd,  modifiers, modifiers);
    338 
    339     XTestFakeKeyEvent(display_, keycode, True, CurrentTime);
    340     XTestFakeKeyEvent(display_, keycode, False, CurrentTime);
    341 
    342     XkbLockModifiers(display_, XkbUseCoreKbd, modifiers, 0);
    343   }
    344 
    345   XFlush(display_);
    346 }
    347 
    348 InputInjectorLinux::Core::~Core() {
    349   CHECK(pressed_keys_.empty());
    350 }
    351 
    352 void InputInjectorLinux::Core::InitClipboard() {
    353   DCHECK(task_runner_->BelongsToCurrentThread());
    354   clipboard_ = Clipboard::Create();
    355 }
    356 
    357 bool InputInjectorLinux::Core::IsAutoRepeatEnabled() {
    358   XKeyboardState state;
    359   if (!XGetKeyboardControl(display_, &state)) {
    360     LOG(ERROR) << "Failed to get keyboard auto-repeat status, assuming ON.";
    361     return true;
    362   }
    363   return state.global_auto_repeat == AutoRepeatModeOn;
    364 }
    365 
    366 void InputInjectorLinux::Core::SetAutoRepeatEnabled(bool mode) {
    367   XKeyboardControl control;
    368   control.auto_repeat_mode = mode ? AutoRepeatModeOn : AutoRepeatModeOff;
    369   XChangeKeyboardControl(display_, KBAutoRepeatMode, &control);
    370 }
    371 
    372 void InputInjectorLinux::Core::InjectScrollWheelClicks(int button, int count) {
    373   if (button < 0) {
    374     LOG(WARNING) << "Ignoring unmapped scroll wheel button";
    375     return;
    376   }
    377   for (int i = 0; i < count; i++) {
    378     // Generate a button-down and a button-up to simulate a wheel click.
    379     XTestFakeButtonEvent(display_, button, true, CurrentTime);
    380     XTestFakeButtonEvent(display_, button, false, CurrentTime);
    381   }
    382 }
    383 
    384 void InputInjectorLinux::Core::InjectMouseEvent(const MouseEvent& event) {
    385   if (!task_runner_->BelongsToCurrentThread()) {
    386     task_runner_->PostTask(FROM_HERE,
    387                            base::Bind(&Core::InjectMouseEvent, this, event));
    388     return;
    389   }
    390 
    391   if (event.has_delta_x() &&
    392       event.has_delta_y() &&
    393       (event.delta_x() != 0 || event.delta_y() != 0)) {
    394     latest_mouse_position_.set(-1, -1);
    395     VLOG(3) << "Moving mouse by " << event.delta_x() << "," << event.delta_y();
    396     XTestFakeRelativeMotionEvent(display_,
    397                                  event.delta_x(), event.delta_y(),
    398                                  CurrentTime);
    399 
    400   } else if (event.has_x() && event.has_y()) {
    401     // Injecting a motion event immediately before a button release results in
    402     // a MotionNotify even if the mouse position hasn't changed, which confuses
    403     // apps which assume MotionNotify implies movement. See crbug.com/138075.
    404     bool inject_motion = true;
    405     webrtc::DesktopVector new_mouse_position(
    406         webrtc::DesktopVector(event.x(), event.y()));
    407     if (event.has_button() && event.has_button_down() && !event.button_down()) {
    408       if (new_mouse_position.equals(latest_mouse_position_))
    409         inject_motion = false;
    410     }
    411 
    412     if (inject_motion) {
    413       latest_mouse_position_.set(std::max(0, new_mouse_position.x()),
    414                                  std::max(0, new_mouse_position.y()));
    415 
    416       VLOG(3) << "Moving mouse to " << latest_mouse_position_.x()
    417               << "," << latest_mouse_position_.y();
    418       XTestFakeMotionEvent(display_, DefaultScreen(display_),
    419                            latest_mouse_position_.x(),
    420                            latest_mouse_position_.y(),
    421                            CurrentTime);
    422     }
    423   }
    424 
    425   if (event.has_button() && event.has_button_down()) {
    426     int button_number = MouseButtonToX11ButtonNumber(event.button());
    427 
    428     if (button_number < 0) {
    429       LOG(WARNING) << "Ignoring unknown button type: " << event.button();
    430       return;
    431     }
    432 
    433     VLOG(3) << "Button " << event.button()
    434             << " received, sending "
    435             << (event.button_down() ? "down " : "up ")
    436             << button_number;
    437     XTestFakeButtonEvent(display_, button_number, event.button_down(),
    438                          CurrentTime);
    439   }
    440 
    441   // Older client plugins always send scroll events in pixels, which
    442   // must be accumulated host-side. Recent client plugins send both
    443   // pixels and ticks with every scroll event, allowing the host to
    444   // choose the best model on a per-platform basis. Since we can only
    445   // inject ticks on Linux, use them if available.
    446   int ticks_y = 0;
    447   if (event.has_wheel_ticks_y()) {
    448     ticks_y = event.wheel_ticks_y();
    449   } else if (event.has_wheel_delta_y()) {
    450     wheel_ticks_y_ += event.wheel_delta_y() * kWheelTicksPerPixel;
    451     ticks_y = static_cast<int>(wheel_ticks_y_);
    452     wheel_ticks_y_ -= ticks_y;
    453   }
    454   if (ticks_y != 0) {
    455     InjectScrollWheelClicks(VerticalScrollWheelToX11ButtonNumber(ticks_y),
    456                             abs(ticks_y));
    457   }
    458 
    459   int ticks_x = 0;
    460   if (event.has_wheel_ticks_x()) {
    461     ticks_x = event.wheel_ticks_x();
    462   } else if (event.has_wheel_delta_x()) {
    463     wheel_ticks_x_ += event.wheel_delta_x() * kWheelTicksPerPixel;
    464     ticks_x = static_cast<int>(wheel_ticks_x_);
    465     wheel_ticks_x_ -= ticks_x;
    466   }
    467   if (ticks_x != 0) {
    468     InjectScrollWheelClicks(HorizontalScrollWheelToX11ButtonNumber(ticks_x),
    469                             abs(ticks_x));
    470   }
    471 
    472   XFlush(display_);
    473 }
    474 
    475 void InputInjectorLinux::Core::InitMouseButtonMap() {
    476   // TODO(rmsousa): Run this on global/device mapping change events.
    477 
    478   // Do not touch global pointer mapping, since this may affect the local user.
    479   // Instead, try to work around it by reversing the mapping.
    480   // Note that if a user has a global mapping that completely disables a button
    481   // (by assigning 0 to it), we won't be able to inject it.
    482   int num_buttons = XGetPointerMapping(display_, NULL, 0);
    483   scoped_ptr<unsigned char[]> pointer_mapping(new unsigned char[num_buttons]);
    484   num_buttons = XGetPointerMapping(display_, pointer_mapping.get(),
    485                                    num_buttons);
    486   for (int i = 0; i < kNumPointerButtons; i++) {
    487     pointer_button_map_[i] = -1;
    488   }
    489   for (int i = 0; i < num_buttons; i++) {
    490     // Reverse the mapping.
    491     if (pointer_mapping[i] > 0 && pointer_mapping[i] <= kNumPointerButtons)
    492       pointer_button_map_[pointer_mapping[i] - 1] = i + 1;
    493   }
    494   for (int i = 0; i < kNumPointerButtons; i++) {
    495     if (pointer_button_map_[i] == -1)
    496       LOG(ERROR) << "Global pointer mapping does not support button " << i + 1;
    497   }
    498 
    499   int opcode, event, error;
    500   if (!XQueryExtension(display_, "XInputExtension", &opcode, &event, &error)) {
    501     // If XInput is not available, we're done. But it would be very unusual to
    502     // have a server that supports XTest but not XInput, so log it as an error.
    503     LOG(ERROR) << "X Input extension not available: " << error;
    504     return;
    505   }
    506 
    507   // Make sure the XTEST XInput pointer device mapping is trivial. It should be
    508   // safe to reset this mapping, as it won't affect the user's local devices.
    509   // In fact, the reason why we do this is because an old gnome-settings-daemon
    510   // may have mistakenly applied left-handed preferences to the XTEST device.
    511   XID device_id = 0;
    512   bool device_found = false;
    513   int num_devices;
    514   XDeviceInfo* devices;
    515   devices = XListInputDevices(display_, &num_devices);
    516   for (int i = 0; i < num_devices; i++) {
    517     XDeviceInfo* device_info = &devices[i];
    518     if (device_info->use == IsXExtensionPointer &&
    519         strcmp(device_info->name, "Virtual core XTEST pointer") == 0) {
    520       device_id = device_info->id;
    521       device_found = true;
    522       break;
    523     }
    524   }
    525   XFreeDeviceList(devices);
    526 
    527   if (!device_found) {
    528     HOST_LOG << "Cannot find XTest device.";
    529     return;
    530   }
    531 
    532   XDevice* device = XOpenDevice(display_, device_id);
    533   if (!device) {
    534     LOG(ERROR) << "Cannot open XTest device.";
    535     return;
    536   }
    537 
    538   int num_device_buttons = XGetDeviceButtonMapping(display_, device, NULL, 0);
    539   scoped_ptr<unsigned char[]> button_mapping(new unsigned char[num_buttons]);
    540   for (int i = 0; i < num_device_buttons; i++) {
    541     button_mapping[i] = i + 1;
    542   }
    543   error = XSetDeviceButtonMapping(display_, device, button_mapping.get(),
    544                                   num_device_buttons);
    545   if (error != Success)
    546     LOG(ERROR) << "Failed to set XTest device button mapping: " << error;
    547 
    548   XCloseDevice(display_, device);
    549 }
    550 
    551 int InputInjectorLinux::Core::MouseButtonToX11ButtonNumber(
    552     MouseEvent::MouseButton button) {
    553   switch (button) {
    554     case MouseEvent::BUTTON_LEFT:
    555       return pointer_button_map_[0];
    556 
    557     case MouseEvent::BUTTON_RIGHT:
    558       return pointer_button_map_[2];
    559 
    560     case MouseEvent::BUTTON_MIDDLE:
    561       return pointer_button_map_[1];
    562 
    563     case MouseEvent::BUTTON_UNDEFINED:
    564     default:
    565       return -1;
    566   }
    567 }
    568 
    569 int InputInjectorLinux::Core::HorizontalScrollWheelToX11ButtonNumber(int dx) {
    570   return (dx > 0 ? pointer_button_map_[5] : pointer_button_map_[6]);
    571 }
    572 
    573 int InputInjectorLinux::Core::VerticalScrollWheelToX11ButtonNumber(int dy) {
    574   // Positive y-values are wheel scroll-up events (button 4), negative y-values
    575   // are wheel scroll-down events (button 5).
    576   return (dy > 0 ? pointer_button_map_[3] : pointer_button_map_[4]);
    577 }
    578 
    579 void InputInjectorLinux::Core::Start(
    580     scoped_ptr<protocol::ClipboardStub> client_clipboard) {
    581   if (!task_runner_->BelongsToCurrentThread()) {
    582     task_runner_->PostTask(
    583         FROM_HERE,
    584         base::Bind(&Core::Start, this, base::Passed(&client_clipboard)));
    585     return;
    586   }
    587 
    588   InitMouseButtonMap();
    589 
    590   clipboard_->Start(client_clipboard.Pass());
    591 }
    592 
    593 void InputInjectorLinux::Core::Stop() {
    594   if (!task_runner_->BelongsToCurrentThread()) {
    595     task_runner_->PostTask(FROM_HERE, base::Bind(&Core::Stop, this));
    596     return;
    597   }
    598 
    599   clipboard_->Stop();
    600 }
    601 
    602 }  // namespace
    603 
    604 scoped_ptr<InputInjector> InputInjector::Create(
    605     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
    606     scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
    607   scoped_ptr<InputInjectorLinux> injector(
    608       new InputInjectorLinux(main_task_runner));
    609   if (!injector->Init())
    610     return scoped_ptr<InputInjector>();
    611   return injector.PassAs<InputInjector>();
    612 }
    613 
    614 }  // namespace remoting
    615