| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 "content/browser/gamepad/gamepad_platform_data_fetcher_mac.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 #include <string.h> | |
| 9 | |
| 10 #include "base/mac/foundation_util.h" | |
| 11 #include "base/mac/scoped_nsobject.h" | |
| 12 #include "base/macros.h" | |
| 13 #include "base/strings/string16.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 #include "base/strings/utf_string_conversions.h" | |
| 16 #include "base/time/time.h" | |
| 17 | |
| 18 #import <Foundation/Foundation.h> | |
| 19 #include <IOKit/hid/IOHIDKeys.h> | |
| 20 | |
| 21 using blink::WebGamepad; | |
| 22 using blink::WebGamepads; | |
| 23 | |
| 24 namespace content { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 void CopyNSStringAsUTF16LittleEndian( | |
| 29 NSString* src, blink::WebUChar* dest, size_t dest_len) { | |
| 30 NSData* as16 = [src dataUsingEncoding:NSUTF16LittleEndianStringEncoding]; | |
| 31 memset(dest, 0, dest_len); | |
| 32 [as16 getBytes:dest length:dest_len - sizeof(blink::WebUChar)]; | |
| 33 } | |
| 34 | |
| 35 NSDictionary* DeviceMatching(uint32_t usage_page, uint32_t usage) { | |
| 36 return [NSDictionary dictionaryWithObjectsAndKeys: | |
| 37 [NSNumber numberWithUnsignedInt:usage_page], | |
| 38 base::mac::CFToNSCast(CFSTR(kIOHIDDeviceUsagePageKey)), | |
| 39 [NSNumber numberWithUnsignedInt:usage], | |
| 40 base::mac::CFToNSCast(CFSTR(kIOHIDDeviceUsageKey)), | |
| 41 nil]; | |
| 42 } | |
| 43 | |
| 44 float NormalizeAxis(CFIndex value, CFIndex min, CFIndex max) { | |
| 45 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; | |
| 46 } | |
| 47 | |
| 48 float NormalizeUInt8Axis(uint8_t value, uint8_t min, uint8_t max) { | |
| 49 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; | |
| 50 } | |
| 51 | |
| 52 float NormalizeUInt16Axis(uint16_t value, uint16_t min, uint16_t max) { | |
| 53 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; | |
| 54 } | |
| 55 | |
| 56 float NormalizeUInt32Axis(uint32_t value, uint32_t min, uint32_t max) { | |
| 57 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; | |
| 58 } | |
| 59 | |
| 60 // http://www.usb.org/developers/hidpage | |
| 61 const uint32_t kGenericDesktopUsagePage = 0x01; | |
| 62 const uint32_t kGameControlsUsagePage = 0x05; | |
| 63 const uint32_t kButtonUsagePage = 0x09; | |
| 64 const uint32_t kJoystickUsageNumber = 0x04; | |
| 65 const uint32_t kGameUsageNumber = 0x05; | |
| 66 const uint32_t kMultiAxisUsageNumber = 0x08; | |
| 67 const uint32_t kAxisMinimumUsageNumber = 0x30; | |
| 68 | |
| 69 } // namespace | |
| 70 | |
| 71 GamepadPlatformDataFetcherMac::GamepadPlatformDataFetcherMac() | |
| 72 : enabled_(true), paused_(false) { | |
| 73 memset(associated_, 0, sizeof(associated_)); | |
| 74 | |
| 75 xbox_fetcher_.reset(new XboxDataFetcher(this)); | |
| 76 if (!xbox_fetcher_->RegisterForNotifications()) | |
| 77 xbox_fetcher_.reset(); | |
| 78 | |
| 79 hid_manager_ref_.reset(IOHIDManagerCreate(kCFAllocatorDefault, | |
| 80 kIOHIDOptionsTypeNone)); | |
| 81 if (CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) { | |
| 82 enabled_ = false; | |
| 83 return; | |
| 84 } | |
| 85 | |
| 86 base::scoped_nsobject<NSArray> criteria([[NSArray alloc] initWithObjects: | |
| 87 DeviceMatching(kGenericDesktopUsagePage, kJoystickUsageNumber), | |
| 88 DeviceMatching(kGenericDesktopUsagePage, kGameUsageNumber), | |
| 89 DeviceMatching(kGenericDesktopUsagePage, kMultiAxisUsageNumber), | |
| 90 nil]); | |
| 91 IOHIDManagerSetDeviceMatchingMultiple( | |
| 92 hid_manager_ref_, | |
| 93 base::mac::NSToCFCast(criteria)); | |
| 94 | |
| 95 RegisterForNotifications(); | |
| 96 } | |
| 97 | |
| 98 void GamepadPlatformDataFetcherMac::RegisterForNotifications() { | |
| 99 // Register for plug/unplug notifications. | |
| 100 IOHIDManagerRegisterDeviceMatchingCallback( | |
| 101 hid_manager_ref_, | |
| 102 DeviceAddCallback, | |
| 103 this); | |
| 104 IOHIDManagerRegisterDeviceRemovalCallback( | |
| 105 hid_manager_ref_, | |
| 106 DeviceRemoveCallback, | |
| 107 this); | |
| 108 | |
| 109 // Register for value change notifications. | |
| 110 IOHIDManagerRegisterInputValueCallback( | |
| 111 hid_manager_ref_, | |
| 112 ValueChangedCallback, | |
| 113 this); | |
| 114 | |
| 115 IOHIDManagerScheduleWithRunLoop( | |
| 116 hid_manager_ref_, | |
| 117 CFRunLoopGetMain(), | |
| 118 kCFRunLoopDefaultMode); | |
| 119 | |
| 120 enabled_ = IOHIDManagerOpen(hid_manager_ref_, | |
| 121 kIOHIDOptionsTypeNone) == kIOReturnSuccess; | |
| 122 | |
| 123 if (xbox_fetcher_) | |
| 124 xbox_fetcher_->RegisterForNotifications(); | |
| 125 } | |
| 126 | |
| 127 void GamepadPlatformDataFetcherMac::UnregisterFromNotifications() { | |
| 128 IOHIDManagerUnscheduleFromRunLoop( | |
| 129 hid_manager_ref_, | |
| 130 CFRunLoopGetCurrent(), | |
| 131 kCFRunLoopDefaultMode); | |
| 132 IOHIDManagerClose(hid_manager_ref_, kIOHIDOptionsTypeNone); | |
| 133 if (xbox_fetcher_) | |
| 134 xbox_fetcher_->UnregisterFromNotifications(); | |
| 135 } | |
| 136 | |
| 137 void GamepadPlatformDataFetcherMac::PauseHint(bool pause) { | |
| 138 paused_ = pause; | |
| 139 } | |
| 140 | |
| 141 GamepadPlatformDataFetcherMac::~GamepadPlatformDataFetcherMac() { | |
| 142 UnregisterFromNotifications(); | |
| 143 } | |
| 144 | |
| 145 GamepadPlatformDataFetcherMac* | |
| 146 GamepadPlatformDataFetcherMac::InstanceFromContext(void* context) { | |
| 147 return reinterpret_cast<GamepadPlatformDataFetcherMac*>(context); | |
| 148 } | |
| 149 | |
| 150 void GamepadPlatformDataFetcherMac::DeviceAddCallback(void* context, | |
| 151 IOReturn result, | |
| 152 void* sender, | |
| 153 IOHIDDeviceRef ref) { | |
| 154 InstanceFromContext(context)->DeviceAdd(ref); | |
| 155 } | |
| 156 | |
| 157 void GamepadPlatformDataFetcherMac::DeviceRemoveCallback(void* context, | |
| 158 IOReturn result, | |
| 159 void* sender, | |
| 160 IOHIDDeviceRef ref) { | |
| 161 InstanceFromContext(context)->DeviceRemove(ref); | |
| 162 } | |
| 163 | |
| 164 void GamepadPlatformDataFetcherMac::ValueChangedCallback(void* context, | |
| 165 IOReturn result, | |
| 166 void* sender, | |
| 167 IOHIDValueRef ref) { | |
| 168 InstanceFromContext(context)->ValueChanged(ref); | |
| 169 } | |
| 170 | |
| 171 bool GamepadPlatformDataFetcherMac::CheckCollection(IOHIDElementRef element) { | |
| 172 // Check that a parent collection of this element matches one of the usage | |
| 173 // numbers that we are looking for. | |
| 174 while ((element = IOHIDElementGetParent(element)) != NULL) { | |
| 175 uint32_t usage_page = IOHIDElementGetUsagePage(element); | |
| 176 uint32_t usage = IOHIDElementGetUsage(element); | |
| 177 if (usage_page == kGenericDesktopUsagePage) { | |
| 178 if (usage == kJoystickUsageNumber || | |
| 179 usage == kGameUsageNumber || | |
| 180 usage == kMultiAxisUsageNumber) { | |
| 181 return true; | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 return false; | |
| 186 } | |
| 187 | |
| 188 bool GamepadPlatformDataFetcherMac::AddButtonsAndAxes(NSArray* elements, | |
| 189 size_t slot) { | |
| 190 WebGamepad& pad = pad_state_[slot].data; | |
| 191 AssociatedData& associated = associated_[slot]; | |
| 192 CHECK(!associated.is_xbox); | |
| 193 | |
| 194 pad.axesLength = 0; | |
| 195 pad.buttonsLength = 0; | |
| 196 pad.timestamp = 0; | |
| 197 memset(pad.axes, 0, sizeof(pad.axes)); | |
| 198 memset(pad.buttons, 0, sizeof(pad.buttons)); | |
| 199 | |
| 200 bool mapped_all_axes = true; | |
| 201 | |
| 202 for (id elem in elements) { | |
| 203 IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem); | |
| 204 if (!CheckCollection(element)) | |
| 205 continue; | |
| 206 | |
| 207 uint32_t usage_page = IOHIDElementGetUsagePage(element); | |
| 208 uint32_t usage = IOHIDElementGetUsage(element); | |
| 209 if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Button && | |
| 210 usage_page == kButtonUsagePage) { | |
| 211 uint32_t button_index = usage - 1; | |
| 212 if (button_index < WebGamepad::buttonsLengthCap) { | |
| 213 associated.hid.button_elements[button_index] = element; | |
| 214 pad.buttonsLength = std::max(pad.buttonsLength, button_index + 1); | |
| 215 } | |
| 216 } | |
| 217 else if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc) { | |
| 218 uint32_t axis_index = usage - kAxisMinimumUsageNumber; | |
| 219 if (axis_index < WebGamepad::axesLengthCap) { | |
| 220 associated.hid.axis_elements[axis_index] = element; | |
| 221 pad.axesLength = std::max(pad.axesLength, axis_index + 1); | |
| 222 } else { | |
| 223 mapped_all_axes = false; | |
| 224 } | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 if (!mapped_all_axes) { | |
| 229 // For axes who's usage puts them outside the standard axesLengthCap range. | |
| 230 uint32_t next_index = 0; | |
| 231 for (id elem in elements) { | |
| 232 IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem); | |
| 233 if (!CheckCollection(element)) | |
| 234 continue; | |
| 235 | |
| 236 uint32_t usage_page = IOHIDElementGetUsagePage(element); | |
| 237 uint32_t usage = IOHIDElementGetUsage(element); | |
| 238 if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc && | |
| 239 usage - kAxisMinimumUsageNumber >= WebGamepad::axesLengthCap && | |
| 240 usage_page <= kGameControlsUsagePage) { | |
| 241 for (; next_index < WebGamepad::axesLengthCap; ++next_index) { | |
| 242 if (associated.hid.axis_elements[next_index] == NULL) | |
| 243 break; | |
| 244 } | |
| 245 if (next_index < WebGamepad::axesLengthCap) { | |
| 246 associated.hid.axis_elements[next_index] = element; | |
| 247 pad.axesLength = std::max(pad.axesLength, next_index + 1); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 if (next_index >= WebGamepad::axesLengthCap) | |
| 252 break; | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 for (uint32_t axis_index = 0; axis_index < pad.axesLength; ++axis_index) { | |
| 257 IOHIDElementRef element = associated.hid.axis_elements[axis_index]; | |
| 258 if (element != NULL) { | |
| 259 CFIndex axis_min = IOHIDElementGetLogicalMin(element); | |
| 260 CFIndex axis_max = IOHIDElementGetLogicalMax(element); | |
| 261 | |
| 262 // Some HID axes report a logical range of -1 to 0 signed, which must be | |
| 263 // interpreted as 0 to -1 unsigned for correct normalization behavior. | |
| 264 if (axis_min == -1 && axis_max == 0) { | |
| 265 axis_max = -1; | |
| 266 axis_min = 0; | |
| 267 } | |
| 268 | |
| 269 associated.hid.axis_minimums[axis_index] = axis_min; | |
| 270 associated.hid.axis_maximums[axis_index] = axis_max; | |
| 271 associated.hid.axis_report_sizes[axis_index] = | |
| 272 IOHIDElementGetReportSize(element); | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 return (pad.axesLength > 0 || pad.buttonsLength > 0); | |
| 277 } | |
| 278 | |
| 279 size_t GamepadPlatformDataFetcherMac::GetEmptySlot() { | |
| 280 // Find a free slot for this device. | |
| 281 for (size_t slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 282 if (!pad_state_[slot].data.connected) | |
| 283 return slot; | |
| 284 } | |
| 285 return WebGamepads::itemsLengthCap; | |
| 286 } | |
| 287 | |
| 288 size_t GamepadPlatformDataFetcherMac::GetSlotForDevice(IOHIDDeviceRef device) { | |
| 289 for (size_t slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 290 // If we already have this device, and it's already connected, don't do | |
| 291 // anything now. | |
| 292 if (pad_state_[slot].data.connected && | |
| 293 !associated_[slot].is_xbox && | |
| 294 associated_[slot].hid.device_ref == device) | |
| 295 return WebGamepads::itemsLengthCap; | |
| 296 } | |
| 297 return GetEmptySlot(); | |
| 298 } | |
| 299 | |
| 300 size_t GamepadPlatformDataFetcherMac::GetSlotForXboxDevice( | |
| 301 XboxController* device) { | |
| 302 for (size_t slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 303 if (associated_[slot].is_xbox && | |
| 304 associated_[slot].xbox.location_id == device->location_id()) { | |
| 305 if (pad_state_[slot].data.connected) { | |
| 306 // The device is already connected. No idea why we got a second "device | |
| 307 // added" call, but let's not add it twice. | |
| 308 DCHECK_EQ(associated_[slot].xbox.device, device); | |
| 309 return WebGamepads::itemsLengthCap; | |
| 310 } else { | |
| 311 // A device with the same location ID was previously connected, so put | |
| 312 // it in the same slot. | |
| 313 return slot; | |
| 314 } | |
| 315 } | |
| 316 } | |
| 317 return GetEmptySlot(); | |
| 318 } | |
| 319 | |
| 320 void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { | |
| 321 using base::mac::CFToNSCast; | |
| 322 using base::mac::CFCastStrict; | |
| 323 | |
| 324 if (!enabled_) | |
| 325 return; | |
| 326 | |
| 327 // Find an index for this device. | |
| 328 size_t slot = GetSlotForDevice(device); | |
| 329 | |
| 330 // We can't handle this many connected devices. | |
| 331 if (slot == WebGamepads::itemsLengthCap) | |
| 332 return; | |
| 333 | |
| 334 // Clear some state that may have been left behind by previous gamepads | |
| 335 memset(&associated_[slot], 0, sizeof(AssociatedData)); | |
| 336 | |
| 337 NSNumber* vendor_id = CFToNSCast(CFCastStrict<CFNumberRef>( | |
| 338 IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)))); | |
| 339 NSNumber* product_id = CFToNSCast(CFCastStrict<CFNumberRef>( | |
| 340 IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)))); | |
| 341 NSString* product = CFToNSCast(CFCastStrict<CFStringRef>( | |
| 342 IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)))); | |
| 343 int vendor_int = [vendor_id intValue]; | |
| 344 int product_int = [product_id intValue]; | |
| 345 | |
| 346 char vendor_as_str[5], product_as_str[5]; | |
| 347 snprintf(vendor_as_str, sizeof(vendor_as_str), "%04x", vendor_int); | |
| 348 snprintf(product_as_str, sizeof(product_as_str), "%04x", product_int); | |
| 349 pad_state_[slot].mapper = | |
| 350 GetGamepadStandardMappingFunction(vendor_as_str, product_as_str); | |
| 351 | |
| 352 NSString* ident = [NSString stringWithFormat: | |
| 353 @"%@ (%sVendor: %04x Product: %04x)", | |
| 354 product, | |
| 355 pad_state_[slot].mapper ? "STANDARD GAMEPAD " : "", | |
| 356 vendor_int, | |
| 357 product_int]; | |
| 358 CopyNSStringAsUTF16LittleEndian( | |
| 359 ident, | |
| 360 pad_state_[slot].data.id, | |
| 361 sizeof(pad_state_[slot].data.id)); | |
| 362 | |
| 363 if (pad_state_[slot].mapper) { | |
| 364 CopyNSStringAsUTF16LittleEndian( | |
| 365 @"standard", | |
| 366 pad_state_[slot].data.mapping, | |
| 367 sizeof(pad_state_[slot].data.mapping)); | |
| 368 } else { | |
| 369 pad_state_[slot].data.mapping[0] = 0; | |
| 370 } | |
| 371 | |
| 372 base::ScopedCFTypeRef<CFArrayRef> elements( | |
| 373 IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone)); | |
| 374 | |
| 375 if (!AddButtonsAndAxes(CFToNSCast(elements), slot)) | |
| 376 return; | |
| 377 | |
| 378 associated_[slot].hid.device_ref = device; | |
| 379 pad_state_[slot].data.connected = true; | |
| 380 pad_state_[slot].axis_mask = 0; | |
| 381 pad_state_[slot].button_mask = 0; | |
| 382 } | |
| 383 | |
| 384 void GamepadPlatformDataFetcherMac::DeviceRemove(IOHIDDeviceRef device) { | |
| 385 if (!enabled_) | |
| 386 return; | |
| 387 | |
| 388 // Find the index for this device. | |
| 389 size_t slot; | |
| 390 for (slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 391 if (pad_state_[slot].data.connected && | |
| 392 !associated_[slot].is_xbox && | |
| 393 associated_[slot].hid.device_ref == device) | |
| 394 break; | |
| 395 } | |
| 396 DCHECK(slot < WebGamepads::itemsLengthCap); | |
| 397 // Leave associated device_ref so that it will be reconnected in the same | |
| 398 // location. Simply mark it as disconnected. | |
| 399 pad_state_[slot].data.connected = false; | |
| 400 } | |
| 401 | |
| 402 void GamepadPlatformDataFetcherMac::ValueChanged(IOHIDValueRef value) { | |
| 403 if (!enabled_ || paused_) | |
| 404 return; | |
| 405 | |
| 406 IOHIDElementRef element = IOHIDValueGetElement(value); | |
| 407 IOHIDDeviceRef device = IOHIDElementGetDevice(element); | |
| 408 | |
| 409 // Find device slot. | |
| 410 size_t slot; | |
| 411 for (slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 412 if (pad_state_[slot].data.connected && | |
| 413 !associated_[slot].is_xbox && | |
| 414 associated_[slot].hid.device_ref == device) | |
| 415 break; | |
| 416 } | |
| 417 if (slot == WebGamepads::itemsLengthCap) | |
| 418 return; | |
| 419 | |
| 420 WebGamepad& pad = pad_state_[slot].data; | |
| 421 AssociatedData& associated = associated_[slot]; | |
| 422 | |
| 423 uint32_t value_length = IOHIDValueGetLength(value); | |
| 424 if (value_length > 4) { | |
| 425 // Workaround for bizarre issue with PS3 controllers that try to return | |
| 426 // massive (30+ byte) values and crash IOHIDValueGetIntegerValue | |
| 427 return; | |
| 428 } | |
| 429 | |
| 430 // Find and fill in the associated button event, if any. | |
| 431 for (size_t i = 0; i < pad.buttonsLength; ++i) { | |
| 432 if (associated.hid.button_elements[i] == element) { | |
| 433 pad.buttons[i].pressed = IOHIDValueGetIntegerValue(value); | |
| 434 pad.buttons[i].value = pad.buttons[i].pressed ? 1.f : 0.f; | |
| 435 pad.timestamp = std::max(pad.timestamp, IOHIDValueGetTimeStamp(value)); | |
| 436 return; | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 // Find and fill in the associated axis event, if any. | |
| 441 for (size_t i = 0; i < pad.axesLength; ++i) { | |
| 442 if (associated.hid.axis_elements[i] == element) { | |
| 443 CFIndex axis_min = associated.hid.axis_minimums[i]; | |
| 444 CFIndex axis_max = associated.hid.axis_maximums[i]; | |
| 445 CFIndex axis_value = IOHIDValueGetIntegerValue(value); | |
| 446 | |
| 447 if (axis_min > axis_max) { | |
| 448 // We'll need to interpret this axis as unsigned during normalization. | |
| 449 switch (associated.hid.axis_report_sizes[i]) { | |
| 450 case 8: | |
| 451 pad.axes[i] = NormalizeUInt8Axis(axis_value, axis_min, axis_max); | |
| 452 break; | |
| 453 case 16: | |
| 454 pad.axes[i] = NormalizeUInt16Axis(axis_value, axis_min, axis_max); | |
| 455 break; | |
| 456 case 32: | |
| 457 pad.axes[i] = NormalizeUInt32Axis(axis_value, axis_min, axis_max); | |
| 458 break; | |
| 459 } | |
| 460 } else { | |
| 461 pad.axes[i] = NormalizeAxis(axis_value, axis_min, axis_max); | |
| 462 } | |
| 463 | |
| 464 pad.timestamp = std::max(pad.timestamp, IOHIDValueGetTimeStamp(value)); | |
| 465 return; | |
| 466 } | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 void GamepadPlatformDataFetcherMac::XboxDeviceAdd(XboxController* device) { | |
| 471 if (!enabled_) | |
| 472 return; | |
| 473 | |
| 474 size_t slot = GetSlotForXboxDevice(device); | |
| 475 | |
| 476 // We can't handle this many connected devices. | |
| 477 if (slot == WebGamepads::itemsLengthCap) | |
| 478 return; | |
| 479 | |
| 480 device->SetLEDPattern( | |
| 481 (XboxController::LEDPattern)(XboxController::LED_FLASH_TOP_LEFT + slot)); | |
| 482 | |
| 483 NSString* ident = | |
| 484 [NSString stringWithFormat: | |
| 485 @"%@ (STANDARD GAMEPAD Vendor: %04x Product: %04x)", | |
| 486 device->GetControllerType() == XboxController::XBOX_360_CONTROLLER | |
| 487 ? @"Xbox 360 Controller" | |
| 488 : @"Xbox One Controller", | |
| 489 device->GetProductId(), device->GetVendorId()]; | |
| 490 CopyNSStringAsUTF16LittleEndian( | |
| 491 ident, | |
| 492 pad_state_[slot].data.id, | |
| 493 sizeof(pad_state_[slot].data.id)); | |
| 494 | |
| 495 CopyNSStringAsUTF16LittleEndian( | |
| 496 @"standard", | |
| 497 pad_state_[slot].data.mapping, | |
| 498 sizeof(pad_state_[slot].data.mapping)); | |
| 499 | |
| 500 associated_[slot].is_xbox = true; | |
| 501 associated_[slot].xbox.device = device; | |
| 502 associated_[slot].xbox.location_id = device->location_id(); | |
| 503 pad_state_[slot].data.connected = true; | |
| 504 pad_state_[slot].data.axesLength = 4; | |
| 505 pad_state_[slot].data.buttonsLength = 17; | |
| 506 pad_state_[slot].data.timestamp = 0; | |
| 507 pad_state_[slot].mapper = 0; | |
| 508 pad_state_[slot].axis_mask = 0; | |
| 509 pad_state_[slot].button_mask = 0; | |
| 510 } | |
| 511 | |
| 512 void GamepadPlatformDataFetcherMac::XboxDeviceRemove(XboxController* device) { | |
| 513 if (!enabled_) | |
| 514 return; | |
| 515 | |
| 516 // Find the index for this device. | |
| 517 size_t slot; | |
| 518 for (slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 519 if (pad_state_[slot].data.connected && | |
| 520 associated_[slot].is_xbox && | |
| 521 associated_[slot].xbox.device == device) | |
| 522 break; | |
| 523 } | |
| 524 DCHECK(slot < WebGamepads::itemsLengthCap); | |
| 525 // Leave associated location id so that the controller will be reconnected in | |
| 526 // the same slot if it is plugged in again. Simply mark it as disconnected. | |
| 527 pad_state_[slot].data.connected = false; | |
| 528 } | |
| 529 | |
| 530 void GamepadPlatformDataFetcherMac::XboxValueChanged( | |
| 531 XboxController* device, const XboxController::Data& data) { | |
| 532 // Find device slot. | |
| 533 size_t slot; | |
| 534 for (slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 535 if (pad_state_[slot].data.connected && | |
| 536 associated_[slot].is_xbox && | |
| 537 associated_[slot].xbox.device == device) | |
| 538 break; | |
| 539 } | |
| 540 if (slot == WebGamepads::itemsLengthCap) | |
| 541 return; | |
| 542 | |
| 543 WebGamepad& pad = pad_state_[slot].data; | |
| 544 | |
| 545 for (size_t i = 0; i < 6; i++) { | |
| 546 pad.buttons[i].pressed = data.buttons[i]; | |
| 547 pad.buttons[i].value = data.buttons[i] ? 1.0f : 0.0f; | |
| 548 } | |
| 549 pad.buttons[6].pressed = data.triggers[0] > kDefaultButtonPressedThreshold; | |
| 550 pad.buttons[6].value = data.triggers[0]; | |
| 551 pad.buttons[7].pressed = data.triggers[1] > kDefaultButtonPressedThreshold; | |
| 552 pad.buttons[7].value = data.triggers[1]; | |
| 553 for (size_t i = 8; i < 17; i++) { | |
| 554 pad.buttons[i].pressed = data.buttons[i - 2]; | |
| 555 pad.buttons[i].value = data.buttons[i - 2] ? 1.0f : 0.0f; | |
| 556 } | |
| 557 for (size_t i = 0; i < arraysize(data.axes); i++) { | |
| 558 pad.axes[i] = data.axes[i]; | |
| 559 } | |
| 560 | |
| 561 pad.timestamp = base::TimeTicks::Now().ToInternalValue(); | |
| 562 } | |
| 563 | |
| 564 void GamepadPlatformDataFetcherMac::GetGamepadData(WebGamepads* pads, bool) { | |
| 565 if (!enabled_ && !xbox_fetcher_) { | |
| 566 pads->length = 0; | |
| 567 return; | |
| 568 } | |
| 569 | |
| 570 pads->length = WebGamepads::itemsLengthCap; | |
| 571 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) | |
| 572 MapAndSanitizeGamepadData(&pad_state_[i], &pads->items[i]); | |
| 573 } | |
| 574 | |
| 575 } // namespace content | |
| OLD | NEW |