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/events/event_constants.h"
  
  #include <cmath>
  #include <string.h>
  #include <X11/extensions/XInput.h>
  #include <X11/extensions/XInput2.h>
  #include <X11/Xlib.h>
  
  #include "base/logging.h"
  #include "base/memory/singleton.h"
  #include "base/message_loop/message_pump_x11.h"
  #include "ui/events/event_utils.h"
  #include "ui/events/keycodes/keyboard_code_conversion_x.h"
  #include "ui/events/x/device_data_manager.h"
  #include "ui/events/x/device_list_cache_x.h"
  #include "ui/events/x/touch_factory_x11.h"
  #include "ui/gfx/display.h"
  #include "ui/gfx/point.h"
  #include "ui/gfx/rect.h"
  #include "ui/gfx/screen.h"
  #include "ui/gfx/x/x11_atom_cache.h"
  #include "ui/gfx/x/x11_types.h"
  
  namespace {
  
  // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+.
  const int kWheelScrollAmount = 53;
  
  const int kMinWheelButton = 4;
  const int kMaxWheelButton = 7;
  
  // 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 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);
  };
  
  #if defined(USE_XI2_MT)
  // Detects if a touch event is a driver-generated 'special event'.
  // A 'special event' is a touch event with maximum radius and pressure at
  // location (0, 0).
  // This needs to be done in a cleaner way: http://crbug.com/169256
  bool TouchEventIsGeneratedHack(const base::NativeEvent& native_event) {
    XIDeviceEvent* event =
        static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    CHECK(event->evtype == XI_TouchBegin ||
          event->evtype == XI_TouchUpdate ||
          event->evtype == XI_TouchEnd);
  
    // Force is normalized to [0, 1].
    if (ui::GetTouchForce(native_event) < 1.0f)
      return false;
  
    if (ui::EventLocationFromNative(native_event) != gfx::Point())
      return false;
  
    // Radius is in pixels, and the valuator is the diameter in pixels.
    double radius = ui::GetTouchRadiusX(native_event), min, max;
    unsigned int deviceid =
        static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid;
    if (!ui::DeviceDataManager::GetInstance()->GetDataRange(
        deviceid, ui::DeviceDataManager::DT_TOUCH_MAJOR, &min, &max)) {
      return false;
    }
  
    return radius * 2 == max;
  }
  #endif
  
  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 & Mod5Mask)
      flags |= ui::EF_ALTGR_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;
    }
  }
  
  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::DeviceDataManager::GetInstance()->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 TouchEventIsGeneratedHack(native_event) ? ui::ET_UNKNOWN :
                                                         ui::ET_TOUCH_PRESSED;
      case XI_TouchUpdate:
        return TouchEventIsGeneratedHack(native_event) ? ui::ET_UNKNOWN :
                                                         ui::ET_TOUCH_MOVED;
      case XI_TouchEnd:
        return TouchEventIsGeneratedHack(native_event) ? ui::ET_TOUCH_CANCELLED :
                                                         ui::ET_TOUCH_RELEASED;
    }
  #endif  // defined(USE_XI2_MT)
  
    DCHECK(ui::TouchFactory::GetInstance()->IsTouchDevice(event->sourceid));
    switch (event->evtype) {
      case XI_ButtonPress:
        return ui::ET_TOUCH_PRESSED;
      case XI_ButtonRelease:
        return ui::ET_TOUCH_RELEASED;
      case XI_Motion:
        // Should not convert any emulated Motion event from touch device to
        // touch event.
        if (!(event->flags & XIPointerEmulated) &&
            GetButtonMaskForX2Event(event))
          return ui::ET_TOUCH_MOVED;
        return ui::ET_UNKNOWN;
      default:
        NOTREACHED();
    }
    return ui::ET_UNKNOWN;
  }
  
  double GetTouchParamFromXEvent(XEvent* xev,
                                ui::DeviceDataManager::DataType val,
                                double default_value) {
    ui::DeviceDataManager::GetInstance()->GetEventData(
        *xev, val, &default_value);
    return default_value;
  }
  
  }  // namespace
  
  namespace ui {
  
  void UpdateDeviceList() {
    XDisplay* display = gfx::GetXDisplay();
    DeviceListCacheX::GetInstance()->UpdateDeviceList(display);
    TouchFactory::GetInstance()->UpdateDeviceList(display);
    DeviceDataManager::GetInstance()->UpdateDeviceList(display);
  }
  
  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:
        // The standard on Windows is to send a MouseMove event when the mouse
        // first enters a window instead of sending a special mouse enter event.
        // To be consistent we follow the same style.
        return ET_MOUSE_MOVED;
      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);
  
        // This check works only for master and floating slave devices. That is
        // why it is necessary to check for the XI_Touch* events in the following
        // switch statement to account for attached-slave touchscreens.
        if (factory->IsTouchDevice(xievent->sourceid))
          return GetTouchEventType(native_event);
  
        switch (xievent->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;
          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: {
            bool is_cancel;
            if (GetFlingData(native_event, NULL, NULL, NULL, NULL, &is_cancel)) {
              return is_cancel ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START;
            } else if (DeviceDataManager::GetInstance()->IsScrollEvent(
                native_event)) {
              return IsTouchpadEvent(native_event) ? ET_SCROLL : ET_MOUSEWHEEL;
            } else if (DeviceDataManager::GetInstance()->IsCMTMetricsEvent(
                native_event)) {
              return ET_UMA_DATA;
            } 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 EnterNotify:
      case LeaveNotify:
        return GetEventFlagsFromXState(native_event->xcrossing.state);
      case MotionNotify:
        return GetEventFlagsFromXState(native_event->xmotion.state);
      case GenericEvent: {
        XIDeviceEvent* xievent =
            static_cast<XIDeviceEvent*>(native_event->xcookie.data);
  
        switch (xievent->evtype) {
  #if defined(USE_XI2_MT)
          case XI_TouchBegin:
          case XI_TouchUpdate:
          case XI_TouchEnd:
            return GetButtonMaskForX2Event(xievent) |
                   GetEventFlagsFromXState(xievent->mods.effective) |
                   GetEventFlagsFromXState(
                       XModifierStateWatcher::GetInstance()->state());
            break;
  #endif
          case XI_ButtonPress:
          case XI_ButtonRelease: {
            const bool touch =
                TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid);
            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 EnterNotify:
      case LeaveNotify:
        return base::TimeDelta::FromMilliseconds(native_event->xcrossing.time);
        break;
      case GenericEvent: {
        double start, end;
        double touch_timestamp;
        if (GetGestureTimes(native_event, &start, &end)) {
          // If the driver supports gesture times, use them.
          return base::TimeDelta::FromMicroseconds(end * 1000000);
        } else if (DeviceDataManager::GetInstance()->GetEventData(*native_event,
                   DeviceDataManager::DT_TOUCH_RAW_TIMESTAMP, &touch_timestamp)) {
          return base::TimeDelta::FromMicroseconds(touch_timestamp * 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 EnterNotify:
      case LeaveNotify:
        return gfx::Point(native_event->xcrossing.x, native_event->xcrossing.y);
      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);
        return gfx::Point(static_cast<int>(xievent->event_x),
                          static_cast<int>(xievent->event_y));
      }
    }
    return gfx::Point();
  }
  
  gfx::Point EventSystemLocationFromNative(
      const base::NativeEvent& native_event) {
    switch (native_event->type) {
      case EnterNotify:
      case LeaveNotify: {
        return gfx::Point(native_event->xcrossing.x_root,
                          native_event->xcrossing.y_root);
      }
      case ButtonPress:
      case ButtonRelease: {
        return gfx::Point(native_event->xbutton.x_root,
                          native_event->xbutton.y_root);
      }
      case MotionNotify: {
        return gfx::Point(native_event->xmotion.x_root,
                          native_event->xmotion.y_root);
      }
      case GenericEvent: {
        XIDeviceEvent* xievent =
            static_cast<XIDeviceEvent*>(native_event->xcookie.data);
        return gfx::Point(xievent->root_x, xievent->root_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) ?
           DeviceDataManager::GetInstance()->GetMappedButton(button) : button;
  }
  
  KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) {
    return KeyboardCodeFromXKeyEvent(native_event);
  }
  
  const char* CodeFromNative(const base::NativeEvent& native_event) {
    return CodeFromXEvent(native_event);
  }
  
  int GetChangedMouseButtonFlagsFromNative(
      const base::NativeEvent& native_event) {
    switch (native_event->type) {
      case ButtonPress:
      case ButtonRelease:
        return GetEventFlagsFromXState(native_event->xbutton.state);
      case GenericEvent: {
        XIDeviceEvent* xievent =
            static_cast<XIDeviceEvent*>(native_event->xcookie.data);
        switch (xievent->evtype) {
          case XI_ButtonPress:
          case XI_ButtonRelease:
            return GetEventFlagsForButton(EventButtonFromNative(native_event));
          default:
            break;
        }
      }
      default:
        break;
    }
    return 0;
  }
  
  gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) {
    float x_offset, y_offset;
    if (GetScrollOffsets(
        native_event, &x_offset, &y_offset, NULL, NULL, NULL)) {
      return gfx::Vector2d(static_cast<int>(x_offset),
                           static_cast<int>(y_offset));
    }
  
    int button = native_event->type == GenericEvent ?
        EventButtonFromNative(native_event) : native_event->xbutton.button;
  
    switch (button) {
      case 4:
        return gfx::Vector2d(0, kWheelScrollAmount);
      case 5:
        return gfx::Vector2d(0, -kWheelScrollAmount);
      default:
        // TODO(derat): Do something for horizontal scrolls (buttons 6 and 7)?
        return gfx::Vector2d();
    }
  }
  
  void ClearTouchIdIfReleased(const base::NativeEvent& xev) {
    ui::EventType type = ui::EventTypeFromNative(xev);
    if (type == ui::ET_TOUCH_CANCELLED ||
        type == ui::ET_TOUCH_RELEASED) {
      ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
      ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
      double tracking_id;
      if (manager->GetEventData(
          *xev, ui::DeviceDataManager::DT_TOUCH_TRACKING_ID, &tracking_id)) {
        factory->ReleaseSlotForTrackingID(tracking_id);
      }
    }
  }
  
  int GetTouchId(const base::NativeEvent& xev) {
    double slot = 0;
    ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance();
    double tracking_id;
    if (!manager->GetEventData(
        *xev, ui::DeviceDataManager::DT_TOUCH_TRACKING_ID, &tracking_id)) {
      LOG(ERROR) << "Could not get the tracking ID for the event. Using 0.";
    } else {
      ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
      slot = factory->GetSlotForTrackingID(tracking_id);
    }
    return slot;
  }
  
  float GetTouchRadiusX(const base::NativeEvent& native_event) {
    return GetTouchParamFromXEvent(native_event,
        ui::DeviceDataManager::DT_TOUCH_MAJOR, 0.0) / 2.0;
  }
  
  float GetTouchRadiusY(const base::NativeEvent& native_event) {
    return GetTouchParamFromXEvent(native_event,
        ui::DeviceDataManager::DT_TOUCH_MINOR, 0.0) / 2.0;
  }
  
  float GetTouchAngle(const base::NativeEvent& native_event) {
    return GetTouchParamFromXEvent(native_event,
        ui::DeviceDataManager::DT_TOUCH_ORIENTATION, 0.0) / 2.0;
  }
  
  float GetTouchForce(const base::NativeEvent& native_event) {
    double force = 0.0;
    force = GetTouchParamFromXEvent(native_event,
        ui::DeviceDataManager::DT_TOUCH_PRESSURE, 0.0);
    unsigned int deviceid =
        static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid;
    // Force is normalized to fall into [0, 1]
    if (!ui::DeviceDataManager::GetInstance()->NormalizeData(
        deviceid, ui::DeviceDataManager::DT_TOUCH_PRESSURE, &force))
      force = 0.0;
    return force;
  }
  
  bool GetScrollOffsets(const base::NativeEvent& native_event,
                        float* x_offset,
                        float* y_offset,
                        float* x_offset_ordinal,
                        float* y_offset_ordinal,
                        int* finger_count) {
    if (!DeviceDataManager::GetInstance()->IsScrollEvent(native_event))
      return false;
  
    // Temp values to prevent passing NULLs to DeviceDataManager.
    float x_offset_, y_offset_;
    float x_offset_ordinal_, y_offset_ordinal_;
    int finger_count_;
    if (!x_offset)
      x_offset = &x_offset_;
    if (!y_offset)
      y_offset = &y_offset_;
    if (!x_offset_ordinal)
      x_offset_ordinal = &x_offset_ordinal_;
    if (!y_offset_ordinal)
      y_offset_ordinal = &y_offset_ordinal_;
    if (!finger_count)
      finger_count = &finger_count_;
  
    DeviceDataManager::GetInstance()->GetScrollOffsets(
        native_event,
        x_offset, y_offset,
        x_offset_ordinal, y_offset_ordinal,
        finger_count);
    return true;
  }
  
  bool GetFlingData(const base::NativeEvent& native_event,
                    float* vx,
                    float* vy,
                    float* vx_ordinal,
                    float* vy_ordinal,
                    bool* is_cancel) {
    if (!DeviceDataManager::GetInstance()->IsFlingEvent(native_event))
      return false;
  
    float vx_, vy_;
    float vx_ordinal_, vy_ordinal_;
    bool is_cancel_;
    if (!vx)
      vx = &vx_;
    if (!vy)
      vy = &vy_;
    if (!vx_ordinal)
      vx_ordinal = &vx_ordinal_;
    if (!vy_ordinal)
      vy_ordinal = &vy_ordinal_;
    if (!is_cancel)
      is_cancel = &is_cancel_;
  
    DeviceDataManager::GetInstance()->GetFlingData(
        native_event, vx, vy, vx_ordinal, vy_ordinal, is_cancel);
    return true;
  }
  
  bool GetGestureTimes(const base::NativeEvent& native_event,
                       double* start_time,
                       double* end_time) {
    if (!DeviceDataManager::GetInstance()->HasGestureTimes(native_event))
      return false;
  
    double start_time_, end_time_;
    if (!start_time)
      start_time = &start_time_;
    if (!end_time)
      end_time = &end_time_;
  
    DeviceDataManager::GetInstance()->GetGestureTimes(
        native_event, start_time, end_time);
    return true;
  }
  
  void SetNaturalScroll(bool enabled) {
    DeviceDataManager::GetInstance()->set_natural_scroll_enabled(enabled);
  }
  
  bool IsNaturalScrollEnabled() {
    return DeviceDataManager::GetInstance()->natural_scroll_enabled();
  }
  
  bool IsTouchpadEvent(const base::NativeEvent& event) {
    return DeviceDataManager::GetInstance()->IsTouchpadXInputEvent(event);
  }
  
  }  // namespace ui
  