| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ash/touch/touchscreen_util.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "base/files/file_util.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "ui/events/devices/input_device.h" | |
| 13 | |
| 14 namespace ash { | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 using DisplayInfoList = std::vector<display::ManagedDisplayInfo*>; | |
| 19 using DeviceList = std::vector<const ui::TouchscreenDevice*>; | |
| 20 | |
| 21 // Helper method to associate |display| and |device|. | |
| 22 void Associate(display::ManagedDisplayInfo* display, | |
| 23 const ui::TouchscreenDevice* device) { | |
| 24 display->AddInputDevice(device->id); | |
| 25 display->set_touch_support(display::Display::TOUCH_SUPPORT_AVAILABLE); | |
| 26 } | |
| 27 | |
| 28 // Returns true if |path| is likely a USB device. | |
| 29 bool IsDeviceConnectedViaUsb(const base::FilePath& path) { | |
| 30 std::vector<base::FilePath::StringType> components; | |
| 31 path.GetComponents(&components); | |
| 32 | |
| 33 for (const auto& component : components) { | |
| 34 if (base::StartsWith(component, "usb", | |
| 35 base::CompareCase::INSENSITIVE_ASCII)) | |
| 36 return true; | |
| 37 } | |
| 38 | |
| 39 return false; | |
| 40 } | |
| 41 | |
| 42 // Returns the UDL association score between |display| and |device|. A score <= | |
| 43 // 0 means that there is no association. | |
| 44 int GetUdlAssociationScore(const display::ManagedDisplayInfo* display, | |
| 45 const ui::TouchscreenDevice* device) { | |
| 46 // If the devices are not both connected via USB, then there cannot be a UDL | |
| 47 // association score. | |
| 48 if (!IsDeviceConnectedViaUsb(display->sys_path()) || | |
| 49 !IsDeviceConnectedViaUsb(device->sys_path)) | |
| 50 return 0; | |
| 51 | |
| 52 // The association score is simply the number of prefix path components that | |
| 53 // sysfs paths have in common. | |
| 54 std::vector<base::FilePath::StringType> display_components; | |
| 55 std::vector<base::FilePath::StringType> device_components; | |
| 56 display->sys_path().GetComponents(&display_components); | |
| 57 device->sys_path.GetComponents(&device_components); | |
| 58 | |
| 59 std::size_t largest_idx = 0; | |
| 60 while (largest_idx < display_components.size() && | |
| 61 largest_idx < device_components.size() && | |
| 62 display_components[largest_idx] == device_components[largest_idx]) { | |
| 63 ++largest_idx; | |
| 64 } | |
| 65 return largest_idx; | |
| 66 } | |
| 67 | |
| 68 // Tries to find a UDL device that best matches |display|. Returns nullptr | |
| 69 // if one is not found. | |
| 70 const ui::TouchscreenDevice* GuessBestUdlDevice( | |
| 71 const display::ManagedDisplayInfo* display, | |
| 72 const DeviceList& devices) { | |
| 73 int best_score = 0; | |
| 74 const ui::TouchscreenDevice* best_device = nullptr; | |
| 75 | |
| 76 for (const ui::TouchscreenDevice* device : devices) { | |
| 77 int score = GetUdlAssociationScore(display, device); | |
| 78 if (score > best_score) { | |
| 79 best_score = score; | |
| 80 best_device = device; | |
| 81 } | |
| 82 } | |
| 83 | |
| 84 return best_device; | |
| 85 } | |
| 86 | |
| 87 void AssociateUdlDevices(DisplayInfoList* displays, DeviceList* devices) { | |
| 88 VLOG(2) << "Trying to match udl devices (" << displays->size() | |
| 89 << " displays and " << devices->size() << " devices to match)"; | |
| 90 | |
| 91 DisplayInfoList::iterator display_it = displays->begin(); | |
| 92 while (display_it != displays->end()) { | |
| 93 display::ManagedDisplayInfo* display = *display_it; | |
| 94 const ui::TouchscreenDevice* device = GuessBestUdlDevice(display, *devices); | |
| 95 | |
| 96 if (device) { | |
| 97 VLOG(2) << "=> Matched device " << device->name << " to display " | |
| 98 << display->name() | |
| 99 << " (score=" << GetUdlAssociationScore(display, device) << ")"; | |
| 100 Associate(display, device); | |
| 101 | |
| 102 display_it = displays->erase(display_it); | |
| 103 devices->erase(std::find(devices->begin(), devices->end(), device)); | |
| 104 | |
| 105 continue; | |
| 106 } | |
| 107 | |
| 108 ++display_it; | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 // Returns true if |display| is internal. | |
| 113 bool IsInternalDisplay(const display::ManagedDisplayInfo* display) { | |
| 114 return display::Display::IsInternalDisplayId(display->id()); | |
| 115 } | |
| 116 | |
| 117 // Returns true if |device| is internal. | |
| 118 bool IsInternalDevice(const ui::TouchscreenDevice* device) { | |
| 119 return device->type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL; | |
| 120 } | |
| 121 | |
| 122 void AssociateInternalDevices(DisplayInfoList* displays, DeviceList* devices) { | |
| 123 VLOG(2) << "Trying to match internal devices (" << displays->size() | |
| 124 << " displays and " << devices->size() << " devices to match)"; | |
| 125 | |
| 126 // Internal device assocation has a couple of gotchas: | |
| 127 // - There can be internal devices but no internal display, or visa-versa. | |
| 128 // - There can be multiple internal devices matching one internal display. We | |
| 129 // assume there is at most one internal display. | |
| 130 // - All internal devices must be removed from |displays| and |devices| after | |
| 131 // this function has returned, since an internal device can never be | |
| 132 // associated with an external device. | |
| 133 | |
| 134 // Capture the internal display reference as we remove it from |displays|. | |
| 135 display::ManagedDisplayInfo* internal_display = nullptr; | |
| 136 DisplayInfoList::iterator display_it = | |
| 137 std::find_if(displays->begin(), displays->end(), &IsInternalDisplay); | |
| 138 if (display_it != displays->end()) { | |
| 139 internal_display = *display_it; | |
| 140 displays->erase(display_it); | |
| 141 } | |
| 142 | |
| 143 bool matched = false; | |
| 144 | |
| 145 // Remove all internal devices from |devices|. If we have an internal display, | |
| 146 // then associate the device with the display before removing it. | |
| 147 DeviceList::iterator device_it = devices->begin(); | |
| 148 while (device_it != devices->end()) { | |
| 149 const ui::TouchscreenDevice* internal_device = *device_it; | |
| 150 | |
| 151 // Not an internal device, skip it. | |
| 152 if (!IsInternalDevice(internal_device)) { | |
| 153 ++device_it; | |
| 154 continue; | |
| 155 } | |
| 156 | |
| 157 if (internal_display) { | |
| 158 VLOG(2) << "=> Matched device " << internal_device->name << " to display " | |
| 159 << internal_display->name(); | |
| 160 Associate(internal_display, internal_device); | |
| 161 matched = true; | |
| 162 } else { | |
| 163 VLOG(2) << "=> Removing internal device " << internal_device->name; | |
| 164 } | |
| 165 | |
| 166 device_it = devices->erase(device_it); | |
| 167 } | |
| 168 | |
| 169 if (!matched && internal_display) | |
| 170 VLOG(2) << "=> Removing internal display " << internal_display->name(); | |
| 171 } | |
| 172 | |
| 173 void AssociateSameSizeDevices(DisplayInfoList* displays, DeviceList* devices) { | |
| 174 // Associate screens/displays with the same size. | |
| 175 VLOG(2) << "Trying to match same-size devices (" << displays->size() | |
| 176 << " displays and " << devices->size() << " devices to match)"; | |
| 177 | |
| 178 DisplayInfoList::iterator display_it = displays->begin(); | |
| 179 while (display_it != displays->end()) { | |
| 180 display::ManagedDisplayInfo* display = *display_it; | |
| 181 const gfx::Size native_size = display->GetNativeModeSize(); | |
| 182 | |
| 183 // Try to find an input device with roughly the same size as the display. | |
| 184 DeviceList::iterator device_it = std::find_if( | |
| 185 devices->begin(), devices->end(), | |
| 186 [&native_size](const ui::TouchscreenDevice* device) { | |
| 187 // Allow 1 pixel difference between screen and touchscreen | |
| 188 // resolutions. Because in some cases for monitor resolution | |
| 189 // 1024x768 touchscreen's resolution would be 1024x768, but for | |
| 190 // some 1023x767. It really depends on touchscreen's firmware | |
| 191 // configuration. | |
| 192 return std::abs(native_size.width() - device->size.width()) <= 1 && | |
| 193 std::abs(native_size.height() - device->size.height()) <= 1; | |
| 194 }); | |
| 195 | |
| 196 if (device_it != devices->end()) { | |
| 197 const ui::TouchscreenDevice* device = *device_it; | |
| 198 VLOG(2) << "=> Matched device " << device->name << " to display " | |
| 199 << display->name() << " (display_size: " << native_size.ToString() | |
| 200 << ", device_size: " << device->size.ToString() << ")"; | |
| 201 Associate(display, device); | |
| 202 | |
| 203 display_it = displays->erase(display_it); | |
| 204 device_it = devices->erase(device_it); | |
| 205 continue; | |
| 206 } | |
| 207 | |
| 208 // Didn't find an input device. Skip this display. | |
| 209 ++display_it; | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 void AssociateToSingleDisplay(DisplayInfoList* displays, DeviceList* devices) { | |
| 214 // If there is only one display left, then we should associate all input | |
| 215 // devices with it. | |
| 216 | |
| 217 VLOG(2) << "Trying to match to single display (" << displays->size() | |
| 218 << " displays and " << devices->size() << " devices to match)"; | |
| 219 | |
| 220 // We only associate to one display. | |
| 221 if (displays->size() != 1 || devices->empty()) | |
| 222 return; | |
| 223 | |
| 224 display::ManagedDisplayInfo* display = *displays->begin(); | |
| 225 for (const ui::TouchscreenDevice* device : *devices) { | |
| 226 VLOG(2) << "=> Matched device " << device->name << " to display " | |
| 227 << display->name(); | |
| 228 Associate(display, device); | |
| 229 } | |
| 230 | |
| 231 displays->clear(); | |
| 232 devices->clear(); | |
| 233 } | |
| 234 | |
| 235 } // namespace | |
| 236 | |
| 237 void AssociateTouchscreens( | |
| 238 std::vector<display::ManagedDisplayInfo>* all_displays, | |
| 239 const std::vector<ui::TouchscreenDevice>& all_devices) { | |
| 240 // |displays| and |devices| contain pointers directly to the values stored | |
| 241 // inside of |all_displays| and |all_devices|. When a display or input device | |
| 242 // has been associated, it is removed from the |displays| or |devices| list. | |
| 243 | |
| 244 // Construct our initial set of display/devices that we will process. | |
| 245 DisplayInfoList displays; | |
| 246 for (display::ManagedDisplayInfo& display : *all_displays) { | |
| 247 display.ClearInputDevices(); | |
| 248 | |
| 249 if (display.GetNativeModeSize().IsEmpty()) { | |
| 250 VLOG(2) << "Will not match display " << display.id() | |
| 251 << " since it doesn't have a native mode"; | |
| 252 continue; | |
| 253 } | |
| 254 displays.push_back(&display); | |
| 255 } | |
| 256 | |
| 257 // Construct initial set of devices. | |
| 258 DeviceList devices; | |
| 259 for (const ui::TouchscreenDevice& device : all_devices) | |
| 260 devices.push_back(&device); | |
| 261 | |
| 262 for (const display::ManagedDisplayInfo* display : displays) { | |
| 263 VLOG(2) << "Received display " << display->name() | |
| 264 << " (size: " << display->GetNativeModeSize().ToString() | |
| 265 << ", sys_path: " << display->sys_path().LossyDisplayName() << ")"; | |
| 266 } | |
| 267 for (const ui::TouchscreenDevice* device : devices) { | |
| 268 VLOG(2) << "Received device " << device->name | |
| 269 << " (size: " << device->size.ToString() | |
| 270 << ", sys_path: " << device->sys_path.LossyDisplayName() << ")"; | |
| 271 } | |
| 272 | |
| 273 AssociateInternalDevices(&displays, &devices); | |
| 274 AssociateUdlDevices(&displays, &devices); | |
| 275 AssociateSameSizeDevices(&displays, &devices); | |
| 276 AssociateToSingleDisplay(&displays, &devices); | |
| 277 | |
| 278 for (const display::ManagedDisplayInfo* display : displays) | |
| 279 LOG(WARNING) << "Unmatched display " << display->name(); | |
| 280 for (const ui::TouchscreenDevice* device : devices) | |
| 281 LOG(WARNING) << "Unmatched device " << device->name; | |
| 282 } | |
| 283 | |
| 284 } // namespace ash | |
| OLD | NEW |