Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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/data_fetcher_mac.h" | |
| 6 | |
| 7 #include "base/string16.h" | |
| 8 #include "base/string_util.h" | |
| 9 #include "base/utf_string_conversions.h" | |
| 10 | |
| 11 #include <IOKit/hid/IOHIDKeys.h> | |
| 12 | |
| 13 namespace content { | |
| 14 | |
| 15 namespace { | |
| 16 | |
| 17 CFMutableDictionaryRef CreateDeviceMatchingDictionary( | |
| 18 unsigned usage_page, | |
| 19 unsigned usage) { | |
| 20 CFMutableDictionaryRef dict = CFDictionaryCreateMutable( | |
| 21 kCFAllocatorDefault, | |
| 22 0, | |
| 23 &kCFTypeDictionaryKeyCallBacks, | |
| 24 &kCFTypeDictionaryValueCallBacks); | |
| 25 | |
| 26 CFNumberRef page_number_ref = CFNumberCreate( | |
| 27 kCFAllocatorDefault, | |
| 28 kCFNumberIntType, | |
| 29 &usage_page); | |
| 30 CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), page_number_ref); | |
| 31 CFRelease(page_number_ref); | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
Rather than create/release, prefer scoped_cftypere
scottmg
2011/12/07 01:20:50
Done.
| |
| 32 | |
| 33 CFNumberRef usage_number_ref = CFNumberCreate( | |
| 34 kCFAllocatorDefault, | |
| 35 kCFNumberIntType, | |
| 36 &usage); | |
| 37 CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), usage_number_ref); | |
| 38 CFRelease(usage_number_ref); | |
| 39 | |
| 40 return dict; | |
| 41 } | |
| 42 | |
| 43 float NormalizeAxis(CFIndex value, CFIndex min, CFIndex max) { | |
| 44 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f; | |
| 45 } | |
| 46 | |
| 47 // http://www.usb.org/developers/hidpage | |
| 48 const uint32_t kGenericDesktopUsagePage = 0x01; | |
| 49 const uint32_t kButtonUsagePage = 0x09; | |
| 50 const uint32_t kJoystickUsageNumber = 0x04; | |
| 51 const uint32_t kGameUsageNumber = 0x05; | |
| 52 const uint32_t kAxisMinimumUsageNumber = 0x30; | |
| 53 const uint32_t kAxisMaximumUsageNumber = 0x35; | |
| 54 | |
| 55 } // namespace | |
| 56 | |
| 57 GamepadDataFetcherMac::GamepadDataFetcherMac() : enabled_(true) { | |
| 58 hid_manager_ref_ = IOHIDManagerCreate(kCFAllocatorDefault, | |
| 59 kIOHIDOptionsTypeNone); | |
| 60 if (CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) { | |
| 61 enabled_ = false; | |
| 62 return; | |
| 63 } | |
| 64 | |
| 65 CFMutableDictionaryRef joystick_criterion = CreateDeviceMatchingDictionary( | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
Make this a scoped_cftyperef...
scottmg
2011/12/07 01:20:50
Done.
| |
| 66 kGenericDesktopUsagePage, | |
| 67 kJoystickUsageNumber); | |
| 68 CFMutableDictionaryRef game_criterion = CreateDeviceMatchingDictionary( | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
...and this...
scottmg
2011/12/07 01:20:50
Done.
| |
| 69 kGenericDesktopUsagePage, | |
| 70 kGameUsageNumber); | |
| 71 CFMutableDictionaryRef criteria_list[] = { | |
| 72 joystick_criterion, | |
| 73 game_criterion | |
| 74 }; | |
| 75 CFArrayRef criteria = CFArrayCreate( | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
...and this should be held in a scoped_cftyperef (
scottmg
2011/12/07 01:20:50
Done.
| |
| 76 kCFAllocatorDefault, | |
| 77 const_cast<const void**>( | |
| 78 reinterpret_cast<const void* const* const>(criteria_list)), | |
| 79 2, | |
| 80 NULL); | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
...and this should be kCFTypeArrayCallBacks, to al
scottmg
2011/12/07 01:20:50
Done.
| |
| 81 IOHIDManagerSetDeviceMatchingMultiple(hid_manager_ref_, criteria); | |
| 82 CFRelease(criteria); | |
| 83 | |
| 84 // Register for plug/unplug notifications | |
| 85 IOHIDManagerRegisterDeviceMatchingCallback( | |
| 86 hid_manager_ref_, | |
| 87 &DeviceAddCallback, | |
| 88 this); | |
| 89 IOHIDManagerRegisterDeviceRemovalCallback( | |
| 90 hid_manager_ref_, | |
| 91 DeviceRemoveCallback, | |
| 92 this); | |
| 93 | |
| 94 // Register for value change notifications | |
| 95 IOHIDManagerRegisterInputValueCallback( | |
| 96 hid_manager_ref_, | |
| 97 ValueChangedCallback, | |
| 98 this); | |
| 99 | |
| 100 IOHIDManagerScheduleWithRunLoop( | |
| 101 hid_manager_ref_, | |
| 102 CFRunLoopGetMain(), | |
| 103 kCFRunLoopDefaultMode); | |
| 104 | |
| 105 IOReturn ret = IOHIDManagerOpen(hid_manager_ref_, | |
| 106 kIOHIDOptionsTypeSeizeDevice); | |
| 107 if (ret != kIOReturnSuccess) { | |
| 108 enabled_ = false; | |
| 109 return; | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 GamepadDataFetcherMac::~GamepadDataFetcherMac() { | |
| 114 IOHIDManagerUnscheduleFromRunLoop( | |
| 115 hid_manager_ref_, | |
| 116 CFRunLoopGetCurrent(), | |
| 117 kCFRunLoopDefaultMode); | |
| 118 IOHIDManagerClose(hid_manager_ref_, kIOHIDOptionsTypeNone); | |
| 119 CFRelease(hid_manager_ref_); | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
Make this member variable a scoped_cftyperef and y
scottmg
2011/12/07 01:20:50
Done.
| |
| 120 } | |
| 121 | |
| 122 GamepadDataFetcherMac* GamepadDataFetcherMac::InstanceFromContext( | |
| 123 void* context) { | |
| 124 return reinterpret_cast<GamepadDataFetcherMac*>(context); | |
| 125 } | |
| 126 | |
| 127 void GamepadDataFetcherMac::DeviceAddCallback(void* context, | |
| 128 IOReturn result, | |
| 129 void* sender, | |
| 130 IOHIDDeviceRef ref) { | |
| 131 InstanceFromContext(context)->DeviceAdd(ref); | |
| 132 } | |
| 133 | |
| 134 void GamepadDataFetcherMac::DeviceRemoveCallback(void* context, | |
| 135 IOReturn result, | |
| 136 void* sender, | |
| 137 IOHIDDeviceRef ref) { | |
| 138 InstanceFromContext(context)->DeviceRemove(ref); | |
| 139 } | |
| 140 | |
| 141 void GamepadDataFetcherMac::ValueChangedCallback(void* context, | |
| 142 IOReturn result, | |
| 143 void* sender, | |
| 144 IOHIDValueRef ref) { | |
| 145 InstanceFromContext(context)->ValueChanged(ref); | |
| 146 } | |
| 147 | |
| 148 void GamepadDataFetcherMac::AddButtonsAndAxes(CFArrayRef elements, | |
| 149 unsigned slot) { | |
| 150 WebKit::WebGamepad& pad = data_.items[slot]; | |
| 151 AssociatedData& associated = associated_[slot]; | |
| 152 | |
| 153 pad.axesLength = 0; | |
| 154 pad.buttonsLength = 0; | |
| 155 memset(pad.axes, 0, sizeof(pad.axes)); | |
| 156 memset(pad.buttons, 0, sizeof(pad.buttons)); | |
| 157 | |
| 158 for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i) { | |
| 159 IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>( | |
| 160 const_cast<void*>(CFArrayGetValueAtIndex(elements, i))); | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
This screams for CFCast from foundation_util.
scottmg
2011/12/07 01:20:50
I mucked around for a bit here, but I'm not clear
| |
| 161 uint32_t usagePage = IOHIDElementGetUsagePage(element); | |
| 162 uint32_t usage = IOHIDElementGetUsage(element); | |
| 163 if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Button && | |
| 164 usagePage == kButtonUsagePage) { | |
| 165 uint32_t button_index = usage - 1; | |
| 166 if (button_index < WebKit::WebGamepad::buttonsLengthCap) { | |
| 167 associated.button_elements[button_index] = element; | |
| 168 pad.buttonsLength = std::max(pad.buttonsLength, button_index + 1); | |
| 169 } | |
| 170 } | |
| 171 else if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc) { | |
| 172 uint32_t axis_index = usage - kAxisMinimumUsageNumber; | |
| 173 if (axis_index < WebKit::WebGamepad::axesLengthCap) { | |
| 174 associated.axis_minimums[axis_index] = | |
| 175 IOHIDElementGetLogicalMin(element); | |
| 176 associated.axis_maximums[axis_index] = | |
| 177 IOHIDElementGetLogicalMax(element); | |
| 178 associated.axis_elements[axis_index] = element; | |
| 179 pad.axesLength = std::max(pad.axesLength, axis_index + 1); | |
| 180 } | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 void GamepadDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) { | |
| 186 using WebKit::WebGamepad; | |
| 187 using WebKit::WebGamepads; | |
| 188 unsigned slot; | |
| 189 | |
| 190 if (!enabled_) | |
| 191 return; | |
| 192 | |
| 193 // Find an index for this device. | |
| 194 for (slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) { | |
| 195 if (associated_[slot].device_ref == device) | |
| 196 return; | |
| 197 if (!data_.items[slot].connected) | |
| 198 break; | |
| 199 } | |
| 200 | |
| 201 // We can't handle this many connected devices. | |
| 202 if (slot == WebGamepads::itemsLengthCap) | |
| 203 return; | |
| 204 | |
| 205 CFNumberRef vendor_id_ref = reinterpret_cast<CFNumberRef>( | |
|
Avi (use Gerrit)
2011/12/06 04:54:19
CFCast? (and the two others below)
scottmg
2011/12/07 01:20:50
Done.
| |
| 206 IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey))); | |
| 207 CFNumberRef product_id_ref = reinterpret_cast<CFNumberRef>( | |
| 208 IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey))); | |
| 209 CFStringRef product_ref = reinterpret_cast<CFStringRef>( | |
| 210 IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))); | |
| 211 | |
| 212 int vendor_id, product_id; | |
| 213 char product_name[WebGamepad::idLengthCap]; | |
| 214 CFNumberGetValue(vendor_id_ref, kCFNumberIntType, &vendor_id); | |
| 215 CFNumberGetValue(product_id_ref, kCFNumberIntType, &product_id); | |
| 216 CFStringGetCString(product_ref, | |
| 217 product_name, | |
| 218 sizeof(product_name), | |
| 219 kCFStringEncodingASCII); | |
| 220 | |
| 221 char into[WebGamepad::idLengthCap]; | |
| 222 base::snprintf(into, | |
| 223 WebGamepad::idLengthCap, | |
| 224 "%s (Vendor: %04x Product: %04x)", | |
| 225 product_name, | |
| 226 vendor_id, | |
| 227 product_id); | |
| 228 string16 as16 = ASCIIToUTF16(into); | |
| 229 memset(&data_.items[slot].id, 0, sizeof(data_.items[slot].id)); | |
| 230 for (unsigned i = 0; i < as16.size() && i < WebGamepad::idLengthCap - 1; ++i) | |
| 231 data_.items[slot].id[i] = as16[i]; | |
| 232 | |
| 233 CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, | |
| 234 NULL, | |
| 235 kIOHIDOptionsTypeNone); | |
| 236 AddButtonsAndAxes(elements, slot); | |
| 237 | |
| 238 associated_[slot].device_ref = device; | |
| 239 data_.items[slot].connected = true; | |
| 240 data_.length = std::max(slot + 1, data_.length); | |
| 241 | |
| 242 } | |
| 243 | |
| 244 void GamepadDataFetcherMac::DeviceRemove(IOHIDDeviceRef device) { | |
| 245 if (!enabled_) | |
| 246 return; | |
| 247 } | |
| 248 | |
| 249 void GamepadDataFetcherMac::ValueChanged(IOHIDValueRef value) { | |
| 250 if (!enabled_) | |
| 251 return; | |
| 252 | |
| 253 IOHIDElementRef element = IOHIDValueGetElement(value); | |
| 254 IOHIDDeviceRef device = IOHIDElementGetDevice(element); | |
| 255 | |
| 256 // Find device slot | |
| 257 unsigned slot; | |
| 258 for (slot = 0; slot < data_.length; ++slot) { | |
| 259 if (data_.items[slot].connected && associated_[slot].device_ref == device) | |
| 260 break; | |
| 261 } | |
| 262 if (slot == data_.length) | |
| 263 return; | |
| 264 | |
| 265 WebKit::WebGamepad& pad = data_.items[slot]; | |
| 266 AssociatedData& associated = associated_[slot]; | |
| 267 | |
| 268 // Find and fill in the associated button event, if any. | |
| 269 for (unsigned i = 0; i < pad.buttonsLength; ++i) { | |
| 270 if (associated.button_elements[i] == element) { | |
| 271 pad.buttons[i] = IOHIDValueGetIntegerValue(value) ? 1.f : 0.f; | |
| 272 return; | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 // Find and fill in the associated axis event, if any. | |
| 277 for (unsigned i = 0; i < pad.axesLength; ++i) { | |
| 278 if (associated.axis_elements[i] == element) { | |
| 279 pad.axes[i] = NormalizeAxis(IOHIDValueGetIntegerValue(value), | |
| 280 associated.axis_minimums[i], | |
| 281 associated.axis_maximums[i]); | |
| 282 return; | |
| 283 } | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 void GamepadDataFetcherMac::GetGamepadData(WebKit::WebGamepads* pads, bool) { | |
| 288 if (!enabled_) { | |
| 289 pads->length = 0; | |
| 290 return; | |
| 291 } | |
| 292 memcpy(pads, &data_, sizeof(WebKit::WebGamepads)); | |
| 293 } | |
| 294 | |
| 295 } // namespace content | |
| OLD | NEW |