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 |