hal/cocoa/CocoaGamepad.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // mostly derived from the Allegro source code at:
michael@0 7 // http://alleg.svn.sourceforge.net/viewvc/alleg/allegro/branches/4.9/src/macosx/hidjoy.m?revision=13760&view=markup
michael@0 8
michael@0 9 #include "mozilla/dom/GamepadService.h"
michael@0 10 #include <CoreFoundation/CoreFoundation.h>
michael@0 11 #include <IOKit/hid/IOHIDBase.h>
michael@0 12 #include <IOKit/hid/IOHIDKeys.h>
michael@0 13 #include <IOKit/hid/IOHIDManager.h>
michael@0 14
michael@0 15 #include <stdio.h>
michael@0 16 #include <vector>
michael@0 17
michael@0 18 namespace {
michael@0 19
michael@0 20 using mozilla::dom::GamepadService;
michael@0 21
michael@0 22 using std::vector;
michael@0 23
michael@0 24 struct Button {
michael@0 25 int id;
michael@0 26 IOHIDElementRef element;
michael@0 27 };
michael@0 28
michael@0 29 struct Axis {
michael@0 30 int id;
michael@0 31 IOHIDElementRef element;
michael@0 32 CFIndex min;
michael@0 33 CFIndex max;
michael@0 34 };
michael@0 35
michael@0 36 // These values can be found in the USB HID Usage Tables:
michael@0 37 // http://www.usb.org/developers/hidpage
michael@0 38 #define GENERIC_DESKTOP_USAGE_PAGE 0x01
michael@0 39 #define JOYSTICK_USAGE_NUMBER 0x04
michael@0 40 #define GAMEPAD_USAGE_NUMBER 0x05
michael@0 41 #define AXIS_MIN_USAGE_NUMBER 0x30
michael@0 42 #define AXIS_MAX_USAGE_NUMBER 0x35
michael@0 43 #define BUTTON_USAGE_PAGE 0x09
michael@0 44
michael@0 45 class Gamepad {
michael@0 46 private:
michael@0 47 IOHIDDeviceRef mDevice;
michael@0 48 vector<Button> buttons;
michael@0 49 vector<Axis> axes;
michael@0 50
michael@0 51 public:
michael@0 52 Gamepad() : mDevice(nullptr), mSuperIndex(-1) {}
michael@0 53 bool operator==(IOHIDDeviceRef device) const { return mDevice == device; }
michael@0 54 bool empty() const { return mDevice == nullptr; }
michael@0 55 void clear() {
michael@0 56 mDevice = nullptr;
michael@0 57 buttons.clear();
michael@0 58 axes.clear();
michael@0 59 mSuperIndex = -1;
michael@0 60 }
michael@0 61 void init(IOHIDDeviceRef device);
michael@0 62 size_t numButtons() { return buttons.size(); }
michael@0 63 size_t numAxes() { return axes.size(); }
michael@0 64
michael@0 65 // Index given by our superclass.
michael@0 66 uint32_t mSuperIndex;
michael@0 67
michael@0 68 const Button* lookupButton(IOHIDElementRef element) const {
michael@0 69 for (size_t i = 0; i < buttons.size(); i++) {
michael@0 70 if (buttons[i].element == element)
michael@0 71 return &buttons[i];
michael@0 72 }
michael@0 73 return nullptr;
michael@0 74 }
michael@0 75
michael@0 76 const Axis* lookupAxis(IOHIDElementRef element) const {
michael@0 77 for (size_t i = 0; i < axes.size(); i++) {
michael@0 78 if (axes[i].element == element)
michael@0 79 return &axes[i];
michael@0 80 }
michael@0 81 return nullptr;
michael@0 82 }
michael@0 83 };
michael@0 84
michael@0 85 void Gamepad::init(IOHIDDeviceRef device) {
michael@0 86 clear();
michael@0 87 mDevice = device;
michael@0 88
michael@0 89 CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device,
michael@0 90 nullptr,
michael@0 91 kIOHIDOptionsTypeNone);
michael@0 92 CFIndex n = CFArrayGetCount(elements);
michael@0 93 for (CFIndex i = 0; i < n; i++) {
michael@0 94 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements,
michael@0 95 i);
michael@0 96 uint32_t usagePage = IOHIDElementGetUsagePage(element);
michael@0 97 uint32_t usage = IOHIDElementGetUsage(element);
michael@0 98
michael@0 99 if (usagePage == GENERIC_DESKTOP_USAGE_PAGE &&
michael@0 100 usage >= AXIS_MIN_USAGE_NUMBER &&
michael@0 101 usage <= AXIS_MAX_USAGE_NUMBER)
michael@0 102 {
michael@0 103 Axis axis = { int(axes.size()),
michael@0 104 element,
michael@0 105 IOHIDElementGetLogicalMin(element),
michael@0 106 IOHIDElementGetLogicalMax(element) };
michael@0 107 axes.push_back(axis);
michael@0 108 } else if (usagePage == BUTTON_USAGE_PAGE) {
michael@0 109 Button button = { int(usage) - 1, element };
michael@0 110 buttons.push_back(button);
michael@0 111 } else {
michael@0 112 //TODO: handle other usage pages
michael@0 113 }
michael@0 114 }
michael@0 115 }
michael@0 116
michael@0 117 class DarwinGamepadService {
michael@0 118 private:
michael@0 119 IOHIDManagerRef mManager;
michael@0 120 vector<Gamepad> mGamepads;
michael@0 121
michael@0 122 static void DeviceAddedCallback(void* data, IOReturn result,
michael@0 123 void* sender, IOHIDDeviceRef device);
michael@0 124 static void DeviceRemovedCallback(void* data, IOReturn result,
michael@0 125 void* sender, IOHIDDeviceRef device);
michael@0 126 static void InputValueChangedCallback(void* data, IOReturn result,
michael@0 127 void* sender, IOHIDValueRef newValue);
michael@0 128
michael@0 129 void DeviceAdded(IOHIDDeviceRef device);
michael@0 130 void DeviceRemoved(IOHIDDeviceRef device);
michael@0 131 void InputValueChanged(IOHIDValueRef value);
michael@0 132
michael@0 133 public:
michael@0 134 DarwinGamepadService();
michael@0 135 ~DarwinGamepadService();
michael@0 136 void Startup();
michael@0 137 void Shutdown();
michael@0 138 };
michael@0 139
michael@0 140 void
michael@0 141 DarwinGamepadService::DeviceAdded(IOHIDDeviceRef device)
michael@0 142 {
michael@0 143 size_t slot = size_t(-1);
michael@0 144 for (size_t i = 0; i < mGamepads.size(); i++) {
michael@0 145 if (mGamepads[i] == device)
michael@0 146 return;
michael@0 147 if (slot == size_t(-1) && mGamepads[i].empty())
michael@0 148 slot = i;
michael@0 149 }
michael@0 150
michael@0 151 if (slot == size_t(-1)) {
michael@0 152 slot = mGamepads.size();
michael@0 153 mGamepads.push_back(Gamepad());
michael@0 154 }
michael@0 155 mGamepads[slot].init(device);
michael@0 156
michael@0 157 // Gather some identifying information
michael@0 158 CFNumberRef vendorIdRef =
michael@0 159 (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
michael@0 160 CFNumberRef productIdRef =
michael@0 161 (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
michael@0 162 CFStringRef productRef =
michael@0 163 (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
michael@0 164 int vendorId, productId;
michael@0 165 CFNumberGetValue(vendorIdRef, kCFNumberIntType, &vendorId);
michael@0 166 CFNumberGetValue(productIdRef, kCFNumberIntType, &productId);
michael@0 167 char product_name[128];
michael@0 168 CFStringGetCString(productRef, product_name,
michael@0 169 sizeof(product_name), kCFStringEncodingASCII);
michael@0 170 char buffer[256];
michael@0 171 sprintf(buffer, "%x-%x-%s", vendorId, productId, product_name);
michael@0 172 nsRefPtr<GamepadService> service(GamepadService::GetService());
michael@0 173 mGamepads[slot].mSuperIndex = service->AddGamepad(buffer,
michael@0 174 mozilla::dom::NoMapping,
michael@0 175 (int)mGamepads[slot].numButtons(),
michael@0 176 (int)mGamepads[slot].numAxes());
michael@0 177 }
michael@0 178
michael@0 179 void
michael@0 180 DarwinGamepadService::DeviceRemoved(IOHIDDeviceRef device)
michael@0 181 {
michael@0 182 nsRefPtr<GamepadService> service(GamepadService::GetService());
michael@0 183 for (size_t i = 0; i < mGamepads.size(); i++) {
michael@0 184 if (mGamepads[i] == device) {
michael@0 185 service->RemoveGamepad(mGamepads[i].mSuperIndex);
michael@0 186 mGamepads[i].clear();
michael@0 187 return;
michael@0 188 }
michael@0 189 }
michael@0 190 }
michael@0 191
michael@0 192 void
michael@0 193 DarwinGamepadService::InputValueChanged(IOHIDValueRef value)
michael@0 194 {
michael@0 195 nsRefPtr<GamepadService> service(GamepadService::GetService());
michael@0 196 IOHIDElementRef element = IOHIDValueGetElement(value);
michael@0 197 IOHIDDeviceRef device = IOHIDElementGetDevice(element);
michael@0 198 for (size_t i = 0; i < mGamepads.size(); i++) {
michael@0 199 const Gamepad &gamepad = mGamepads[i];
michael@0 200 if (gamepad == device) {
michael@0 201 if (const Axis* axis = gamepad.lookupAxis(element)) {
michael@0 202 double d = IOHIDValueGetIntegerValue(value);
michael@0 203 double v = 2.0f * (d - axis->min) /
michael@0 204 (double)(axis->max - axis->min) - 1.0f;
michael@0 205 service->NewAxisMoveEvent(i, axis->id, v);
michael@0 206 } else if (const Button* button = gamepad.lookupButton(element)) {
michael@0 207 bool pressed = IOHIDValueGetIntegerValue(value) != 0;
michael@0 208 service->NewButtonEvent(i, button->id, pressed);
michael@0 209 }
michael@0 210 return;
michael@0 211 }
michael@0 212 }
michael@0 213 }
michael@0 214
michael@0 215 void
michael@0 216 DarwinGamepadService::DeviceAddedCallback(void* data, IOReturn result,
michael@0 217 void* sender, IOHIDDeviceRef device)
michael@0 218 {
michael@0 219 DarwinGamepadService* service = (DarwinGamepadService*)data;
michael@0 220 service->DeviceAdded(device);
michael@0 221 }
michael@0 222
michael@0 223 void
michael@0 224 DarwinGamepadService::DeviceRemovedCallback(void* data, IOReturn result,
michael@0 225 void* sender, IOHIDDeviceRef device)
michael@0 226 {
michael@0 227 DarwinGamepadService* service = (DarwinGamepadService*)data;
michael@0 228 service->DeviceRemoved(device);
michael@0 229 }
michael@0 230
michael@0 231 void
michael@0 232 DarwinGamepadService::InputValueChangedCallback(void* data,
michael@0 233 IOReturn result,
michael@0 234 void* sender,
michael@0 235 IOHIDValueRef newValue)
michael@0 236 {
michael@0 237 DarwinGamepadService* service = (DarwinGamepadService*)data;
michael@0 238 service->InputValueChanged(newValue);
michael@0 239 }
michael@0 240
michael@0 241 static CFMutableDictionaryRef
michael@0 242 MatchingDictionary(UInt32 inUsagePage, UInt32 inUsage)
michael@0 243 {
michael@0 244 CFMutableDictionaryRef dict =
michael@0 245 CFDictionaryCreateMutable(kCFAllocatorDefault,
michael@0 246 0,
michael@0 247 &kCFTypeDictionaryKeyCallBacks,
michael@0 248 &kCFTypeDictionaryValueCallBacks);
michael@0 249 if (!dict)
michael@0 250 return nullptr;
michael@0 251 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault,
michael@0 252 kCFNumberIntType,
michael@0 253 &inUsagePage);
michael@0 254 if (!number) {
michael@0 255 CFRelease(dict);
michael@0 256 return nullptr;
michael@0 257 }
michael@0 258 CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
michael@0 259 CFRelease(number);
michael@0 260
michael@0 261 number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
michael@0 262 if (!number) {
michael@0 263 CFRelease(dict);
michael@0 264 return nullptr;
michael@0 265 }
michael@0 266 CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
michael@0 267 CFRelease(number);
michael@0 268
michael@0 269 return dict;
michael@0 270 }
michael@0 271
michael@0 272 DarwinGamepadService::DarwinGamepadService() : mManager(nullptr) {}
michael@0 273
michael@0 274 DarwinGamepadService::~DarwinGamepadService()
michael@0 275 {
michael@0 276 if (mManager != nullptr)
michael@0 277 CFRelease(mManager);
michael@0 278 }
michael@0 279
michael@0 280 void DarwinGamepadService::Startup()
michael@0 281 {
michael@0 282 if (mManager != nullptr)
michael@0 283 return;
michael@0 284
michael@0 285 IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault,
michael@0 286 kIOHIDOptionsTypeNone);
michael@0 287
michael@0 288 CFMutableDictionaryRef criteria_arr[2];
michael@0 289 criteria_arr[0] = MatchingDictionary(GENERIC_DESKTOP_USAGE_PAGE,
michael@0 290 JOYSTICK_USAGE_NUMBER);
michael@0 291 if (!criteria_arr[0]) {
michael@0 292 CFRelease(manager);
michael@0 293 return;
michael@0 294 }
michael@0 295
michael@0 296 criteria_arr[1] = MatchingDictionary(GENERIC_DESKTOP_USAGE_PAGE,
michael@0 297 GAMEPAD_USAGE_NUMBER);
michael@0 298 if (!criteria_arr[1]) {
michael@0 299 CFRelease(criteria_arr[0]);
michael@0 300 CFRelease(manager);
michael@0 301 return;
michael@0 302 }
michael@0 303
michael@0 304 CFArrayRef criteria =
michael@0 305 CFArrayCreate(kCFAllocatorDefault, (const void**)criteria_arr, 2, nullptr);
michael@0 306 if (!criteria) {
michael@0 307 CFRelease(criteria_arr[1]);
michael@0 308 CFRelease(criteria_arr[0]);
michael@0 309 CFRelease(manager);
michael@0 310 return;
michael@0 311 }
michael@0 312
michael@0 313 IOHIDManagerSetDeviceMatchingMultiple(manager, criteria);
michael@0 314 CFRelease(criteria);
michael@0 315 CFRelease(criteria_arr[1]);
michael@0 316 CFRelease(criteria_arr[0]);
michael@0 317
michael@0 318 IOHIDManagerRegisterDeviceMatchingCallback(manager,
michael@0 319 DeviceAddedCallback,
michael@0 320 this);
michael@0 321 IOHIDManagerRegisterDeviceRemovalCallback(manager,
michael@0 322 DeviceRemovedCallback,
michael@0 323 this);
michael@0 324 IOHIDManagerRegisterInputValueCallback(manager,
michael@0 325 InputValueChangedCallback,
michael@0 326 this);
michael@0 327 IOHIDManagerScheduleWithRunLoop(manager,
michael@0 328 CFRunLoopGetCurrent(),
michael@0 329 kCFRunLoopDefaultMode);
michael@0 330 IOReturn rv = IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
michael@0 331 if (rv != kIOReturnSuccess) {
michael@0 332 CFRelease(manager);
michael@0 333 return;
michael@0 334 }
michael@0 335
michael@0 336 mManager = manager;
michael@0 337 }
michael@0 338
michael@0 339 void DarwinGamepadService::Shutdown()
michael@0 340 {
michael@0 341 IOHIDManagerRef manager = (IOHIDManagerRef)mManager;
michael@0 342 if (manager) {
michael@0 343 IOHIDManagerClose(manager, 0);
michael@0 344 CFRelease(manager);
michael@0 345 mManager = nullptr;
michael@0 346 }
michael@0 347 }
michael@0 348
michael@0 349 } // namespace
michael@0 350
michael@0 351 namespace mozilla {
michael@0 352 namespace hal_impl {
michael@0 353
michael@0 354 DarwinGamepadService* gService = nullptr;
michael@0 355
michael@0 356 void StartMonitoringGamepadStatus()
michael@0 357 {
michael@0 358 if (gService)
michael@0 359 return;
michael@0 360
michael@0 361 gService = new DarwinGamepadService();
michael@0 362 gService->Startup();
michael@0 363 }
michael@0 364
michael@0 365 void StopMonitoringGamepadStatus()
michael@0 366 {
michael@0 367 if (!gService)
michael@0 368 return;
michael@0 369
michael@0 370 gService->Shutdown();
michael@0 371 delete gService;
michael@0 372 gService = nullptr;
michael@0 373 }
michael@0 374
michael@0 375 } // namespace hal_impl
michael@0 376 } // namespace mozilla

mercurial