summaryrefslogtreecommitdiff
path: root/chromium/content/browser/gamepad
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/content/browser/gamepad
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/content/browser/gamepad')
-rw-r--r--chromium/content/browser/gamepad/OWNERS1
-rw-r--r--chromium/content/browser/gamepad/gamepad_data_fetcher.h26
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher.cc19
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h55
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc257
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.h56
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h106
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm441
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc498
-rw-r--r--chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h100
-rw-r--r--chromium/content/browser/gamepad/gamepad_provider.cc220
-rw-r--r--chromium/content/browser/gamepad/gamepad_provider.h130
-rw-r--r--chromium/content/browser/gamepad/gamepad_provider_unittest.cc158
-rw-r--r--chromium/content/browser/gamepad/gamepad_service.cc69
-rw-r--r--chromium/content/browser/gamepad/gamepad_service.h77
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings.h61
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc164
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm219
-rw-r--r--chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc124
-rw-r--r--chromium/content/browser/gamepad/gamepad_test_helpers.cc55
-rw-r--r--chromium/content/browser/gamepad/gamepad_test_helpers.h84
-rw-r--r--chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc625
-rw-r--r--chromium/content/browser/gamepad/xbox_data_fetcher_mac.h167
23 files changed, 3712 insertions, 0 deletions
diff --git a/chromium/content/browser/gamepad/OWNERS b/chromium/content/browser/gamepad/OWNERS
new file mode 100644
index 00000000000..3f809e82b19
--- /dev/null
+++ b/chromium/content/browser/gamepad/OWNERS
@@ -0,0 +1 @@
+scottmg@chromium.org
diff --git a/chromium/content/browser/gamepad/gamepad_data_fetcher.h b/chromium/content/browser/gamepad/gamepad_data_fetcher.h
new file mode 100644
index 00000000000..e5e009dc42b
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_data_fetcher.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_DATA_FETCHER_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_DATA_FETCHER_H_
+
+namespace WebKit {
+class WebGamepads;
+}
+
+namespace content {
+
+// Abstract interface for imlementing platform- (and test-) specific behaviro
+// for getting the gamepad data.
+class GamepadDataFetcher {
+ public:
+ virtual ~GamepadDataFetcher() {}
+ virtual void GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) = 0;
+ virtual void PauseHint(bool paused) {}
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_DATA_FETCHER_H_
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.cc b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.cc
new file mode 100644
index 00000000000..b4499d6fd41
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
+
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+namespace content {
+
+GamepadDataFetcherEmpty::GamepadDataFetcherEmpty() {
+}
+
+void GamepadDataFetcherEmpty::GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) {
+ pads->length = 0;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h
new file mode 100644
index 00000000000..f073a538d6f
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Define the default data fetcher that GamepadProvider will use if none is
+// supplied. (GamepadPlatformDataFetcher).
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+
+#if defined(OS_WIN)
+#include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h"
+#elif defined(OS_MACOSX)
+#include "content/browser/gamepad/gamepad_platform_data_fetcher_mac.h"
+#elif defined(OS_LINUX)
+#include "content/browser/gamepad/gamepad_platform_data_fetcher_linux.h"
+#endif
+
+namespace content {
+
+#if defined(OS_WIN)
+
+typedef GamepadPlatformDataFetcherWin GamepadPlatformDataFetcher;
+
+#elif defined(OS_MACOSX)
+
+typedef GamepadPlatformDataFetcherMac GamepadPlatformDataFetcher;
+
+#elif defined(OS_LINUX)
+
+typedef GamepadPlatformDataFetcherLinux GamepadPlatformDataFetcher;
+
+#else
+
+class GamepadDataFetcherEmpty : public GamepadDataFetcher {
+ public:
+ GamepadDataFetcherEmpty();
+
+ virtual void GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GamepadDataFetcherEmpty);
+};
+typedef GamepadDataFetcherEmpty GamepadPlatformDataFetcher;
+
+#endif
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_H_
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc
new file mode 100644
index 00000000000..c82997e22a1
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.cc
@@ -0,0 +1,257 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_platform_data_fetcher_linux.h"
+
+#include <fcntl.h>
+#include <libudev.h>
+#include <linux/joystick.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/debug/trace_event.h"
+#include "base/message_loop/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/udev_linux.h"
+
+namespace {
+
+const char kInputSubsystem[] = "input";
+const char kUsbSubsystem[] = "usb";
+const char kUsbDeviceType[] = "usb_device";
+const float kMaxLinuxAxisValue = 32767.0;
+
+void CloseFileDescriptorIfValid(int fd) {
+ if (fd >= 0)
+ close(fd);
+}
+
+bool IsGamepad(udev_device* dev, int* index, std::string* path) {
+ if (!udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"))
+ return false;
+
+ const char* node_path = udev_device_get_devnode(dev);
+ if (!node_path)
+ return false;
+
+ static const char kJoystickRoot[] = "/dev/input/js";
+ bool is_gamepad = StartsWithASCII(node_path, kJoystickRoot, true);
+ if (!is_gamepad)
+ return false;
+
+ int tmp_idx = -1;
+ const int base_len = sizeof(kJoystickRoot) - 1;
+ base::StringPiece str(&node_path[base_len], strlen(node_path) - base_len);
+ if (!base::StringToInt(str, &tmp_idx))
+ return false;
+ if (tmp_idx < 0 ||
+ tmp_idx >= static_cast<int>(WebKit::WebGamepads::itemsLengthCap)) {
+ return false;
+ }
+ *index = tmp_idx;
+ *path = node_path;
+ return true;
+}
+
+} // namespace
+
+namespace content {
+
+using WebKit::WebGamepad;
+using WebKit::WebGamepads;
+
+GamepadPlatformDataFetcherLinux::GamepadPlatformDataFetcherLinux() {
+ for (size_t i = 0; i < arraysize(device_fds_); ++i)
+ device_fds_[i] = -1;
+ memset(mappers_, 0, sizeof(mappers_));
+
+ std::vector<UdevLinux::UdevMonitorFilter> filters;
+ filters.push_back(UdevLinux::UdevMonitorFilter(kInputSubsystem, NULL));
+ udev_.reset(
+ new UdevLinux(filters,
+ base::Bind(&GamepadPlatformDataFetcherLinux::RefreshDevice,
+ base::Unretained(this))));
+
+ EnumerateDevices();
+}
+
+GamepadPlatformDataFetcherLinux::~GamepadPlatformDataFetcherLinux() {
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i)
+ CloseFileDescriptorIfValid(device_fds_[i]);
+}
+
+void GamepadPlatformDataFetcherLinux::GetGamepadData(WebGamepads* pads, bool) {
+ TRACE_EVENT0("GAMEPAD", "GetGamepadData");
+
+ data_.length = WebGamepads::itemsLengthCap;
+
+ // Update our internal state.
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (device_fds_[i] >= 0) {
+ ReadDeviceData(i);
+ }
+ }
+
+ // Copy to the current state to the output buffer, using the mapping
+ // function, if there is one available.
+ pads->length = data_.length;
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (mappers_[i])
+ mappers_[i](data_.items[i], &pads->items[i]);
+ else
+ pads->items[i] = data_.items[i];
+ }
+}
+
+// Used during enumeration, and monitor notifications.
+void GamepadPlatformDataFetcherLinux::RefreshDevice(udev_device* dev) {
+ int index;
+ std::string node_path;
+ if (IsGamepad(dev, &index, &node_path)) {
+ int& device_fd = device_fds_[index];
+ WebGamepad& pad = data_.items[index];
+ GamepadStandardMappingFunction& mapper = mappers_[index];
+
+ CloseFileDescriptorIfValid(device_fd);
+
+ // The device pointed to by dev contains information about the logical
+ // joystick device. In order to get the information about the physical
+ // hardware, get the parent device that is also in the "input" subsystem.
+ // This function should just walk up the tree one level.
+ dev = udev_device_get_parent_with_subsystem_devtype(
+ dev,
+ kInputSubsystem,
+ NULL);
+ if (!dev) {
+ // Unable to get device information, don't use this device.
+ device_fd = -1;
+ pad.connected = false;
+ return;
+ }
+
+ device_fd = HANDLE_EINTR(open(node_path.c_str(), O_RDONLY | O_NONBLOCK));
+ if (device_fd < 0) {
+ // Unable to open device, don't use.
+ pad.connected = false;
+ return;
+ }
+
+ const char* vendor_id = udev_device_get_sysattr_value(dev, "id/vendor");
+ const char* product_id = udev_device_get_sysattr_value(dev, "id/product");
+ mapper = GetGamepadStandardMappingFunction(vendor_id, product_id);
+
+ // Driver returns utf-8 strings here, so combine in utf-8 first and
+ // convert to WebUChar later once we've picked an id string.
+ const char* name = udev_device_get_sysattr_value(dev, "name");
+ std::string name_string = base::StringPrintf("%s", name);
+
+ // In many cases the information the input subsystem contains isn't
+ // as good as the information that the device bus has, walk up further
+ // to the subsystem/device type "usb"/"usb_device" and if this device
+ // has the same vendor/product id, prefer the description from that.
+ struct udev_device *usb_dev = udev_device_get_parent_with_subsystem_devtype(
+ dev,
+ kUsbSubsystem,
+ kUsbDeviceType);
+ if (usb_dev) {
+ const char* usb_vendor_id =
+ udev_device_get_sysattr_value(usb_dev, "idVendor");
+ const char* usb_product_id =
+ udev_device_get_sysattr_value(usb_dev, "idProduct");
+
+ if (strcmp(vendor_id, usb_vendor_id) == 0 &&
+ strcmp(product_id, usb_product_id) == 0) {
+ const char* manufacturer =
+ udev_device_get_sysattr_value(usb_dev, "manufacturer");
+ const char* product = udev_device_get_sysattr_value(usb_dev, "product");
+
+ // Replace the previous name string with one containing the better
+ // information, again driver returns utf-8 strings here so combine
+ // in utf-8 for conversion to WebUChar below.
+ name_string = base::StringPrintf("%s %s", manufacturer, product);
+ }
+ }
+
+ // Append the vendor and product information then convert the utf-8
+ // id string to WebUChar.
+ std::string id = name_string + base::StringPrintf(
+ " (%sVendor: %s Product: %s)",
+ mapper ? "STANDARD GAMEPAD " : "",
+ vendor_id,
+ product_id);
+ TruncateUTF8ToByteSize(id, WebGamepad::idLengthCap - 1, &id);
+ string16 tmp16 = UTF8ToUTF16(id);
+ memset(pad.id, 0, sizeof(pad.id));
+ tmp16.copy(pad.id, arraysize(pad.id) - 1);
+
+ pad.connected = true;
+ }
+}
+
+void GamepadPlatformDataFetcherLinux::EnumerateDevices() {
+ udev_enumerate* enumerate = udev_enumerate_new(udev_->udev_handle());
+ if (!enumerate)
+ return;
+ int ret = udev_enumerate_add_match_subsystem(enumerate, kInputSubsystem);
+ if (ret != 0)
+ return;
+ ret = udev_enumerate_scan_devices(enumerate);
+ if (ret != 0)
+ return;
+
+ udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
+ for (udev_list_entry* dev_list_entry = devices;
+ dev_list_entry != NULL;
+ dev_list_entry = udev_list_entry_get_next(dev_list_entry)) {
+ // Get the filename of the /sys entry for the device and create a
+ // udev_device object (dev) representing it
+ const char* path = udev_list_entry_get_name(dev_list_entry);
+ udev_device* dev = udev_device_new_from_syspath(udev_->udev_handle(), path);
+ if (!dev)
+ continue;
+ RefreshDevice(dev);
+ udev_device_unref(dev);
+ }
+ // Free the enumerator object
+ udev_enumerate_unref(enumerate);
+}
+
+void GamepadPlatformDataFetcherLinux::ReadDeviceData(size_t index) {
+ // Linker does not like CHECK_LT(index, WebGamepads::itemsLengthCap). =/
+ if (index >= WebGamepads::itemsLengthCap) {
+ CHECK(false);
+ return;
+ }
+
+ const int& fd = device_fds_[index];
+ WebGamepad& pad = data_.items[index];
+ DCHECK_GE(fd, 0);
+
+ js_event event;
+ while (HANDLE_EINTR(read(fd, &event, sizeof(struct js_event))) > 0) {
+ size_t item = event.number;
+ if (event.type & JS_EVENT_AXIS) {
+ if (item >= WebGamepad::axesLengthCap)
+ continue;
+ pad.axes[item] = event.value / kMaxLinuxAxisValue;
+ if (item >= pad.axesLength)
+ pad.axesLength = item + 1;
+ } else if (event.type & JS_EVENT_BUTTON) {
+ if (item >= WebGamepad::buttonsLengthCap)
+ continue;
+ pad.buttons[item] = event.value ? 1.0 : 0.0;
+ if (item >= pad.buttonsLength)
+ pad.buttonsLength = item + 1;
+ }
+ pad.timestamp = event.time;
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.h
new file mode 100644
index 00000000000..916eda311f4
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_linux.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+extern "C" {
+struct udev_device;
+}
+
+namespace content {
+
+class UdevLinux;
+
+class GamepadPlatformDataFetcherLinux : public GamepadDataFetcher {
+ public:
+ GamepadPlatformDataFetcherLinux();
+ virtual ~GamepadPlatformDataFetcherLinux();
+
+ // GamepadDataFetcher implementation.
+ virtual void GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) OVERRIDE;
+
+ private:
+ void RefreshDevice(udev_device* dev);
+ void EnumerateDevices();
+ void ReadDeviceData(size_t index);
+
+ // File descriptors for the /dev/input/js* devices. -1 if not in use.
+ int device_fds_[WebKit::WebGamepads::itemsLengthCap];
+
+ // Functions to map from device data to standard layout, if available. May
+ // be null if no mapping is available.
+ GamepadStandardMappingFunction mappers_[WebKit::WebGamepads::itemsLengthCap];
+
+ // Data that's returned to the consumer.
+ WebKit::WebGamepads data_;
+
+ scoped_ptr<UdevLinux> udev_;
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadPlatformDataFetcherLinux);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_LINUX_H_
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h
new file mode 100644
index 00000000000..3b22b69350c
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+#include "content/browser/gamepad/xbox_data_fetcher_mac.h"
+#include "content/common/gamepad_hardware_buffer.h"
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+#if defined(__OBJC__)
+@class NSArray;
+#else
+class NSArray;
+#endif
+
+namespace content {
+
+class GamepadPlatformDataFetcherMac : public GamepadDataFetcher,
+ public XboxDataFetcher::Delegate {
+ public:
+ GamepadPlatformDataFetcherMac();
+ virtual ~GamepadPlatformDataFetcherMac();
+ virtual void GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) OVERRIDE;
+ virtual void PauseHint(bool paused) OVERRIDE;
+
+ private:
+ bool enabled_;
+ base::ScopedCFTypeRef<IOHIDManagerRef> hid_manager_ref_;
+
+ static GamepadPlatformDataFetcherMac* InstanceFromContext(void* context);
+ static void DeviceAddCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref);
+ static void DeviceRemoveCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref);
+ static void ValueChangedCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDValueRef ref);
+
+ size_t GetEmptySlot();
+ size_t GetSlotForDevice(IOHIDDeviceRef device);
+ size_t GetSlotForXboxDevice(XboxController* device);
+
+ void DeviceAdd(IOHIDDeviceRef device);
+ void AddButtonsAndAxes(NSArray* elements, size_t slot);
+ void DeviceRemove(IOHIDDeviceRef device);
+ void ValueChanged(IOHIDValueRef value);
+
+ virtual void XboxDeviceAdd(XboxController* device) OVERRIDE;
+ virtual void XboxDeviceRemove(XboxController* device) OVERRIDE;
+ virtual void XboxValueChanged(XboxController* device,
+ const XboxController::Data& data) OVERRIDE;
+
+ void RegisterForNotifications();
+ void UnregisterFromNotifications();
+
+ scoped_ptr<XboxDataFetcher> xbox_fetcher_;
+
+ WebKit::WebGamepads data_;
+
+ // Side-band data that's not passed to the consumer, but we need to maintain
+ // to update data_.
+ struct AssociatedData {
+ bool is_xbox;
+ union {
+ struct {
+ IOHIDDeviceRef device_ref;
+ IOHIDElementRef button_elements[WebKit::WebGamepad::buttonsLengthCap];
+ IOHIDElementRef axis_elements[WebKit::WebGamepad::buttonsLengthCap];
+ CFIndex axis_minimums[WebKit::WebGamepad::axesLengthCap];
+ CFIndex axis_maximums[WebKit::WebGamepad::axesLengthCap];
+ // Function to map from device data to standard layout, if available.
+ // May be null if no mapping is available.
+ GamepadStandardMappingFunction mapper;
+ } hid;
+ struct {
+ XboxController* device;
+ UInt32 location_id;
+ } xbox;
+ };
+ };
+ AssociatedData associated_[WebKit::WebGamepads::itemsLengthCap];
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadPlatformDataFetcherMac);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_MAC_H_
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm
new file mode 100644
index 00000000000..51524d7f523
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_mac.mm
@@ -0,0 +1,441 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_platform_data_fetcher_mac.h"
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+
+#import <Foundation/Foundation.h>
+#include <IOKit/hid/IOHIDKeys.h>
+
+using WebKit::WebGamepad;
+using WebKit::WebGamepads;
+
+namespace content {
+
+namespace {
+
+NSDictionary* DeviceMatching(uint32_t usage_page, uint32_t usage) {
+ return [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithUnsignedInt:usage_page],
+ base::mac::CFToNSCast(CFSTR(kIOHIDDeviceUsagePageKey)),
+ [NSNumber numberWithUnsignedInt:usage],
+ base::mac::CFToNSCast(CFSTR(kIOHIDDeviceUsageKey)),
+ nil];
+}
+
+float NormalizeAxis(CFIndex value, CFIndex min, CFIndex max) {
+ return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f;
+}
+
+// http://www.usb.org/developers/hidpage
+const uint32_t kGenericDesktopUsagePage = 0x01;
+const uint32_t kButtonUsagePage = 0x09;
+const uint32_t kJoystickUsageNumber = 0x04;
+const uint32_t kGameUsageNumber = 0x05;
+const uint32_t kMultiAxisUsageNumber = 0x08;
+const uint32_t kAxisMinimumUsageNumber = 0x30;
+const uint32_t kAxisMaximumUsageNumber = 0x35;
+
+} // namespace
+
+GamepadPlatformDataFetcherMac::GamepadPlatformDataFetcherMac()
+ : enabled_(true) {
+ memset(associated_, 0, sizeof(associated_));
+
+ xbox_fetcher_.reset(new XboxDataFetcher(this));
+ if (!xbox_fetcher_->RegisterForNotifications())
+ xbox_fetcher_.reset();
+
+ hid_manager_ref_.reset(IOHIDManagerCreate(kCFAllocatorDefault,
+ kIOHIDOptionsTypeNone));
+ if (CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) {
+ enabled_ = false;
+ return;
+ }
+
+ base::scoped_nsobject<NSArray> criteria([[NSArray alloc] initWithObjects:
+ DeviceMatching(kGenericDesktopUsagePage, kJoystickUsageNumber),
+ DeviceMatching(kGenericDesktopUsagePage, kGameUsageNumber),
+ DeviceMatching(kGenericDesktopUsagePage, kMultiAxisUsageNumber),
+ nil]);
+ IOHIDManagerSetDeviceMatchingMultiple(
+ hid_manager_ref_,
+ base::mac::NSToCFCast(criteria));
+
+ RegisterForNotifications();
+}
+
+void GamepadPlatformDataFetcherMac::RegisterForNotifications() {
+ // Register for plug/unplug notifications.
+ IOHIDManagerRegisterDeviceMatchingCallback(
+ hid_manager_ref_,
+ &DeviceAddCallback,
+ this);
+ IOHIDManagerRegisterDeviceRemovalCallback(
+ hid_manager_ref_,
+ DeviceRemoveCallback,
+ this);
+
+ // Register for value change notifications.
+ IOHIDManagerRegisterInputValueCallback(
+ hid_manager_ref_,
+ ValueChangedCallback,
+ this);
+
+ IOHIDManagerScheduleWithRunLoop(
+ hid_manager_ref_,
+ CFRunLoopGetMain(),
+ kCFRunLoopDefaultMode);
+
+ enabled_ = IOHIDManagerOpen(hid_manager_ref_,
+ kIOHIDOptionsTypeNone) == kIOReturnSuccess;
+
+ if (xbox_fetcher_)
+ xbox_fetcher_->RegisterForNotifications();
+}
+
+void GamepadPlatformDataFetcherMac::UnregisterFromNotifications() {
+ IOHIDManagerUnscheduleFromRunLoop(
+ hid_manager_ref_,
+ CFRunLoopGetCurrent(),
+ kCFRunLoopDefaultMode);
+ IOHIDManagerClose(hid_manager_ref_, kIOHIDOptionsTypeNone);
+ if (xbox_fetcher_)
+ xbox_fetcher_->UnregisterFromNotifications();
+}
+
+void GamepadPlatformDataFetcherMac::PauseHint(bool pause) {
+ if (pause)
+ UnregisterFromNotifications();
+ else
+ RegisterForNotifications();
+}
+
+GamepadPlatformDataFetcherMac::~GamepadPlatformDataFetcherMac() {
+ UnregisterFromNotifications();
+}
+
+GamepadPlatformDataFetcherMac*
+GamepadPlatformDataFetcherMac::InstanceFromContext(void* context) {
+ return reinterpret_cast<GamepadPlatformDataFetcherMac*>(context);
+}
+
+void GamepadPlatformDataFetcherMac::DeviceAddCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref) {
+ InstanceFromContext(context)->DeviceAdd(ref);
+}
+
+void GamepadPlatformDataFetcherMac::DeviceRemoveCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDDeviceRef ref) {
+ InstanceFromContext(context)->DeviceRemove(ref);
+}
+
+void GamepadPlatformDataFetcherMac::ValueChangedCallback(void* context,
+ IOReturn result,
+ void* sender,
+ IOHIDValueRef ref) {
+ InstanceFromContext(context)->ValueChanged(ref);
+}
+
+void GamepadPlatformDataFetcherMac::AddButtonsAndAxes(NSArray* elements,
+ size_t slot) {
+ WebGamepad& pad = data_.items[slot];
+ AssociatedData& associated = associated_[slot];
+ CHECK(!associated.is_xbox);
+
+ pad.axesLength = 0;
+ pad.buttonsLength = 0;
+ pad.timestamp = 0;
+ memset(pad.axes, 0, sizeof(pad.axes));
+ memset(pad.buttons, 0, sizeof(pad.buttons));
+
+ for (id elem in elements) {
+ IOHIDElementRef element = reinterpret_cast<IOHIDElementRef>(elem);
+ uint32_t usagePage = IOHIDElementGetUsagePage(element);
+ uint32_t usage = IOHIDElementGetUsage(element);
+ if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Button &&
+ usagePage == kButtonUsagePage) {
+ uint32_t button_index = usage - 1;
+ if (button_index < WebGamepad::buttonsLengthCap) {
+ associated.hid.button_elements[button_index] = element;
+ pad.buttonsLength = std::max(pad.buttonsLength, button_index + 1);
+ }
+ }
+ else if (IOHIDElementGetType(element) == kIOHIDElementTypeInput_Misc) {
+ uint32_t axis_index = usage - kAxisMinimumUsageNumber;
+ if (axis_index < WebGamepad::axesLengthCap) {
+ associated.hid.axis_minimums[axis_index] =
+ IOHIDElementGetLogicalMin(element);
+ associated.hid.axis_maximums[axis_index] =
+ IOHIDElementGetLogicalMax(element);
+ associated.hid.axis_elements[axis_index] = element;
+ pad.axesLength = std::max(pad.axesLength, axis_index + 1);
+ }
+ }
+ }
+}
+
+size_t GamepadPlatformDataFetcherMac::GetEmptySlot() {
+ // Find a free slot for this device.
+ for (size_t slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) {
+ if (!data_.items[slot].connected)
+ return slot;
+ }
+ return WebGamepads::itemsLengthCap;
+}
+
+size_t GamepadPlatformDataFetcherMac::GetSlotForDevice(IOHIDDeviceRef device) {
+ for (size_t slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) {
+ // If we already have this device, and it's already connected, don't do
+ // anything now.
+ if (data_.items[slot].connected &&
+ !associated_[slot].is_xbox &&
+ associated_[slot].hid.device_ref == device)
+ return WebGamepads::itemsLengthCap;
+ }
+ return GetEmptySlot();
+}
+
+size_t GamepadPlatformDataFetcherMac::GetSlotForXboxDevice(
+ XboxController* device) {
+ for (size_t slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) {
+ if (associated_[slot].is_xbox &&
+ associated_[slot].xbox.location_id == device->location_id()) {
+ if (data_.items[slot].connected) {
+ // The device is already connected. No idea why we got a second "device
+ // added" call, but let's not add it twice.
+ DCHECK_EQ(associated_[slot].xbox.device, device);
+ return WebGamepads::itemsLengthCap;
+ } else {
+ // A device with the same location ID was previously connected, so put
+ // it in the same slot.
+ return slot;
+ }
+ }
+ }
+ return GetEmptySlot();
+}
+
+void GamepadPlatformDataFetcherMac::DeviceAdd(IOHIDDeviceRef device) {
+ using base::mac::CFToNSCast;
+ using base::mac::CFCastStrict;
+
+ if (!enabled_)
+ return;
+
+ // Find an index for this device.
+ size_t slot = GetSlotForDevice(device);
+
+ // We can't handle this many connected devices.
+ if (slot == WebGamepads::itemsLengthCap)
+ return;
+
+ NSNumber* vendor_id = CFToNSCast(CFCastStrict<CFNumberRef>(
+ IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey))));
+ NSNumber* product_id = CFToNSCast(CFCastStrict<CFNumberRef>(
+ IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey))));
+ NSString* product = CFToNSCast(CFCastStrict<CFStringRef>(
+ IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey))));
+ int vendor_int = [vendor_id intValue];
+ int product_int = [product_id intValue];
+
+ char vendor_as_str[5], product_as_str[5];
+ snprintf(vendor_as_str, sizeof(vendor_as_str), "%04x", vendor_int);
+ snprintf(product_as_str, sizeof(product_as_str), "%04x", product_int);
+ associated_[slot].hid.mapper =
+ GetGamepadStandardMappingFunction(vendor_as_str, product_as_str);
+
+ NSString* ident = [NSString stringWithFormat:
+ @"%@ (%sVendor: %04x Product: %04x)",
+ product,
+ associated_[slot].hid.mapper ? "STANDARD GAMEPAD " : "",
+ vendor_int,
+ product_int];
+ NSData* as16 = [ident dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
+
+ const size_t kOutputLengthBytes = sizeof(data_.items[slot].id);
+ memset(&data_.items[slot].id, 0, kOutputLengthBytes);
+ [as16 getBytes:data_.items[slot].id
+ length:kOutputLengthBytes - sizeof(WebKit::WebUChar)];
+
+ base::ScopedCFTypeRef<CFArrayRef> elements(
+ IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone));
+ AddButtonsAndAxes(CFToNSCast(elements), slot);
+
+ associated_[slot].hid.device_ref = device;
+ data_.items[slot].connected = true;
+ if (slot >= data_.length)
+ data_.length = slot + 1;
+}
+
+void GamepadPlatformDataFetcherMac::DeviceRemove(IOHIDDeviceRef device) {
+ if (!enabled_)
+ return;
+
+ // Find the index for this device.
+ size_t slot;
+ for (slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) {
+ if (data_.items[slot].connected &&
+ !associated_[slot].is_xbox &&
+ associated_[slot].hid.device_ref == device)
+ break;
+ }
+ DCHECK(slot < WebGamepads::itemsLengthCap);
+ // Leave associated device_ref so that it will be reconnected in the same
+ // location. Simply mark it as disconnected.
+ data_.items[slot].connected = false;
+}
+
+void GamepadPlatformDataFetcherMac::ValueChanged(IOHIDValueRef value) {
+ if (!enabled_)
+ return;
+
+ IOHIDElementRef element = IOHIDValueGetElement(value);
+ IOHIDDeviceRef device = IOHIDElementGetDevice(element);
+
+ // Find device slot.
+ size_t slot;
+ for (slot = 0; slot < data_.length; ++slot) {
+ if (data_.items[slot].connected &&
+ !associated_[slot].is_xbox &&
+ associated_[slot].hid.device_ref == device)
+ break;
+ }
+ if (slot == data_.length)
+ return;
+
+ WebGamepad& pad = data_.items[slot];
+ AssociatedData& associated = associated_[slot];
+
+ // Find and fill in the associated button event, if any.
+ for (size_t i = 0; i < pad.buttonsLength; ++i) {
+ if (associated.hid.button_elements[i] == element) {
+ pad.buttons[i] = IOHIDValueGetIntegerValue(value) ? 1.f : 0.f;
+ pad.timestamp = std::max(pad.timestamp, IOHIDValueGetTimeStamp(value));
+ return;
+ }
+ }
+
+ // Find and fill in the associated axis event, if any.
+ for (size_t i = 0; i < pad.axesLength; ++i) {
+ if (associated.hid.axis_elements[i] == element) {
+ pad.axes[i] = NormalizeAxis(IOHIDValueGetIntegerValue(value),
+ associated.hid.axis_minimums[i],
+ associated.hid.axis_maximums[i]);
+ pad.timestamp = std::max(pad.timestamp, IOHIDValueGetTimeStamp(value));
+ return;
+ }
+ }
+}
+
+void GamepadPlatformDataFetcherMac::XboxDeviceAdd(XboxController* device) {
+ if (!enabled_)
+ return;
+
+ size_t slot = GetSlotForXboxDevice(device);
+
+ // We can't handle this many connected devices.
+ if (slot == WebGamepads::itemsLengthCap)
+ return;
+
+ device->SetLEDPattern(
+ (XboxController::LEDPattern)(XboxController::LED_FLASH_TOP_LEFT + slot));
+
+ NSString* ident =
+ [NSString stringWithFormat:
+ @"Xbox 360 Controller (STANDARD GAMEPAD Vendor: %04x Product: %04x)",
+ device->GetProductId(), device->GetVendorId()];
+ NSData* as16 = [ident dataUsingEncoding:NSUTF16StringEncoding];
+ const size_t kOutputLengthBytes = sizeof(data_.items[slot].id);
+ memset(&data_.items[slot].id, 0, kOutputLengthBytes);
+ [as16 getBytes:data_.items[slot].id
+ length:kOutputLengthBytes - sizeof(WebKit::WebUChar)];
+
+ associated_[slot].is_xbox = true;
+ associated_[slot].xbox.device = device;
+ associated_[slot].xbox.location_id = device->location_id();
+ data_.items[slot].connected = true;
+ data_.items[slot].axesLength = 4;
+ data_.items[slot].buttonsLength = 17;
+ data_.items[slot].timestamp = 0;
+ if (slot >= data_.length)
+ data_.length = slot + 1;
+}
+
+void GamepadPlatformDataFetcherMac::XboxDeviceRemove(XboxController* device) {
+ if (!enabled_)
+ return;
+
+ // Find the index for this device.
+ size_t slot;
+ for (slot = 0; slot < WebGamepads::itemsLengthCap; ++slot) {
+ if (data_.items[slot].connected &&
+ associated_[slot].is_xbox &&
+ associated_[slot].xbox.device == device)
+ break;
+ }
+ DCHECK(slot < WebGamepads::itemsLengthCap);
+ // Leave associated location id so that the controller will be reconnected in
+ // the same slot if it is plugged in again. Simply mark it as disconnected.
+ data_.items[slot].connected = false;
+}
+
+void GamepadPlatformDataFetcherMac::XboxValueChanged(
+ XboxController* device, const XboxController::Data& data) {
+ // Find device slot.
+ size_t slot;
+ for (slot = 0; slot < data_.length; ++slot) {
+ if (data_.items[slot].connected &&
+ associated_[slot].is_xbox &&
+ associated_[slot].xbox.device == device)
+ break;
+ }
+ if (slot == data_.length)
+ return;
+
+ WebGamepad& pad = data_.items[slot];
+
+ for (size_t i = 0; i < 6; i++) {
+ pad.buttons[i] = data.buttons[i] ? 1.0f : 0.0f;
+ }
+ pad.buttons[6] = data.triggers[0];
+ pad.buttons[7] = data.triggers[1];
+ for (size_t i = 8; i < 17; i++) {
+ pad.buttons[i] = data.buttons[i - 2] ? 1.0f : 0.0f;
+ }
+ for (size_t i = 0; i < arraysize(data.axes); i++) {
+ pad.axes[i] = data.axes[i];
+ }
+
+ pad.timestamp = base::TimeTicks::Now().ToInternalValue();
+}
+
+void GamepadPlatformDataFetcherMac::GetGamepadData(WebGamepads* pads, bool) {
+ if (!enabled_ && !xbox_fetcher_) {
+ pads->length = 0;
+ return;
+ }
+
+ // Copy to the current state to the output buffer, using the mapping
+ // function, if there is one available.
+ pads->length = WebGamepads::itemsLengthCap;
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (!associated_[i].is_xbox && associated_[i].hid.mapper)
+ associated_[i].hid.mapper(data_.items[i], &pads->items[i]);
+ else
+ pads->items[i] = data_.items[i];
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc
new file mode 100644
index 00000000000..d59ac180aa8
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.cc
@@ -0,0 +1,498 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h"
+
+#include <dinput.h>
+#include <dinputd.h>
+
+#include "base/debug/trace_event.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/windows_version.h"
+#include "content/common/gamepad_hardware_buffer.h"
+#include "content/common/gamepad_messages.h"
+
+// This was removed from the Windows 8 SDK for some reason.
+// We need it so we can get state for axes without worrying if they
+// exist.
+#ifndef DIDFT_OPTIONAL
+#define DIDFT_OPTIONAL 0x80000000
+#endif
+
+namespace content {
+
+using namespace WebKit;
+
+namespace {
+
+// See http://goo.gl/5VSJR. These are not available in all versions of the
+// header, but they can be returned from the driver, so we define our own
+// versions here.
+static const BYTE kDeviceSubTypeGamepad = 1;
+static const BYTE kDeviceSubTypeWheel = 2;
+static const BYTE kDeviceSubTypeArcadeStick = 3;
+static const BYTE kDeviceSubTypeFlightStick = 4;
+static const BYTE kDeviceSubTypeDancePad = 5;
+static const BYTE kDeviceSubTypeGuitar = 6;
+static const BYTE kDeviceSubTypeGuitarAlternate = 7;
+static const BYTE kDeviceSubTypeDrumKit = 8;
+static const BYTE kDeviceSubTypeGuitarBass = 11;
+static const BYTE kDeviceSubTypeArcadePad = 19;
+
+float NormalizeXInputAxis(SHORT value) {
+ return ((value + 32768.f) / 32767.5f) - 1.f;
+}
+
+const WebUChar* const GamepadSubTypeName(BYTE sub_type) {
+ switch (sub_type) {
+ case kDeviceSubTypeGamepad: return L"GAMEPAD";
+ case kDeviceSubTypeWheel: return L"WHEEL";
+ case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK";
+ case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK";
+ case kDeviceSubTypeDancePad: return L"DANCE_PAD";
+ case kDeviceSubTypeGuitar: return L"GUITAR";
+ case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE";
+ case kDeviceSubTypeDrumKit: return L"DRUM_KIT";
+ case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS";
+ case kDeviceSubTypeArcadePad: return L"ARCADE_PAD";
+ default: return L"<UNKNOWN>";
+ }
+}
+
+bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad,
+ std::string* vendor,
+ std::string* product) {
+ DIPROPDWORD prop;
+ prop.diph.dwSize = sizeof(DIPROPDWORD);
+ prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+ prop.diph.dwObj = 0;
+ prop.diph.dwHow = DIPH_DEVICE;
+
+ if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &prop.diph)))
+ return false;
+ *vendor = base::StringPrintf("%04x", LOWORD(prop.dwData));
+ *product = base::StringPrintf("%04x", HIWORD(prop.dwData));
+ return true;
+}
+
+// Sets the deadzone value for all axes of a gamepad.
+// deadzone values range from 0 (no deadzone) to 10,000 (entire range
+// is dead).
+bool SetDirectInputDeadZone(IDirectInputDevice8* gamepad,
+ int deadzone) {
+ DIPROPDWORD prop;
+ prop.diph.dwSize = sizeof(DIPROPDWORD);
+ prop.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+ prop.diph.dwObj = 0;
+ prop.diph.dwHow = DIPH_DEVICE;
+ prop.dwData = deadzone;
+ return SUCCEEDED(gamepad->SetProperty(DIPROP_DEADZONE, &prop.diph));
+}
+
+struct InternalDirectInputDevice {
+ IDirectInputDevice8* gamepad;
+ GamepadStandardMappingFunction mapper;
+ wchar_t id[WebGamepad::idLengthCap];
+ GUID guid;
+};
+
+struct EnumDevicesContext {
+ IDirectInput8* directinput_interface;
+ std::vector<InternalDirectInputDevice>* directinput_devices;
+};
+
+// We define our own data format structure to attempt to get as many
+// axes as possible.
+struct JoyData {
+ long axes[10];
+ char buttons[24];
+ DWORD pov; // Often used for D-pads.
+};
+
+BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance,
+ void* context) {
+ EnumDevicesContext* ctxt = reinterpret_cast<EnumDevicesContext*>(context);
+ IDirectInputDevice8* gamepad;
+
+ if (FAILED(ctxt->directinput_interface->CreateDevice(instance->guidInstance,
+ &gamepad,
+ NULL)))
+ return DIENUM_CONTINUE;
+
+ gamepad->Acquire();
+
+#define MAKE_AXIS(i) \
+ {0, FIELD_OFFSET(JoyData, axes) + 4 * i, \
+ DIDFT_AXIS | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0}
+#define MAKE_BUTTON(i) \
+ {&GUID_Button, FIELD_OFFSET(JoyData, buttons) + i, \
+ DIDFT_BUTTON | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0}
+#define MAKE_POV() \
+ {&GUID_POV, FIELD_OFFSET(JoyData, pov), DIDFT_POV | DIDFT_OPTIONAL, 0}
+ DIOBJECTDATAFORMAT rgodf[] = {
+ MAKE_AXIS(0),
+ MAKE_AXIS(1),
+ MAKE_AXIS(2),
+ MAKE_AXIS(3),
+ MAKE_AXIS(4),
+ MAKE_AXIS(5),
+ MAKE_AXIS(6),
+ MAKE_AXIS(7),
+ MAKE_AXIS(8),
+ MAKE_AXIS(9),
+ MAKE_BUTTON(0),
+ MAKE_BUTTON(1),
+ MAKE_BUTTON(2),
+ MAKE_BUTTON(3),
+ MAKE_BUTTON(4),
+ MAKE_BUTTON(5),
+ MAKE_BUTTON(6),
+ MAKE_BUTTON(7),
+ MAKE_BUTTON(8),
+ MAKE_BUTTON(9),
+ MAKE_BUTTON(10),
+ MAKE_BUTTON(11),
+ MAKE_BUTTON(12),
+ MAKE_BUTTON(13),
+ MAKE_BUTTON(14),
+ MAKE_BUTTON(15),
+ MAKE_BUTTON(16),
+ MAKE_POV(),
+ };
+#undef MAKE_AXIS
+#undef MAKE_BUTTON
+#undef MAKE_POV
+
+ DIDATAFORMAT df = {
+ sizeof (DIDATAFORMAT),
+ sizeof (DIOBJECTDATAFORMAT),
+ DIDF_ABSAXIS,
+ sizeof (JoyData),
+ sizeof (rgodf) / sizeof (rgodf[0]),
+ rgodf
+ };
+
+ // If we can't set the data format on the device, don't add it to our
+ // list, since we won't know how to read data from it.
+ if (FAILED(gamepad->SetDataFormat(&df))) {
+ gamepad->Release();
+ return DIENUM_CONTINUE;
+ }
+
+ InternalDirectInputDevice device;
+ device.guid = instance->guidInstance;
+ device.gamepad = gamepad;
+ std::string vendor;
+ std::string product;
+ if (!GetDirectInputVendorProduct(gamepad, &vendor, &product)) {
+ gamepad->Release();
+ return DIENUM_CONTINUE;
+ }
+
+ // Set the dead zone to 10% of the axis length for all axes. This
+ // gives us a larger space for what's "neutral" so the controls don't
+ // slowly drift.
+ SetDirectInputDeadZone(gamepad, 1000);
+ device.mapper = GetGamepadStandardMappingFunction(vendor, product);
+ if (device.mapper) {
+ base::swprintf(device.id,
+ WebGamepad::idLengthCap,
+ L"STANDARD GAMEPAD (%ls)",
+ instance->tszProductName);
+ ctxt->directinput_devices->push_back(device);
+ } else {
+ gamepad->Release();
+ }
+ return DIENUM_CONTINUE;
+}
+
+} // namespace
+
+GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin()
+ : xinput_dll_(base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))),
+ xinput_available_(GetXInputDllFunctions()) {
+ // TODO(teravest): http://crbug.com/260187
+ if (base::win::GetVersion() > base::win::VERSION_XP) {
+ directinput_available_ = SUCCEEDED(DirectInput8Create(
+ GetModuleHandle(NULL),
+ DIRECTINPUT_VERSION,
+ IID_IDirectInput8,
+ reinterpret_cast<void**>(&directinput_interface_),
+ NULL));
+ } else {
+ directinput_available_ = false;
+ }
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i)
+ pad_state_[i].status = DISCONNECTED;
+}
+
+GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() {
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (pad_state_[i].status == DIRECTINPUT_CONNECTED)
+ pad_state_[i].directinput_gamepad->Release();
+ }
+}
+
+int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const {
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (pad_state_[i].status == DISCONNECTED)
+ return i;
+ }
+ return -1;
+}
+
+bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const {
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (pad_state_[i].status == XINPUT_CONNECTED &&
+ pad_state_[i].xinput_index == index)
+ return true;
+ }
+ return false;
+}
+
+bool GamepadPlatformDataFetcherWin::HasDirectInputGamepad(
+ const GUID& guid) const {
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (pad_state_[i].status == DIRECTINPUT_CONNECTED &&
+ pad_state_[i].guid == guid)
+ return true;
+ }
+ return false;
+}
+
+void GamepadPlatformDataFetcherWin::EnumerateDevices(
+ WebGamepads* pads) {
+ TRACE_EVENT0("GAMEPAD", "EnumerateDevices");
+
+ // Mark all disconnected pads DISCONNECTED.
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ if (!pads->items[i].connected)
+ pad_state_[i].status = DISCONNECTED;
+ }
+
+ for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
+ if (HasXInputGamepad(i))
+ continue;
+ int pad_index = FirstAvailableGamepadId();
+ if (pad_index == -1)
+ return; // We can't add any more gamepads.
+ WebGamepad& pad = pads->items[pad_index];
+ if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) {
+ pad_state_[pad_index].status = XINPUT_CONNECTED;
+ pad_state_[pad_index].xinput_index = i;
+ }
+ }
+
+ if (directinput_available_) {
+ struct EnumDevicesContext context;
+ std::vector<InternalDirectInputDevice> directinput_gamepads;
+ context.directinput_interface = directinput_interface_;
+ context.directinput_devices = &directinput_gamepads;
+
+ directinput_interface_->EnumDevices(
+ DI8DEVCLASS_GAMECTRL,
+ &DirectInputEnumDevicesCallback,
+ &context,
+ DIEDFL_ATTACHEDONLY);
+ for (size_t i = 0; i < directinput_gamepads.size(); ++i) {
+ if (HasDirectInputGamepad(directinput_gamepads[i].guid)) {
+ directinput_gamepads[i].gamepad->Release();
+ continue;
+ }
+ int pad_index = FirstAvailableGamepadId();
+ if (pad_index == -1)
+ return;
+ WebGamepad& pad = pads->items[pad_index];
+ pad.connected = true;
+ wcscpy_s(pad.id, WebGamepad::idLengthCap, directinput_gamepads[i].id);
+ PadState& state = pad_state_[pad_index];
+ state.status = DIRECTINPUT_CONNECTED;
+ state.guid = directinput_gamepads[i].guid;
+ state.directinput_gamepad = directinput_gamepads[i].gamepad;
+ state.mapper = directinput_gamepads[i].mapper;
+ }
+ }
+}
+
+
+void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads,
+ bool devices_changed_hint) {
+ TRACE_EVENT0("GAMEPAD", "GetGamepadData");
+
+ if (!xinput_available_ && !directinput_available_) {
+ pads->length = 0;
+ return;
+ }
+
+ // A note on XInput devices:
+ // If we got notification that system devices have been updated, then
+ // run GetCapabilities to update the connected status and the device
+ // identifier. It can be slow to do to both GetCapabilities and
+ // GetState on unconnected devices, so we want to avoid a 2-5ms pause
+ // here by only doing this when the devices are updated (despite
+ // documentation claiming it's OK to call it any time).
+ if (devices_changed_hint)
+ EnumerateDevices(pads);
+
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ WebGamepad& pad = pads->items[i];
+ if (pad_state_[i].status == XINPUT_CONNECTED)
+ GetXInputPadData(i, &pad);
+ else if (pad_state_[i].status == DIRECTINPUT_CONNECTED)
+ GetDirectInputPadData(i, &pad);
+ }
+ pads->length = WebGamepads::itemsLengthCap;
+}
+
+bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
+ int i,
+ WebGamepad* pad) const {
+ DCHECK(pad);
+ TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i);
+ XINPUT_CAPABILITIES caps;
+ DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
+ if (res == ERROR_DEVICE_NOT_CONNECTED) {
+ pad->connected = false;
+ return false;
+ } else {
+ pad->connected = true;
+ base::swprintf(pad->id,
+ WebGamepad::idLengthCap,
+ L"Xbox 360 Controller (XInput STANDARD %ls)",
+ GamepadSubTypeName(caps.SubType));
+ return true;
+ }
+}
+
+void GamepadPlatformDataFetcherWin::GetXInputPadData(
+ int i,
+ WebGamepad* pad) {
+ // We rely on device_changed and GetCapabilities to tell us that
+ // something's been connected, but we will mark as disconnected if
+ // GetState returns that we've lost the pad.
+ if (!pad->connected)
+ return;
+
+ XINPUT_STATE state;
+ memset(&state, 0, sizeof(XINPUT_STATE));
+ TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i);
+ DWORD dwResult = xinput_get_state_(pad_state_[i].xinput_index, &state);
+ TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i);
+
+ if (dwResult == ERROR_SUCCESS) {
+ pad->timestamp = state.dwPacketNumber;
+ pad->buttonsLength = 0;
+#define ADD(b) pad->buttons[pad->buttonsLength++] = \
+ ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0);
+ ADD(XINPUT_GAMEPAD_A);
+ ADD(XINPUT_GAMEPAD_B);
+ ADD(XINPUT_GAMEPAD_X);
+ ADD(XINPUT_GAMEPAD_Y);
+ ADD(XINPUT_GAMEPAD_LEFT_SHOULDER);
+ ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER);
+ pad->buttons[pad->buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0;
+ pad->buttons[pad->buttonsLength++] = state.Gamepad.bRightTrigger / 255.0;
+ ADD(XINPUT_GAMEPAD_BACK);
+ ADD(XINPUT_GAMEPAD_START);
+ ADD(XINPUT_GAMEPAD_LEFT_THUMB);
+ ADD(XINPUT_GAMEPAD_RIGHT_THUMB);
+ ADD(XINPUT_GAMEPAD_DPAD_UP);
+ ADD(XINPUT_GAMEPAD_DPAD_DOWN);
+ ADD(XINPUT_GAMEPAD_DPAD_LEFT);
+ ADD(XINPUT_GAMEPAD_DPAD_RIGHT);
+#undef ADD
+ pad->axesLength = 0;
+ // XInput are +up/+right, -down/-left, we want -up/-left.
+ pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbLX);
+ pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbLY);
+ pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbRX);
+ pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbRY);
+ } else {
+ pad->connected = false;
+ }
+}
+
+void GamepadPlatformDataFetcherWin::GetDirectInputPadData(
+ int index,
+ WebGamepad* pad) {
+ if (!pad->connected)
+ return;
+
+ IDirectInputDevice8* gamepad = pad_state_[index].directinput_gamepad;
+ if (FAILED(gamepad->Poll())) {
+ // Polling didn't work, try acquiring the gamepad.
+ if (FAILED(gamepad->Acquire())) {
+ pad->buttonsLength = 0;
+ pad->axesLength = 0;
+ return;
+ }
+ // Try polling again.
+ if (FAILED(gamepad->Poll())) {
+ pad->buttonsLength = 0;
+ pad->axesLength = 0;
+ return;
+ }
+ }
+ JoyData state;
+ if (FAILED(gamepad->GetDeviceState(sizeof(JoyData), &state))) {
+ pad->connected = false;
+ return;
+ }
+
+ WebGamepad raw;
+ raw.connected = true;
+ for (int i = 0; i < 16; i++)
+ raw.buttons[i] = (state.buttons[i] & 0x80) ? 1.0 : 0.0;
+
+ // We map the POV (often a D-pad) into the buttons 16-19.
+ // DirectInput gives pov measurements in hundredths of degrees,
+ // clockwise from "North".
+ // We use 22.5 degree slices so we can handle diagonal D-raw presses.
+ static const int arc_segment = 2250; // 22.5 degrees = 1/16 circle
+ if (state.pov > arc_segment && state.pov < 7 * arc_segment)
+ raw.buttons[19] = 1.0;
+ else
+ raw.buttons[19] = 0.0;
+
+ if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment)
+ raw.buttons[17] = 1.0;
+ else
+ raw.buttons[17] = 0.0;
+
+ if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment)
+ raw.buttons[18] = 1.0;
+ else
+ raw.buttons[18] = 0.0;
+
+ if (state.pov < 3 * arc_segment ||
+ (state.pov > 13 * arc_segment && state.pov < 36000))
+ raw.buttons[16] = 1.0;
+ else
+ raw.buttons[16] = 0.0;
+
+ for (int i = 0; i < 10; i++)
+ raw.axes[i] = state.axes[i];
+ pad_state_[index].mapper(raw, pad);
+}
+
+bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
+ xinput_get_capabilities_ = NULL;
+ xinput_get_state_ = NULL;
+ xinput_enable_ = reinterpret_cast<XInputEnableFunc>(
+ xinput_dll_.GetFunctionPointer("XInputEnable"));
+ if (!xinput_enable_)
+ return false;
+ xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
+ xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
+ if (!xinput_get_capabilities_)
+ return false;
+ xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
+ xinput_dll_.GetFunctionPointer("XInputGetState"));
+ if (!xinput_get_state_)
+ return false;
+ xinput_enable_(true);
+ return true;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h
new file mode 100644
index 00000000000..7551ee91c4c
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_platform_data_fetcher_win.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_
+
+#include "build/build_config.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+#include <stdlib.h>
+#include <Unknwn.h>
+#include <WinDef.h>
+#include <windows.h>
+#include <XInput.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/scoped_native_library.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+namespace content {
+
+class GamepadPlatformDataFetcherWin : public GamepadDataFetcher {
+ public:
+ GamepadPlatformDataFetcherWin();
+ virtual ~GamepadPlatformDataFetcherWin();
+ virtual void GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) OVERRIDE;
+ private:
+ // XInput-specific implementation for GetGamepadData.
+ bool GetXInputGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint);
+ bool GetDirectInputGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint);
+
+ // The three function types we use from xinput1_3.dll.
+ typedef void (WINAPI *XInputEnableFunc)(BOOL enable);
+ typedef DWORD (WINAPI *XInputGetCapabilitiesFunc)(
+ DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities);
+ typedef DWORD (WINAPI *XInputGetStateFunc)(
+ DWORD dwUserIndex, XINPUT_STATE* pState);
+
+ // Get functions from dynamically loaded xinput1_3.dll. We don't use
+ // DELAYLOAD because the import library for Win8 SDK pulls xinput1_4 which
+ // isn't redistributable. Returns true if loading was successful. We include
+ // xinput1_3.dll with Chrome.
+ bool GetXInputDllFunctions();
+
+ // Scan for connected XInput and DirectInput gamepads.
+ void EnumerateDevices(WebKit::WebGamepads* pads);
+ bool GetXInputPadConnectivity(int i, WebKit::WebGamepad* pad) const;
+
+ void GetXInputPadData(int i, WebKit::WebGamepad* pad);
+ void GetDirectInputPadData(int i, WebKit::WebGamepad* pad);
+
+ int FirstAvailableGamepadId() const;
+ bool HasXInputGamepad(int index) const;
+ bool HasDirectInputGamepad(const GUID &guid) const;
+
+ base::ScopedNativeLibrary xinput_dll_;
+ bool xinput_available_;
+ bool directinput_available_;
+
+ // Function pointers to XInput functionality, retrieved in
+ // |GetXinputDllFunctions|.
+ XInputEnableFunc xinput_enable_;
+ XInputGetCapabilitiesFunc xinput_get_capabilities_;
+ XInputGetStateFunc xinput_get_state_;
+
+ IDirectInput8* directinput_interface_;
+
+ enum PadConnectionStatus {
+ DISCONNECTED,
+ XINPUT_CONNECTED,
+ DIRECTINPUT_CONNECTED
+ };
+
+ struct PadState {
+ PadConnectionStatus status;
+ int xinput_index; // XInput-only.
+ // Fields below are for DirectInput devices only.
+ GUID guid;
+ IDirectInputDevice8* directinput_gamepad;
+ GamepadStandardMappingFunction mapper;
+ };
+ PadState pad_state_[WebKit::WebGamepads::itemsLengthCap];
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadPlatformDataFetcherWin);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PLATFORM_DATA_FETCHER_WIN_H_
diff --git a/chromium/content/browser/gamepad/gamepad_provider.cc b/chromium/content/browser/gamepad/gamepad_provider.cc
new file mode 100644
index 00000000000..7e8e6dee132
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_provider.cc
@@ -0,0 +1,220 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cmath>
+#include <set>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_provider.h"
+#include "content/common/gamepad_hardware_buffer.h"
+#include "content/common/gamepad_messages.h"
+#include "content/common/gamepad_user_gesture.h"
+
+namespace content {
+
+GamepadProvider::ClosureAndThread::ClosureAndThread(
+ const base::Closure& c,
+ const scoped_refptr<base::MessageLoopProxy>& m)
+ : closure(c),
+ message_loop(m) {
+}
+
+GamepadProvider::ClosureAndThread::~ClosureAndThread() {
+}
+
+GamepadProvider::GamepadProvider()
+ : is_paused_(true),
+ have_scheduled_do_poll_(false),
+ devices_changed_(true) {
+ Initialize(scoped_ptr<GamepadDataFetcher>());
+}
+
+GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher)
+ : is_paused_(true),
+ have_scheduled_do_poll_(false),
+ devices_changed_(true) {
+ Initialize(fetcher.Pass());
+}
+
+GamepadProvider::~GamepadProvider() {
+ base::SystemMonitor* monitor = base::SystemMonitor::Get();
+ if (monitor)
+ monitor->RemoveDevicesChangedObserver(this);
+
+ // Use Stop() to join the polling thread, as there may be pending callbacks
+ // which dereference |polling_thread_|.
+ polling_thread_->Stop();
+ data_fetcher_.reset();
+}
+
+base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
+ base::ProcessHandle process) {
+ base::SharedMemoryHandle renderer_handle;
+ gamepad_shared_memory_.ShareToProcess(process, &renderer_handle);
+ return renderer_handle;
+}
+
+void GamepadProvider::Pause() {
+ {
+ base::AutoLock lock(is_paused_lock_);
+ is_paused_ = true;
+ }
+ base::MessageLoop* polling_loop = polling_thread_->message_loop();
+ polling_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true));
+}
+
+void GamepadProvider::Resume() {
+ {
+ base::AutoLock lock(is_paused_lock_);
+ if (!is_paused_)
+ return;
+ is_paused_ = false;
+ }
+
+ base::MessageLoop* polling_loop = polling_thread_->message_loop();
+ polling_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), false));
+ polling_loop->PostTask(
+ FROM_HERE,
+ base::Bind(&GamepadProvider::ScheduleDoPoll, Unretained(this)));
+}
+
+void GamepadProvider::RegisterForUserGesture(const base::Closure& closure) {
+ base::AutoLock lock(user_gesture_lock_);
+ user_gesture_observers_.push_back(ClosureAndThread(
+ closure, base::MessageLoop::current()->message_loop_proxy()));
+}
+
+void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
+ base::AutoLock lock(devices_changed_lock_);
+ devices_changed_ = true;
+}
+
+void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) {
+ size_t data_size = sizeof(GamepadHardwareBuffer);
+ base::SystemMonitor* monitor = base::SystemMonitor::Get();
+ if (monitor)
+ monitor->AddDevicesChangedObserver(this);
+ bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
+ CHECK(res);
+ GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
+ memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
+
+ polling_thread_.reset(new base::Thread("Gamepad polling thread"));
+#if defined(OS_MACOSX)
+ // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
+ // message loop needs to be a UI-type loop.
+ const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
+#else
+ // On Linux, the data fetcher needs to watch file descriptors, so the message
+ // loop needs to be a libevent loop. On Windows it doesn't matter what the
+ // loop is.
+ const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
+#endif
+ polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
+
+ polling_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&GamepadProvider::DoInitializePollingThread,
+ base::Unretained(this),
+ base::Passed(&fetcher)));
+}
+
+void GamepadProvider::DoInitializePollingThread(
+ scoped_ptr<GamepadDataFetcher> fetcher) {
+ DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
+ DCHECK(!data_fetcher_.get()); // Should only initialize once.
+
+ if (!fetcher)
+ fetcher.reset(new GamepadPlatformDataFetcher);
+ data_fetcher_ = fetcher.Pass();
+}
+
+void GamepadProvider::SendPauseHint(bool paused) {
+ DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
+ if (data_fetcher_)
+ data_fetcher_->PauseHint(paused);
+}
+
+void GamepadProvider::DoPoll() {
+ DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
+ DCHECK(have_scheduled_do_poll_);
+ have_scheduled_do_poll_ = false;
+
+ bool changed;
+ GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
+
+ ANNOTATE_BENIGN_RACE_SIZED(
+ &hwbuf->buffer,
+ sizeof(WebKit::WebGamepads),
+ "Racey reads are discarded");
+
+ {
+ base::AutoLock lock(devices_changed_lock_);
+ changed = devices_changed_;
+ devices_changed_ = false;
+ }
+
+ // Acquire the SeqLock. There is only ever one writer to this data.
+ // See gamepad_hardware_buffer.h.
+ hwbuf->sequence.WriteBegin();
+ data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
+ hwbuf->sequence.WriteEnd();
+
+ CheckForUserGesture();
+
+ // Schedule our next interval of polling.
+ ScheduleDoPoll();
+}
+
+void GamepadProvider::ScheduleDoPoll() {
+ DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
+ if (have_scheduled_do_poll_)
+ return;
+
+ {
+ base::AutoLock lock(is_paused_lock_);
+ if (is_paused_)
+ return;
+ }
+
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
+ have_scheduled_do_poll_ = true;
+}
+
+GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
+ void* mem = gamepad_shared_memory_.memory();
+ CHECK(mem);
+ return static_cast<GamepadHardwareBuffer*>(mem);
+}
+
+void GamepadProvider::CheckForUserGesture() {
+ base::AutoLock lock(user_gesture_lock_);
+ if (user_gesture_observers_.empty())
+ return; // Don't need to check if nobody is listening.
+
+ if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) {
+ for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
+ user_gesture_observers_[i].message_loop->PostTask(FROM_HERE,
+ user_gesture_observers_[i].closure);
+ }
+ user_gesture_observers_.clear();
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_provider.h b/chromium/content/browser/gamepad/gamepad_provider.h
new file mode 100644
index 00000000000..5f10f137a14
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_provider.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_PROVIDER_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_PROVIDER_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "base/system_monitor/system_monitor.h"
+#include "content/common/content_export.h"
+
+namespace base {
+class MessageLoopProxy;
+class Thread;
+}
+
+namespace content {
+
+class GamepadDataFetcher;
+struct GamepadHardwareBuffer;
+
+class CONTENT_EXPORT GamepadProvider :
+ public base::SystemMonitor::DevicesChangedObserver {
+ public:
+ GamepadProvider();
+
+ // Manually specifies the data fetcher. Used for testing.
+ explicit GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher);
+
+ virtual ~GamepadProvider();
+
+ // Returns the shared memory handle of the gamepad data duplicated into the
+ // given process.
+ base::SharedMemoryHandle GetSharedMemoryHandleForProcess(
+ base::ProcessHandle renderer_process);
+
+ // Pause and resume the background polling thread. Can be called from any
+ // thread.
+ void Pause();
+ void Resume();
+
+ // Registers the given closure for calling when the user has interacted with
+ // the device. This callback will only be issued once.
+ void RegisterForUserGesture(const base::Closure& closure);
+
+ // base::SystemMonitor::DevicesChangedObserver implementation.
+ virtual void OnDevicesChanged(base::SystemMonitor::DeviceType type) OVERRIDE;
+
+ private:
+ void Initialize(scoped_ptr<GamepadDataFetcher> fetcher);
+
+ // Method for setting up the platform-specific data fetcher. Takes ownership
+ // of |fetcher|.
+ void DoInitializePollingThread(scoped_ptr<GamepadDataFetcher> fetcher);
+
+ // Method for sending pause hints to the low-level data fetcher. Runs on
+ // polling_thread_.
+ void SendPauseHint(bool paused);
+
+ // Method for polling a GamepadDataFetcher. Runs on the polling_thread_.
+ void DoPoll();
+ void ScheduleDoPoll();
+
+ GamepadHardwareBuffer* SharedMemoryAsHardwareBuffer();
+
+ // Checks the gamepad state to see if the user has interacted with it.
+ void CheckForUserGesture();
+
+ enum { kDesiredSamplingIntervalMs = 16 };
+
+ // Keeps track of when the background thread is paused. Access to is_paused_
+ // must be guarded by is_paused_lock_.
+ base::Lock is_paused_lock_;
+ bool is_paused_;
+
+ // Keep track of when a polling task is schedlued, so as to prevent us from
+ // accidentally scheduling more than one at any time, when rapidly toggling
+ // |is_paused_|.
+ bool have_scheduled_do_poll_;
+
+ // Lists all observers registered for user gestures, and the thread which
+ // to issue the callbacks on. Since we always issue the callback on the
+ // thread which the registration happened, and this class lives on the I/O
+ // thread, the message loop proxies will normally just be the I/O thread.
+ // However, this will be the main thread for unit testing.
+ base::Lock user_gesture_lock_;
+ struct ClosureAndThread {
+ ClosureAndThread(const base::Closure& c,
+ const scoped_refptr<base::MessageLoopProxy>& m);
+ ~ClosureAndThread();
+
+ base::Closure closure;
+ scoped_refptr<base::MessageLoopProxy> message_loop;
+ };
+ typedef std::vector<ClosureAndThread> UserGestureObserverVector;
+ UserGestureObserverVector user_gesture_observers_;
+
+ // Updated based on notification from SystemMonitor when the system devices
+ // have been updated, and this notification is passed on to the data fetcher
+ // to enable it to avoid redundant (and possibly expensive) is-connected
+ // tests. Access to devices_changed_ must be guarded by
+ // devices_changed_lock_.
+ base::Lock devices_changed_lock_;
+ bool devices_changed_;
+
+ // When polling_thread_ is running, members below are only to be used
+ // from that thread.
+ scoped_ptr<GamepadDataFetcher> data_fetcher_;
+ base::SharedMemory gamepad_shared_memory_;
+
+ // Polling is done on this background thread.
+ scoped_ptr<base::Thread> polling_thread_;
+
+ static GamepadProvider* instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadProvider);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_PROVIDER_H_
diff --git a/chromium/content/browser/gamepad/gamepad_provider_unittest.cc b/chromium/content/browser/gamepad/gamepad_provider_unittest.cc
new file mode 100644
index 00000000000..0e5785cc598
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_provider_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_provider.h"
+#include "content/browser/gamepad/gamepad_test_helpers.h"
+#include "content/common/gamepad_hardware_buffer.h"
+#include "content/common/gamepad_messages.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+using WebKit::WebGamepads;
+
+// Helper class to generate and record user gesture callbacks.
+class UserGestureListener {
+ public:
+ UserGestureListener()
+ : weak_factory_(this),
+ has_user_gesture_(false) {
+ }
+
+ base::Closure GetClosure() {
+ return base::Bind(&UserGestureListener::GotUserGesture,
+ weak_factory_.GetWeakPtr());
+ }
+
+ bool has_user_gesture() const { return has_user_gesture_; }
+
+ private:
+ void GotUserGesture() {
+ has_user_gesture_ = true;
+ }
+
+ base::WeakPtrFactory<UserGestureListener> weak_factory_;
+ bool has_user_gesture_;
+};
+
+// Main test fixture
+class GamepadProviderTest : public testing::Test, public GamepadTestHelper {
+ public:
+ GamepadProvider* CreateProvider(const WebGamepads& test_data) {
+ mock_data_fetcher_ = new MockGamepadDataFetcher(test_data);
+ provider_.reset(new GamepadProvider(
+ scoped_ptr<GamepadDataFetcher>(mock_data_fetcher_)));
+ return provider_.get();
+ }
+
+ protected:
+ GamepadProviderTest() {
+ }
+
+ scoped_ptr<GamepadProvider> provider_;
+
+ // Pointer owned by the provider.
+ MockGamepadDataFetcher* mock_data_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadProviderTest);
+};
+
+// Crashes. http://crbug.com/106163
+TEST_F(GamepadProviderTest, PollingAccess) {
+ WebGamepads test_data;
+ test_data.length = 1;
+ test_data.items[0].connected = true;
+ test_data.items[0].timestamp = 0;
+ test_data.items[0].buttonsLength = 1;
+ test_data.items[0].axesLength = 2;
+ test_data.items[0].buttons[0] = 1.f;
+ test_data.items[0].axes[0] = -1.f;
+ test_data.items[0].axes[1] = .5f;
+
+ GamepadProvider* provider = CreateProvider(test_data);
+ provider->Resume();
+
+ message_loop().RunUntilIdle();
+
+ mock_data_fetcher_->WaitForDataRead();
+
+ // Renderer-side, pull data out of poll buffer.
+ base::SharedMemoryHandle handle = provider->GetSharedMemoryHandleForProcess(
+ base::GetCurrentProcessHandle());
+ scoped_ptr<base::SharedMemory> shared_memory(
+ new base::SharedMemory(handle, true));
+ EXPECT_TRUE(shared_memory->Map(sizeof(GamepadHardwareBuffer)));
+ void* mem = shared_memory->memory();
+
+ GamepadHardwareBuffer* hwbuf = static_cast<GamepadHardwareBuffer*>(mem);
+ // See gamepad_hardware_buffer.h for details on the read discipline.
+ WebGamepads output;
+
+ base::subtle::Atomic32 version;
+ do {
+ version = hwbuf->sequence.ReadBegin();
+ memcpy(&output, &hwbuf->buffer, sizeof(output));
+ } while (hwbuf->sequence.ReadRetry(version));
+
+ EXPECT_EQ(1u, output.length);
+ EXPECT_EQ(1u, output.items[0].buttonsLength);
+ EXPECT_EQ(1.f, output.items[0].buttons[0]);
+ EXPECT_EQ(2u, output.items[0].axesLength);
+ EXPECT_EQ(-1.f, output.items[0].axes[0]);
+ EXPECT_EQ(0.5f, output.items[0].axes[1]);
+}
+
+// Tests that waiting for a user gesture works properly.
+TEST_F(GamepadProviderTest, UserGesture) {
+ WebGamepads no_button_data;
+ no_button_data.length = 1;
+ no_button_data.items[0].connected = true;
+ no_button_data.items[0].timestamp = 0;
+ no_button_data.items[0].buttonsLength = 1;
+ no_button_data.items[0].axesLength = 2;
+ no_button_data.items[0].buttons[0] = 0.f;
+ no_button_data.items[0].axes[0] = -1.f;
+ no_button_data.items[0].axes[1] = .5f;
+
+ WebGamepads button_down_data = no_button_data;
+ button_down_data.items[0].buttons[0] = 1.f;
+
+ UserGestureListener listener;
+ GamepadProvider* provider = CreateProvider(no_button_data);
+ provider->Resume();
+
+ // Register for a user gesture and make sure the provider reads it twice
+ // see below for why).
+ provider->RegisterForUserGesture(listener.GetClosure());
+ mock_data_fetcher_->WaitForDataRead();
+ mock_data_fetcher_->WaitForDataRead();
+
+ // It should not have issued our callback.
+ message_loop().RunUntilIdle();
+ EXPECT_FALSE(listener.has_user_gesture());
+
+ // Set a button down and wait for it to be read twice.
+ //
+ // We wait for two reads before calling RunAllPending because the provider
+ // will read the data on the background thread (setting the event) and *then*
+ // will issue the callback on our thread. Waiting for it to read twice
+ // ensures that it was able to issue callbacks for the first read (if it
+ // issued one) before we try to check for it.
+ mock_data_fetcher_->SetTestData(button_down_data);
+ mock_data_fetcher_->WaitForDataRead();
+ mock_data_fetcher_->WaitForDataRead();
+
+ // It should have issued our callback.
+ message_loop().RunUntilIdle();
+ EXPECT_TRUE(listener.has_user_gesture());
+}
+
+} // namespace
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_service.cc b/chromium/content/browser/gamepad/gamepad_service.cc
new file mode 100644
index 00000000000..11a6964b3ab
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_service.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_service.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "content/browser/gamepad/gamepad_provider.h"
+#include "content/public/browser/render_process_host.h"
+
+namespace content {
+
+GamepadService::GamepadService() : num_readers_(0) {
+}
+
+GamepadService::GamepadService(scoped_ptr<GamepadDataFetcher> fetcher)
+ : num_readers_(0),
+ provider_(new GamepadProvider(fetcher.Pass())) {
+ thread_checker_.DetachFromThread();
+}
+
+GamepadService::~GamepadService() {
+}
+
+GamepadService* GamepadService::GetInstance() {
+ return Singleton<GamepadService,
+ LeakySingletonTraits<GamepadService> >::get();
+}
+
+void GamepadService::AddConsumer() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ num_readers_++;
+ DCHECK(num_readers_ > 0);
+ if (!provider_)
+ provider_.reset(new GamepadProvider);
+ provider_->Resume();
+}
+
+void GamepadService::RemoveConsumer() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ --num_readers_;
+ DCHECK(num_readers_ >= 0);
+
+ if (num_readers_ == 0)
+ provider_->Pause();
+}
+
+void GamepadService::RegisterForUserGesture(const base::Closure& closure) {
+ DCHECK(num_readers_ > 0);
+ DCHECK(thread_checker_.CalledOnValidThread());
+ provider_->RegisterForUserGesture(closure);
+}
+
+void GamepadService::Terminate() {
+ provider_.reset();
+}
+
+base::SharedMemoryHandle GamepadService::GetSharedMemoryHandleForProcess(
+ base::ProcessHandle handle) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return provider_->GetSharedMemoryHandleForProcess(handle);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_service.h b/chromium/content/browser/gamepad/gamepad_service.h
new file mode 100644
index 00000000000..94620b34b60
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_service.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory.h"
+#include "base/memory/singleton.h"
+#include "base/threading/thread_checker.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+class GamepadDataFetcher;
+class GamepadProvider;
+class GamepadServiceTestConstructor;
+class RenderProcessHost;
+
+// Owns the GamepadProvider (the background polling thread) and keeps track of
+// the number of consumers currently using the data (and pausing the provider
+// when not in use).
+class CONTENT_EXPORT GamepadService {
+ public:
+ // Returns the GamepadService singleton.
+ static GamepadService* GetInstance();
+
+ // Increments the number of users of the provider. The Provider is running
+ // when there's > 0 users, and is paused when the count drops to 0.
+ //
+ // Must be called on the I/O thread.
+ void AddConsumer();
+
+ // Removes a consumer. Should be matched with an AddConsumer call.
+ //
+ // Must be called on the I/O thread.
+ void RemoveConsumer();
+
+ // Registers the given closure for calling when the user has interacted with
+ // the device. This callback will only be issued once. Should only be called
+ // while a consumer is active.
+ void RegisterForUserGesture(const base::Closure& closure);
+
+ // Returns the shared memory handle of the gamepad data duplicated into the
+ // given process.
+ base::SharedMemoryHandle GetSharedMemoryHandleForProcess(
+ base::ProcessHandle handle);
+
+ // Stop/join with the background thread in GamepadProvider |provider_|.
+ void Terminate();
+
+ private:
+ friend struct DefaultSingletonTraits<GamepadService>;
+ friend class GamepadServiceTestConstructor;
+
+ GamepadService();
+
+ // Constructor for testing. This specifies the data fetcher to use for a
+ // provider, bypassing the default platform one.
+ GamepadService(scoped_ptr<GamepadDataFetcher> fetcher);
+
+ virtual ~GamepadService();
+
+ int num_readers_;
+ scoped_ptr<GamepadProvider> provider_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadService);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H_
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings.h b/chromium/content/browser/gamepad/gamepad_standard_mappings.h
new file mode 100644
index 00000000000..6f5d72fc0a7
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_STANDARD_MAPPINGS_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_STANDARD_MAPPINGS_H_
+
+#include "base/strings/string_piece.h"
+
+namespace WebKit {
+class WebGamepad;
+}
+
+namespace content {
+
+typedef void (*GamepadStandardMappingFunction)(
+ const WebKit::WebGamepad& original,
+ WebKit::WebGamepad* mapped);
+
+GamepadStandardMappingFunction GetGamepadStandardMappingFunction(
+ const base::StringPiece& vendor_id,
+ const base::StringPiece& product_id);
+
+// This defines our canonical mapping order for gamepad-like devices. If these
+// items cannot all be satisfied, it is a case-by-case judgement as to whether
+// it is better to leave the device unmapped, or to partially map it. In
+// general, err towards leaving it *unmapped* so that content can handle
+// appropriately.
+
+enum CanonicalButtonIndex {
+ kButtonPrimary,
+ kButtonSecondary,
+ kButtonTertiary,
+ kButtonQuaternary,
+ kButtonLeftShoulder,
+ kButtonRightShoulder,
+ kButtonLeftTrigger,
+ kButtonRightTrigger,
+ kButtonBackSelect,
+ kButtonStart,
+ kButtonLeftThumbstick,
+ kButtonRightThumbstick,
+ kButtonDpadUp,
+ kButtonDpadDown,
+ kButtonDpadLeft,
+ kButtonDpadRight,
+ kButtonMeta,
+ kNumButtons
+};
+
+enum CanonicalAxisIndex {
+ kAxisLeftStickX,
+ kAxisLeftStickY,
+ kAxisRightStickX,
+ kAxisRightStickY,
+ kNumAxes
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_STANDARD_MAPPINGS_H_
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc b/chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc
new file mode 100644
index 00000000000..e9e7094c532
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings_linux.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+
+#include "content/common/gamepad_hardware_buffer.h"
+
+namespace content {
+
+namespace {
+
+float AxisToButton(float input) {
+ return (input + 1.f) / 2.f;
+}
+
+float AxisNegativeAsButton(float input) {
+ return (input < -0.5f) ? 1.f : 0.f;
+}
+
+float AxisPositiveAsButton(float input) {
+ return (input > 0.5f) ? 1.f : 0.f;
+}
+
+void MapperXInputStyleGamepad(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[2]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[5]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[6];
+ mapped->buttons[kButtonStart] = input.buttons[7];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[9];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[10];
+ mapped->buttons[kButtonDpadUp] = AxisNegativeAsButton(input.axes[7]);
+ mapped->buttons[kButtonDpadDown] = AxisPositiveAsButton(input.axes[7]);
+ mapped->buttons[kButtonDpadLeft] = AxisNegativeAsButton(input.axes[6]);
+ mapped->buttons[kButtonDpadRight] = AxisPositiveAsButton(input.axes[6]);
+ mapped->buttons[kButtonMeta] = input.buttons[8];
+ mapped->axes[kAxisRightStickX] = input.axes[3];
+ mapped->axes[kAxisRightStickY] = input.axes[4];
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperLakeviewResearch(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[3];
+ mapped->buttons[kButtonQuaternary] = input.buttons[0];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[6];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[7];
+ mapped->buttons[kButtonLeftTrigger] = input.buttons[4];
+ mapped->buttons[kButtonRightTrigger] = input.buttons[5];
+ mapped->buttons[kButtonBackSelect] = input.buttons[9];
+ mapped->buttons[kButtonStart] = input.buttons[8];
+ mapped->buttons[kButtonDpadUp] = AxisNegativeAsButton(input.axes[5]);
+ mapped->buttons[kButtonDpadDown] = AxisPositiveAsButton(input.axes[5]);
+ mapped->buttons[kButtonDpadLeft] = AxisNegativeAsButton(input.axes[4]);
+ mapped->buttons[kButtonDpadRight] = AxisPositiveAsButton(input.axes[4]);
+ mapped->buttonsLength = kNumButtons - 1; // no Meta on this device
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperPlaystationSixAxis(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[14];
+ mapped->buttons[kButtonSecondary] = input.buttons[13];
+ mapped->buttons[kButtonTertiary] = input.buttons[15];
+ mapped->buttons[kButtonQuaternary] = input.buttons[12];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[10];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[11];
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[12]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[13]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[0];
+ mapped->buttons[kButtonStart] = input.buttons[3];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[1];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[2];
+ mapped->buttons[kButtonDpadUp] = AxisToButton(input.axes[8]);
+ mapped->buttons[kButtonDpadDown] = AxisToButton(input.axes[10]);
+ mapped->buttons[kButtonDpadLeft] = input.buttons[7];
+ mapped->buttons[kButtonDpadRight] = AxisToButton(input.axes[9]);
+ mapped->buttons[kButtonMeta] = input.buttons[16];
+
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperXGEAR(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[2];
+ mapped->buttons[kButtonSecondary] = input.buttons[1];
+ mapped->buttons[kButtonTertiary] = input.buttons[3];
+ mapped->buttons[kButtonQuaternary] = input.buttons[0];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[6];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[7];
+ mapped->buttons[kButtonLeftTrigger] = input.buttons[4];
+ mapped->buttons[kButtonRightTrigger] = input.buttons[5];
+ mapped->buttons[kButtonDpadUp] = AxisNegativeAsButton(input.axes[5]);
+ mapped->buttons[kButtonDpadDown] = AxisPositiveAsButton(input.axes[5]);
+ mapped->buttons[kButtonDpadLeft] = AxisNegativeAsButton(input.axes[4]);
+ mapped->buttons[kButtonDpadRight] = AxisPositiveAsButton(input.axes[4]);
+ mapped->axes[kAxisRightStickX] = input.axes[3];
+ mapped->axes[kAxisRightStickY] = input.axes[2];
+ mapped->buttonsLength = kNumButtons - 1; // no Meta on this device
+ mapped->axesLength = kNumAxes;
+}
+
+
+void MapperDragonRiseGeneric(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonDpadUp] = AxisNegativeAsButton(input.axes[6]);
+ mapped->buttons[kButtonDpadDown] = AxisPositiveAsButton(input.axes[6]);
+ mapped->buttons[kButtonDpadLeft] = AxisNegativeAsButton(input.axes[5]);
+ mapped->buttons[kButtonDpadRight] = AxisPositiveAsButton(input.axes[5]);
+ mapped->axes[kAxisLeftStickX] = input.axes[0];
+ mapped->axes[kAxisLeftStickY] = input.axes[1];
+ mapped->axes[kAxisRightStickX] = input.axes[3];
+ mapped->axes[kAxisRightStickY] = input.axes[4];
+ mapped->buttonsLength = kNumButtons - 1; // no Meta on this device
+ mapped->axesLength = kNumAxes;
+}
+
+
+struct MappingData {
+ const char* const vendor_id;
+ const char* const product_id;
+ GamepadStandardMappingFunction function;
+} AvailableMappings[] = {
+ // http://www.linux-usb.org/usb.ids
+ { "0079", "0006", MapperDragonRiseGeneric }, // DragonRise Generic USB
+ { "045e", "028e", MapperXInputStyleGamepad }, // Xbox 360 Controller
+ { "045e", "028f", MapperXInputStyleGamepad }, // Xbox 360 Wireless Controller
+ { "046d", "c21d", MapperXInputStyleGamepad }, // Logitech F310
+ { "046d", "c21e", MapperXInputStyleGamepad }, // Logitech F510
+ { "046d", "c21f", MapperXInputStyleGamepad }, // Logitech F710
+ { "054c", "0268", MapperPlaystationSixAxis }, // Playstation SIXAXIS
+ { "0925", "0005", MapperLakeviewResearch }, // SmartJoy PLUS Adapter
+ { "0925", "8866", MapperLakeviewResearch }, // WiseGroup MP-8866
+ { "0e8f", "0003", MapperXGEAR }, // XFXforce XGEAR PS2 Controller
+};
+
+} // namespace
+
+GamepadStandardMappingFunction GetGamepadStandardMappingFunction(
+ const base::StringPiece& vendor_id,
+ const base::StringPiece& product_id) {
+ for (size_t i = 0; i < arraysize(AvailableMappings); ++i) {
+ MappingData& item = AvailableMappings[i];
+ if (vendor_id == item.vendor_id && product_id == item.product_id)
+ return item.function;
+ }
+ return NULL;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm b/chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm
new file mode 100644
index 00000000000..0ffb35f8fec
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings_mac.mm
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+
+#include "content/common/gamepad_hardware_buffer.h"
+
+namespace content {
+
+namespace {
+
+float AxisToButton(float input) {
+ return (input + 1.f) / 2.f;
+}
+
+void DpadFromAxis(WebKit::WebGamepad* mapped, float dir) {
+ // Dpad is mapped as a direction on one axis, where -1 is up and it
+ // increases clockwise to 1, which is up + left. It's set to a large (> 1.f)
+ // number when nothing is depressed, except on start up, sometimes it's 0.0
+ // for no data, rather than the large number.
+ if (dir == 0.0f) {
+ mapped->buttons[kButtonDpadUp] = 0.f;
+ mapped->buttons[kButtonDpadDown] = 0.f;
+ mapped->buttons[kButtonDpadLeft] = 0.f;
+ mapped->buttons[kButtonDpadRight] = 0.f;
+ } else {
+ mapped->buttons[kButtonDpadUp] = (dir >= -1.f && dir < -0.7f) ||
+ (dir >= .95f && dir <= 1.f);
+ mapped->buttons[kButtonDpadRight] = dir >= -.75f && dir < -.1f;
+ mapped->buttons[kButtonDpadDown] = dir >= -.2f && dir < .45f;
+ mapped->buttons[kButtonDpadLeft] = dir >= .4f && dir <= 1.f;
+ }
+}
+
+void MapperXbox360Gamepad(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonLeftTrigger] = AxisToButton(input.axes[2]);
+ mapped->buttons[kButtonRightTrigger] = AxisToButton(input.axes[5]);
+ mapped->buttons[kButtonBackSelect] = input.buttons[9];
+ mapped->buttons[kButtonStart] = input.buttons[8];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[6];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[7];
+ mapped->buttons[kButtonDpadUp] = input.buttons[11];
+ mapped->buttons[kButtonDpadDown] = input.buttons[12];
+ mapped->buttons[kButtonDpadLeft] = input.buttons[13];
+ mapped->buttons[kButtonDpadRight] = input.buttons[14];
+ mapped->buttons[kButtonMeta] = input.buttons[10];
+ mapped->axes[kAxisRightStickX] = input.axes[3];
+ mapped->axes[kAxisRightStickY] = input.axes[4];
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperPlaystationSixAxis(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[14];
+ mapped->buttons[kButtonSecondary] = input.buttons[13];
+ mapped->buttons[kButtonTertiary] = input.buttons[15];
+ mapped->buttons[kButtonQuaternary] = input.buttons[12];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[10];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[11];
+ mapped->buttons[kButtonLeftTrigger] = input.buttons[8];
+ mapped->buttons[kButtonRightTrigger] = input.buttons[9];
+ mapped->buttons[kButtonBackSelect] = input.buttons[0];
+ mapped->buttons[kButtonStart] = input.buttons[3];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[1];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[2];
+ mapped->buttons[kButtonDpadUp] = input.buttons[4];
+ mapped->buttons[kButtonDpadDown] = input.buttons[6];
+ mapped->buttons[kButtonDpadLeft] = input.buttons[7];
+ mapped->buttons[kButtonDpadRight] = input.buttons[5];
+ mapped->buttons[kButtonMeta] = input.buttons[16];
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+
+ mapped->buttonsLength = kNumButtons;
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperDirectInputStyle(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[1];
+ mapped->buttons[kButtonSecondary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[0];
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+ DpadFromAxis(mapped, input.axes[9]);
+ mapped->buttonsLength = kNumButtons - 1; /* no meta */
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperMacallyIShock(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ enum IShockButtons {
+ kButtonC = kNumButtons,
+ kButtonD,
+ kButtonE,
+ kNumIShockButtons
+ };
+
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[6];
+ mapped->buttons[kButtonSecondary] = input.buttons[5];
+ mapped->buttons[kButtonTertiary] = input.buttons[7];
+ mapped->buttons[kButtonQuaternary] = input.buttons[4];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[14];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[12];
+ mapped->buttons[kButtonLeftTrigger] = input.buttons[15];
+ mapped->buttons[kButtonRightTrigger] = input.buttons[13];
+ mapped->buttons[kButtonBackSelect] = input.buttons[9];
+ mapped->buttons[kButtonStart] = input.buttons[10];
+ mapped->buttons[kButtonLeftThumbstick] = input.buttons[16];
+ mapped->buttons[kButtonRightThumbstick] = input.buttons[17];
+ mapped->buttons[kButtonDpadUp] = input.buttons[0];
+ mapped->buttons[kButtonDpadDown] = input.buttons[1];
+ mapped->buttons[kButtonDpadLeft] = input.buttons[2];
+ mapped->buttons[kButtonDpadRight] = input.buttons[3];
+ mapped->buttons[kButtonMeta] = input.buttons[11];
+ mapped->buttons[kButtonC] = input.buttons[8];
+ mapped->buttons[kButtonD] = input.buttons[18];
+ mapped->buttons[kButtonE] = input.buttons[19];
+ mapped->axes[kAxisLeftStickX] = input.axes[0];
+ mapped->axes[kAxisLeftStickY] = input.axes[1];
+ mapped->axes[kAxisRightStickX] = -input.axes[5];
+ mapped->axes[kAxisRightStickY] = input.axes[6];
+
+ mapped->buttonsLength = kNumIShockButtons;
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperXGEAR(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[3];
+ mapped->buttons[kButtonQuaternary] = input.buttons[0];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[6];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[7];
+ mapped->buttons[kButtonLeftTrigger] = input.buttons[4];
+ mapped->buttons[kButtonRightTrigger] = input.buttons[5];
+ DpadFromAxis(mapped, input.axes[9]);
+ mapped->axes[kAxisRightStickX] = input.axes[5];
+ mapped->axes[kAxisRightStickY] = input.axes[2];
+ mapped->buttonsLength = kNumButtons - 1; /* no meta */
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperSmartJoyPLUS(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonPrimary] = input.buttons[2];
+ mapped->buttons[kButtonTertiary] = input.buttons[3];
+ mapped->buttons[kButtonQuaternary] = input.buttons[0];
+ mapped->buttons[kButtonStart] = input.buttons[8];
+ mapped->buttons[kButtonBackSelect] = input.buttons[9];
+ mapped->buttons[kButtonLeftShoulder] = input.buttons[6];
+ mapped->buttons[kButtonRightShoulder] = input.buttons[7];
+ mapped->buttons[kButtonLeftTrigger] = input.buttons[4];
+ mapped->buttons[kButtonRightTrigger] = input.buttons[5];
+ DpadFromAxis(mapped, input.axes[9]);
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+ mapped->buttonsLength = kNumButtons - 1; /* no meta */
+ mapped->axesLength = kNumAxes;
+}
+
+void MapperDragonRiseGeneric(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ DpadFromAxis(mapped, input.axes[9]);
+ mapped->axes[kAxisLeftStickX] = input.axes[0];
+ mapped->axes[kAxisLeftStickY] = input.axes[1];
+ mapped->axes[kAxisRightStickX] = input.axes[2];
+ mapped->axes[kAxisRightStickY] = input.axes[5];
+ mapped->buttonsLength = kNumButtons - 1; /* no meta */
+ mapped->axesLength = kNumAxes;
+}
+
+struct MappingData {
+ const char* const vendor_id;
+ const char* const product_id;
+ GamepadStandardMappingFunction function;
+} AvailableMappings[] = {
+ // http://www.linux-usb.org/usb.ids
+ { "0079", "0006", MapperDragonRiseGeneric }, // DragonRise Generic USB
+ { "045e", "028e", MapperXbox360Gamepad }, // Xbox 360 Controller
+ { "045e", "028f", MapperXbox360Gamepad }, // Xbox 360 Wireless Controller
+ { "046d", "c216", MapperDirectInputStyle }, // Logitech F310, D mode
+ { "046d", "c218", MapperDirectInputStyle }, // Logitech F510, D mode
+ { "046d", "c219", MapperDirectInputStyle }, // Logitech F710, D mode
+ { "054c", "0268", MapperPlaystationSixAxis }, // Playstation SIXAXIS
+ { "0925", "0005", MapperSmartJoyPLUS }, // SmartJoy PLUS Adapter
+ { "0e8f", "0003", MapperXGEAR }, // XFXforce XGEAR PS2 Controller
+ { "2222", "0060", MapperDirectInputStyle }, // Macally iShockX, analog mode
+ { "2222", "4010", MapperMacallyIShock }, // Macally iShock
+};
+
+} // namespace
+
+GamepadStandardMappingFunction GetGamepadStandardMappingFunction(
+ const base::StringPiece& vendor_id,
+ const base::StringPiece& product_id) {
+ for (size_t i = 0; i < arraysize(AvailableMappings); ++i) {
+ MappingData& item = AvailableMappings[i];
+ if (vendor_id == item.vendor_id && product_id == item.product_id)
+ return item.function;
+ }
+ return NULL;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc b/chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc
new file mode 100644
index 00000000000..fa57c032c77
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_standard_mappings_win.cc
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_standard_mappings.h"
+
+#include "content/common/gamepad_hardware_buffer.h"
+
+namespace content {
+
+namespace {
+
+// Maps 0..65535 to -1..1.
+float NormalizeDirectInputAxis(long value) {
+ return (value * 1.f / 32767.5f) - 1.f;
+}
+
+float AxisNegativeAsButton(long value) {
+ return (value < 32767) ? 1.f : 0.f;
+}
+
+float AxisPositiveAsButton(long value) {
+ return (value > 32767) ? 1.f : 0.f;
+}
+
+void MapperDragonRiseGeneric(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[0] = input.buttons[1];
+ mapped->buttons[1] = input.buttons[2];
+ mapped->buttons[2] = input.buttons[0];
+ mapped->buttons[12] = input.buttons[16];
+ mapped->buttons[13] = input.buttons[17];
+ mapped->buttons[14] = input.buttons[18];
+ mapped->buttons[15] = input.buttons[19];
+ mapped->buttonsLength = 16;
+ mapped->axes[0] = NormalizeDirectInputAxis(input.axes[0]);
+ mapped->axes[1] = NormalizeDirectInputAxis(input.axes[1]);
+ mapped->axes[2] = NormalizeDirectInputAxis(input.axes[2]);
+ mapped->axes[3] = NormalizeDirectInputAxis(input.axes[5]);
+ mapped->axesLength = 4;
+}
+
+void MapperLogitechDualAction(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[0] = input.buttons[1];
+ mapped->buttons[1] = input.buttons[2];
+ mapped->buttons[2] = input.buttons[0];
+ mapped->buttons[12] = input.buttons[16];
+ mapped->buttons[13] = input.buttons[17];
+ mapped->buttons[14] = input.buttons[18];
+ mapped->buttons[15] = input.buttons[19];
+ mapped->buttonsLength = 16;
+ mapped->axes[0] = NormalizeDirectInputAxis(input.axes[0]);
+ mapped->axes[1] = NormalizeDirectInputAxis(input.axes[1]);
+ mapped->axes[2] = NormalizeDirectInputAxis(input.axes[2]);
+ mapped->axes[3] = NormalizeDirectInputAxis(input.axes[5]);
+ mapped->axesLength = 4;
+}
+
+void MapperLogitechPrecision(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[0] = input.buttons[1];
+ mapped->buttons[1] = input.buttons[2];
+ mapped->buttons[2] = input.buttons[0];
+ mapped->buttons[kButtonLeftThumbstick] = 0; // Not present
+ mapped->buttons[kButtonRightThumbstick] = 0; // Not present
+ mapped->buttons[12] = AxisNegativeAsButton(input.axes[1]);
+ mapped->buttons[13] = AxisPositiveAsButton(input.axes[1]);
+ mapped->buttons[14] = AxisNegativeAsButton(input.axes[0]);
+ mapped->buttons[15] = AxisPositiveAsButton(input.axes[0]);
+ mapped->buttonsLength = 16;
+ mapped->axesLength = 0;
+}
+
+void Mapper2Axes8Keys(
+ const WebKit::WebGamepad& input,
+ WebKit::WebGamepad* mapped) {
+ *mapped = input;
+ mapped->buttons[kButtonLeftTrigger] = 0; // Not present
+ mapped->buttons[kButtonRightTrigger] = 0; // Not present
+ mapped->buttons[8] = input.buttons[6];
+ mapped->buttons[9] = input.buttons[7];
+ mapped->buttons[kButtonLeftThumbstick] = 0; // Not present
+ mapped->buttons[kButtonRightThumbstick] = 0; // Not present
+ mapped->buttons[12] = AxisNegativeAsButton(input.axes[1]);
+ mapped->buttons[13] = AxisPositiveAsButton(input.axes[1]);
+ mapped->buttons[14] = AxisNegativeAsButton(input.axes[0]);
+ mapped->buttons[15] = AxisPositiveAsButton(input.axes[0]);
+ mapped->buttonsLength = 16;
+ mapped->axesLength = 0;
+}
+
+struct MappingData {
+ const char* const vendor_id;
+ const char* const product_id;
+ GamepadStandardMappingFunction function;
+} AvailableMappings[] = {
+ // http://www.linux-usb.org/usb.ids
+ { "0079", "0006", MapperDragonRiseGeneric }, // DragonRise Generic USB
+ { "046d", "c216", MapperLogitechDualAction }, // Logitech DualAction
+ { "046d", "c21a", MapperLogitechPrecision }, // Logitech Precision
+ { "12bd", "d012", Mapper2Axes8Keys }, // 2Axes 8Keys Game Pad
+};
+
+} // namespace
+
+GamepadStandardMappingFunction GetGamepadStandardMappingFunction(
+ const base::StringPiece& vendor_id,
+ const base::StringPiece& product_id) {
+ for (size_t i = 0; i < arraysize(AvailableMappings); ++i) {
+ MappingData& item = AvailableMappings[i];
+ if (vendor_id == item.vendor_id && product_id == item.product_id)
+ return item.function;
+ }
+ return NULL;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_test_helpers.cc b/chromium/content/browser/gamepad/gamepad_test_helpers.cc
new file mode 100644
index 00000000000..e4db14ce080
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_test_helpers.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/gamepad_test_helpers.h"
+
+#include "content/browser/gamepad/gamepad_service.h"
+
+namespace content {
+
+MockGamepadDataFetcher::MockGamepadDataFetcher(
+ const WebKit::WebGamepads& test_data)
+ : test_data_(test_data),
+ read_data_(false, false) {
+}
+
+MockGamepadDataFetcher::~MockGamepadDataFetcher() {
+}
+
+void MockGamepadDataFetcher::GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) {
+ {
+ base::AutoLock lock(lock_);
+ *pads = test_data_;
+ }
+ read_data_.Signal();
+}
+
+void MockGamepadDataFetcher::WaitForDataRead() {
+ return read_data_.Wait();
+}
+
+void MockGamepadDataFetcher::SetTestData(const WebKit::WebGamepads& new_data) {
+ base::AutoLock lock(lock_);
+ test_data_ = new_data;
+}
+
+GamepadTestHelper::GamepadTestHelper() {
+}
+
+GamepadTestHelper::~GamepadTestHelper() {
+}
+
+GamepadServiceTestConstructor::GamepadServiceTestConstructor(
+ const WebKit::WebGamepads& test_data) {
+ data_fetcher_ = new MockGamepadDataFetcher(test_data);
+ gamepad_service_ =
+ new GamepadService(scoped_ptr<GamepadDataFetcher>(data_fetcher_));
+}
+
+GamepadServiceTestConstructor::~GamepadServiceTestConstructor() {
+ delete gamepad_service_;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/gamepad/gamepad_test_helpers.h b/chromium/content/browser/gamepad/gamepad_test_helpers.h
new file mode 100644
index 00000000000..3b43f7787ff
--- /dev/null
+++ b/chromium/content/browser/gamepad/gamepad_test_helpers.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_TEST_HELPERS_H_
+#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_TEST_HELPERS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "content/browser/gamepad/gamepad_data_fetcher.h"
+#include "third_party/WebKit/public/platform/WebGamepads.h"
+
+namespace content {
+
+class GamepadService;
+
+// Data fetcher that returns canned data for the gamepad provider.
+class MockGamepadDataFetcher : public GamepadDataFetcher {
+ public:
+ // Initializes the fetcher with the given gamepad data, which will be
+ // returned when the provider queries us.
+ explicit MockGamepadDataFetcher(const WebKit::WebGamepads& test_data);
+
+ virtual ~MockGamepadDataFetcher();
+
+ // GamepadDataFetcher.
+ virtual void GetGamepadData(WebKit::WebGamepads* pads,
+ bool devices_changed_hint) OVERRIDE;
+
+ // Blocks the current thread until the GamepadProvider reads from this
+ // fetcher on the background thread.
+ void WaitForDataRead();
+
+ // Updates the test data.
+ void SetTestData(const WebKit::WebGamepads& new_data);
+
+ private:
+ base::Lock lock_;
+ WebKit::WebGamepads test_data_;
+ base::WaitableEvent read_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockGamepadDataFetcher);
+};
+
+// Base class for the other test helpers. This just sets up the system monitor.
+class GamepadTestHelper {
+ public:
+ GamepadTestHelper();
+ virtual ~GamepadTestHelper();
+
+ base::MessageLoop& message_loop() { return message_loop_; }
+
+ private:
+ // This must be constructed before the system monitor.
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadTestHelper);
+};
+
+// Constructs a GamepadService with a mock data source. This bypasses the
+// global singleton for the gamepad service.
+class GamepadServiceTestConstructor : public GamepadTestHelper {
+ public:
+ explicit GamepadServiceTestConstructor(const WebKit::WebGamepads& test_data);
+ virtual ~GamepadServiceTestConstructor();
+
+ GamepadService* gamepad_service() { return gamepad_service_; }
+ MockGamepadDataFetcher* data_fetcher() { return data_fetcher_; }
+
+ private:
+ // Owning pointer (can't be a scoped_ptr due to private destructor).
+ GamepadService* gamepad_service_;
+
+ // Pointer owned by the provider (which is owned by the gamepad service).
+ MockGamepadDataFetcher* data_fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(GamepadServiceTestConstructor);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_TEST_HELPERS_H_
diff --git a/chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc
new file mode 100644
index 00000000000..6619b9b48f7
--- /dev/null
+++ b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.cc
@@ -0,0 +1,625 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/gamepad/xbox_data_fetcher_mac.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/usb/USB.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+
+namespace {
+const int kVendorMicrosoft = 0x045e;
+const int kProduct360Controller = 0x028e;
+
+const int kReadEndpoint = 1;
+const int kControlEndpoint = 2;
+
+enum {
+ STATUS_MESSAGE_BUTTONS = 0,
+ STATUS_MESSAGE_LED = 1,
+
+ // Apparently this message tells you if the rumble pack is disabled in the
+ // controller. If the rumble pack is disabled, vibration control messages
+ // have no effect.
+ STATUS_MESSAGE_RUMBLE = 3,
+};
+
+enum {
+ CONTROL_MESSAGE_SET_RUMBLE = 0,
+ CONTROL_MESSAGE_SET_LED = 1,
+};
+
+#pragma pack(push, 1)
+struct ButtonData {
+ bool dpad_up : 1;
+ bool dpad_down : 1;
+ bool dpad_left : 1;
+ bool dpad_right : 1;
+
+ bool start : 1;
+ bool back : 1;
+ bool stick_left_click : 1;
+ bool stick_right_click : 1;
+
+ bool bumper_left : 1;
+ bool bumper_right : 1;
+ bool guide : 1;
+ bool dummy1 : 1; // Always 0.
+
+ bool a : 1;
+ bool b : 1;
+ bool x : 1;
+ bool y : 1;
+
+ uint8 trigger_left;
+ uint8 trigger_right;
+
+ int16 stick_left_x;
+ int16 stick_left_y;
+ int16 stick_right_x;
+ int16 stick_right_y;
+
+ // Always 0.
+ uint32 dummy2;
+ uint16 dummy3;
+};
+#pragma pack(pop)
+
+COMPILE_ASSERT(sizeof(ButtonData) == 0x12, xbox_button_data_wrong_size);
+
+// From MSDN:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ee417001(v=vs.85).aspx#dead_zone
+const int16 kLeftThumbDeadzone = 7849;
+const int16 kRightThumbDeadzone = 8689;
+const uint8 kTriggerDeadzone = 30;
+
+void NormalizeAxis(int16 x,
+ int16 y,
+ int16 deadzone,
+ float* x_out,
+ float* y_out) {
+ float x_val = x;
+ float y_val = y;
+
+ // Determine how far the stick is pushed.
+ float real_magnitude = std::sqrt(x_val * x_val + y_val * y_val);
+
+ // Check if the controller is outside a circular dead zone.
+ if (real_magnitude > deadzone) {
+ // Clip the magnitude at its expected maximum value.
+ float magnitude = std::min(32767.0f, real_magnitude);
+
+ // Adjust magnitude relative to the end of the dead zone.
+ magnitude -= deadzone;
+
+ // Normalize the magnitude with respect to its expected range giving a
+ // magnitude value of 0.0 to 1.0
+ float ratio = (magnitude / (32767 - deadzone)) / real_magnitude;
+
+ // Y is negated because xbox controllers have an opposite sign from
+ // the 'standard controller' recommendations.
+ *x_out = x_val * ratio;
+ *y_out = -y_val * ratio;
+ } else {
+ // If the controller is in the deadzone zero out the magnitude.
+ *x_out = *y_out = 0.0f;
+ }
+}
+
+float NormalizeTrigger(uint8 value) {
+ return value < kTriggerDeadzone ? 0 :
+ static_cast<float>(value - kTriggerDeadzone) /
+ (std::numeric_limits<uint8>::max() - kTriggerDeadzone);
+}
+
+void NormalizeButtonData(const ButtonData& data,
+ XboxController::Data* normalized_data) {
+ normalized_data->buttons[0] = data.a;
+ normalized_data->buttons[1] = data.b;
+ normalized_data->buttons[2] = data.x;
+ normalized_data->buttons[3] = data.y;
+ normalized_data->buttons[4] = data.bumper_left;
+ normalized_data->buttons[5] = data.bumper_right;
+ normalized_data->buttons[6] = data.back;
+ normalized_data->buttons[7] = data.start;
+ normalized_data->buttons[8] = data.stick_left_click;
+ normalized_data->buttons[9] = data.stick_right_click;
+ normalized_data->buttons[10] = data.dpad_up;
+ normalized_data->buttons[11] = data.dpad_down;
+ normalized_data->buttons[12] = data.dpad_left;
+ normalized_data->buttons[13] = data.dpad_right;
+ normalized_data->buttons[14] = data.guide;
+ normalized_data->triggers[0] = NormalizeTrigger(data.trigger_left);
+ normalized_data->triggers[1] = NormalizeTrigger(data.trigger_right);
+ NormalizeAxis(data.stick_left_x,
+ data.stick_left_y,
+ kLeftThumbDeadzone,
+ &normalized_data->axes[0],
+ &normalized_data->axes[1]);
+ NormalizeAxis(data.stick_right_x,
+ data.stick_right_y,
+ kRightThumbDeadzone,
+ &normalized_data->axes[2],
+ &normalized_data->axes[3]);
+}
+
+} // namespace
+
+XboxController::XboxController(Delegate* delegate)
+ : device_(NULL),
+ interface_(NULL),
+ device_is_open_(false),
+ interface_is_open_(false),
+ read_buffer_size_(0),
+ led_pattern_(LED_NUM_PATTERNS),
+ location_id_(0),
+ delegate_(delegate) {
+}
+
+XboxController::~XboxController() {
+ if (source_)
+ CFRunLoopSourceInvalidate(source_);
+ if (interface_ && interface_is_open_)
+ (*interface_)->USBInterfaceClose(interface_);
+ if (device_ && device_is_open_)
+ (*device_)->USBDeviceClose(device_);
+}
+
+bool XboxController::OpenDevice(io_service_t service) {
+ IOCFPlugInInterface **plugin;
+ SInt32 score; // Unused, but required for IOCreatePlugInInterfaceForService.
+ kern_return_t kr =
+ IOCreatePlugInInterfaceForService(service,
+ kIOUSBDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugin,
+ &score);
+ if (kr != KERN_SUCCESS)
+ return false;
+ base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_ref(plugin);
+
+ HRESULT res =
+ (*plugin)->QueryInterface(plugin,
+ CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320),
+ (LPVOID *)&device_);
+ if (!SUCCEEDED(res) || !device_)
+ return false;
+
+ UInt16 vendor_id;
+ kr = (*device_)->GetDeviceVendor(device_, &vendor_id);
+ if (kr != KERN_SUCCESS)
+ return false;
+ UInt16 product_id;
+ kr = (*device_)->GetDeviceProduct(device_, &product_id);
+ if (kr != KERN_SUCCESS)
+ return false;
+ if (vendor_id != kVendorMicrosoft || product_id != kProduct360Controller)
+ return false;
+
+ // Open the device and configure it.
+ kr = (*device_)->USBDeviceOpen(device_);
+ if (kr != KERN_SUCCESS)
+ return false;
+ device_is_open_ = true;
+
+ // Xbox controllers have one configuration option which has configuration
+ // value 1. Try to set it and fail if it couldn't be configured.
+ IOUSBConfigurationDescriptorPtr config_desc;
+ kr = (*device_)->GetConfigurationDescriptorPtr(device_, 0, &config_desc);
+ if (kr != KERN_SUCCESS)
+ return false;
+ kr = (*device_)->SetConfiguration(device_, config_desc->bConfigurationValue);
+ if (kr != KERN_SUCCESS)
+ return false;
+
+ // The device has 4 interfaces. They are as follows:
+ // Protocol 1:
+ // - Endpoint 1 (in) : Controller events, including button presses.
+ // - Endpoint 2 (out): Rumble pack and LED control
+ // Protocol 2 has a single endpoint to read from a connected ChatPad device.
+ // Protocol 3 is used by a connected headset device.
+ // The device also has an interface on subclass 253, protocol 10 with no
+ // endpoints. It is unused.
+ //
+ // We don't currently support the ChatPad or headset, so protocol 1 is the
+ // only protocol we care about.
+ //
+ // For more detail, see
+ // https://github.com/Grumbel/xboxdrv/blob/master/PROTOCOL
+ IOUSBFindInterfaceRequest request;
+ request.bInterfaceClass = 255;
+ request.bInterfaceSubClass = 93;
+ request.bInterfaceProtocol = 1;
+ request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+ io_iterator_t iter;
+ kr = (*device_)->CreateInterfaceIterator(device_, &request, &iter);
+ if (kr != KERN_SUCCESS)
+ return false;
+ base::mac::ScopedIOObject<io_iterator_t> iter_ref(iter);
+
+ // There should be exactly one USB interface which matches the requested
+ // settings.
+ io_service_t usb_interface = IOIteratorNext(iter);
+ if (!usb_interface)
+ return false;
+
+ // We need to make an InterfaceInterface to communicate with the device
+ // endpoint. This is the same process as earlier: first make a
+ // PluginInterface from the io_service then make the InterfaceInterface from
+ // that.
+ IOCFPlugInInterface **plugin_interface;
+ kr = IOCreatePlugInInterfaceForService(usb_interface,
+ kIOUSBInterfaceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugin_interface,
+ &score);
+ if (kr != KERN_SUCCESS || !plugin_interface)
+ return false;
+ base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> interface_ref(
+ plugin_interface);
+
+ // Release the USB interface, and any subsequent interfaces returned by the
+ // iterator. (There shouldn't be any, but in case a future device does
+ // contain more interfaces, this will serve to avoid memory leaks.)
+ do {
+ IOObjectRelease(usb_interface);
+ } while ((usb_interface = IOIteratorNext(iter)));
+
+ // Actually create the interface.
+ res = (*plugin_interface)->QueryInterface(
+ plugin_interface,
+ CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300),
+ (LPVOID *)&interface_);
+
+ if (!SUCCEEDED(res) || !interface_)
+ return false;
+
+ // Actually open the interface.
+ kr = (*interface_)->USBInterfaceOpen(interface_);
+ if (kr != KERN_SUCCESS)
+ return false;
+ interface_is_open_ = true;
+
+ CFRunLoopSourceRef source_ref;
+ kr = (*interface_)->CreateInterfaceAsyncEventSource(interface_, &source_ref);
+ if (kr != KERN_SUCCESS || !source_ref)
+ return false;
+ source_.reset(source_ref);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopDefaultMode);
+
+ // The interface should have two pipes. Pipe 1 with direction kUSBIn and pipe
+ // 2 with direction kUSBOut. Both pipes should have type kUSBInterrupt.
+ uint8 num_endpoints;
+ kr = (*interface_)->GetNumEndpoints(interface_, &num_endpoints);
+ if (kr != KERN_SUCCESS || num_endpoints < 2)
+ return false;
+
+ for (int i = 1; i <= 2; i++) {
+ uint8 direction;
+ uint8 number;
+ uint8 transfer_type;
+ uint16 max_packet_size;
+ uint8 interval;
+
+ kr = (*interface_)->GetPipeProperties(interface_,
+ i,
+ &direction,
+ &number,
+ &transfer_type,
+ &max_packet_size,
+ &interval);
+ if (kr != KERN_SUCCESS || transfer_type != kUSBInterrupt)
+ return false;
+ if (i == kReadEndpoint) {
+ if (direction != kUSBIn)
+ return false;
+ if (max_packet_size > 32)
+ return false;
+ read_buffer_.reset(new uint8[max_packet_size]);
+ read_buffer_size_ = max_packet_size;
+ QueueRead();
+ } else if (i == kControlEndpoint) {
+ if (direction != kUSBOut)
+ return false;
+ }
+ }
+
+ // The location ID is unique per controller, and can be used to track
+ // controllers through reconnections (though if a controller is detached from
+ // one USB hub and attached to another, the location ID will change).
+ kr = (*device_)->GetLocationID(device_, &location_id_);
+ if (kr != KERN_SUCCESS)
+ return false;
+
+ return true;
+}
+
+void XboxController::SetLEDPattern(LEDPattern pattern) {
+ led_pattern_ = pattern;
+ const UInt8 length = 3;
+
+ // This buffer will be released in WriteComplete when WritePipeAsync
+ // finishes.
+ UInt8* buffer = new UInt8[length];
+ buffer[0] = static_cast<UInt8>(CONTROL_MESSAGE_SET_LED);
+ buffer[1] = length;
+ buffer[2] = static_cast<UInt8>(pattern);
+ kern_return_t kr = (*interface_)->WritePipeAsync(interface_,
+ kControlEndpoint,
+ buffer,
+ (UInt32)length,
+ WriteComplete,
+ buffer);
+ if (kr != KERN_SUCCESS) {
+ delete[] buffer;
+ IOError();
+ return;
+ }
+}
+
+int XboxController::GetVendorId() const {
+ return kVendorMicrosoft;
+}
+
+int XboxController::GetProductId() const {
+ return kProduct360Controller;
+}
+
+void XboxController::WriteComplete(void* context, IOReturn result, void* arg0) {
+ UInt8* buffer = static_cast<UInt8*>(context);
+ delete[] buffer;
+
+ // Ignoring any errors sending data, because they will usually only occur
+ // when the device is disconnected, in which case it really doesn't matter if
+ // the data got to the controller or not.
+ if (result != kIOReturnSuccess)
+ return;
+}
+
+void XboxController::GotData(void* context, IOReturn result, void* arg0) {
+ size_t bytes_read = reinterpret_cast<size_t>(arg0);
+ XboxController* controller = static_cast<XboxController*>(context);
+
+ if (result != kIOReturnSuccess) {
+ // This will happen if the device was disconnected. The gamepad has
+ // probably been destroyed by a meteorite.
+ controller->IOError();
+ return;
+ }
+
+ controller->ProcessPacket(bytes_read);
+
+ // Queue up another read.
+ controller->QueueRead();
+}
+
+void XboxController::ProcessPacket(size_t length) {
+ if (length < 2) return;
+ DCHECK(length <= read_buffer_size_);
+ if (length > read_buffer_size_) {
+ IOError();
+ return;
+ }
+ uint8* buffer = read_buffer_.get();
+
+ if (buffer[1] != length)
+ // Length in packet doesn't match length reported by USB.
+ return;
+
+ uint8 type = buffer[0];
+ buffer += 2;
+ length -= 2;
+ switch (type) {
+ case STATUS_MESSAGE_BUTTONS: {
+ if (length != sizeof(ButtonData))
+ return;
+ ButtonData* data = reinterpret_cast<ButtonData*>(buffer);
+ Data normalized_data;
+ NormalizeButtonData(*data, &normalized_data);
+ delegate_->XboxControllerGotData(this, normalized_data);
+ break;
+ }
+ case STATUS_MESSAGE_LED:
+ if (length != 3)
+ return;
+ // The controller sends one of these messages every time the LED pattern
+ // is set, as well as once when it is plugged in.
+ if (led_pattern_ == LED_NUM_PATTERNS && buffer[0] < LED_NUM_PATTERNS)
+ led_pattern_ = static_cast<LEDPattern>(buffer[0]);
+ break;
+ default:
+ // Unknown packet: ignore!
+ break;
+ }
+}
+
+void XboxController::QueueRead() {
+ kern_return_t kr = (*interface_)->ReadPipeAsync(interface_,
+ kReadEndpoint,
+ read_buffer_.get(),
+ read_buffer_size_,
+ GotData,
+ this);
+ if (kr != KERN_SUCCESS)
+ IOError();
+}
+
+void XboxController::IOError() {
+ delegate_->XboxControllerError(this);
+}
+
+//-----------------------------------------------------------------------------
+
+XboxDataFetcher::XboxDataFetcher(Delegate* delegate)
+ : delegate_(delegate),
+ listening_(false),
+ source_(NULL),
+ port_(NULL) {
+}
+
+XboxDataFetcher::~XboxDataFetcher() {
+ while (!controllers_.empty()) {
+ RemoveController(*controllers_.begin());
+ }
+ UnregisterFromNotifications();
+}
+
+void XboxDataFetcher::DeviceAdded(void* context, io_iterator_t iterator) {
+ DCHECK(context);
+ XboxDataFetcher* fetcher = static_cast<XboxDataFetcher*>(context);
+ io_service_t ref;
+ while ((ref = IOIteratorNext(iterator))) {
+ base::mac::ScopedIOObject<io_service_t> scoped_ref(ref);
+ XboxController* controller = new XboxController(fetcher);
+ if (controller->OpenDevice(ref)) {
+ fetcher->AddController(controller);
+ } else {
+ delete controller;
+ }
+ }
+}
+
+void XboxDataFetcher::DeviceRemoved(void* context, io_iterator_t iterator) {
+ DCHECK(context);
+ XboxDataFetcher* fetcher = static_cast<XboxDataFetcher*>(context);
+ io_service_t ref;
+ while ((ref = IOIteratorNext(iterator))) {
+ base::mac::ScopedIOObject<io_service_t> scoped_ref(ref);
+ base::ScopedCFTypeRef<CFNumberRef> number(
+ base::mac::CFCastStrict<CFNumberRef>(
+ IORegistryEntryCreateCFProperty(ref,
+ CFSTR(kUSBDevicePropertyLocationID),
+ kCFAllocatorDefault,
+ kNilOptions)));
+ UInt32 location_id = 0;
+ CFNumberGetValue(number, kCFNumberSInt32Type, &location_id);
+ fetcher->RemoveControllerByLocationID(location_id);
+ }
+}
+
+bool XboxDataFetcher::RegisterForNotifications() {
+ if (listening_)
+ return true;
+ base::ScopedCFTypeRef<CFNumberRef> vendor_cf(CFNumberCreate(
+ kCFAllocatorDefault, kCFNumberSInt32Type, &kVendorMicrosoft));
+ base::ScopedCFTypeRef<CFNumberRef> product_cf(CFNumberCreate(
+ kCFAllocatorDefault, kCFNumberSInt32Type, &kProduct360Controller));
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
+ IOServiceMatching(kIOUSBDeviceClassName));
+ if (!matching_dict)
+ return false;
+ CFDictionarySetValue(matching_dict, CFSTR(kUSBVendorID), vendor_cf);
+ CFDictionarySetValue(matching_dict, CFSTR(kUSBProductID), product_cf);
+ port_ = IONotificationPortCreate(kIOMasterPortDefault);
+ if (!port_)
+ return false;
+ source_ = IONotificationPortGetRunLoopSource(port_);
+ if (!source_)
+ return false;
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopDefaultMode);
+
+ listening_ = true;
+
+ // IOServiceAddMatchingNotification() releases the dictionary when it's done.
+ // Retain it before each call to IOServiceAddMatchingNotification to keep
+ // things balanced.
+ CFRetain(matching_dict);
+ io_iterator_t device_added_iter;
+ IOReturn ret;
+ ret = IOServiceAddMatchingNotification(port_,
+ kIOFirstMatchNotification,
+ matching_dict,
+ DeviceAdded,
+ this,
+ &device_added_iter);
+ device_added_iter_.reset(device_added_iter);
+ if (ret != kIOReturnSuccess) {
+ LOG(ERROR) << "Error listening for Xbox controller add events: " << ret;
+ return false;
+ }
+ DeviceAdded(this, device_added_iter_.get());
+
+ CFRetain(matching_dict);
+ io_iterator_t device_removed_iter;
+ ret = IOServiceAddMatchingNotification(port_,
+ kIOTerminatedNotification,
+ matching_dict,
+ DeviceRemoved,
+ this,
+ &device_removed_iter);
+ device_removed_iter_.reset(device_removed_iter);
+ if (ret != kIOReturnSuccess) {
+ LOG(ERROR) << "Error listening for Xbox controller remove events: " << ret;
+ return false;
+ }
+ DeviceRemoved(this, device_removed_iter_.get());
+ return true;
+}
+
+void XboxDataFetcher::UnregisterFromNotifications() {
+ if (!listening_)
+ return;
+ listening_ = false;
+ if (source_)
+ CFRunLoopSourceInvalidate(source_);
+ if (port_)
+ IONotificationPortDestroy(port_);
+ port_ = NULL;
+}
+
+XboxController* XboxDataFetcher::ControllerForLocation(UInt32 location_id) {
+ for (std::set<XboxController*>::iterator i = controllers_.begin();
+ i != controllers_.end();
+ ++i) {
+ if ((*i)->location_id() == location_id)
+ return *i;
+ }
+ return NULL;
+}
+
+void XboxDataFetcher::AddController(XboxController* controller) {
+ DCHECK(!ControllerForLocation(controller->location_id()))
+ << "Controller with location ID " << controller->location_id()
+ << " already exists in the set of controllers.";
+ controllers_.insert(controller);
+ delegate_->XboxDeviceAdd(controller);
+}
+
+void XboxDataFetcher::RemoveController(XboxController* controller) {
+ delegate_->XboxDeviceRemove(controller);
+ controllers_.erase(controller);
+ delete controller;
+}
+
+void XboxDataFetcher::RemoveControllerByLocationID(uint32 location_id) {
+ XboxController* controller = NULL;
+ for (std::set<XboxController*>::iterator i = controllers_.begin();
+ i != controllers_.end();
+ ++i) {
+ if ((*i)->location_id() == location_id) {
+ controller = *i;
+ break;
+ }
+ }
+ if (controller)
+ RemoveController(controller);
+}
+
+void XboxDataFetcher::XboxControllerGotData(XboxController* controller,
+ const XboxController::Data& data) {
+ delegate_->XboxValueChanged(controller, data);
+}
+
+void XboxDataFetcher::XboxControllerError(XboxController* controller) {
+ RemoveController(controller);
+}
diff --git a/chromium/content/browser/gamepad/xbox_data_fetcher_mac.h b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.h
new file mode 100644
index 00000000000..ca8f7fcd88c
--- /dev/null
+++ b/chromium/content/browser/gamepad/xbox_data_fetcher_mac.h
@@ -0,0 +1,167 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_GAMEPAD_XBOX_DATA_FETCHER_MAC_H_
+#define CONTENT_BROWSER_GAMEPAD_XBOX_DATA_FETCHER_MAC_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_ioobject.h"
+#include "base/mac/scoped_ioplugininterface.h"
+#include "base/memory/scoped_ptr.h"
+
+class XboxController {
+ public:
+ enum LEDPattern {
+ LED_OFF = 0,
+
+ // 2 quick flashes, then a series of slow flashes (about 1 per second).
+ LED_FLASH = 1,
+
+ // Flash three times then hold the LED on. This is the standard way to tell
+ // the player which player number they are.
+ LED_FLASH_TOP_LEFT = 2,
+ LED_FLASH_TOP_RIGHT = 3,
+ LED_FLASH_BOTTOM_LEFT = 4,
+ LED_FLASH_BOTTOM_RIGHT = 5,
+
+ // Simply turn on the specified LED and turn all other LEDs off.
+ LED_HOLD_TOP_LEFT = 6,
+ LED_HOLD_TOP_RIGHT = 7,
+ LED_HOLD_BOTTOM_LEFT = 8,
+ LED_HOLD_BOTTOM_RIGHT = 9,
+
+ LED_ROTATE = 10,
+
+ LED_FLASH_FAST = 11,
+ LED_FLASH_SLOW = 12, // Flash about once per 3 seconds
+
+ // Flash alternating LEDs for a few seconds, then flash all LEDs about once
+ // per second
+ LED_ALTERNATE_PATTERN = 13,
+
+ // 14 is just another boring flashing speed.
+
+ // Flash all LEDs once then go black.
+ LED_FLASH_ONCE = 15,
+
+ LED_NUM_PATTERNS
+ };
+
+ struct Data {
+ bool buttons[15];
+ float triggers[2];
+ float axes[4];
+ };
+
+ class Delegate {
+ public:
+ virtual void XboxControllerGotData(XboxController* controller,
+ const Data& data) = 0;
+ virtual void XboxControllerError(XboxController* controller) = 0;
+ };
+
+ explicit XboxController(Delegate* delegate_);
+ virtual ~XboxController();
+
+ bool OpenDevice(io_service_t service);
+
+ void SetLEDPattern(LEDPattern pattern);
+
+ UInt32 location_id() { return location_id_; }
+ int GetVendorId() const;
+ int GetProductId() const;
+
+ private:
+ static void WriteComplete(void* context, IOReturn result, void* arg0);
+ static void GotData(void* context, IOReturn result, void* arg0);
+
+ void ProcessPacket(size_t length);
+ void QueueRead();
+
+ void IOError();
+
+ // Handle for the USB device. IOUSBDeviceStruct320 is the latest version of
+ // the device API that is supported on Mac OS 10.6.
+ base::mac::ScopedIOPluginInterface<struct IOUSBDeviceStruct320> device_;
+
+ // Handle for the interface on the device which sends button and analog data.
+ // The other interfaces (for the ChatPad and headset) are ignored.
+ base::mac::ScopedIOPluginInterface<struct IOUSBInterfaceStruct300> interface_;
+
+ bool device_is_open_;
+ bool interface_is_open_;
+
+ base::ScopedCFTypeRef<CFRunLoopSourceRef> source_;
+
+ // This will be set to the max packet size reported by the interface, which
+ // is 32 bytes. I would have expected USB to do message framing itself, but
+ // somehow we still sometimes (rarely!) get packets off the interface which
+ // aren't correctly framed. The 360 controller frames its packets with a 2
+ // byte header (type, total length) so we can reframe the packet data
+ // ourselves.
+ uint16 read_buffer_size_;
+ scoped_ptr<uint8[]> read_buffer_;
+
+ // The pattern that the LEDs on the device are currently displaying, or
+ // LED_NUM_PATTERNS if unknown.
+ LEDPattern led_pattern_;
+
+ UInt32 location_id_;
+
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(XboxController);
+};
+
+class XboxDataFetcher : public XboxController::Delegate {
+ public:
+ class Delegate {
+ public:
+ virtual void XboxDeviceAdd(XboxController* device) = 0;
+ virtual void XboxDeviceRemove(XboxController* device) = 0;
+ virtual void XboxValueChanged(XboxController* device,
+ const XboxController::Data& data) = 0;
+ };
+
+ explicit XboxDataFetcher(Delegate* delegate);
+ virtual ~XboxDataFetcher();
+
+ bool RegisterForNotifications();
+ void UnregisterFromNotifications();
+
+ XboxController* ControllerForLocation(UInt32 location_id);
+
+ private:
+ static void DeviceAdded(void* context, io_iterator_t iterator);
+ static void DeviceRemoved(void* context, io_iterator_t iterator);
+ void AddController(XboxController* controller);
+ void RemoveController(XboxController* controller);
+ void RemoveControllerByLocationID(uint32 id);
+ virtual void XboxControllerGotData(XboxController* controller,
+ const XboxController::Data& data) OVERRIDE;
+ virtual void XboxControllerError(XboxController* controller) OVERRIDE;
+
+ Delegate* delegate_;
+
+ std::set<XboxController*> controllers_;
+
+ bool listening_;
+
+ // port_ owns source_, so this doesn't need to be a ScopedCFTypeRef, but we
+ // do need to maintain a reference to it so we can invalidate it.
+ CFRunLoopSourceRef source_;
+ IONotificationPortRef port_;
+ base::mac::ScopedIOObject<io_iterator_t> device_added_iter_;
+ base::mac::ScopedIOObject<io_iterator_t> device_removed_iter_;
+
+ DISALLOW_COPY_AND_ASSIGN(XboxDataFetcher);
+};
+
+#endif // CONTENT_BROWSER_GAMEPAD_XBOX_DATA_FETCHER_MAC_H_