blob: 1c87c85a613bc68758d5e79b12866ff8fc44b460 [file] [log] [blame]
// Copyright 2017 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/display/manager/touch_device_manager.h"
#include <algorithm>
#include <set>
#include <string>
#include <tuple>
#include "base/files/file_util.h"
#include "base/hash.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/devices/input_device_manager.h"
#include "ui/events/devices/touchscreen_device.h"
namespace display {
namespace {
using ManagedDisplayInfoList = std::vector<ManagedDisplayInfo*>;
using DeviceList = std::vector<ui::TouchscreenDevice>;
constexpr char kFallbackTouchDeviceName[] = "fallback_touch_device_name";
constexpr char kFallbackTouchDevicePhys[] = "fallback_touch_device_phys";
// Returns true if |path| is likely a USB device.
bool IsDeviceConnectedViaUsb(const base::FilePath& path) {
std::vector<base::FilePath::StringType> components;
path.GetComponents(&components);
for (const auto& component : components) {
if (base::StartsWith(component, "usb",
base::CompareCase::INSENSITIVE_ASCII)) {
return true;
}
// TODO(malaykeshav): When evdi starts registering with the usb subsystem
// in the kernel, this would no longer be needed. All evdi displays are USB
// right now. This might change in the future however.
// See https://crbug.com/923165 for more info.
if (base::StartsWith(component, "evdi", base::CompareCase::SENSITIVE))
return true;
}
return false;
}
// Returns the UDL association score between |display| and |device|. A score <=
// 0 means that there is no association.
int GetUdlAssociationScore(const ManagedDisplayInfo* display,
const ui::TouchscreenDevice& device) {
// If the devices are not both connected via USB, then there cannot be a UDL
// association score.
if (!IsDeviceConnectedViaUsb(display->sys_path()) ||
!IsDeviceConnectedViaUsb(device.sys_path))
return 0;
// The association score is simply the number of prefix path components that
// sysfs paths have in common.
std::vector<base::FilePath::StringType> display_components;
std::vector<base::FilePath::StringType> device_components;
display->sys_path().GetComponents(&display_components);
device.sys_path.GetComponents(&device_components);
std::size_t largest_idx = 0;
while (largest_idx < display_components.size() &&
largest_idx < device_components.size() &&
display_components[largest_idx] == device_components[largest_idx]) {
++largest_idx;
}
return largest_idx;
}
// Tries to find a UDL device that best matches |display|. Returns
// |devices.end()| if one is not found.
DeviceList::const_iterator GuessBestUdlDevice(const ManagedDisplayInfo* display,
const DeviceList& devices) {
int best_score = 0;
DeviceList::const_iterator best_device_it = devices.end();
// TODO(malaykeshav): Migrate to std::max_element in the future.
for (auto it = devices.begin(); it != devices.end(); it++) {
int score = GetUdlAssociationScore(display, *it);
if (score > best_score) {
best_score = score;
best_device_it = it;
}
}
return best_device_it;
}
// Returns true if |display| is internal.
bool IsInternalDisplay(const ManagedDisplayInfo* display) {
return Display::IsInternalDisplayId(display->id());
}
// Returns true if |device| is internal.
bool IsInternalDevice(const ui::TouchscreenDevice& device) {
return device.type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL;
}
// Returns a pointer to the internal display from the list of |displays|. Will
// return null if there is no internal display in the list.
ManagedDisplayInfo* GetInternalDisplay(ManagedDisplayInfoList* displays) {
auto it =
std::find_if(displays->begin(), displays->end(), &IsInternalDisplay);
return it == displays->end() ? nullptr : *it;
}
// Clears any calibration data from |info_map| for the display identified by
// |display_id|.
void ClearCalibrationDataInMap(TouchDeviceManager::AssociationInfoMap& info_map,
int64_t display_id) {
if (info_map.find(display_id) == info_map.end())
return;
info_map[display_id].calibration_data = TouchCalibrationData();
}
// Returns a pointer to the ManagedDisplayInfo of the display that the device
// identified by |identifier| should be associated with. Returns |nullptr| if
// no match is found. |displays| should be the list of active displays.
ManagedDisplayInfo* GetBestMatchForDevice(
const TouchDeviceManager::TouchAssociationMap& touch_associations,
const TouchDeviceIdentifier& identifier,
ManagedDisplayInfoList* displays) {
ManagedDisplayInfo* display_info = nullptr;
base::Time most_recent_timestamp;
// If we have no historical information for the touch device identified by
// |identifier|, do an early return.
if (!base::ContainsKey(touch_associations, identifier))
return display_info;
const TouchDeviceManager::AssociationInfoMap& info_map =
touch_associations.at(identifier);
// Iterate over each active display to see which one was most recently
// associated with the touch device identified by |identifier|.
for (auto* display : *displays) {
// We do not want to match anything to the internal display.
if (Display::IsInternalDisplayId(display->id()))
continue;
if (!base::ContainsKey(info_map, display->id()))
continue;
const TouchDeviceManager::TouchAssociationInfo& info =
info_map.at(display->id());
if (info.timestamp > most_recent_timestamp) {
display_info = display;
most_recent_timestamp = info.timestamp;
}
}
return display_info;
}
// Returns a set of TouchDeviceIdentifiers (sans their port information) that
// are associated with more than 1 touch device from the list |devices|.
std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp>
GetCollisionSet(const DeviceList& devices) {
std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp>
collision_set;
std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp> ids;
for (const ui::TouchscreenDevice& device : devices) {
TouchDeviceIdentifier id = TouchDeviceIdentifier::FromDevice(device);
if (ids.find(id) != ids.end())
collision_set.insert(id);
else
ids.insert(id);
}
return collision_set;
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// TouchDeviceIdentifier
// static
const TouchDeviceIdentifier&
TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier() {
static const TouchDeviceIdentifier kFallTouchDeviceIdentifier(
GenerateIdentifier(kFallbackTouchDeviceName, 0, 0),
base::PersistentHash(kFallbackTouchDevicePhys));
return kFallTouchDeviceIdentifier;
}
// static
uint32_t TouchDeviceIdentifier::GenerateIdentifier(std::string name,
uint16_t vendor_id,
uint16_t product_id) {
std::string hash_str = name + "-" + base::UintToString(vendor_id) + "-" +
base::UintToString(product_id);
return base::PersistentHash(hash_str);
}
// static
TouchDeviceIdentifier TouchDeviceIdentifier::FromDevice(
const ui::TouchscreenDevice& touch_device) {
return TouchDeviceIdentifier(
GenerateIdentifier(touch_device.name, touch_device.vendor_id,
touch_device.product_id),
base::PersistentHash(touch_device.phys));
}
TouchDeviceIdentifier::TouchDeviceIdentifier(uint32_t identifier)
: id_(identifier),
secondary_id_(base::PersistentHash(kFallbackTouchDevicePhys)) {}
TouchDeviceIdentifier::TouchDeviceIdentifier(uint32_t identifier,
uint32_t secondary_id)
: id_(identifier), secondary_id_(secondary_id) {}
TouchDeviceIdentifier::TouchDeviceIdentifier(const TouchDeviceIdentifier& other)
: id_(other.id_), secondary_id_(other.secondary_id_) {}
TouchDeviceIdentifier& TouchDeviceIdentifier::operator=(
TouchDeviceIdentifier other) {
id_ = other.id_;
secondary_id_ = other.secondary_id_;
return *this;
}
bool TouchDeviceIdentifier::operator<(const TouchDeviceIdentifier& rhs) const {
return std::tie(id_, secondary_id_) < std::tie(rhs.id_, rhs.secondary_id_);
}
bool TouchDeviceIdentifier::operator==(const TouchDeviceIdentifier& rhs) const {
return id_ == rhs.id_ && secondary_id_ == rhs.secondary_id_;
}
bool TouchDeviceIdentifier::operator!=(const TouchDeviceIdentifier& rhs) const {
return !(*this == rhs);
}
std::string TouchDeviceIdentifier::ToString() const {
return base::UintToString(id_);
}
std::string TouchDeviceIdentifier::SecondaryIdToString() const {
return base::UintToString(secondary_id_);
}
////////////////////////////////////////////////////////////////////////////////
// TouchCalibrationData
// static
bool TouchCalibrationData::CalibrationPointPairCompare(
const CalibrationPointPair& pair_1,
const CalibrationPointPair& pair_2) {
return pair_1.first.y() < pair_2.first.y()
? true
: pair_1.first.x() < pair_2.first.x();
}
TouchCalibrationData::TouchCalibrationData() {}
TouchCalibrationData::TouchCalibrationData(
const TouchCalibrationData::CalibrationPointPairQuad& point_pairs,
const gfx::Size& bounds)
: point_pairs(point_pairs), bounds(bounds) {}
TouchCalibrationData::TouchCalibrationData(
const TouchCalibrationData& calibration_data)
: point_pairs(calibration_data.point_pairs),
bounds(calibration_data.bounds) {}
bool TouchCalibrationData::operator==(const TouchCalibrationData& other) const {
if (bounds != other.bounds)
return false;
CalibrationPointPairQuad quad_1 = point_pairs;
CalibrationPointPairQuad quad_2 = other.point_pairs;
// Make sure the point pairs are in the correct order.
std::sort(quad_1.begin(), quad_1.end(), CalibrationPointPairCompare);
std::sort(quad_2.begin(), quad_2.end(), CalibrationPointPairCompare);
return quad_1 == quad_2;
}
bool TouchCalibrationData::IsEmpty() const {
return bounds.IsEmpty();
}
////////////////////////////////////////////////////////////////////////////////
// TouchDeviceManager
TouchDeviceManager::TouchDeviceManager() {}
TouchDeviceManager::~TouchDeviceManager() {}
////////////////////////////////////////////////////////////////////////////////
// TouchDeviceManager
// Touch screen association logic
void TouchDeviceManager::AssociateTouchscreens(
std::vector<ManagedDisplayInfo>* all_displays,
const std::vector<ui::TouchscreenDevice>& all_devices) {
active_touch_associations_.clear();
// |displays| and |devices| contain pointers directly to the values stored
// inside of |all_displays| and |all_devices|. When a display or input device
// has been associated, it is removed from the |displays| or |devices| list.
// Construct our initial set of display/devices that we will process.
ManagedDisplayInfoList displays;
for (ManagedDisplayInfo& display : *all_displays) {
// Reset touch support from the display.
display.set_touch_support(Display::TouchSupport::UNAVAILABLE);
displays.push_back(&display);
}
// Construct initial set of devices.
DeviceList devices;
for (const ui::TouchscreenDevice& device : all_devices)
devices.push_back(device);
if (VLOG_IS_ON(2)) {
for (const ManagedDisplayInfo* display : displays) {
VLOG(2) << "Received display " << display->name()
<< " (size: " << display->GetNativeModeSize().ToString() << ", "
<< "sys_path: " << display->sys_path().LossyDisplayName() << ")";
}
for (const ui::TouchscreenDevice& device : devices) {
VLOG(2) << "Received device " << device.name
<< " (size: " << device.size.ToString()
<< ", sys_path: " << device.sys_path.LossyDisplayName() << ")";
}
}
AssociateInternalDevices(&displays, &devices);
AssociateDevicesWithCollision(&displays, &devices);
AssociateFromHistoricalData(&displays, &devices);
AssociateUdlDevices(&displays, &devices);
AssociateSameSizeDevices(&displays, &devices);
AssociateToSingleDisplay(&displays, &devices);
AssociateAnyRemainingDevices(&displays, &devices);
for (const ui::TouchscreenDevice& device : devices)
LOG(WARNING) << "Unmatched device " << device.name;
}
void TouchDeviceManager::AssociateInternalDevices(
ManagedDisplayInfoList* displays,
DeviceList* devices) {
VLOG(2) << "Trying to match internal devices (" << displays->size()
<< " displays and " << devices->size() << " devices to match)";
// Internal device assocation has a couple of gotchas:
// - There can be internal devices but no internal display, or visa-versa.
// - There can be multiple internal devices matching one internal display. We
// assume there is at most one internal display.
// Capture the internal display reference as we remove it from |displays|.
ManagedDisplayInfo* internal_display = GetInternalDisplay(displays);
bool matched = false;
// Remove all internal devices from |devices|. If we have an internal display,
// then associate the device with the display before removing it.
for (auto device_it = devices->begin(); device_it != devices->end();) {
const ui::TouchscreenDevice& internal_device = *device_it;
// Not an internal device, skip it.
if (!IsInternalDevice(internal_device)) {
++device_it;
continue;
}
if (internal_display) {
VLOG(2) << "=> Matched device " << internal_device.name << " to display "
<< internal_display->name();
Associate(internal_display, internal_device);
matched = true;
} else {
// We do not want to associate an internal device to any other display.
VLOG(2) << "=> Removing internal device " << internal_device.name;
}
device_it = devices->erase(device_it);
}
if (!matched && internal_display) {
VLOG(2) << "=> No device found to match with internal display "
<< internal_display->name();
}
}
void TouchDeviceManager::AssociateDevicesWithCollision(
ManagedDisplayInfoList* displays,
DeviceList* devices) {
if (!devices->size() || !displays->size())
return;
// Get a list of touch devices that have the same primary ids but connected
// via different interfaces.
std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp>
collision_set = GetCollisionSet(*devices);
if (collision_set.empty())
return;
VLOG(2) << "Trying to match " << devices->size() << " devices "
<< "and " << displays->size() << " displays where there is/are "
<< collision_set.size() << " collisions with the touch device ids";
for (auto device_it = devices->begin(); device_it != devices->end();) {
const auto identifier = TouchDeviceIdentifier::FromDevice(*device_it);
// If this device is not the one that has a collision or if this device is
// the one that has collision but we have no past port mapping information
// associated with it, then we skip.
if (!base::ContainsKey(collision_set, identifier) ||
!base::ContainsKey(port_associations_, identifier)) {
device_it++;
continue;
}
int64_t display_id = port_associations_.at(identifier);
// Find the display associated with |display_id| from |displays|.
ManagedDisplayInfoList::iterator display_it =
std::find_if(displays->begin(), displays->end(),
[&display_id](ManagedDisplayInfo* info) {
return info->id() == display_id;
});
if (display_it != displays->end()) {
VLOG(2) << "=> Matched device " << (*device_it).name << " to display "
<< (*display_it)->name();
Associate(*display_it, *device_it);
device_it = devices->erase(device_it);
} else {
device_it++;
}
}
}
void TouchDeviceManager::AssociateFromHistoricalData(
ManagedDisplayInfoList* displays,
DeviceList* devices) {
if (!devices->size() || !displays->size())
return;
VLOG(2) << "Trying to match " << devices->size() << " devices "
<< "and " << displays->size() << " displays based on historical "
<< "preferences.";
for (auto device_it = devices->begin(); device_it != devices->end();) {
auto* matched_display_info = GetBestMatchForDevice(
touch_associations_, TouchDeviceIdentifier::FromDevice(*device_it),
displays);
if (matched_display_info) {
VLOG(2) << "=> Matched device " << (*device_it).name << " to display "
<< matched_display_info->name();
Associate(matched_display_info, *device_it);
device_it = devices->erase(device_it);
} else {
device_it++;
}
}
}
void TouchDeviceManager::AssociateUdlDevices(ManagedDisplayInfoList* displays,
DeviceList* devices) {
VLOG(2) << "Trying to match udl devices (" << displays->size()
<< " displays and " << devices->size() << " devices to match)";
for (auto display_it = displays->begin(); display_it != displays->end();
display_it++) {
ManagedDisplayInfo* display = *display_it;
auto device_it = GuessBestUdlDevice(display, *devices);
if (device_it != devices->end()) {
const ui::TouchscreenDevice& device = *device_it;
VLOG(2) << "=> Matched device " << device.name << " to display "
<< display->name()
<< " (score=" << GetUdlAssociationScore(display, device) << ")";
Associate(display, device);
devices->erase(device_it);
}
}
}
void TouchDeviceManager::AssociateSameSizeDevices(
ManagedDisplayInfoList* displays,
DeviceList* devices) {
// Associate screens/displays with the same size.
VLOG(2) << "Trying to match same-size devices (" << displays->size()
<< " displays and " << devices->size() << " devices to match)";
for (auto display_it = displays->begin(); display_it != displays->end();
display_it++) {
ManagedDisplayInfo* display = *display_it;
// We do not want to associate any other devices to the internal display.
if (IsInternalDisplay(display))
continue;
const gfx::Size native_size = display->GetNativeModeSize();
// Try to find an input device with roughly the same size as the display.
DeviceList::iterator device_it = std::find_if(
devices->begin(), devices->end(),
[&native_size](const ui::TouchscreenDevice& device) {
// Allow 1 pixel difference between screen and touchscreen
// resolutions. Because in some cases for monitor resolution
// 1024x768 touchscreen's resolution would be 1024x768, but for
// some 1023x767. It really depends on touchscreen's firmware
// configuration.
return std::abs(native_size.width() - device.size.width()) <= 1 &&
std::abs(native_size.height() - device.size.height()) <= 1;
});
if (device_it != devices->end()) {
const ui::TouchscreenDevice& device = *device_it;
VLOG(2) << "=> Matched device " << device.name << " to display "
<< display->name() << " (display_size: " << native_size.ToString()
<< ", device_size: " << device.size.ToString() << ")";
Associate(display, device);
device_it = devices->erase(device_it);
}
}
}
void TouchDeviceManager::AssociateToSingleDisplay(
ManagedDisplayInfoList* displays,
DeviceList* devices) {
// If there is only one display left, then we should associate all input
// devices with it.
VLOG(2) << "Trying to match to single display (" << displays->size()
<< " displays and " << devices->size() << " devices to match)";
std::size_t num_displays_excluding_internal = displays->size();
ManagedDisplayInfo* internal_display = GetInternalDisplay(displays);
if (internal_display)
num_displays_excluding_internal--;
// We only associate to one display.
if (num_displays_excluding_internal != 1 || devices->empty())
return;
// Pick the non internal display.
ManagedDisplayInfo* display = *displays->begin();
if (display == internal_display)
display = (*displays)[1];
for (const ui::TouchscreenDevice& device : *devices) {
VLOG(2) << "=> Matched device " << device.name << " to display "
<< display->name();
Associate(display, device);
}
devices->clear();
}
void TouchDeviceManager::AssociateAnyRemainingDevices(
ManagedDisplayInfoList* displays,
DeviceList* devices) {
if (!displays->size() || !devices->size())
return;
VLOG(2) << "Trying to match remaining " << devices->size()
<< " devices to a display.";
// Try to match all devices to the internal display.
ManagedDisplayInfo* display = GetInternalDisplay(displays);
if (!display) {
// If no internal displays were found, then associate the devices to any of
// the other displays.
display = *(displays->begin());
VLOG(2) << "Could not find any internal display. Matching all devices to a "
<< "random non internal display.";
}
VLOG(2) << "Matching " << devices->size() << " touch devices to "
<< display->name() << "[" << display->id() << "]";
// device_it is iterated within the loop.
for (auto device_it = devices->begin(); device_it != devices->end();) {
// We do not want to associate an internal touch device with anything other
// than internal display.
if ((*device_it).type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL) {
device_it++;
continue;
}
VLOG(2) << "=> Matched " << (*device_it).name << " to " << display->name();
Associate(display, *device_it);
device_it = devices->erase(device_it);
}
}
void TouchDeviceManager::Associate(ManagedDisplayInfo* display,
const ui::TouchscreenDevice& device) {
display->set_touch_support(Display::TouchSupport::AVAILABLE);
active_touch_associations_[TouchDeviceIdentifier::FromDevice(device)] =
display->id();
}
////////////////////////////////////////////////////////////////////////////////
// TouchDeviceManager
// Managing Touch device calibration data
void TouchDeviceManager::AddTouchCalibrationData(
const TouchDeviceIdentifier& identifier,
int64_t display_id,
const TouchCalibrationData& data) {
if (!base::ContainsKey(touch_associations_, identifier))
touch_associations_.emplace(identifier, AssociationInfoMap());
// Update the current touch association and associate the display identified
// by |display_id| to the touch device identified by |identifier|.
active_touch_associations_[identifier] = display_id;
auto it = touch_associations_.at(identifier).find(display_id);
if (it != touch_associations_.at(identifier).end()) {
// Update the timestamp and calibration data if information about the
// display identified by |display_id| already exists for the touch device
// identified by |identifier|.
it->second.calibration_data = data;
it->second.timestamp = base::Time::Now();
} else {
// Add a new entry for the display identified by |display_id| in the map
// of associations for the touch device identified by |identifier|.
TouchAssociationInfo info;
info.timestamp = base::Time::Now();
info.calibration_data = data;
touch_associations_.at(identifier).emplace(display_id, info);
}
// Store the port association information, i.e. the touch device identified by
// |identifier| when connected to port |identifier.secondary_id()| was
// associated with display identified by |display_id|.
port_associations_[identifier] = display_id;
}
void TouchDeviceManager::ClearTouchCalibrationData(
const TouchDeviceIdentifier& identifier,
int64_t display_id) {
if (base::ContainsKey(touch_associations_, identifier)) {
ClearCalibrationDataInMap(touch_associations_.at(identifier), display_id);
}
}
void TouchDeviceManager::ClearAllTouchCalibrationData(int64_t display_id) {
for (auto it : touch_associations_) {
// Erase all calibration data from the persistent storage associated with
// the display identified by |display_id|.
ClearCalibrationDataInMap(it.second, display_id);
}
}
TouchCalibrationData TouchDeviceManager::GetCalibrationData(
const ui::TouchscreenDevice& touchscreen,
int64_t display_id) const {
TouchDeviceIdentifier identifier =
TouchDeviceIdentifier::FromDevice(touchscreen);
if (display_id == kInvalidDisplayId) {
// If the touch device is currently not associated with any display and the
// |display_id| was not provided, then this is an invalid query.
if (!base::ContainsKey(active_touch_associations_, identifier))
return TouchCalibrationData();
// If the display id is not provided, we return the calibration information
// for the touch device |touchscreen| and the display it is actively
// associated with.
display_id = active_touch_associations_.at(identifier);
}
if (base::ContainsKey(touch_associations_, identifier)) {
const AssociationInfoMap& info_map = touch_associations_.at(identifier);
if (info_map.find(display_id) != info_map.end())
return info_map.at(display_id).calibration_data;
}
// Check for legacy calibration data.
TouchDeviceIdentifier fallback_identifier(
TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier());
if (base::ContainsKey(touch_associations_, fallback_identifier)) {
const AssociationInfoMap& info_map =
touch_associations_.at(fallback_identifier);
if (info_map.find(display_id) != info_map.end())
return info_map.at(display_id).calibration_data;
}
// Return an empty calibration data if none was found.
return TouchCalibrationData();
}
bool TouchDeviceManager::DisplayHasTouchDevice(
int64_t display_id,
const TouchDeviceIdentifier& identifier) const {
return base::ContainsKey(active_touch_associations_, identifier) &&
active_touch_associations_.at(identifier) == display_id;
}
int64_t TouchDeviceManager::GetAssociatedDisplay(
const TouchDeviceIdentifier& identifier) const {
if (base::ContainsKey(active_touch_associations_, identifier))
return active_touch_associations_.at(identifier);
return kInvalidDisplayId;
}
std::vector<TouchDeviceIdentifier>
TouchDeviceManager::GetAssociatedTouchDevicesForDisplay(
int64_t display_id) const {
std::vector<TouchDeviceIdentifier> identifiers;
for (const auto& association : active_touch_associations_) {
if (association.second == display_id)
identifiers.push_back(association.first);
}
return identifiers;
}
void TouchDeviceManager::RegisterTouchAssociations(
const TouchAssociationMap& touch_associations,
const PortAssociationMap& port_associations) {
touch_associations_ = touch_associations;
port_associations_ = port_associations;
}
std::ostream& operator<<(std::ostream& os,
const TouchDeviceIdentifier& identifier) {
return os << identifier.ToString() << " [" << identifier.SecondaryIdToString()
<< "]";
}
bool HasExternalTouchscreenDevice() {
for (const auto& device :
ui::InputDeviceManager::GetInstance()->GetTouchscreenDevices()) {
if (device.type == ui::InputDeviceType::INPUT_DEVICE_USB ||
device.type == ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH) {
return true;
}
}
return false;
}
bool IsInternalTouchscreenDevice(const TouchDeviceIdentifier& identifier) {
for (const auto& device :
ui::InputDeviceManager::GetInstance()->GetTouchscreenDevices()) {
if (TouchDeviceIdentifier::FromDevice(device) == identifier)
return device.type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL;
}
VLOG(1) << "Touch device identified by " << identifier << " is currently"
<< " not connected to the device or is an invalid device.";
return false;
}
} // namespace display