text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "ui/base/events.h"
  
  #include <X11/Xlib.h>
  #include <X11/extensions/XInput.h>
  #include <X11/extensions/XInput2.h>
  #include <string.h>
  
  #include "base/logging.h"
  #include "ui/base/keycodes/keyboard_code_conversion_x.h"
  #include "ui/base/touch/touch_factory.h"
  #include "ui/base/x/x11_util.h"
  #include "ui/gfx/point.h"
  
  #if !defined(TOOLKIT_USES_GTK)
  #include "base/message_pump_x.h"
  #endif
  
  // Copied from xserver-properties.h
  #define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel"
  #define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel"
  
  // CMT specific timings
  #define AXIS_LABEL_PROP_ABS_START_TIME "Abs Start Timestamp"
  #define AXIS_LABEL_PROP_ABS_END_TIME "Abs End Timestamp"
  
  // Fling properties
  #define AXIS_LABEL_PROP_ABS_FLING_X       "Abs Fling X Velocity"
  #define AXIS_LABEL_PROP_ABS_FLING_Y       "Abs Fling Y Velocity"
  #define AXIS_LABEL_PROP_ABS_FLING_STATE   "Abs Fling State"
  
  namespace {
  
  // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+.
  const int kWheelScrollAmount = 53;
  
  const int kMinWheelButton = 4;
  #if defined(OS_CHROMEOS)
  // TODO(davemoore) For now use the button to decide how much to scroll by.
  // When we go to XI2 scroll events this won't be necessary. If this doesn't
  // happen for some reason we can better detect which devices are touchpads.
  const int kTouchpadScrollAmount = 3;
  
  // Chrome OS also uses buttons 8 and 9 for scrolling.
  const int kMaxWheelButton = 9;
  #else
  const int kMaxWheelButton = 7;
  #endif
  
  // A class to support the detection of scroll events, using X11 valuators.
  class UI_EXPORT CMTEventData {
   public:
    // Returns the ScrollEventData singleton.
    static CMTEventData* GetInstance() {
      return Singleton<CMTEventData>::get();
    }
  
    // Updates the list of devices.
    void UpdateDeviceList(Display* display) {
      cmt_devices_.reset();
      touchpads_.reset();
      device_to_valuators_.clear();
  
      int count = 0;
  
      // Find all the touchpad devices.
      XDeviceInfo* dev_list = XListInputDevices(display, &count);
      Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false);
      for (int i = 0; i < count; ++i) {
        XDeviceInfo* dev = dev_list + i;
        if (dev->type == xi_touchpad)
          touchpads_[dev_list[i].id] = true;
      }
      if (dev_list)
        XFreeDeviceList(dev_list);
  
      XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &count);
      Atom x_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_HWHEEL, false);
      Atom y_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_WHEEL, false);
      Atom start_time =
          XInternAtom(display, AXIS_LABEL_PROP_ABS_START_TIME, false);
      Atom end_time = XInternAtom(display, AXIS_LABEL_PROP_ABS_END_TIME, false);
      Atom fling_vx = XInternAtom(display, AXIS_LABEL_PROP_ABS_FLING_X, false);
      Atom fling_vy = XInternAtom(display, AXIS_LABEL_PROP_ABS_FLING_Y, false);
      Atom fling_state =
          XInternAtom(display, AXIS_LABEL_PROP_ABS_FLING_STATE, false);
  
      for (int i = 0; i < count; ++i) {
        XIDeviceInfo* info = info_list + i;
  
        if (info->use != XISlavePointer && info->use != XIFloatingSlave)
          continue;
  
        Valuators valuators;
        bool is_cmt = false;
        for (int j = 0; j < info->num_classes; ++j) {
          if (info->classes[j]->type != XIValuatorClass)
            continue;
  
          XIValuatorClassInfo* v =
              reinterpret_cast<XIValuatorClassInfo*>(info->classes[j]);
          int number = v->number;
          if (number > valuators.max)
            valuators.max = number;
          if (v->label == x_axis) {
            valuators.scroll_x = number;
            is_cmt = true;
          } else if (v->label == y_axis) {
            valuators.scroll_y = number;
            is_cmt = true;
          } else if (v->label == start_time) {
            valuators.start_time = number;
            is_cmt = true;
          } else if (v->label == end_time) {
            valuators.end_time = number;
            is_cmt = true;
          } else if (v->label == fling_vx) {
            valuators.fling_vx = number;
            is_cmt = true;
          } else if (v->label == fling_vy) {
            valuators.fling_vy = number;
            is_cmt = true;
          } else if (v->label == fling_state) {
            valuators.fling_state = number;
            is_cmt = true;
          }
        }
        if (is_cmt) {
          device_to_valuators_[info->deviceid] = valuators;
          cmt_devices_[info->deviceid] = true;
        }
      }
      XIFreeDeviceInfo(info_list);
    }
  
    void set_natural_scroll_enabled(bool enabled) {
      natural_scroll_enabled_ = enabled;
    }
  
    float GetNaturalScrollFactor(int deviceid) {
      // Natural scroll is touchpad-only.
      if (!touchpads_[deviceid])
        return -1.0f;
  
      return natural_scroll_enabled_ ? 1.0f : -1.0f;
    }
  
    // Returns true if this is a scroll event (a motion event with the necessary
    // valuators. Also returns the offsets. |x_offset| and |y_offset| can be
    // NULL.
    bool GetScrollOffsets(const XEvent& xev, float* x_offset, float* y_offset) {
      XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
  
      if (x_offset)
        *x_offset = 0;
      if (y_offset)
        *y_offset = 0;
  
      const int deviceid = xiev->deviceid;
      if (!cmt_devices_[deviceid])
        return false;
  
      const float natural_scroll_factor = GetNaturalScrollFactor(deviceid);
      const Valuators v = device_to_valuators_[deviceid];
      const bool has_x_offset = XIMaskIsSet(xiev->valuators.mask, v.scroll_x);
      const bool has_y_offset = XIMaskIsSet(xiev->valuators.mask, v.scroll_y);
      const bool is_scroll = has_x_offset || has_y_offset;
  
      if (!is_scroll || (!x_offset && !y_offset))
        return is_scroll;
  
      double* valuators = xiev->valuators.values;
      for (int i = 0; i <= v.max; ++i) {
        if (XIMaskIsSet(xiev->valuators.mask, i)) {
          if (x_offset && v.scroll_x == i)
            *x_offset = *valuators * natural_scroll_factor;
          else if (y_offset && v.scroll_y == i)
            *y_offset = *valuators * natural_scroll_factor;
          valuators++;
        }
      }
  
      return true;
    }
  
    bool GetFlingData(const XEvent& xev,
                      float* vx, float* vy,
                      bool* is_cancel) {
      XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
  
      *vx = 0;
      *vy = 0;
      *is_cancel = false;
  
      const int deviceid = xiev->deviceid;
      if (!cmt_devices_[deviceid])
        return false;
  
      const float natural_scroll_factor = GetNaturalScrollFactor(deviceid);
      const Valuators v = device_to_valuators_[deviceid];
      if (!XIMaskIsSet(xiev->valuators.mask, v.fling_vx) ||
          !XIMaskIsSet(xiev->valuators.mask, v.fling_vy) ||
          !XIMaskIsSet(xiev->valuators.mask, v.fling_state))
        return false;
  
      double* valuators = xiev->valuators.values;
      for (int i = 0; i <= v.max; ++i) {
        if (XIMaskIsSet(xiev->valuators.mask, i)) {
          // Convert values to unsigned ints represending ms before storing them,
          // as that is how they were encoded before conversion to doubles.
          if (v.fling_vx == i)
            *vx = natural_scroll_factor *
                  (static_cast<int>(*valuators)) / 1000.0f;
          else if (v.fling_vy == i)
            *vy = natural_scroll_factor *
                  (static_cast<int>(*valuators)) / 1000.0f;
          else if (v.fling_state == i)
            *is_cancel = !!static_cast<unsigned int>(*valuators);
          valuators++;
        }
      }
  
      return true;
    }
  
    bool GetGestureTimes(const XEvent& xev,
                         double* start_time,
                         double* end_time) {
      *start_time = 0;
      *end_time = 0;
  
      XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
      if (!cmt_devices_[xiev->deviceid])
        return false;
  
      Valuators v = device_to_valuators_[xiev->deviceid];
      if (!XIMaskIsSet(xiev->valuators.mask, v.start_time) ||
          !XIMaskIsSet(xiev->valuators.mask, v.end_time))
        return false;
  
      double* valuators = xiev->valuators.values;
      for (int i = 0; i <= v.max; ++i) {
        if (XIMaskIsSet(xiev->valuators.mask, i)) {
          // Convert values to unsigned ints represending ms before storing them,
          // as that is how they were encoded before conversion to doubles.
          if (v.start_time == i)
            *start_time =
                static_cast<double>(static_cast<unsigned int>(*valuators)) / 1000;
          else if (v.end_time == i)
            *end_time =
                static_cast<double>(static_cast<unsigned int>(*valuators)) / 1000;
          valuators++;
        }
      }
  
      return true;
    }
  
   private:
    // Requirement for Singleton
    friend struct DefaultSingletonTraits<CMTEventData>;
  
    struct Valuators {
      int max;
      int scroll_x;
      int scroll_y;
      int start_time;
      int end_time;
      int fling_vx;
      int fling_vy;
      int fling_state;
  
      Valuators()
          : max(-1),
            scroll_x(-1),
            scroll_y(-1),
            start_time(-1),
            end_time(-1),
            fling_vx(-1),
            fling_vy(-1),
            fling_state(-1) {
  
      }
  
    };
  
    CMTEventData() : natural_scroll_enabled_(true) {
      UpdateDeviceList(ui::GetXDisplay());
    }
  
    ~CMTEventData() {}
  
    // A quick lookup table for determining if events from the pointer device
    // should be processed.
    static const int kMaxDeviceNum = 128;
    bool natural_scroll_enabled_;
    std::bitset<kMaxDeviceNum> cmt_devices_;
    std::bitset<kMaxDeviceNum> touchpads_;
    std::map<int, Valuators> device_to_valuators_;
  
    DISALLOW_COPY_AND_ASSIGN(CMTEventData);
  };
  
  // A class to track current modifier state on master device. Only track ctrl,
  // alt, shift and caps lock keys currently. The tracked state can then be used
  // by floating device.
  class UI_EXPORT XModifierStateWatcher{
   public:
    static XModifierStateWatcher* GetInstance() {
      return Singleton<XModifierStateWatcher>::get();
    }
  
    void UpdateStateFromEvent(const base::NativeEvent& native_event) {
      // Floating device can't access the modifer state from master device.
      // We need to track the states of modifier keys in a singleton for
      // floating devices such as touch screen. Issue 106426 is one example
      // of why we need the modifier states for floating device.
      state_ = native_event->xkey.state;
      // master_state is the state before key press. We need to track the
      // state after key press for floating device. Currently only ctrl,
      // shift, alt and caps lock keys are tracked.
      ui::KeyboardCode keyboard_code = ui::KeyboardCodeFromNative(native_event);
      unsigned int mask = 0;
  
      switch (keyboard_code) {
        case ui::VKEY_CONTROL: {
          mask = ControlMask;
          break;
        }
        case ui::VKEY_SHIFT: {
          mask = ShiftMask;
          break;
        }
        case ui::VKEY_MENU: {
          mask = Mod1Mask;
          break;
        }
        case ui::VKEY_CAPITAL: {
          mask = LockMask;
          break;
        }
        default:
          break;
      }
  
      if (native_event->type == KeyPress)
        state_ |= mask;
      else
        state_ &= ~mask;
    }
  
    // Returns the current modifer state in master device. It only contains the
    // state of ctrl, shift, alt and caps lock keys.
    unsigned int state() { return state_; }
  
   private:
    friend struct DefaultSingletonTraits<XModifierStateWatcher>;
  
    XModifierStateWatcher() : state_(0) { }
  
    unsigned int state_;
  
    DISALLOW_COPY_AND_ASSIGN(XModifierStateWatcher);
  };
  
  int GetEventFlagsFromXState(unsigned int state) {
    int flags = 0;
    if (state & ControlMask)
      flags |= ui::EF_CONTROL_DOWN;
    if (state & ShiftMask)
      flags |= ui::EF_SHIFT_DOWN;
    if (state & Mod1Mask)
      flags |= ui::EF_ALT_DOWN;
    if (state & LockMask)
      flags |= ui::EF_CAPS_LOCK_DOWN;
    if (state & Button1Mask)
      flags |= ui::EF_LEFT_MOUSE_BUTTON;
    if (state & Button2Mask)
      flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
    if (state & Button3Mask)
      flags |= ui::EF_RIGHT_MOUSE_BUTTON;
    return flags;
  }
  
  // Get the event flag for the button in XButtonEvent. During a ButtonPress
  // event, |state| in XButtonEvent does not include the button that has just been
  // pressed. Instead |state| contains flags for the buttons (if any) that had
  // already been pressed before the current button, and |button| stores the most
  // current pressed button. So, if you press down left mouse button, and while
  // pressing it down, press down the right mouse button, then for the latter
  // event, |state| would have Button1Mask set but not Button3Mask, and |button|
  // would be 3.
  int GetEventFlagsForButton(int button) {
    switch (button) {
      case 1:
        return ui::EF_LEFT_MOUSE_BUTTON;
      case 2:
        return ui::EF_MIDDLE_MOUSE_BUTTON;
      case 3:
        return ui::EF_RIGHT_MOUSE_BUTTON;
      default:
        return 0;
    }
  }
  
  void DetectAltClick(XIDeviceEvent* xievent) {
    if ((xievent->evtype == XI_ButtonPress ||
        xievent->evtype == XI_ButtonRelease) &&
          (xievent->mods.effective & Mod1Mask) &&
          xievent->detail == 1) {
      xievent->mods.effective &= ~Mod1Mask;
      xievent->detail = 3;
      if (xievent->evtype == XI_ButtonRelease) {
        // On the release clear the left button from the existing state and the
        // mods, and set the right button.
        XISetMask(xievent->buttons.mask, 3);
        XIClearMask(xievent->buttons.mask, 1);
        xievent->mods.effective &= ~Button1Mask;
      }
    }
  }
  
  int GetButtonMaskForX2Event(XIDeviceEvent* xievent) {
    int buttonflags = 0;
    for (int i = 0; i < 8 * xievent->buttons.mask_len; i++) {
      if (XIMaskIsSet(xievent->buttons.mask, i)) {
        int button = (xievent->sourceid == xievent->deviceid) ?
                     ui::GetMappedButton(i) : i;
        buttonflags |= GetEventFlagsForButton(button);
      }
    }
    return buttonflags;
  }
  
  ui::EventType GetTouchEventType(const base::NativeEvent& native_event) {
    XIDeviceEvent* event =
        static_cast<XIDeviceEvent*>(native_event->xcookie.data);
  #if defined(USE_XI2_MT)
    switch(event->evtype) {
      case XI_TouchBegin:
        return ui::ET_TOUCH_PRESSED;
      case XI_TouchUpdate:
        return ui::ET_TOUCH_MOVED;
      case XI_TouchEnd:
        return ui::ET_TOUCH_RELEASED;
    }
  
    return ui::ET_UNKNOWN;
  #else
    ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
  
    // If this device doesn't support multi-touch, then just use the normal
    // pressed/release events to indicate touch start/end.  With multi-touch,
    // these events are sent only for the first (pressed) or last (released)
    // touch point, and so we must infer start/end from motion events.
    if (!factory->IsMultiTouchDevice(event->sourceid)) {
      switch (event->evtype) {
        case XI_ButtonPress:
          return ui::ET_TOUCH_PRESSED;
        case XI_ButtonRelease:
          return ui::ET_TOUCH_RELEASED;
        case XI_Motion:
          if (GetButtonMaskForX2Event(event))
            return ui::ET_TOUCH_MOVED;
          return ui::ET_UNKNOWN;
        default:
          NOTREACHED();
      }
    }
  
    DCHECK_EQ(event->evtype, XI_Motion);
  
    // Note: We will not generate a _STATIONARY event here. It will be created,
    // when necessary, by a RWHVV.
    // TODO(sad): When should _CANCELLED be generated?
  
    float slot;
    if (!factory->ExtractTouchParam(*native_event, ui::TouchFactory::TP_SLOT_ID,
                                    &slot))
      return ui::ET_UNKNOWN;
  
    if (!factory->IsSlotUsed(slot)) {
      // This is a new touch point.
      return ui::ET_TOUCH_PRESSED;
    }
  
    float tracking;
    if (!factory->ExtractTouchParam(*native_event,
                                    ui::TouchFactory::TP_TRACKING_ID, &tracking))
      return ui::ET_UNKNOWN;
  
    if (tracking == 0l) {
      // The touch point has been released.
      return ui::ET_TOUCH_RELEASED;
    }
  
    return ui::ET_TOUCH_MOVED;
  #endif  // defined(USE_XI2_MT)
  }
  
  float GetTouchParamFromXEvent(XEvent* xev,
                                ui::TouchFactory::TouchParam tp,
                                float default_value) {
    ui::TouchFactory::GetInstance()->ExtractTouchParam(*xev, tp, &default_value);
    return default_value;
  }
  
  #if !defined(TOOLKIT_USES_GTK)
  Atom GetNoopEventAtom() {
    return XInternAtom(
        base::MessagePumpX::GetDefaultXDisplay(),
        "noop", False);
  }
  #endif
  
  }  // namespace
  
  namespace ui {
  
  EventType EventTypeFromNative(const base::NativeEvent& native_event) {
    switch (native_event->type) {
      case KeyPress:
        return ET_KEY_PRESSED;
      case KeyRelease:
        return ET_KEY_RELEASED;
      case ButtonPress:
        if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton &&
            static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton)
          return ET_MOUSEWHEEL;
        return ET_MOUSE_PRESSED;
      case ButtonRelease:
        // Drop wheel events; we should've already scrolled on the press.
        if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton &&
            static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton)
          return ET_UNKNOWN;
        return ET_MOUSE_RELEASED;
      case MotionNotify:
        if (native_event->xmotion.state &
            (Button1Mask | Button2Mask | Button3Mask))
          return ET_MOUSE_DRAGGED;
        return ET_MOUSE_MOVED;
      case EnterNotify:
        return ET_MOUSE_ENTERED;
      case LeaveNotify:
        return ET_MOUSE_EXITED;
      case GenericEvent: {
        TouchFactory* factory = TouchFactory::GetInstance();
        if (!factory->ShouldProcessXI2Event(native_event))
          return ET_UNKNOWN;
  
        XIDeviceEvent* xievent =
            static_cast<XIDeviceEvent*>(native_event->xcookie.data);
  
        // Map Alt+Button1 to Button3
        DetectAltClick(xievent);
  
        if (factory->IsTouchDevice(xievent->sourceid))
          return GetTouchEventType(native_event);
  
        switch (xievent->evtype) {
          case XI_ButtonPress: {
            int button = EventButtonFromNative(native_event);
            if (button >= kMinWheelButton && button <= kMaxWheelButton)
              return ET_MOUSEWHEEL;
            return ET_MOUSE_PRESSED;
          }
          case XI_ButtonRelease: {
            int button = EventButtonFromNative(native_event);
            // Drop wheel events; we should've already scrolled on the press.
            if (button >= kMinWheelButton && button <= kMaxWheelButton)
              return ET_UNKNOWN;
            return ET_MOUSE_RELEASED;
          }
          case XI_Motion: {
            float vx, vy;
            bool is_cancel;
            if (GetFlingData(native_event, &vx, &vy, &is_cancel)) {
              return is_cancel ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START;
            } else if (GetScrollOffsets(native_event, NULL, NULL))
              return ET_SCROLL;
            else if (GetButtonMaskForX2Event(xievent)) {
              return ET_MOUSE_DRAGGED;
            } else
              return ET_MOUSE_MOVED;
          }
        }
      }
      default:
        break;
    }
    return ET_UNKNOWN;
  }
  
  int EventFlagsFromNative(const base::NativeEvent& native_event) {
    switch (native_event->type) {
      case KeyPress:
      case KeyRelease: {
        XModifierStateWatcher::GetInstance()->UpdateStateFromEvent(native_event);
        return GetEventFlagsFromXState(native_event->xkey.state);
      }
      case ButtonPress:
      case ButtonRelease: {
        int flags = GetEventFlagsFromXState(native_event->xbutton.state);
        const EventType type = EventTypeFromNative(native_event);
        if (type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED)
          flags |= GetEventFlagsForButton(native_event->xbutton.button);
        return flags;
      }
      case MotionNotify:
        return GetEventFlagsFromXState(native_event->xmotion.state);
      case GenericEvent: {
        XIDeviceEvent* xievent =
            static_cast<XIDeviceEvent*>(native_event->xcookie.data);
  
        // Map Alt+Button1 to Button3.
        DetectAltClick(xievent);
  
        const bool touch =
            TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid);
        switch (xievent->evtype) {
          case XI_ButtonPress:
          case XI_ButtonRelease: {
            int flags = GetButtonMaskForX2Event(xievent) |
                GetEventFlagsFromXState(xievent->mods.effective);
            if (touch) {
              flags |= GetEventFlagsFromXState(
                  XModifierStateWatcher::GetInstance()->state());
            }
  
            const EventType type = EventTypeFromNative(native_event);
            int button = EventButtonFromNative(native_event);
            if ((type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) && !touch)
              flags |= GetEventFlagsForButton(button);
            return flags;
          }
          case XI_Motion:
             return GetButtonMaskForX2Event(xievent) |
                    GetEventFlagsFromXState(xievent->mods.effective);
        }
      }
    }
    return 0;
  }
  
  base::TimeDelta EventTimeFromNative(const base::NativeEvent& native_event) {
    switch(native_event->type) {
      case KeyPress:
      case KeyRelease:
        return base::TimeDelta::FromMilliseconds(native_event->xkey.time);
      case ButtonPress:
      case ButtonRelease:
        return base::TimeDelta::FromMilliseconds(native_event->xbutton.time);
        break;
      case MotionNotify:
        return base::TimeDelta::FromMilliseconds(native_event->xmotion.time);
        break;
      case GenericEvent: {
        double start, end;
        if (GetGestureTimes(native_event, &start, &end)) {
          // If the driver supports gesture times, use them.
          return base::TimeDelta::FromMicroseconds(start * 1000000);
        } else {
          XIDeviceEvent* xide =
              static_cast<XIDeviceEvent*>(native_event->xcookie.data);
          return base::TimeDelta::FromMilliseconds(xide->time);
        }
        break;
      }
    }
    NOTREACHED();
    return base::TimeDelta();
  }
  
  gfx::Point EventLocationFromNative(const base::NativeEvent& native_event) {
    switch (native_event->type) {
      case ButtonPress:
      case ButtonRelease:
        return gfx::Point(native_event->xbutton.x, native_event->xbutton.y);
      case MotionNotify:
        return gfx::Point(native_event->xmotion.x, native_event->xmotion.y);
      case GenericEvent: {
        XIDeviceEvent* xievent =
            static_cast<XIDeviceEvent*>(native_event->xcookie.data);
  
  #if defined(USE_XI2_MT)
        // Touch event valuators aren't coordinates.
        // Return the |event_x|/|event_y| directly as event's position.
        if (xievent->evtype == XI_TouchBegin ||
            xievent->evtype == XI_TouchUpdate ||
            xievent->evtype == XI_TouchEnd)
          return gfx::Point(static_cast<int>(xievent->event_x),
                            static_cast<int>(xievent->event_y));
  #endif
        // Read the position from the valuators, because the location reported in
        // event_x/event_y seems to be different (and doesn't match for events
        // coming from slave device and master device) from the values in the
        // valuators. See more on crbug.com/103981. The position in the valuators
        // is in the global screen coordinates. But it is necessary to convert it
        // into the window's coordinates. If the valuator is not set, that means
        // the value hasn't changed, and so we can use the value from
        // event_x/event_y (which are in the window's coordinates).
        double* valuators = xievent->valuators.values;
  
        double x = xievent->event_x;
        if (XIMaskIsSet(xievent->valuators.mask, 0))
          x = *valuators++ - (xievent->root_x - xievent->event_x);
  
        double y = xievent->event_y;
        if (XIMaskIsSet(xievent->valuators.mask, 1))
          y = *valuators++ - (xievent->root_y - xievent->event_y);
  
        return gfx::Point(static_cast<int>(x), static_cast<int>(y));
      }
    }
    return gfx::Point();
  }
  
  int EventButtonFromNative(const base::NativeEvent& native_event) {
    CHECK_EQ(GenericEvent, native_event->type);
    XIDeviceEvent* xievent =
        static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    int button = xievent->detail;
  
    return (xievent->sourceid == xievent->deviceid) ?
           ui::GetMappedButton(button) : button;
  }
  
  KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) {
    return KeyboardCodeFromXKeyEvent(native_event);
  }
  
  bool IsMouseEvent(const base::NativeEvent& native_event) {
    if (native_event->type == ButtonPress ||
        native_event->type == ButtonRelease ||
        native_event->type == MotionNotify)
      return true;
    if (native_event->type == GenericEvent) {
      XIDeviceEvent* xievent =
          static_cast<XIDeviceEvent*>(native_event->xcookie.data);
      return xievent->evtype == XI_ButtonPress ||
             xievent->evtype == XI_ButtonRelease ||
             xievent->evtype == XI_Motion;
    }
    return false;
  }
  
  int GetMouseWheelOffset(const base::NativeEvent& native_event) {
    int button = native_event->type == GenericEvent
      ? EventButtonFromNative(native_event) : native_event->xbutton.button;
  
    switch (button) {
      case 4:
  #if defined(OS_CHROMEOS)
        return kTouchpadScrollAmount;
      case 8:
  #endif
        return kWheelScrollAmount;
  
      case 5:
  #if defined(OS_CHROMEOS)
        return -kTouchpadScrollAmount;
      case 9:
  #endif
        return -kWheelScrollAmount;
  
      default:
        // TODO(derat): Do something for horizontal scrolls (buttons 6 and 7)?
        return 0;
    }
  }
  
  int GetTouchId(const base::NativeEvent& xev) {
    float slot = 0;
    ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
    XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data);
    if (!factory->IsMultiTouchDevice(xievent->sourceid)) {
      // TODO(sad): Come up with a way to generate touch-ids for multi-touch
      // events when touch-events are generated from a single-touch device.
      return slot;
    }
  
  #if defined(USE_XI2_MT)
    float tracking_id;
    if (!factory->ExtractTouchParam(
           *xev, ui::TouchFactory::TP_TRACKING_ID, &tracking_id)) {
      LOG(ERROR) << "Could not get the slot ID for the event. Using 0.";
    } else {
      slot = factory->GetSlotForTrackingID(tracking_id);
      ui::EventType type = ui::EventTypeFromNative(xev);
      if (type == ui::ET_TOUCH_CANCELLED ||
          type == ui::ET_TOUCH_RELEASED) {
        factory->ReleaseSlotForTrackingID(tracking_id);
      }
    }
  #else
    if (!factory->ExtractTouchParam(
           *xev, ui::TouchFactory::TP_SLOT_ID, &slot))
      LOG(ERROR) << "Could not get the slot ID for the event. Using 0.";
  #endif
    return slot;
  }
  
  float GetTouchRadiusX(const base::NativeEvent& native_event) {
    return GetTouchParamFromXEvent(native_event,
        ui::TouchFactory::TP_TOUCH_MAJOR, 2.0) / 2.0;
  }
  
  float GetTouchRadiusY(const base::NativeEvent& native_event) {
    return GetTouchParamFromXEvent(native_event,
        ui::TouchFactory::TP_TOUCH_MINOR, 2.0) / 2.0;
  }
  
  float GetTouchAngle(const base::NativeEvent& native_event) {
    return GetTouchParamFromXEvent(native_event,
        ui::TouchFactory::TP_ORIENTATION, 0.0) / 2.0;
  }
  
  float GetTouchForce(const base::NativeEvent& native_event) {
    float force = 0.0;
    force = GetTouchParamFromXEvent(native_event, ui::TouchFactory::TP_PRESSURE,
                                    0.0);
    unsigned int deviceid =
        static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid;
    // Force is normalized to fall into [0, 1]
    if (!ui::TouchFactory::GetInstance()->NormalizeTouchParam(
        deviceid, ui::TouchFactory::TP_PRESSURE, &force))
      force = 0.0;
    return force;
  }
  
  bool GetScrollOffsets(const base::NativeEvent& native_event,
                        float* x_offset,
                        float* y_offset) {
    return CMTEventData::GetInstance()->GetScrollOffsets(
        *native_event, x_offset, y_offset);
  }
  
  bool GetFlingData(const base::NativeEvent& native_event,
                    float* vx,
                    float* vy,
                    bool* is_cancel) {
    return CMTEventData::GetInstance()->GetFlingData(
        *native_event, vx, vy, is_cancel);
  }
  
  bool GetGestureTimes(const base::NativeEvent& native_event,
                       double* start_time,
                       double* end_time) {
    return CMTEventData::GetInstance()->GetGestureTimes(
        *native_event, start_time, end_time);
  }
  
  void SetNaturalScroll(bool enabled) {
    CMTEventData::GetInstance()->set_natural_scroll_enabled(enabled);
  }
  
  void UpdateDeviceList() {
    Display* display = GetXDisplay();
    CMTEventData::GetInstance()->UpdateDeviceList(display);
    TouchFactory::GetInstance()->UpdateDeviceList(display);
  }
  
  bool IsNoopEvent(const base::NativeEvent& event) {
  #if defined(TOOLKIT_USES_GTK)
    NOTREACHED();
    return false;
  #else
    return (event->type == ClientMessage &&
        event->xclient.message_type == GetNoopEventAtom());
  #endif
  }
  
  base::NativeEvent CreateNoopEvent() {
    static XEvent* noop = NULL;
    if (!noop) {
      noop = new XEvent();
      memset(noop, 0, sizeof(XEvent));
      noop->xclient.type = ClientMessage;
      noop->xclient.window = None;
      noop->xclient.format = 8;
      DCHECK(!noop->xclient.display);
    }
    // TODO(oshima): Remove ifdef once gtk is removed from views.
  #if defined(TOOLKIT_USES_GTK)
    NOTREACHED();
  #else
    // Make sure we use atom from current xdisplay, which may
    // change during the test.
    noop->xclient.message_type = GetNoopEventAtom();
  #endif
    return noop;
  }
  
  }  // namespace ui
  