// 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/files/file_path.h" #include "base/logging.h" #include "base/macros.h" #include "base/threading/thread_restrictions.h" #include "ui/events/devices/device_util_linux.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, const base::FilePath& path, unsigned int type, void* buf, unsigned int size) { if (ioctl(fd, EVIOCGBIT(type, size), buf) < 0) { PLOG(ERROR) << "Failed EVIOCGBIT (path=" << path.value() << " type=" << type << " size=" << size << ")"; return false; } return true; } bool GetPropBits(int fd, const base::FilePath& path, void* buf, unsigned int size) { if (ioctl(fd, EVIOCGPROP(size), buf) < 0) { PLOG(ERROR) << "Failed EVIOCGPROP (path=" << path.value() << ")"; return false; } return true; } bool GetAbsInfo(int fd, const base::FilePath& path, int code, struct input_absinfo* absinfo) { if (ioctl(fd, EVIOCGABS(code), absinfo)) { PLOG(ERROR) << "Failed EVIOCGABS (path=" << path.value() << " code=" << code << ")"; return false; } return true; } bool GetDeviceName(int fd, const base::FilePath& path, std::string* name) { char device_name[kMaximumDeviceNameLength]; if (ioctl(fd, EVIOCGNAME(kMaximumDeviceNameLength - 1), &device_name) < 0) { PLOG(INFO) << "Failed EVIOCGNAME (path=" << path.value() << ")"; return false; } *name = device_name; return true; } bool GetDeviceIdentifiers(int fd, const base::FilePath& path, uint16_t* vendor, uint16_t* product) { struct input_id evdev_id; if (ioctl(fd, EVIOCGID, &evdev_id) < 0) { PLOG(INFO) << "Failed EVIOCGID (path=" << path.value() << ")"; 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). void GetSlotValues(int fd, const base::FilePath& path, int32_t* request, unsigned int size) { size_t data_size = size * sizeof(*request); if (ioctl(fd, EVIOCGMTSLOTS(data_size), request) < 0) { PLOG(ERROR) << "Failed EVIOCGMTSLOTS (code=" << request[0] << " path=" << path.value() << ")"; } } 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, const base::FilePath& path) { if (!GetEventBits(fd, path, 0, ev_bits_, sizeof(ev_bits_))) return false; if (!GetEventBits(fd, path, EV_KEY, key_bits_, sizeof(key_bits_))) return false; if (!GetEventBits(fd, path, EV_REL, rel_bits_, sizeof(rel_bits_))) return false; if (!GetEventBits(fd, path, EV_ABS, abs_bits_, sizeof(abs_bits_))) return false; if (!GetEventBits(fd, path, EV_MSC, msc_bits_, sizeof(msc_bits_))) return false; if (!GetEventBits(fd, path, EV_SW, sw_bits_, sizeof(sw_bits_))) return false; if (!GetEventBits(fd, path, EV_LED, led_bits_, sizeof(led_bits_))) return false; if (!GetPropBits(fd, path, prop_bits_, sizeof(prop_bits_))) return false; for (unsigned int i = 0; i < ABS_CNT; ++i) if (HasAbsEvent(i)) if (!GetAbsInfo(fd, path, 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; GetSlotValues(fd, path, request, max_num_slots + 1); std::vector<int32_t>* slots = &slot_values_[i - EVDEV_ABS_MT_FIRST]; slots->assign(request_slots, request_slots + max_num_slots); } if (!GetDeviceName(fd, path, &name_)) return false; if (!GetDeviceIdentifiers(fd, path, &vendor_id_, &product_id_)) return false; device_type_ = GetInputDeviceTypeFromPath(path); 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; } void EventDeviceInfo::SetDeviceType(InputDeviceType type) { device_type_ = type; } 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; } input_absinfo EventDeviceInfo::GetAbsInfoByCode(unsigned int code) const { return abs_info_[code]; } 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; // Treat internal stylus devices as touchscreens. if (device_type_ == INPUT_DEVICE_INTERNAL && HasStylus()) return LegacyAbsoluteDeviceType::LADT_TOUCHSCREEN; if (HasStylus()) return LegacyAbsoluteDeviceType::LADT_TABLET; if (HasKeyEvent(BTN_TOOL_FINGER) && HasKeyEvent(BTN_TOUCH)) return LegacyAbsoluteDeviceType::LADT_TOUCHPAD; if (HasKeyEvent(BTN_TOUCH)) return LegacyAbsoluteDeviceType::LADT_TOUCHSCREEN; // ABS_Z mitigation for extra device on some Elo devices. if (HasKeyEvent(BTN_LEFT) && !HasAbsEvent(ABS_Z)) return LegacyAbsoluteDeviceType::LADT_TOUCHSCREEN; return LegacyAbsoluteDeviceType::LADT_NONE; } } // namespace ui