| // 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/containers/contains.h" |
| #include "base/files/file_util.h" |
| #include "base/hash/hash.h" |
| #include "base/logging.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/device_data_manager.h" |
| #include "ui/events/devices/input_device.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 USB association score between |display| and |device|. A score <= |
| // 0 means that there is no association. |
| int GetUsbAssociationScore(const ManagedDisplayInfo* display, |
| const ui::TouchscreenDevice& device) { |
| // If the devices are not both connected via USB, then there cannot be a USB |
| // 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 USB device that best matches |display|. Returns |
| // |devices.end()| if one is not found. |
| DeviceList::const_iterator GuessBestUsbDevice(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 = GetUsbAssociationScore(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::Contains(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::Contains(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::NumberToString(vendor_id) + "-" + |
| base::NumberToString(product_id); |
| return base::PersistentHash(hash_str); |
| } |
| |
| // static |
| TouchDeviceIdentifier TouchDeviceIdentifier::FromDevice( |
| const ui::TouchscreenDevice& touch_device) { |
| if (!touch_device.id) |
| return GetFallbackTouchDeviceIdentifier(); |
| 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::NumberToString(id_); |
| } |
| |
| std::string TouchDeviceIdentifier::SecondaryIdToString() const { |
| return base::NumberToString(secondary_id_); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // TouchCalibrationData |
| |
| // static |
| bool TouchCalibrationData::CalibrationPointPairCompare( |
| const CalibrationPointPair& pair_1, |
| const CalibrationPointPair& pair_2) { |
| return pair_1.first < pair_2.first; |
| } |
| |
| 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); |
| AssociateUsbDevices(&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::Contains(collision_set, identifier) || |
| !base::Contains(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::AssociateUsbDevices(ManagedDisplayInfoList* displays, |
| DeviceList* devices) { |
| VLOG(2) << "Trying to match usb 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 = GuessBestUsbDevice(display, *devices); |
| |
| if (device_it != devices->end()) { |
| const ui::TouchscreenDevice& device = *device_it; |
| VLOG(2) << "=> Matched device " << device.name << " to display " |
| << display->name() |
| << " (score=" << GetUsbAssociationScore(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 ui::TouchscreenDevice& device, |
| int64_t display_id, |
| const TouchCalibrationData& data) { |
| const TouchDeviceIdentifier identifier = |
| TouchDeviceIdentifier::FromDevice(device); |
| if (!base::Contains(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 ui::TouchscreenDevice& device, |
| int64_t display_id) { |
| const TouchDeviceIdentifier identifier = |
| TouchDeviceIdentifier::FromDevice(device); |
| if (base::Contains(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::Contains(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::Contains(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::Contains(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 ui::TouchscreenDevice& device) const { |
| const TouchDeviceIdentifier identifier = |
| TouchDeviceIdentifier::FromDevice(device); |
| return base::Contains(active_touch_associations_, identifier) && |
| active_touch_associations_.at(identifier) == display_id; |
| } |
| |
| int64_t TouchDeviceManager::GetAssociatedDisplay( |
| const ui::TouchscreenDevice& device) const { |
| const TouchDeviceIdentifier identifier = |
| TouchDeviceIdentifier::FromDevice(device); |
| if (base::Contains(active_touch_associations_, identifier)) |
| return active_touch_associations_.at(identifier); |
| return kInvalidDisplayId; |
| } |
| |
| std::vector<ui::TouchscreenDevice> |
| TouchDeviceManager::GetAssociatedTouchDevicesForDisplay( |
| int64_t display_id) const { |
| std::vector<ui::TouchscreenDevice> result; |
| for (const auto& device : |
| ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices()) { |
| const TouchDeviceIdentifier identifier = |
| TouchDeviceIdentifier::FromDevice(device); |
| |
| const auto it = active_touch_associations_.find(identifier); |
| if (it != active_touch_associations_.end() && it->second == display_id) |
| result.push_back(device); |
| } |
| return result; |
| } |
| |
| 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::DeviceDataManager::GetInstance()->GetTouchscreenDevices()) { |
| if (device.type == ui::InputDeviceType::INPUT_DEVICE_USB || |
| device.type == ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace display |