| // Copyright 2013 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/ozone/evdev/event_device_info.h" | 
 |  | 
 | #include <linux/input.h> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 |  | 
 | #if !defined(EVIOCGMTSLOTS) | 
 | #define EVIOCGMTSLOTS(len) _IOC(_IOC_READ, 'E', 0x0a, len) | 
 | #endif | 
 |  | 
 | namespace ui { | 
 |  | 
 | namespace { | 
 |  | 
 | // USB vendor and product strings are pragmatically limited to 126 | 
 | // characters each, so device names more than twice that should be | 
 | // unusual. | 
 | const size_t kMaximumDeviceNameLength = 256; | 
 |  | 
 | bool GetEventBits(int fd, unsigned int type, void* buf, unsigned int size) { | 
 |   if (ioctl(fd, EVIOCGBIT(type, size), buf) < 0) { | 
 |     PLOG(ERROR) << "EVIOCGBIT(" << type << ", " << size << ") on fd " << fd; | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool GetPropBits(int fd, void* buf, unsigned int size) { | 
 |   if (ioctl(fd, EVIOCGPROP(size), buf) < 0) { | 
 |     PLOG(ERROR) << "EVIOCGPROP(" << size << ") on fd " << fd; | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool GetAbsInfo(int fd, int code, struct input_absinfo* absinfo) { | 
 |   if (ioctl(fd, EVIOCGABS(code), absinfo)) { | 
 |     PLOG(ERROR) << "EVIOCGABS(" << code << ") on fd " << fd; | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool GetDeviceName(int fd, std::string* name) { | 
 |   char device_name[kMaximumDeviceNameLength]; | 
 |   if (ioctl(fd, EVIOCGNAME(kMaximumDeviceNameLength - 1), &device_name) < 0) { | 
 |     PLOG(INFO) << "Can't read device name on fd " << fd; | 
 |     return false; | 
 |   } | 
 |   *name = device_name; | 
 |   return true; | 
 | } | 
 |  | 
 | bool GetDeviceIdentifiers(int fd, uint16_t* vendor, uint16_t* product) { | 
 |   struct input_id evdev_id; | 
 |   if (ioctl(fd, EVIOCGID, &evdev_id) < 0) { | 
 |     PLOG(INFO) << "Can't read device name on fd " << fd; | 
 |     return false; | 
 |   } | 
 |   *vendor = evdev_id.vendor; | 
 |   *product = evdev_id.product; | 
 |   return true; | 
 | } | 
 |  | 
 | // |request| needs to be the equivalent to: | 
 | // struct input_mt_request_layout { | 
 | //   uint32_t code; | 
 | //   int32_t values[num_slots]; | 
 | // }; | 
 | // | 
 | // |size| is num_slots + 1 (for code). | 
 | bool GetSlotValues(int fd, int32_t* request, unsigned int size) { | 
 |   size_t data_size = size * sizeof(*request); | 
 |  | 
 |   if (ioctl(fd, EVIOCGMTSLOTS(data_size), request) < 0) { | 
 |     PLOG(ERROR) << "EVIOCGMTSLOTS(" << request[0] << ") on fd " << fd; | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void AssignBitset(const unsigned long* src, | 
 |                   size_t src_len, | 
 |                   unsigned long* dst, | 
 |                   size_t dst_len) { | 
 |   memcpy(dst, src, std::min(src_len, dst_len) * sizeof(unsigned long)); | 
 |   if (src_len < dst_len) | 
 |     memset(&dst[src_len], 0, (dst_len - src_len) * sizeof(unsigned long)); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | EventDeviceInfo::EventDeviceInfo() { | 
 |   memset(ev_bits_, 0, sizeof(ev_bits_)); | 
 |   memset(key_bits_, 0, sizeof(key_bits_)); | 
 |   memset(rel_bits_, 0, sizeof(rel_bits_)); | 
 |   memset(abs_bits_, 0, sizeof(abs_bits_)); | 
 |   memset(msc_bits_, 0, sizeof(msc_bits_)); | 
 |   memset(sw_bits_, 0, sizeof(sw_bits_)); | 
 |   memset(led_bits_, 0, sizeof(led_bits_)); | 
 |   memset(prop_bits_, 0, sizeof(prop_bits_)); | 
 |   memset(abs_info_, 0, sizeof(abs_info_)); | 
 | } | 
 |  | 
 | EventDeviceInfo::~EventDeviceInfo() {} | 
 |  | 
 | bool EventDeviceInfo::Initialize(int fd) { | 
 |   if (!GetEventBits(fd, 0, ev_bits_, sizeof(ev_bits_))) | 
 |     return false; | 
 |  | 
 |   if (!GetEventBits(fd, EV_KEY, key_bits_, sizeof(key_bits_))) | 
 |     return false; | 
 |  | 
 |   if (!GetEventBits(fd, EV_REL, rel_bits_, sizeof(rel_bits_))) | 
 |     return false; | 
 |  | 
 |   if (!GetEventBits(fd, EV_ABS, abs_bits_, sizeof(abs_bits_))) | 
 |     return false; | 
 |  | 
 |   if (!GetEventBits(fd, EV_MSC, msc_bits_, sizeof(msc_bits_))) | 
 |     return false; | 
 |  | 
 |   if (!GetEventBits(fd, EV_SW, sw_bits_, sizeof(sw_bits_))) | 
 |     return false; | 
 |  | 
 |   if (!GetEventBits(fd, EV_LED, led_bits_, sizeof(led_bits_))) | 
 |     return false; | 
 |  | 
 |   if (!GetPropBits(fd, prop_bits_, sizeof(prop_bits_))) | 
 |     return false; | 
 |  | 
 |   for (unsigned int i = 0; i < ABS_CNT; ++i) | 
 |     if (HasAbsEvent(i)) | 
 |       if (!GetAbsInfo(fd, i, &abs_info_[i])) | 
 |         return false; | 
 |  | 
 |   int max_num_slots = GetAbsMtSlotCount(); | 
 |  | 
 |   // |request| is MT code + slots. | 
 |   int32_t request[max_num_slots + 1]; | 
 |   int32_t* request_code = &request[0]; | 
 |   int32_t* request_slots = &request[1]; | 
 |   for (unsigned int i = EVDEV_ABS_MT_FIRST; i <= EVDEV_ABS_MT_LAST; ++i) { | 
 |     if (!HasAbsEvent(i)) | 
 |       continue; | 
 |  | 
 |     memset(request, 0, sizeof(request)); | 
 |     *request_code = i; | 
 |     if (!GetSlotValues(fd, request, max_num_slots + 1)) | 
 |       LOG(WARNING) << "Failed to get multitouch values for code " << i; | 
 |  | 
 |     std::vector<int32_t>* slots = &slot_values_[i - EVDEV_ABS_MT_FIRST]; | 
 |     slots->assign(request_slots, request_slots + max_num_slots); | 
 |   } | 
 |  | 
 |   if (!GetDeviceName(fd, &name_)) | 
 |     return false; | 
 |  | 
 |   if (!GetDeviceIdentifiers(fd, &vendor_id_, &product_id_)) | 
 |     return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetEventTypes(const unsigned long* ev_bits, size_t len) { | 
 |   AssignBitset(ev_bits, len, ev_bits_, arraysize(ev_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetKeyEvents(const unsigned long* key_bits, size_t len) { | 
 |   AssignBitset(key_bits, len, key_bits_, arraysize(key_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetRelEvents(const unsigned long* rel_bits, size_t len) { | 
 |   AssignBitset(rel_bits, len, rel_bits_, arraysize(rel_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetAbsEvents(const unsigned long* abs_bits, size_t len) { | 
 |   AssignBitset(abs_bits, len, abs_bits_, arraysize(abs_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetMscEvents(const unsigned long* msc_bits, size_t len) { | 
 |   AssignBitset(msc_bits, len, msc_bits_, arraysize(msc_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetSwEvents(const unsigned long* sw_bits, size_t len) { | 
 |   AssignBitset(sw_bits, len, sw_bits_, arraysize(sw_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetLedEvents(const unsigned long* led_bits, size_t len) { | 
 |   AssignBitset(led_bits, len, led_bits_, arraysize(led_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetProps(const unsigned long* prop_bits, size_t len) { | 
 |   AssignBitset(prop_bits, len, prop_bits_, arraysize(prop_bits_)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetAbsInfo(unsigned int code, | 
 |                                  const input_absinfo& abs_info) { | 
 |   if (code > ABS_MAX) | 
 |     return; | 
 |  | 
 |   memcpy(&abs_info_[code], &abs_info, sizeof(abs_info)); | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetAbsMtSlots(unsigned int code, | 
 |                                     const std::vector<int32_t>& values) { | 
 |   DCHECK_EQ(GetAbsMtSlotCount(), values.size()); | 
 |   int index = code - EVDEV_ABS_MT_FIRST; | 
 |   if (index < 0 || index >= EVDEV_ABS_MT_COUNT) | 
 |     return; | 
 |   slot_values_[index] = values; | 
 | } | 
 |  | 
 | void EventDeviceInfo::SetAbsMtSlot(unsigned int code, | 
 |                                    unsigned int slot, | 
 |                                    uint32_t value) { | 
 |   int index = code - EVDEV_ABS_MT_FIRST; | 
 |   if (index < 0 || index >= EVDEV_ABS_MT_COUNT) | 
 |     return; | 
 |   slot_values_[index][slot] = value; | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasEventType(unsigned int type) const { | 
 |   if (type > EV_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(ev_bits_, type); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasKeyEvent(unsigned int code) const { | 
 |   if (code > KEY_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(key_bits_, code); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasRelEvent(unsigned int code) const { | 
 |   if (code > REL_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(rel_bits_, code); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasAbsEvent(unsigned int code) const { | 
 |   if (code > ABS_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(abs_bits_, code); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasMscEvent(unsigned int code) const { | 
 |   if (code > MSC_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(msc_bits_, code); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasSwEvent(unsigned int code) const { | 
 |   if (code > SW_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(sw_bits_, code); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasLedEvent(unsigned int code) const { | 
 |   if (code > LED_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(led_bits_, code); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasProp(unsigned int code) const { | 
 |   if (code > INPUT_PROP_MAX) | 
 |     return false; | 
 |   return EvdevBitIsSet(prop_bits_, code); | 
 | } | 
 |  | 
 | int32_t EventDeviceInfo::GetAbsMinimum(unsigned int code) const { | 
 |   return abs_info_[code].minimum; | 
 | } | 
 |  | 
 | int32_t EventDeviceInfo::GetAbsMaximum(unsigned int code) const { | 
 |   return abs_info_[code].maximum; | 
 | } | 
 |  | 
 | int32_t EventDeviceInfo::GetAbsValue(unsigned int code) const { | 
 |   return abs_info_[code].value; | 
 | } | 
 |  | 
 | uint32_t EventDeviceInfo::GetAbsMtSlotCount() const { | 
 |   if (!HasAbsEvent(ABS_MT_SLOT)) | 
 |     return 0; | 
 |   return GetAbsMaximum(ABS_MT_SLOT) + 1; | 
 | } | 
 |  | 
 | int32_t EventDeviceInfo::GetAbsMtSlotValue(unsigned int code, | 
 |                                            unsigned int slot) const { | 
 |   unsigned int index = code - EVDEV_ABS_MT_FIRST; | 
 |   DCHECK(index < EVDEV_ABS_MT_COUNT); | 
 |   return slot_values_[index][slot]; | 
 | } | 
 |  | 
 | int32_t EventDeviceInfo::GetAbsMtSlotValueWithDefault( | 
 |     unsigned int code, | 
 |     unsigned int slot, | 
 |     int32_t default_value) const { | 
 |   if (!HasAbsEvent(code)) | 
 |     return default_value; | 
 |   return GetAbsMtSlotValue(code, slot); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasAbsXY() const { | 
 |   return HasAbsEvent(ABS_X) && HasAbsEvent(ABS_Y); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasMTAbsXY() const { | 
 |   return HasAbsEvent(ABS_MT_POSITION_X) && HasAbsEvent(ABS_MT_POSITION_Y); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasRelXY() const { | 
 |   return HasRelEvent(REL_X) && HasRelEvent(REL_Y); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasMultitouch() const { | 
 |   return HasAbsEvent(ABS_MT_SLOT); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasDirect() const { | 
 |   bool has_direct = HasProp(INPUT_PROP_DIRECT); | 
 |   bool has_pointer = HasProp(INPUT_PROP_POINTER); | 
 |   if (has_direct || has_pointer) | 
 |     return has_direct; | 
 |  | 
 |   switch (ProbeLegacyAbsoluteDevice()) { | 
 |     case LegacyAbsoluteDeviceType::LADT_TOUCHSCREEN: | 
 |       return true; | 
 |  | 
 |     case LegacyAbsoluteDeviceType::LADT_TABLET: | 
 |     case LegacyAbsoluteDeviceType::LADT_TOUCHPAD: | 
 |     case LegacyAbsoluteDeviceType::LADT_NONE: | 
 |       return false; | 
 |   } | 
 |  | 
 |   NOTREACHED(); | 
 |   return false; | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasPointer() const { | 
 |   bool has_direct = HasProp(INPUT_PROP_DIRECT); | 
 |   bool has_pointer = HasProp(INPUT_PROP_POINTER); | 
 |   if (has_direct || has_pointer) | 
 |     return has_pointer; | 
 |  | 
 |   switch (ProbeLegacyAbsoluteDevice()) { | 
 |     case LegacyAbsoluteDeviceType::LADT_TOUCHPAD: | 
 |     case LegacyAbsoluteDeviceType::LADT_TABLET: | 
 |       return true; | 
 |  | 
 |     case LegacyAbsoluteDeviceType::LADT_TOUCHSCREEN: | 
 |     case LegacyAbsoluteDeviceType::LADT_NONE: | 
 |       return false; | 
 |   } | 
 |  | 
 |   NOTREACHED(); | 
 |   return false; | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasStylus() const { | 
 |   return HasKeyEvent(BTN_TOOL_PEN) || HasKeyEvent(BTN_STYLUS) || | 
 |          HasKeyEvent(BTN_STYLUS2); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasKeyboard() const { | 
 |   if (!HasEventType(EV_KEY)) | 
 |     return false; | 
 |  | 
 |   // Check first 31 keys: If we have all of them, consider it a full | 
 |   // keyboard. This is exactly what udev does for ID_INPUT_KEYBOARD. | 
 |   for (int key = KEY_ESC; key <= KEY_D; ++key) | 
 |     if (!HasKeyEvent(key)) | 
 |       return false; | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasMouse() const { | 
 |   return HasRelXY(); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasTouchpad() const { | 
 |   return HasAbsXY() && HasPointer() && !HasStylus(); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasTablet() const { | 
 |   return HasAbsXY() && HasPointer() && HasStylus(); | 
 | } | 
 |  | 
 | bool EventDeviceInfo::HasTouchscreen() const { | 
 |   return HasAbsXY() && HasDirect(); | 
 | } | 
 |  | 
 | EventDeviceInfo::LegacyAbsoluteDeviceType | 
 | EventDeviceInfo::ProbeLegacyAbsoluteDevice() const { | 
 |   if (!HasAbsXY()) | 
 |     return LegacyAbsoluteDeviceType::LADT_NONE; | 
 |  | 
 |   if (HasStylus()) | 
 |     return LegacyAbsoluteDeviceType::LADT_TABLET; | 
 |  | 
 |   if (HasKeyEvent(BTN_TOOL_FINGER) && HasKeyEvent(BTN_TOUCH)) | 
 |     return LegacyAbsoluteDeviceType::LADT_TOUCHPAD; | 
 |  | 
 |   if (HasKeyEvent(BTN_TOUCH) || HasKeyEvent(BTN_LEFT)) | 
 |     return LegacyAbsoluteDeviceType::LADT_TOUCHSCREEN; | 
 |  | 
 |   return LegacyAbsoluteDeviceType::LADT_NONE; | 
 | } | 
 |  | 
 | }  // namespace ui |