Diff
Modified: trunk/Source/WebCore/ChangeLog (264873 => 264874)
--- trunk/Source/WebCore/ChangeLog 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebCore/ChangeLog 2020-07-25 01:28:03 UTC (rev 264874)
@@ -1,3 +1,47 @@
+2020-07-24 Brady Eidson <[email protected]>
+
+ Hard code some MFi controller devices instead of dynamically managing HID vs GameController.framework.
+ rdar://problem/65961406 and https://bugs.webkit.org/show_bug.cgi?id=214664
+
+ Reviewed by Tim Horton.
+
+ Covered by API tests.
+
+ I'd added code to dynamically tell whether a device would be handled by GameController.framework or IOHID.
+ On macOS Big Sur, it works flawlessly.
+ On Catalina, it doesn't.
+
+ The key problem being that GameController.framework will try to handle some controllers it should not.
+
+ Instead, we can use a hard coded list of the controllers that are most important to support using GCF.
+
+ The GCF provider and HID provider coordinate on that list when deciding if they should handle a new device.
+
+ The worst case scenario for that list missing an important controller is that it would be handled in HID
+ instead of GCF, so some features may be missing.
+
+ * Modules/gamepad/GamepadManager.cpp:
+ (WebCore::GamepadManager::makeGamepadVisible):
+
+ * platform/gamepad/cocoa/GameControllerGamepadProvider.h:
+ (WebCore::GameControllerGamepadProvider::numberOfConnectedGamepads const):
+ * platform/gamepad/cocoa/GameControllerGamepadProvider.mm:
+ (WebCore::GameControllerGamepadProvider::willHandleVendorAndProduct):
+ (WebCore::GameControllerGamepadProvider::controllerDidConnect):
+
+ * platform/gamepad/cocoa/GameControllerSPI.h:
+
+ * platform/gamepad/mac/HIDGamepadProvider.h:
+ (WebCore::HIDGamepadProvider::numberOfConnectedGamepads const):
+ * platform/gamepad/mac/HIDGamepadProvider.mm:
+ (WebCore::deviceAddedCallback):
+ (WebCore::gameControllerFrameworkWillHandleHIDDevice):
+ (WebCore::HIDGamepadProvider::deviceAdded):
+ (WebCore::HIDGamepadProvider::deviceRemoved):
+ (WebCore::HIDGamepadProvider::waitForManagementDecisionForDevice): Deleted.
+ (WebCore::HIDGamepadProvider::removeDeviceWaitingForManagementDecision): Deleted.
+ (WebCore::HIDGamepadProvider::gamePadServiceWasPublished): Deleted.
+
2020-07-24 Mark Lam <[email protected]>
Fix exception check handling below postMessage().
Modified: trunk/Source/WebCore/Modules/gamepad/GamepadManager.cpp (264873 => 264874)
--- trunk/Source/WebCore/Modules/gamepad/GamepadManager.cpp 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebCore/Modules/gamepad/GamepadManager.cpp 2020-07-25 01:28:03 UTC (rev 264874)
@@ -135,6 +135,8 @@
void GamepadManager::makeGamepadVisible(PlatformGamepad& platformGamepad, HashSet<NavigatorGamepad*>& navigatorSet, HashSet<DOMWindow*>& domWindowSet)
{
+ LOG(Gamepad, "GamepadManager::makeGamepadVisible - New gamepad '%s' is visible", platformGamepad.id().utf8().data());
+
if (navigatorSet.isEmpty() && domWindowSet.isEmpty())
return;
@@ -158,6 +160,8 @@
continue;
Ref<Gamepad> gamepad(navigator->gamepadFromPlatformGamepad(platformGamepad));
+
+ LOG(Gamepad, "GamepadManager::makeGamepadVisible - Dispatching gamepadconnected event for gamepad '%s'", platformGamepad.id().utf8().data());
window->dispatchEvent(GamepadEvent::create(eventNames().gamepadconnectedEvent, gamepad.get()), window->document());
}
}
Modified: trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerGamepadProvider.h (264873 => 264874)
--- trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerGamepadProvider.h 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerGamepadProvider.h 2020-07-25 01:28:03 UTC (rev 264874)
@@ -58,6 +58,12 @@
void makeInvisibleGamepadsVisible();
+ size_t numberOfConnectedGamepads() const { return m_gamepadMap.size(); };
+
+#if !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+ static bool willHandleVendorAndProduct(uint16_t vendorID, uint16_t productID);
+#endif
+
private:
GameControllerGamepadProvider();
Modified: trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerGamepadProvider.mm (264873 => 264874)
--- trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerGamepadProvider.mm 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerGamepadProvider.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -38,6 +38,51 @@
namespace WebCore {
+#if !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+bool GameControllerGamepadProvider::willHandleVendorAndProduct(uint16_t vendorID, uint16_t productID)
+{
+ // Check the vendor/product IDs agains a hard coded list of controllers we expect to work well with
+ // GameController.framework on 10.15.
+ enum GameControllerCompatible {
+ Nimbus1 = 0x01111420,
+ Nimbus2 = 0x10381420,
+ StratusXL1 = 0x01111418,
+ StratusXL2 = 0x10381418,
+ StratusXL3 = 0x01111419,
+ StratusXL4 = 0x10381419,
+ HoripadUltimate = 0x0f0d0090,
+ GamesirM2 = 0x0ec20475,
+ XboxOne1 = 0x045e02e0,
+ XboxOne2 = 0x045e02ea,
+ XboxOne3 = 0x045e02fd,
+ Dualshock4_1 = 0x054c05c4,
+ Dualshock4_2 = 0x054c09cc,
+ };
+
+ GameControllerCompatible fullProductID = (GameControllerCompatible)((vendorID << 16) | (productID & 0xFFFF));
+
+ switch (fullProductID) {
+ case Nimbus1:
+ case Nimbus2:
+ case StratusXL1:
+ case StratusXL2:
+ case StratusXL3:
+ case StratusXL4:
+ case HoripadUltimate:
+ case GamesirM2:
+ case XboxOne1:
+ case XboxOne2:
+ case XboxOne3:
+ case Dualshock4_1:
+ case Dualshock4_2:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif // !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+
+
static const Seconds inputNotificationDelay { 16_ms };
GameControllerGamepadProvider& GameControllerGamepadProvider::singleton()
@@ -56,6 +101,29 @@
{
LOG(Gamepad, "GameControllerGamepadProvider controller %p added", controller);
+#if HAVE(MULTIGAMEPADPROVIDER_SUPPORT) && !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+ // On macOS 10.15, we use GameController framework for some controllers, but it's much too aggressive in handling devices it shouldn't.
+ // So we check Vendor/Product against an explicit allow-list to determine if we should let GCF handle the device.
+ // (We have the opposite check in HIDGamepadProvider, as well)
+ for (_GCCControllerHIDServiceInfo *serviceInfo in controller.hidServices) {
+ if (!serviceInfo.service)
+ continue;
+
+ auto cfVendorID = adoptCF((CFNumberRef)IOHIDServiceClientCopyProperty(serviceInfo.service, CFSTR(kIOHIDVendorIDKey)));
+ auto cfProductID = adoptCF((CFNumberRef)IOHIDServiceClientCopyProperty(serviceInfo.service, CFSTR(kIOHIDProductIDKey)));
+
+ int vendorID, productID;
+ CFNumberGetValue(cfVendorID.get(), kCFNumberIntType, &vendorID);
+ CFNumberGetValue(cfProductID.get(), kCFNumberIntType, &productID);
+
+ if (!willHandleVendorAndProduct(vendorID, productID)) {
+ LOG(Gamepad, "GameControllerGamepadProvider::controllerDidConnect - GCController service does not match known VID/PID. Letting the HID provider handle it.");
+ return;
+ }
+ }
+#endif // HAVE(MULTIGAMEPADPROVIDER_SUPPORT) && !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+
+
// When initially starting up the GameController framework machinery,
// we might get the connection notification for an already-connected controller.
if (m_gamepadMap.contains(controller))
Modified: trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerSPI.h (264873 => 264874)
--- trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerSPI.h 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebCore/platform/gamepad/cocoa/GameControllerSPI.h 2020-07-25 01:28:03 UTC (rev 264874)
@@ -27,8 +27,15 @@
#import <GameController/GCController.h>
+#if !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+@class _GCCControllerHIDServiceInfo;
+#endif
+
@interface GCController ()
+ (void)__openXPC_and_CBApplicationDidBecomeActive__;
+#if !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+@property (nonatomic, retain, readonly, nonnull) NSArray<_GCCControllerHIDServiceInfo *> *hidServices;
+#endif
@end
#if HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
@@ -46,5 +53,12 @@
WTF_EXTERN_C_END
#endif // USE(APPLE_INTERNAL_SDK)
+
+#if !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
+@interface _GCCControllerHIDServiceInfo : NSObject
+@property (nonatomic, readonly, nullable) IOHIDServiceClientRef service;
+@end
+#endif
+
#endif // HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
#endif // ENABLE(GAMEPAD) && PLATFORM(COCOA)
Modified: trunk/Source/WebCore/platform/gamepad/mac/HIDGamepadProvider.h (264873 => 264874)
--- trunk/Source/WebCore/platform/gamepad/mac/HIDGamepadProvider.h 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebCore/platform/gamepad/mac/HIDGamepadProvider.h 2020-07-25 01:28:03 UTC (rev 264874)
@@ -54,16 +54,14 @@
WEBCORE_EXPORT void stopMonitoringInput();
WEBCORE_EXPORT void startMonitoringInput();
- enum class AllowManagementDecisionDelay : bool {
- No,
- Yes
- };
- void deviceAdded(IOHIDDeviceRef, AllowManagementDecisionDelay);
+ void deviceAdded(IOHIDDeviceRef);
void deviceRemoved(IOHIDDeviceRef);
void valuesChanged(IOHIDValueRef);
void ignoreGameControllerFrameworkDevices() { m_ignoresGameControllerFrameworkDevices = true; }
+ size_t numberOfConnectedGamepads() const { return m_gamepadMap.size(); };
+
private:
HIDGamepadProvider();
@@ -90,15 +88,6 @@
#if HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
HashSet<IOHIDDeviceRef> m_gameControllerManagedGamepads;
-#if !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
- void waitForManagementDecisionForDevice(IOHIDDeviceRef);
- bool removeDeviceWaitingForManagementDecision(IOHIDDeviceRef);
- void gamePadServiceWasPublished();
-
- HashSet<IOHIDDeviceRef> m_devicesWaitingManagementDecision;
- RefPtr<WorkQueue> m_delayManagementDecisionQueue;
- RetainPtr<IOHIDEventSystemClientRef> m_eventSystemClient;
-#endif // !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
#endif // HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
};
Modified: trunk/Source/WebCore/platform/gamepad/mac/HIDGamepadProvider.mm (264873 => 264874)
--- trunk/Source/WebCore/platform/gamepad/mac/HIDGamepadProvider.mm 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebCore/platform/gamepad/mac/HIDGamepadProvider.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -62,7 +62,7 @@
static void deviceAddedCallback(void* context, IOReturn, void*, IOHIDDeviceRef device)
{
HIDGamepadProvider* listener = static_cast<HIDGamepadProvider*>(context);
- listener->deviceAdded(device, HIDGamepadProvider::AllowManagementDecisionDelay::Yes);
+ listener->deviceAdded(device);
}
static void deviceRemovedCallback(void* context, IOReturn, void*, IOHIDDeviceRef device)
@@ -188,7 +188,6 @@
enum class GameControllerFrameworkHandlesDevice : uint8_t {
No,
Yes,
- Maybe,
};
static GameControllerFrameworkHandlesDevice gameControllerFrameworkWillHandleHIDDevice(IOHIDDeviceRef device)
@@ -198,123 +197,23 @@
#if HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
return [getGCControllerClass() supportsHIDDevice:device] ? GameControllerFrameworkHandlesDevice::Yes : GameControllerFrameworkHandlesDevice::No;
-#endif
+#else
+ CFNumberRef cfVendorID = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
+ CFNumberRef cfProductID = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
- auto deviceService = IOHIDDeviceGetService(device);
- if (!deviceService)
- return GameControllerFrameworkHandlesDevice::Maybe;
+ int vendorID = ((NSNumber *)cfVendorID).intValue;
+ int productID = ((NSNumber *)cfProductID).intValue;
- // Check the service directly backing this device
- uint64_t registryID;
- if (IORegistryEntryGetRegistryEntryID(deviceService, ®istryID) != KERN_SUCCESS)
- return GameControllerFrameworkHandlesDevice::Maybe;
+ if (vendorID < 0 || vendorID > std::numeric_limits<uint16_t>::max() || productID < 0 || productID > std::numeric_limits<uint16_t>::max())
+ return GameControllerFrameworkHandlesDevice::No;
- auto eventSystemClient = adoptCF(IOHIDEventSystemClientCreate(nullptr));
- IOHIDEventSystemClientSetMatching(eventSystemClient.get(), nullptr);
- auto serviceClient = adoptCF(IOHIDEventSystemClientCopyServiceForRegistryID(eventSystemClient.get(), registryID));
- if (serviceClient && ControllerClassForService(serviceClient.get()))
- return GameControllerFrameworkHandlesDevice::Yes;
-
- // Otherwise, check its grandchild service
- io_registry_entry_t child;
- if (IORegistryEntryGetChildEntry(deviceService, kIOServicePlane, &child) != KERN_SUCCESS)
- return GameControllerFrameworkHandlesDevice::Maybe;
- if (!child)
- return GameControllerFrameworkHandlesDevice::Maybe;
-
- if (IORegistryEntryGetChildEntry(child, kIOServicePlane, &child) != KERN_SUCCESS)
- return GameControllerFrameworkHandlesDevice::Maybe;
- if (!child)
- return GameControllerFrameworkHandlesDevice::Maybe;
-
- if (IORegistryEntryGetRegistryEntryID(child, ®istryID) != KERN_SUCCESS)
- return GameControllerFrameworkHandlesDevice::Maybe;
-
- serviceClient = adoptCF(IOHIDEventSystemClientCopyServiceForRegistryID(eventSystemClient.get(), registryID));
- if (!serviceClient)
- return GameControllerFrameworkHandlesDevice::Maybe;
-
- return ControllerClassForService(serviceClient.get()) ? GameControllerFrameworkHandlesDevice::Yes : GameControllerFrameworkHandlesDevice::No;
+ return GameControllerGamepadProvider::willHandleVendorAndProduct((uint16_t)vendorID, (uint16_t)productID) ? GameControllerFrameworkHandlesDevice::Yes : GameControllerFrameworkHandlesDevice::No;
+#endif // HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
}
-#if !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
-
-static const Seconds managementDecisionDelay { 1000_ms };
-
-void HIDGamepadProvider::waitForManagementDecisionForDevice(IOHIDDeviceRef device)
-{
- LOG(Gamepad, "Unable to determine if GameController framework will handle newly attached device %p - Delaying management decision", device);
- m_devicesWaitingManagementDecision.add(device);
-
- if (!m_delayManagementDecisionQueue)
- m_delayManagementDecisionQueue = WorkQueue::create("com.apple.WebKit.GamepadManagementDecision", WorkQueue::Type::Serial, WorkQueue::QOS::Utility);
-
- auto delayedDevice = RetainPtr<IOHIDDeviceRef>(device);
- m_delayManagementDecisionQueue->dispatchAfter(managementDecisionDelay, [this, delayedDevice = WTFMove(delayedDevice)] {
- // If this device was still waiting for its management decision, then the HID provider will handle it.
- if (removeDeviceWaitingForManagementDecision(delayedDevice.get()))
- deviceAdded(delayedDevice.get(), AllowManagementDecisionDelay::No);
- else
- LOG(Gamepad, "Management decision delay for device %p has expired, but device has already been handled", delayedDevice.get());
- });
-
- if (m_devicesWaitingManagementDecision.size() > 1)
- return;
-
- m_eventSystemClient = adoptCF(IOHIDEventSystemClientCreateWithType(kCFAllocatorDefault, kIOHIDEventSystemClientTypeMonitor, 0));
-
- NSMutableArray *matchingAttributes = [NSMutableArray array];
- [matchingAttributes addObject:@{@(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), @(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_GamePad)}];
- [matchingAttributes addObject:@{@(kIOHIDDeviceUsagePageKey) : @(kHIDPage_GenericDesktop), @(kIOHIDDeviceUsageKey) : @(kHIDUsage_GD_Joystick)}];
-
- IOHIDEventSystemClientSetMatchingMultiple(m_eventSystemClient.get(), (__bridge CFArrayRef)matchingAttributes);
- IOHIDEventSystemClientScheduleWithDispatchQueue(m_eventSystemClient.get(), dispatch_get_main_queue());
- IOHIDEventSystemClientRegisterDeviceMatchingBlock(m_eventSystemClient.get(), ^(void *, void *, IOHIDServiceClientRef) {
- // We don't actually care about the actual published service.
- // Any new GamePad service is worth re-checking every pending IOHIDDevice against GameController framework.
- HIDGamepadProvider::singleton().gamePadServiceWasPublished();
- }, nullptr, nullptr);
-}
-
-bool HIDGamepadProvider::removeDeviceWaitingForManagementDecision(IOHIDDeviceRef device)
-{
- if (!m_devicesWaitingManagementDecision.remove(device))
- return false;
-
- if (m_devicesWaitingManagementDecision.isEmpty()) {
- // That was the last device, so we can stop monitoring for IOHIDServiceClients
- ASSERT(m_eventSystemClient);
- IOHIDEventSystemClientUnregisterDeviceMatchingBlock(m_eventSystemClient.get());
- m_eventSystemClient = nullptr;
- m_delayManagementDecisionQueue = nullptr;
- }
-
- return true;
-}
-
-void HIDGamepadProvider::gamePadServiceWasPublished()
-{
- LOG(Gamepad, "HIDGamepadProvider::gamePadServiceWasPublished()");
- IOHIDDeviceRef matchedDevice = nullptr;
- for (auto device : m_devicesWaitingManagementDecision) {
- auto result = gameControllerFrameworkWillHandleHIDDevice(device);
- if (result == GameControllerFrameworkHandlesDevice::Yes || result == GameControllerFrameworkHandlesDevice::No) {
- matchedDevice = device;
- break;
- }
- }
-
- if (matchedDevice) {
- LOG(Gamepad, "Device %p pending management decision can now be managed", matchedDevice);
- removeDeviceWaitingForManagementDecision(matchedDevice);
- deviceAdded(matchedDevice, AllowManagementDecisionDelay::No);
- }
-}
-
-#endif // !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
#endif // HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
-void HIDGamepadProvider::deviceAdded(IOHIDDeviceRef device, AllowManagementDecisionDelay allowManagementDecisionDelay)
+void HIDGamepadProvider::deviceAdded(IOHIDDeviceRef device)
{
#if HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
if (m_ignoresGameControllerFrameworkDevices) {
@@ -324,30 +223,12 @@
LOG(Gamepad, "GameController framework will handle newly attached device %p - HIDGamepadProvider ignoring it", device);
m_gameControllerManagedGamepads.add(device);
return;
-
- case GameControllerFrameworkHandlesDevice::Maybe:
-#if HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
- UNUSED_PARAM(allowManagementDecisionDelay);
- RELEASE_ASSERT_NOT_REACHED();
-#else
- if (allowManagementDecisionDelay == AllowManagementDecisionDelay::No) {
- LOG(Gamepad, "Unable to determine if GameController framework will handle attached device %p - HIDGamepadProvider will handle it", device);
- break;
- }
-
- // Give IOHIDServiceClient callbacks a chance to confirm whether or not
- // this device will be handled by GameController framework
- waitForManagementDecisionForDevice(device);
- return;
-#endif // HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
case GameControllerFrameworkHandlesDevice::No:
LOG(Gamepad, "GameController framework does not handle attached device %p - HIDGamepadProvider will handle it", device);
break;
}
}
-#else
- UNUSED_PARAM(allowManagementDecisionDelay);
-#endif // HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
+#endif
ASSERT(!m_gamepadMap.get(device));
@@ -384,29 +265,14 @@
void HIDGamepadProvider::deviceRemoved(IOHIDDeviceRef device)
{
-#if HAVE(MULTIGAMEPADPROVIDER_SUPPORT) && !HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
- bool wasRemoved = removeDeviceWaitingForManagementDecision(device);
-#endif
-
std::unique_ptr<HIDGamepad> removedGamepad = removeGamepadForDevice(device);
if (!removedGamepad) {
#if HAVE(MULTIGAMEPADPROVIDER_SUPPORT)
auto taken = m_gameControllerManagedGamepads.take(device);
-
- // If this device didn't show up in the gamepad map, then it was either managed by GameController framework
- // or we were still waiting to decide which provider would manage it.
-#if HAVE(GCCONTROLLER_HID_DEVICE_CHECK)
- ASSERT(taken);
-#else
- ASSERT_UNUSED(wasRemoved, taken || wasRemoved);
+ ASSERT_UNUSED(taken, taken);
+ LOG(Gamepad, "HIDGamepadProvider informed of removal of device %p, which is managed by GameController framework. Ignoring.", device);
#endif
-
- if (taken)
- LOG(Gamepad, "HIDGamepadProvider informed of removal of device %p, which is managed by GameController framework. Ignoring.", device);
- else
- LOG(Gamepad, "HIDGamepadProvider informed of removal of device %p, which was not yet managed. Ignoring.", device);
-#endif
return;
}
Modified: trunk/Source/WebKit/ChangeLog (264873 => 264874)
--- trunk/Source/WebKit/ChangeLog 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebKit/ChangeLog 2020-07-25 01:28:03 UTC (rev 264874)
@@ -1,3 +1,21 @@
+2020-07-24 Brady Eidson <[email protected]>
+
+ Hard code some MFi controller devices instead of dynamically managing HID vs GameController.framework.
+ rdar://problem/65961406 and https://bugs.webkit.org/show_bug.cgi?id=214664
+
+ Reviewed by Tim Horton.
+
+ Add more test-only SPI to monitor the state of connected gamepads.
+
+ * UIProcess/API/Cocoa/WKProcessPool.mm:
+ (-[WKProcessPool _numberOfConnectedGamepadsForTesting]):
+ (-[WKProcessPool _numberOfConnectedHIDGamepadsForTesting]):
+ (-[WKProcessPool _numberOfConnectedGameControllerFrameworkGamepadsForTesting]):
+ * UIProcess/API/Cocoa/WKProcessPoolPrivate.h:
+ * UIProcess/WebProcessPool.cpp:
+ (WebKit::WebProcessPool::numberOfConnectedGamepadsForTesting):
+ * UIProcess/WebProcessPool.h:
+
2020-07-24 Devin Rousso <[email protected]>
Followup, adopt more descriptive symbol image name for non-LTR
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm (264873 => 264874)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPool.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -653,9 +653,19 @@
- (size_t)_numberOfConnectedGamepadsForTesting
{
- return _processPool->numberOfConnectedGamepadsForTesting();
+ return _processPool->numberOfConnectedGamepadsForTesting(WebKit::WebProcessPool::GamepadType::All);
}
+- (size_t)_numberOfConnectedHIDGamepadsForTesting
+{
+ return _processPool->numberOfConnectedGamepadsForTesting(WebKit::WebProcessPool::GamepadType::HID);
+}
+
+- (size_t)_numberOfConnectedGameControllerFrameworkGamepadsForTesting
+{
+ return _processPool->numberOfConnectedGamepadsForTesting(WebKit::WebProcessPool::GamepadType::GameControllerFramework);
+}
+
- (void)_setUsesOnlyHIDGamepadProviderForTesting:(BOOL)usesHIDProvider
{
_processPool->setUsesOnlyHIDGamepadProviderForTesting(usesHIDProvider);
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h (264873 => 264874)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKProcessPoolPrivate.h 2020-07-25 01:28:03 UTC (rev 264874)
@@ -136,5 +136,7 @@
- (void)_seedResourceLoadStatisticsForTestingWithFirstParty:(NSURL *)firstPartyURL thirdParty:(NSURL *)thirdPartyURL shouldScheduleNotification:(BOOL)shouldScheduleNotification completionHandler:(void(^)(void))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
- (void)_garbageCollectJavaScriptObjectsForTesting WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
- (size_t)_numberOfConnectedGamepadsForTesting WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+- (size_t)_numberOfConnectedHIDGamepadsForTesting WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+- (size_t)_numberOfConnectedGameControllerFrameworkGamepadsForTesting WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
- (void)_setUsesOnlyHIDGamepadProviderForTesting:(BOOL)usesHIDProvider WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
@end
Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.cpp (264873 => 264874)
--- trunk/Source/WebKit/UIProcess/WebProcessPool.cpp 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.cpp 2020-07-25 01:28:03 UTC (rev 264874)
@@ -127,6 +127,7 @@
#if PLATFORM(COCOA)
#include "VersionChecks.h"
+#include <WebCore/GameControllerGamepadProvider.h>
#include <WebCore/HIDGamepadProvider.h>
#include <WebCore/MultiGamepadProvider.h>
#include <WebCore/PowerSourceNotifier.h>
@@ -1974,11 +1975,22 @@
#endif // ENABLE(GAMEPAD)
-size_t WebProcessPool::numberOfConnectedGamepadsForTesting()
+size_t WebProcessPool::numberOfConnectedGamepadsForTesting(GamepadType gamepadType)
{
#if ENABLE(GAMEPAD)
- return UIGamepadProvider::singleton().numberOfConnectedGamepads();
+ switch (gamepadType) {
+ case GamepadType::All:
+ return UIGamepadProvider::singleton().numberOfConnectedGamepads();
+ case GamepadType::HID:
+#if PLATFORM(MAC)
+ return HIDGamepadProvider::singleton().numberOfConnectedGamepads();
#else
+ return 0;
+#endif
+ case GamepadType::GameControllerFramework:
+ return GameControllerGamepadProvider::singleton().numberOfConnectedGamepads();
+ }
+#else
return 0;
#endif
}
Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.h (264873 => 264874)
--- trunk/Source/WebKit/UIProcess/WebProcessPool.h 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.h 2020-07-25 01:28:03 UTC (rev 264874)
@@ -370,7 +370,12 @@
void garbageCollectJavaScriptObjects();
void setJavaScriptGarbageCollectorTimerEnabled(bool flag);
- size_t numberOfConnectedGamepadsForTesting();
+ enum class GamepadType {
+ All,
+ HID,
+ GameControllerFramework,
+ };
+ size_t numberOfConnectedGamepadsForTesting(GamepadType);
void setUsesOnlyHIDGamepadProviderForTesting(bool);
#if PLATFORM(COCOA)
Modified: trunk/Tools/ChangeLog (264873 => 264874)
--- trunk/Tools/ChangeLog 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Tools/ChangeLog 2020-07-25 01:28:03 UTC (rev 264874)
@@ -1,3 +1,48 @@
+2020-07-24 Brady Eidson <[email protected]>
+
+ Hard code some MFi controller devices instead of dynamically managing HID vs GameController.framework.
+ rdar://problem/65961406 and https://bugs.webkit.org/show_bug.cgi?id=214664
+
+ Reviewed by Tim Horton.
+
+ Now that we have more than 1 kind of virtual gamepad (5 to be precise), refactor the VirtualGamepad
+ mechanism to support easily configurable gamepad mappings.
+
+ Each gamepad mapping has its own descriptor and a report derived from it.
+
+ The virtual devices trigger the correct "HID vs GCF" behavior via their complete descriptors.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+
+ * TestWebKitAPI/Tests/mac/HIDGamepads.mm:
+ (-[GamepadMessageHandler userContentController:didReceiveScriptMessage:]):
+
+ * TestWebKitAPI/mac/GamepadMappings/MicrosoftXboxOne.mm: Added.
+ (TestWebKitAPI::publishReportCallback):
+ (TestWebKitAPI::VirtualGamepad::microsoftXboxOneMapping):
+
+ * TestWebKitAPI/mac/GamepadMappings/ShenzhenLongshengweiTechnologyGamepad.mm: Added.
+ (TestWebKitAPI::publishReportCallback):
+ (TestWebKitAPI::VirtualGamepad::shenzhenLongshengweiTechnologyGamepadMapping):
+
+ * TestWebKitAPI/mac/GamepadMappings/SonyDualShock4.mm: Added.
+ (TestWebKitAPI::publishReportCallback):
+ (TestWebKitAPI::VirtualGamepad::sonyDualshock4Mapping):
+
+ * TestWebKitAPI/mac/GamepadMappings/SteelSeriesNimbus.mm: Copied from Tools/TestWebKitAPI/mac/VirtualGamepad.mm.
+ (TestWebKitAPI::publishReportCallback):
+ (TestWebKitAPI::VirtualGamepad::steelSeriesNimbusMapping):
+
+ * TestWebKitAPI/mac/GamepadMappings/SunLightApplicationGenericNES.mm: Added.
+ (TestWebKitAPI::publishReportCallback):
+ (TestWebKitAPI::VirtualGamepad::sunLightApplicationGenericNESMapping):
+
+ * TestWebKitAPI/mac/VirtualGamepad.h:
+ * TestWebKitAPI/mac/VirtualGamepad.mm:
+ (TestWebKitAPI::VirtualGamepad::VirtualGamepad):
+ (TestWebKitAPI::VirtualGamepad::publishReport):
+ (TestWebKitAPI::VirtualGamepad::makeVirtualNimbus): Deleted.
+
2020-07-24 Jonathan Bedard <[email protected]>
pep8.py:110: FutureWarning: Possible nested set at position 1 (Follow-up fix)
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (264873 => 264874)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2020-07-25 01:28:03 UTC (rev 264874)
@@ -329,6 +329,11 @@
51E6A8961D2F1CA700C004B6 /* LocalStorageClear.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 51E6A8951D2F1C7700C004B6 /* LocalStorageClear.html */; };
51EB125724C67257000CB030 /* VirtualGamepad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB125624C66FE8000CB030 /* VirtualGamepad.mm */; };
51EB125924C68592000CB030 /* HIDGamepads.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB125824C68589000CB030 /* HIDGamepads.mm */; };
+ 51EB126224CA6B66000CB030 /* MicrosoftXboxOne.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB126024CA6B5F000CB030 /* MicrosoftXboxOne.mm */; };
+ 51EB126324CA6B66000CB030 /* ShenzhenLongshengweiTechnologyGamepad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB125E24CA6B5F000CB030 /* ShenzhenLongshengweiTechnologyGamepad.mm */; };
+ 51EB126424CA6B66000CB030 /* SonyDualShock4.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB125F24CA6B5F000CB030 /* SonyDualShock4.mm */; };
+ 51EB126524CA6B66000CB030 /* SteelSeriesNimbus.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB126124CA6B5F000CB030 /* SteelSeriesNimbus.mm */; };
+ 51EB126724CB8753000CB030 /* SunLightApplicationGenericNES.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB126624CB8493000CB030 /* SunLightApplicationGenericNES.mm */; };
51EB12941FDF052500A5A1BD /* ServiceWorkerBasic.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */; };
520BCF4C141EB09E00937EA8 /* WebArchive_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 520BCF4A141EB09E00937EA8 /* WebArchive_Bundle.cpp */; };
5245178721B9F57B0082CB34 /* RenderingProgressPlugIn.mm in Sources */ = {isa = PBXBuildFile; fileRef = 52D5D6BE21B9F1B20046ABA6 /* RenderingProgressPlugIn.mm */; };
@@ -2028,6 +2033,11 @@
51EB125424C66FE8000CB030 /* VirtualGamepad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VirtualGamepad.h; sourceTree = "<group>"; };
51EB125624C66FE8000CB030 /* VirtualGamepad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = VirtualGamepad.mm; sourceTree = "<group>"; };
51EB125824C68589000CB030 /* HIDGamepads.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HIDGamepads.mm; sourceTree = "<group>"; };
+ 51EB125E24CA6B5F000CB030 /* ShenzhenLongshengweiTechnologyGamepad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ShenzhenLongshengweiTechnologyGamepad.mm; sourceTree = "<group>"; };
+ 51EB125F24CA6B5F000CB030 /* SonyDualShock4.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SonyDualShock4.mm; sourceTree = "<group>"; };
+ 51EB126024CA6B5F000CB030 /* MicrosoftXboxOne.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MicrosoftXboxOne.mm; sourceTree = "<group>"; };
+ 51EB126124CA6B5F000CB030 /* SteelSeriesNimbus.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SteelSeriesNimbus.mm; sourceTree = "<group>"; };
+ 51EB126624CB8493000CB030 /* SunLightApplicationGenericNES.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SunLightApplicationGenericNES.mm; sourceTree = "<group>"; };
51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ServiceWorkerBasic.mm; sourceTree = "<group>"; };
51FBBB4C1513D4E900822738 /* WebViewCanPasteURL.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebViewCanPasteURL.mm; sourceTree = "<group>"; };
520BCF4A141EB09E00937EA8 /* WebArchive_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebArchive_Bundle.cpp; sourceTree = "<group>"; };
@@ -3440,6 +3450,18 @@
path = WebCore;
sourceTree = "<group>";
};
+ 51EB125D24CA6B3E000CB030 /* GamepadMappings */ = {
+ isa = PBXGroup;
+ children = (
+ 51EB126024CA6B5F000CB030 /* MicrosoftXboxOne.mm */,
+ 51EB125E24CA6B5F000CB030 /* ShenzhenLongshengweiTechnologyGamepad.mm */,
+ 51EB126624CB8493000CB030 /* SunLightApplicationGenericNES.mm */,
+ 51EB125F24CA6B5F000CB030 /* SonyDualShock4.mm */,
+ 51EB126124CA6B5F000CB030 /* SteelSeriesNimbus.mm */,
+ );
+ path = GamepadMappings;
+ sourceTree = "<group>";
+ };
53FCDE69229EFF6800598ECF /* bmalloc */ = {
isa = PBXGroup;
children = (
@@ -4251,6 +4273,7 @@
BCA61C3A11700B9400460D1E /* mac */ = {
isa = PBXGroup;
children = (
+ 51EB125D24CA6B3E000CB030 /* GamepadMappings */,
F46128B6211C8ED500D9FADB /* DragAndDropSimulatorMac.mm */,
1AEDE22413E5E7A000E62FE8 /* InjectedBundleControllerMac.mm */,
C081224013FC172400DC39AE /* _javascript_TestMac.mm */,
@@ -4981,6 +5004,7 @@
7CCE7EBA1A411A7E00447C4C /* DeviceScaleFactorOnBack.mm in Sources */,
7C83E04D1D0A641800FEBCF3 /* DFACombiner.cpp in Sources */,
7C83E04E1D0A641800FEBCF3 /* DFAMinimizer.cpp in Sources */,
+ 51EB126324CA6B66000CB030 /* ShenzhenLongshengweiTechnologyGamepad.mm in Sources */,
7CCE7EE91A411AE600447C4C /* DidAssociateFormControls.cpp in Sources */,
7CCE7EEA1A411AE600447C4C /* DidNotHandleKeyDown.cpp in Sources */,
AD57AC211DA7465B00FF1BDE /* DidRemoveFrameFromHiearchyInPageCache.cpp in Sources */,
@@ -5107,6 +5131,7 @@
51EB125924C68592000CB030 /* HIDGamepads.mm in Sources */,
51820A4D22F4EE7F00DF0A01 /* _javascript_URLNavigation.mm in Sources */,
5C0160C121A132460077FA32 /* JITEnabled.mm in Sources */,
+ 51EB126424CA6B66000CB030 /* SonyDualShock4.mm in Sources */,
E35FC7B222B82A7300F32F98 /* JSLockTakesWebThreadLock.mm in Sources */,
14CC42E624B8D8FA00E64F48 /* JSRunLoopTimer.mm in Sources */,
7CCE7EC41A411A7E00447C4C /* JSWrapperForNodeInWebFrame.mm in Sources */,
@@ -5212,6 +5237,7 @@
41882F0321010C0D002FF288 /* ProcessPreWarming.mm in Sources */,
CDA4438E21F7A47700379489 /* ProcessSuspendMediaBuffering.mm in Sources */,
83A2CFDE2481B3520018B2D3 /* ProcessSuspension.mm in Sources */,
+ 51EB126724CB8753000CB030 /* SunLightApplicationGenericNES.mm in Sources */,
518C1153205B0504001FF4AE /* ProcessSwapOnNavigation.mm in Sources */,
7C83E0C11D0A652F00FEBCF3 /* ProvisionalURLNotChange.mm in Sources */,
5CFACF65226FD2DC0056C7D0 /* Proxy.mm in Sources */,
@@ -5223,6 +5249,7 @@
7C83E0C31D0A653A00FEBCF3 /* RemoteObjectRegistry.mm in Sources */,
7CCE7EC91A411A7E00447C4C /* RenderedImageFromDOMNode.mm in Sources */,
7CCE7ECA1A411A7E00447C4C /* RenderedImageFromDOMRange.mm in Sources */,
+ 51EB126524CA6B66000CB030 /* SteelSeriesNimbus.mm in Sources */,
A12DDBFB1E836F0700CF6CAE /* RenderedImageWithOptions.mm in Sources */,
52D5D6C021B9F1B30046ABA6 /* RenderingProgress.mm in Sources */,
F464AF9220BB66EA007F9B18 /* RenderingProgressTests.mm in Sources */,
@@ -5353,6 +5380,7 @@
57A79857224AB34E00A7F6F1 /* WebCryptoMasterKey.mm in Sources */,
C1FF9EDB244644F000839AE4 /* WebFilter.mm in Sources */,
5C973F5C1F58EF8B00359C27 /* WebGLPolicy.mm in Sources */,
+ 51EB126224CA6B66000CB030 /* MicrosoftXboxOne.mm in Sources */,
31E9BDA1247F4C62002E51A2 /* WebGLPrepareDisplayOnWebThread.mm in Sources */,
7CCE7EAB1A411A2400447C4C /* WebKitAgnosticTest.mm in Sources */,
51714EB81CF8CA17004723C4 /* WebProcessKillIDBCleanup.mm in Sources */,
Modified: trunk/Tools/TestWebKitAPI/Tests/mac/HIDGamepads.mm (264873 => 264874)
--- trunk/Tools/TestWebKitAPI/Tests/mac/HIDGamepads.mm 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Tools/TestWebKitAPI/Tests/mac/HIDGamepads.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -35,6 +35,7 @@
#import <WebKit/WKProcessPoolPrivate.h>
@interface GamepadMessageHandler : NSObject <WKScriptMessageHandler>
+@property (readonly, nonatomic) Vector<RetainPtr<NSString>> messages;
@end
static bool didReceiveMessage = false;
@@ -43,7 +44,7 @@
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
- EXPECT_TRUE([message.body isEqualToString:@"\"ffff-1420-Virtual Nimbus\""]);
+ _messages.append(message.body);
didReceiveMessage = true;
}
@end
@@ -100,8 +101,10 @@
[[webView window] resignKeyWindow];
[[webView window] makeKeyWindow];
- // Create a gamepad
- auto gamepad = VirtualGamepad::makeVirtualNimbus();
+ // Create a gamepad with a fake vendorID
+ auto mapping = VirtualGamepad::steelSeriesNimbusMapping();
+ mapping.vendorID = HIDVendorID::Fake;
+ auto gamepad = makeUnique<VirtualGamepad>(mapping);
while (![webView.get().configuration.processPool _numberOfConnectedGamepadsForTesting])
Util::sleep(0.1);
@@ -111,8 +114,88 @@
Util::run(&didReceiveMessage);
didReceiveMessage = false;
+
+ EXPECT_EQ(messageHandler.get().messages.size(), 1u);
+ EXPECT_TRUE([messageHandler.get().messages[0] isEqualToString:@"\"ffff-1420-Virtual Nimbus\""]);
+ EXPECT_EQ([webView.get().configuration.processPool _numberOfConnectedGamepadsForTesting], 1u);
}
+TEST(Gamepad, GCFVersusHID)
+{
+ auto keyWindowSwizzler = makeUnique<InstanceMethodSwizzler>([NSApplication class], @selector(keyWindow), reinterpret_cast<IMP>(getKeyWindowForTesting));
+
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ auto messageHandler = adoptNS([[GamepadMessageHandler alloc] init]);
+ [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"gamepad"];
+
+ auto schemeHandler = adoptNS([[TestURLSchemeHandler alloc] init]);
+ [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"gamepad"];
+
+ [schemeHandler setStartURLSchemeTaskHandler:^(WKWebView *, id<WKURLSchemeTask> task) {
+ auto response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"text/html" expectedContentLength:0 textEncodingName:nil]);
+ [task didReceiveResponse:response.get()];
+ [task didReceiveData:[NSData dataWithBytes:mainBytes length:strlen(mainBytes)]];
+ [task didFinish];
+ }];
+
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+ keyWindowForTesting = [webView window];
+ [webView synchronouslyLoadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"gamepad://host/main.html"]]];
+
+ [[webView window] makeFirstResponder:webView.get()];
+
+ // Resigning/reinstating the key window state triggers the "key window did change" notification that WKWebView currently
+ // needs to convince it to monitor gamepad devices
+ [[webView window] resignKeyWindow];
+ [[webView window] makeKeyWindow];
+
+ static const size_t TestGamepadCount = 5;
+ std::unique_ptr<VirtualGamepad> gamepads[TestGamepadCount];
+
+ // Create gamepads that should go through GameController.framework
+ gamepads[0] = makeUnique<VirtualGamepad>(VirtualGamepad::steelSeriesNimbusMapping());
+ gamepads[1] = makeUnique<VirtualGamepad>(VirtualGamepad::microsoftXboxOneMapping());
+ gamepads[2] = makeUnique<VirtualGamepad>(VirtualGamepad::sonyDualshock4Mapping());
+
+ // Create one that should go through HID
+ gamepads[3] = makeUnique<VirtualGamepad>(VirtualGamepad::shenzhenLongshengweiTechnologyGamepadMapping());
+
+ // Create one that GameController framework tries to handle, but shouldn't:
+ // Make sure HID handles it instead.
+ gamepads[4] = makeUnique<VirtualGamepad>(VirtualGamepad::sunLightApplicationGenericNESMapping());
+
+ while ([webView.get().configuration.processPool _numberOfConnectedGamepadsForTesting] != TestGamepadCount)
+ Util::sleep(0.1);
+
+ EXPECT_EQ([webView.get().configuration.processPool _numberOfConnectedHIDGamepadsForTesting], 2u);
+ EXPECT_EQ([webView.get().configuration.processPool _numberOfConnectedGameControllerFrameworkGamepadsForTesting], 3u);
+
+ // Press buttons on them, waiting for the web page to see them
+ for (size_t i = 0; i < TestGamepadCount; ++i) {
+ gamepads[i]->setButtonValue(0, 1.0);
+ gamepads[i]->publishReport();
+ }
+
+ // The page messages back once for each controller
+ while (messageHandler.get().messages.size() < TestGamepadCount) {
+ didReceiveMessage = false;
+ Util::run(&didReceiveMessage);
+ }
+
+ NSSet *expectedGamepadNames = [NSSet setWithArray:@[
+ @"\"Virtual Nimbus Extended Gamepad\"",
+ @"\"Virtual Dualshock4 Extended Gamepad\"",
+ @"\"Virtual Xbox One Gamepad\"",
+ @"\"79-11-Virtual Shenzhen Longshengwei Technology Gamepad\"",
+ @"\"12bd-d015-Virtual Sun Light Application Generic NES Controller\""]];
+
+ for (size_t i = 0; i < TestGamepadCount; ++i)
+ EXPECT_TRUE([expectedGamepadNames containsObject:messageHandler.get().messages[i].get()]);
+
+ EXPECT_EQ([webView.get().configuration.processPool _numberOfConnectedHIDGamepadsForTesting], 2u);
+ EXPECT_EQ([webView.get().configuration.processPool _numberOfConnectedGameControllerFrameworkGamepadsForTesting], 3u);
+}
+
} // namespace TestWebKitAPI
#endif // HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
Added: trunk/Tools/TestWebKitAPI/mac/GamepadMappings/MicrosoftXboxOne.mm (0 => 264874)
--- trunk/Tools/TestWebKitAPI/mac/GamepadMappings/MicrosoftXboxOne.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/mac/GamepadMappings/MicrosoftXboxOne.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "VirtualGamepad.h"
+
+#if HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
+
+#import <HID/HIDUserDevice.h>
+
+namespace TestWebKitAPI {
+
+const uint8_t XboxOneDescriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x05, // Usage (Game Pad)
+ 0xA1, 0x01, // Collection (Application)
+ // (1) byte for Report ID - Total (1) byte
+ 0x85, 0x01, // Report ID (1)
+ 0x09, 0x01, // Usage (Pointer)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x31, // Usage (Y)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
+ 0x95, 0x02, // Report Count (2)
+ 0x75, 0x10, // Report Size (16)
+ // (2) Axes, (2) bytes each - Total (5) bytes
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+ 0x09, 0x01, // Usage (Pointer)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x09, 0x32, // Usage (Z)
+ 0x09, 0x35, // Usage (Rz)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x27, 0xFF, 0xFF, 0x00, 0x00, // Logical Maximum (65534)
+ 0x95, 0x02, // Report Count (2)
+ 0x75, 0x10, // Report Size (16)
+ // (2) Axes, (2) bytes each, for (4) bytes - Total (9) bytes
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+ 0x05, 0x02, // Usage Page (Sim Ctrls)
+ 0x09, 0xC5, // Usage (Brake)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x03, // Logical Maximum (1023)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x0A, // Report Size (10)
+ // 10-bit "Brake" - Total (10) bytes (2) bits
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x00, // Logical Maximum (0)
+ 0x75, 0x06, // Report Size (6)
+ 0x95, 0x01, // Report Count (1)
+ // 6-bit padding - Total (11) bytes
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x02, // Usage Page (Sim Ctrls)
+ 0x09, 0xC4, // Usage (Accelerator)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x03, // Logical Maximum (1023)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x0A, // Report Size (10)
+ // 10-bit "Accelerator" - Total (12) bytes (2) bits
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x00, // Logical Maximum (0)
+ 0x75, 0x06, // Report Size (6)
+ 0x95, 0x01, // Report Count (1)
+ // 6-bit padding - Total (13) bytes
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x39, // Usage (Hat switch)
+ 0x15, 0x01, // Logical Minimum (1)
+ 0x25, 0x08, // Logical Maximum (8)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x46, 0x3B, 0x01, // Physical Maximum (315)
+ 0x66, 0x14, 0x00, // Unit (System: English Rotation, Length: Centimeter)
+ 0x75, 0x04, // Report Size (4)
+ 0x95, 0x01, // Report Count (1)
+ // 4-bit "Hatswitch" - Total (13) bytes (4) bits
+ 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
+ 0x75, 0x04, // Report Size (4)
+ 0x95, 0x01, // Report Count (1)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x00, // Logical Maximum (0)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x45, 0x00, // Physical Maximum (0)
+ 0x65, 0x00, // Unit (None)
+ // 4-bit padding - Total (14) bytes
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (0x01)
+ 0x29, 0x0F, // Usage Maximum (0x0F)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x0F, // Report Count (15)
+ // 15-bits for 15 buttons - Total (15) bytes (7) bits
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x00, // Logical Maximum (0)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x01, // Report Count (1)
+ // 1-bit padding - Total (16) bytes
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x0C, // Usage Page (Consumer)
+ 0x0A, 0x24, 0x02, // Usage (AC Back)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x01, // Report Size (1)
+ // 1-bit for AC Back button - Total (16) bytes (1) bit
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x00, // Logical Maximum (0)
+ 0x75, 0x07, // Report Size (7)
+ 0x95, 0x01, // Report Count (1)
+ // 7-bit padding - Total (17) bytes
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x0C, // Usage Page (Consumer)
+ 0x09, 0x01, // Usage (Consumer Control)
+ 0x85, 0x02, // Report ID (2)
+ 0xA1, 0x01, // Collection (Application)
+ 0x05, 0x0C, // Usage Page (Consumer)
+ 0x0A, 0x23, 0x02, // Usage (AC Home)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x00, // Logical Maximum (0)
+ 0x75, 0x07, // Report Size (7)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+ 0x05, 0x0F, // Usage Page (PID Page)
+ 0x09, 0x21, // Usage (0x21)
+ 0x85, 0x03, // Report ID (3)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x09, 0x97, // Usage (0x97)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x04, // Report Size (4)
+ 0x95, 0x01, // Report Count (1)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x00, // Logical Maximum (0)
+ 0x75, 0x04, // Report Size (4)
+ 0x95, 0x01, // Report Count (1)
+ 0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x09, 0x70, // Usage (0x70)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x64, // Logical Maximum (100)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x04, // Report Count (4)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x09, 0x50, // Usage (0x50)
+ 0x66, 0x01, 0x10, // Unit (System: SI Linear, Time: Seconds)
+ 0x55, 0x0E, // Unit Exponent (-2)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x01, // Report Count (1)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x09, 0xA7, // Usage (0xA7)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x01, // Report Count (1)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x65, 0x00, // Unit (None)
+ 0x55, 0x00, // Unit Exponent (0)
+ 0x09, 0x7C, // Usage (0x7C)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x01, // Report Count (1)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0xC0, // End Collection
+ 0x85, 0x04, // Report ID (4)
+ 0x05, 0x06, // Usage Page (Generic Dev Ctrls)
+ 0x09, 0x20, // Usage (Battery Strength)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+};
+
+const size_t XboxOneButtonCount = 18;
+const size_t XboxOneAxisCount = 4;
+const size_t XboxOneReportSize = 17;
+const char* XboxOneName = "Virtual Xbox One";
+
+// Report layout:
+// |ReportID| |Axis1-L| |Axis1-H| |Axis2-L| |Axis2-H| |Axis3-L| |Axis3-H| |Axis4-L| |Axis4-H|
+// |B0 "Brake"L| |B0 "Brake"H| |B1 "Accelerator"L| |B1 "Accelerator"L| |HatSwitch| |Buttons0-8| |Buttons9-15| |AC Back button|
+
+static void publishReportCallback(Vector<float>& buttonValues, Vector<float>& axisValues, HIDUserDevice *userDevice)
+{
+ uint8_t reportData[XboxOneReportSize];
+ size_t reportIndex = 0;
+
+ // Report ID
+ reportData[reportIndex++] = 0x01;
+
+ // First 4 axes (2 bytes each)
+ // Each axis has a range of 0-65534
+ // We track -1 to 1 internally
+ // Low byte first, high byte second
+ for (size_t i = 0; i < 4; ++i) {
+ uint16_t intValue = (axisValues[i] + 1.0) * 32767;
+ reportData[reportIndex++] = intValue & 0x00ff;
+ reportData[reportIndex++] = intValue >> 8;
+ }
+
+ // 10bit + 6bit padding "button"
+ uint16_t intValue = buttonValues[0] * 1023;
+ reportData[reportIndex++] = intValue & 0x00ff;
+ reportData[reportIndex++] = intValue >> 8;
+
+ // 10bit + 6bit padding "button"
+ intValue = buttonValues[1] * 1023;
+ reportData[reportIndex++] = intValue & 0x00ff;
+ reportData[reportIndex++] = intValue >> 8;
+
+ // Don't support changing the hat switch value yet, but fill in a 0 value for it
+ reportData[reportIndex++] = 0x00;
+
+ // The "15 buttons" take up 2 bytes, but there aren't nearly 15 physical buttons on the device
+ // |b8|b7|b6|b5|b4|b3|b2|b1| |0|bf|be|bd|bc|bb|ba|b9|
+
+ uint8_t buttonByte = 0x00;
+ for (size_t i = 2; i < 10; ++i) {
+ long buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (i - 2);
+ }
+ reportData[reportIndex++] = buttonByte;
+
+ buttonByte = 0x00;
+ for (size_t i = 10; i < 17; ++i) {
+ long buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (i - 10);
+ }
+ reportData[reportIndex++] = buttonByte;
+
+ // The final byte is the final standalone AC back button
+ reportData[reportIndex++] = lround(buttonValues[17]) & 0x01;
+
+ auto nsReportData = adoptNS([[NSData alloc] initWithBytes:reportData length:XboxOneReportSize]);
+ [userDevice handleReport:nsReportData.get() error:nil];
+}
+
+GamepadMapping VirtualGamepad::microsoftXboxOneMapping()
+{
+ return {
+ XboxOneDescriptor,
+ sizeof(XboxOneDescriptor),
+ XboxOneName,
+ HIDVendorID::Microsoft,
+ HIDProductID::XboxOne1,
+ XboxOneButtonCount,
+ XboxOneAxisCount,
+ publishReportCallback
+ };
+}
+
+} // namespace TestWebKitAPI
+
+#endif // HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
Added: trunk/Tools/TestWebKitAPI/mac/GamepadMappings/ShenzhenLongshengweiTechnologyGamepad.mm (0 => 264874)
--- trunk/Tools/TestWebKitAPI/mac/GamepadMappings/ShenzhenLongshengweiTechnologyGamepad.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/mac/GamepadMappings/ShenzhenLongshengweiTechnologyGamepad.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "VirtualGamepad.h"
+
+#if HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
+
+#import <HID/HIDUserDevice.h>
+
+namespace TestWebKitAPI {
+
+const uint8_t SLTGamepadDescriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x04, // Usage (Joystick)
+ 0xA1, 0x01, // Collection (Application)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x05, // Report Count (5)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x46, 0xFF, 0x00, // Physical Maximum (255)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x31, // Usage (Y)
+ // (5) 1 byte axes (yes, it replicates the X axis 4 times) - Total 5 bytes.
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x04, // Report Size (4)
+ 0x95, 0x01, // Report Count (1)
+ 0x25, 0x07, // Logical Maximum (7)
+ 0x46, 0x3B, 0x01, // Physical Maximum (315)
+ 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
+ 0x09, 0x00, // Usage (Undefined)
+ // 4 bits, unknown - Total (5) bytes (4) bits
+ 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
+ 0x65, 0x00, // Unit (None)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x0A, // Report Count (10)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x45, 0x01, // Physical Maximum (1)
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (0x01)
+ 0x29, 0x0A, // Usage Maximum (0x0A)
+ // (10) 1-bit buttons - Total (6) bytes (6) bits
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x0A, // Report Count (10)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x45, 0x01, // Physical Maximum (1)
+ 0x09, 0x01, // Usage (0x01)
+ // (10) vendor defined bits - Total (8) bytes
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+ 0xA1, 0x02, // Collection (Logical)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x04, // Report Count (4)
+ 0x46, 0xFF, 0x00, // Physical Maximum (255)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x09, 0x02, // Usage (0x02)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0xC0, // End Collection
+ 0xC0, // End Collection
+};
+
+const size_t SLTGamepadButtonCount = 10;
+const size_t SLTGamepadAxisCount = 5;
+const size_t SLTGamepadReportSize = 8;
+const char* SLTGamepadName = "Virtual Shenzhen Longshengwei Technology Gamepad";
+
+// Report layout:
+// |Axis1| |Axis2| |Axis3| |Axis4| |Axis5| |b1-b4| |b5-b10| |0|
+static void publishReportCallback(Vector<float>& buttonValues, Vector<float>& axisValues, HIDUserDevice *userDevice)
+{
+ uint8_t reportData[SLTGamepadReportSize];
+
+ // First 3 report bytes are constant, no matter what the values of axis 0, 1, or 2 are.
+ reportData[0] = 0x01;
+ reportData[1] = 0x7f;
+ reportData[2] = 0x7f;
+
+ for (size_t i = 3; i < SLTGamepadAxisCount; ++i) {
+ if (axisValues[i] <= -0.5)
+ reportData[i] = 0x00;
+ else if (axisValues[i] >= 0.5)
+ reportData[i] = 0xff;
+ else
+ reportData[i] = 0x7f;
+ }
+
+ // The final 3 bytes of the report have the following layout:
+ // (Note, button 7 and button 8 do not physically exist on the device and tehrefore can never get activated)
+ // |b4|b3|b2|b1|1|1|1|1| |0|0|b6|b5|0|0|ba|b9| |0|0|0|0|0|0|0|0|
+
+ uint8_t buttonByte = 0x0f;
+ long buttonBit;
+ for (size_t i = 0; i < 4; ++i) {
+ buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (4 + i);
+ }
+ reportData[5] = buttonByte;
+
+ buttonByte = 0;
+ buttonByte |= lround(buttonValues[4]) << 4;
+ buttonByte |= lround(buttonValues[5]) << 5;
+ buttonByte |= lround(buttonValues[8]);
+ buttonByte |= lround(buttonValues[9]) << 1;
+ reportData[6] = buttonByte;
+
+ reportData[7] = 0x00;
+
+ auto nsReportData = adoptNS([[NSData alloc] initWithBytes:reportData length:SLTGamepadReportSize]);
+ [userDevice handleReport:nsReportData.get() error:nil];
+}
+
+GamepadMapping VirtualGamepad::shenzhenLongshengweiTechnologyGamepadMapping()
+{
+ return {
+ SLTGamepadDescriptor,
+ sizeof(SLTGamepadDescriptor),
+ SLTGamepadName,
+ HIDVendorID::ShenzhenLongshengweiTechnology,
+ HIDProductID::Gamepad,
+ SLTGamepadButtonCount,
+ SLTGamepadAxisCount,
+ publishReportCallback
+ };
+}
+
+} // namespace TestWebKitAPI
+
+#endif // HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
Added: trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SonyDualShock4.mm (0 => 264874)
--- trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SonyDualShock4.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SonyDualShock4.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "VirtualGamepad.h"
+
+#if HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
+
+#import <HID/HIDUserDevice.h>
+
+namespace TestWebKitAPI {
+
+const uint8_t Dualshock4Descriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x05, // Usage (Game Pad)
+ 0xA1, 0x01, // Collection (Application)
+ // Report ID - Total (1) byte
+ 0x85, 0x01, // Report ID (1)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x31, // Usage (Y)
+ 0x09, 0x32, // Usage (Z)
+ 0x09, 0x35, // Usage (Rz)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x04, // Report Count (4)
+ // (4) Axes at 1 byte each - Total (5) bytes
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x39, // Usage (Hat switch)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x07, // Logical Maximum (7)
+ 0x75, 0x04, // Report Size (4)
+ 0x95, 0x01, // Report Count (1)
+ // 4-bits for Hatswitch - Total (5) bytes (4) bits
+ 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (0x01)
+ 0x29, 0x0E, // Usage Maximum (0x0E)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x0E, // Report Count (14)
+ // 14 buttons - Total (7) bytes (2) bits
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x75, 0x06, // Report Size (6)
+ 0x95, 0x01, // Report Count (1)
+ // 6-bits padding - Total (8) bytes
+ 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x33, // Usage (Rx)
+ 0x09, 0x34, // Usage (Ry)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x02, // Report Count (2)
+ // (2) axes 1 byte each - Total (10) bytes
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x06, 0x04, 0xFF, // Usage Page (Vendor Defined 0xFF04)
+ 0x85, 0x02, // Report ID (2)
+ 0x09, 0x24, // Usage (0x24)
+ 0x95, 0x24, // Report Count (36)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xA3, // Report ID (-93)
+ 0x09, 0x25, // Usage (0x25)
+ 0x95, 0x30, // Report Count (48)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x05, // Report ID (5)
+ 0x09, 0x26, // Usage (0x26)
+ 0x95, 0x28, // Report Count (40)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x06, // Report ID (6)
+ 0x09, 0x27, // Usage (0x27)
+ 0x95, 0x34, // Report Count (52)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x07, // Report ID (7)
+ 0x09, 0x28, // Usage (0x28)
+ 0x95, 0x30, // Report Count (48)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x08, // Report ID (8)
+ 0x09, 0x29, // Usage (0x29)
+ 0x95, 0x2F, // Report Count (47)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x09, // Report ID (9)
+ 0x09, 0x2A, // Usage (0x2A)
+ 0x95, 0x13, // Report Count (19)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x06, 0x03, 0xFF, // Usage Page (Vendor Defined 0xFF03)
+ 0x85, 0x03, // Report ID (3)
+ 0x09, 0x21, // Usage (0x21)
+ 0x95, 0x26, // Report Count (38)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x04, // Report ID (4)
+ 0x09, 0x22, // Usage (0x22)
+ 0x95, 0x2E, // Report Count (46)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xF0, // Report ID (-16)
+ 0x09, 0x47, // Usage (0x47)
+ 0x95, 0x3F, // Report Count (63)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xF1, // Report ID (-15)
+ 0x09, 0x48, // Usage (0x48)
+ 0x95, 0x3F, // Report Count (63)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xF2, // Report ID (-14)
+ 0x09, 0x49, // Usage (0x49)
+ 0x95, 0x0F, // Report Count (15)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
+ 0x85, 0x11, // Report ID (17)
+ 0x09, 0x20, // Usage (0x20)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x4D, // Report Count (77)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x21, // Usage (0x21)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x12, // Report ID (18)
+ 0x09, 0x22, // Usage (0x22)
+ 0x95, 0x8D, // Report Count (-115)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x23, // Usage (0x23)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x13, // Report ID (19)
+ 0x09, 0x24, // Usage (0x24)
+ 0x95, 0xCD, // Report Count (-51)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x25, // Usage (0x25)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x14, // Report ID (20)
+ 0x09, 0x26, // Usage (0x26)
+ 0x96, 0x0D, 0x01, // Report Count (269)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x27, // Usage (0x27)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x15, // Report ID (21)
+ 0x09, 0x28, // Usage (0x28)
+ 0x96, 0x4D, 0x01, // Report Count (333)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x29, // Usage (0x29)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x16, // Report ID (22)
+ 0x09, 0x2A, // Usage (0x2A)
+ 0x96, 0x8D, 0x01, // Report Count (397)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x2B, // Usage (0x2B)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x17, // Report ID (23)
+ 0x09, 0x2C, // Usage (0x2C)
+ 0x96, 0xCD, 0x01, // Report Count (461)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x2D, // Usage (0x2D)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x18, // Report ID (24)
+ 0x09, 0x2E, // Usage (0x2E)
+ 0x96, 0x0D, 0x02, // Report Count (525)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x2F, // Usage (0x2F)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x19, // Report ID (25)
+ 0x09, 0x30, // Usage (0x30)
+ 0x96, 0x22, 0x02, // Report Count (546)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x09, 0x31, // Usage (0x31)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x06, 0x80, 0xFF, // Usage Page (Vendor Defined 0xFF80)
+ 0x85, 0x82, // Report ID (-126)
+ 0x09, 0x22, // Usage (0x22)
+ 0x95, 0x3F, // Report Count (63)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x83, // Report ID (-125)
+ 0x09, 0x23, // Usage (0x23)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x84, // Report ID (-124)
+ 0x09, 0x24, // Usage (0x24)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x90, // Report ID (-112)
+ 0x09, 0x30, // Usage (0x30)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x91, // Report ID (-111)
+ 0x09, 0x31, // Usage (0x31)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x92, // Report ID (-110)
+ 0x09, 0x32, // Usage (0x32)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x93, // Report ID (-109)
+ 0x09, 0x33, // Usage (0x33)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0x94, // Report ID (-108)
+ 0x09, 0x34, // Usage (0x34)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xA0, // Report ID (-96)
+ 0x09, 0x40, // Usage (0x40)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xA4, // Report ID (-92)
+ 0x09, 0x44, // Usage (0x44)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xA7, // Report ID (-89)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xA8, // Report ID (-88)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xA9, // Report ID (-87)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xAA, // Report ID (-86)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xAB, // Report ID (-85)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xAC, // Report ID (-84)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xAD, // Report ID (-83)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xB3, // Report ID (-77)
+ 0x09, 0x45, // Usage (0x45)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xB4, // Report ID (-76)
+ 0x09, 0x46, // Usage (0x46)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xB5, // Report ID (-75)
+ 0x09, 0x47, // Usage (0x47)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xD0, // Report ID (-48)
+ 0x09, 0x40, // Usage (0x40)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x85, 0xD4, // Report ID (-44)
+ 0x09, 0x44, // Usage (0x44)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0xC0, // End Collection
+};
+
+// FIXME: Hatswitch is not yet addressable by test
+const size_t Dualshock4ButtonCount = 14;
+const size_t Dualshock4AxisCount = 6;
+const size_t Dualshock4ReportSize = 10;
+const char* Dualshock4Name = "Virtual Dualshock4";
+
+// Report layout:
+// |ReportID| |Axis1| |Axis2| |Axis3| |Axis4| |b1-b4 high, HS low| |b5-b12| |b13-b14| |Axis5| |Axis6|
+
+static void publishReportCallback(Vector<float>& buttonValues, Vector<float>& axisValues, HIDUserDevice *userDevice)
+{
+ uint8_t reportData[Dualshock4ReportSize];
+ size_t reportIndex = 0;
+
+ // Report ID
+ reportData[reportIndex++] = 0x01;
+
+ // First 4 axes (1 byte each)
+ for (size_t i = 0; i < 4; ++i)
+ reportData[reportIndex++] = (uint8_t)(axisValues[i] * 255);
+
+ // The hatswitch, 14 buttons, and 6 bits of padding take up 3 bytes
+ // The bit layout is as follows:
+ // |b4|b3|b2|b1|hs|hs|hs|hs| |bc|bb|ba|b9|b8|b7|b6|b5| |0|0|0|0|0|0|be|bd|
+
+ // For now, always leave the hatswitch in the neutral position of "8"
+ uint8_t buttonByte = 0x08;
+ long buttonBit;
+ for (size_t i = 0; i < 4; ++i) {
+ buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (4 + i);
+ }
+ reportData[reportIndex++] = buttonByte;
+
+ buttonByte = 0x00;
+ for (size_t i = 4; i < 12; ++i) {
+ buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (i - 4);
+ }
+ reportData[reportIndex++] = buttonByte;
+
+ buttonByte = 0x00;
+ for (size_t i = 12; i < 13; ++i) {
+ buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (i - 12);
+ }
+ reportData[reportIndex++] = buttonByte;
+
+ // Final 2 axes (1 byte each)
+ for (size_t i = 4; i < 5; ++i)
+ reportData[reportIndex++] = (uint8_t)(axisValues[i] * 255);
+
+ auto nsReportData = adoptNS([[NSData alloc] initWithBytes:reportData length:Dualshock4ReportSize]);
+ [userDevice handleReport:nsReportData.get() error:nil];
+}
+
+GamepadMapping VirtualGamepad::sonyDualshock4Mapping()
+{
+ return {
+ Dualshock4Descriptor,
+ sizeof(Dualshock4Descriptor),
+ Dualshock4Name,
+ HIDVendorID::Sony,
+ HIDProductID::Dualshock4_1,
+ Dualshock4ButtonCount,
+ Dualshock4AxisCount,
+ publishReportCallback
+ };
+}
+
+} // namespace TestWebKitAPI
+
+#endif // HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
Copied: trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SteelSeriesNimbus.mm (from rev 264873, trunk/Tools/TestWebKitAPI/mac/VirtualGamepad.mm) (0 => 264874)
--- trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SteelSeriesNimbus.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SteelSeriesNimbus.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "VirtualGamepad.h"
+
+#if HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
+
+#import <HID/HIDUserDevice.h>
+
+namespace TestWebKitAPI {
+
+// HID descriptors are an interesting language.
+// This is the descriptor dumped from an MFi Nimbus controller, which is fairly representative of a modern controller.
+// I got this formatting by dumping the raw descriptor from my device into https://eleccelerator.com/usbdescreqparser/
+// In addition to the raw HID language, I've added commentary along the way to further document how this works.
+const uint8_t NimbusDescriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x05, // Usage (Game Pad)
+ 0xA1, 0x01, // Collection (Application)
+ 0x09, 0x05, // Usage (Game Pad)
+ 0xA1, 0x02, // Collection (Logical)
+ 0x75, 0x08, // Report Size (8) - All reports past this point will be 1 byte per element until Report Size changes.
+ 0x95, 0x04, // Report Count (4) - The upcoming report will have 4 elements, 1 byte each.
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x46, 0xFF, 0x00, // Physical Maximum (255)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x90, // Usage (D-pad Up) - These 4 d-pad elements will each be a button.
+ 0x09, 0x92, // Usage (D-pad Right)
+ 0x09, 0x91, // Usage (D-pad Down)
+ 0x09, 0x93, // Usage (D-pad Left)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (4 bytes)
+ 0x95, 0x08, // Report Count (8) - The next report will have 8 elements, 1 byte each (cascaded from above)
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (0x01) - And it will be buttons...
+ 0x29, 0x08, // Usage Maximum (0x08) - 8 of them!
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (8 bytes)
+ 0x15, 0x00, // Logical Minimum (0) - The next elements are pure digital instead of 'analog'
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x45, 0x01, // Physical Maximum (1)
+ 0x75, 0x01, // Report Size (1) - The report size is 1 bit
+ 0x95, 0x01, // Report Count (1) - And there is one such element
+ 0x05, 0x0C, // Usage Page (Consumer)
+ 0x0A, 0x23, 0x02, // Usage (AC Home) - And it is an "AC Home" element, which is basically a fancy button
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (1 bit)
+ 0x95, 0x07, // Report Count (7) - The next report will have 7 elements (of 1 bit each)
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - And it's constant padding. Lock it in for (7 bits).
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x45, 0x01, // Physical Maximum (1)
+ 0x75, 0x01, // Report Size (1) - The next report will also be 1 bit each (like the AC Home button above)
+ 0x95, 0x04, // Report Count (4) - But there will be 4 of them
+ 0x05, 0x08, // Usage Page (LEDs) - And they are LEDs (This will probably be an output report)
+ 0x1A, 0x00, 0xFF, // Usage Minimum (0xFF00) 0 ...
+ 0x2A, 0x03, 0xFF, // Usage Maximum (0xFF03) ... to 3
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - Lock in the output report of (4 bits)
+ 0x75, 0x04, // Report Size (4) - Also 4 elements in the next report
+ 0x95, 0x01, // Report Count (1) - Also 1 bit each
+ 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - Lock in the constant padding of (4 bits)
+ 0x15, 0x81, // Logical Minimum (-127) - The next elements will have a neg-to-pos output range
+ 0x25, 0x7F, // Logical Maximum (127)
+ 0x35, 0x81, // Physical Minimum (-127)
+ 0x45, 0x7F, // Physical Maximum (127)
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x01, // Usage (Pointer)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x75, 0x08, // Report Size (8) - The report size is 1 byte per element (so that -127 to 127 maps to the 256 possible values)
+ 0x95, 0x04, // Report Count (4) - And there will be 4 of them
+ 0x09, 0x30, // Usage (X) - And they are the first 4 axis usages
+ 0x09, 0x31, // Usage (Y)
+ 0x09, 0x32, // Usage (Z)
+ 0x09, 0x35, // Usage (Rz)
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (4 bytes)
+ 0xC0, // End Collection
+ 0xC0, // End Collection
+ 0xC0, // End Collection
+};
+
+// The total input report size from the above descriptor is:
+// 4 bytes + 8 bytes + (1 bit + 7 bits) + 4 bytes = 17 bytes total.
+// The total output report size from the above descriptor is:
+// (4 bits + 4 bits) == 1 byte total
+const size_t NimbusButtonCount = 13;
+const size_t NimbusAxisCount = 4;
+const size_t NimbusInputReportSize = 17;
+const char* NimbusName = "Virtual Nimbus";
+
+// Report layout:
+// |b1| |b2| |b3| |b4| |b5| |b6| |b7| |b8| |b9| |b10| |b11| |b12| |b13| |Axis1| |Axis2| |Axis3| |Axis4|
+
+static void publishReportCallback(Vector<float>& buttonValues, Vector<float>& axisValues, HIDUserDevice *userDevice)
+{
+ uint8_t reportData[NimbusInputReportSize];
+
+ for (size_t i = 0; i < 12; ++i)
+ reportData[i] = (uint8_t)(buttonValues[i] * 255);
+ reportData[12] = buttonValues[12] == 0.0 ? 0x01 : 0x00;
+ for (size_t i = 13; i < 17; ++i)
+ reportData[i] = (uint8_t)((int8_t)(axisValues[i - 13] * 127));
+
+ auto nsReportData = adoptNS([[NSData alloc] initWithBytes:reportData length:NimbusInputReportSize]);
+ [userDevice handleReport:nsReportData.get() error:nil];
+}
+
+GamepadMapping VirtualGamepad::steelSeriesNimbusMapping()
+{
+ return {
+ NimbusDescriptor,
+ sizeof(NimbusDescriptor),
+ NimbusName,
+ HIDVendorID::SteelSeries2,
+ HIDProductID::Nimbus,
+ NimbusButtonCount,
+ NimbusAxisCount,
+ publishReportCallback
+ };
+}
+
+} // namespace TestWebKitAPI
+
+#endif // HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
Added: trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SunLightApplicationGenericNES.mm (0 => 264874)
--- trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SunLightApplicationGenericNES.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/mac/GamepadMappings/SunLightApplicationGenericNES.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "VirtualGamepad.h"
+
+#if HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
+
+#import <HID/HIDUserDevice.h>
+
+namespace TestWebKitAPI {
+
+const uint8_t SLAGenericNESDescriptor[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
+ 0x09, 0x05, // Usage (Game Pad)
+ 0xA1, 0x01, // Collection (Application)
+ 0x09, 0x01, // Usage (Pointer)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x31, // Usage (Y)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x95, 0x02, // Report Count (2)
+ 0x75, 0x08, // Report Size (8)
+ // (2) Axes, 1 byte each. Total - (2) bytes
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x08, // Report Size (8)
+ // (1) Byte, unknown. Total - (3) bytes
+ 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0xC0, // End Collection
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (0x01)
+ 0x29, 0x0B, // Usage Maximum (0x0B)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x95, 0x0B, // Report Count (11)
+ 0x75, 0x01, // Report Size (1)
+ // (11) buttons. Total - (4) bytes (3) bits
+ 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x95, 0x1D, // Report Count (29)
+ // (29) padding bits. Total (8) bytes
+ 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ 0x05, 0x8C, // Usage Page (Bar Code Scanner Page)
+ 0x09, 0x01, // Usage (0x01)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x09, 0x02, // Usage (0x02)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x04, // Report Count (4)
+ 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0x09, 0x02, // Usage (0x02)
+ 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ 0xC0, // End Collection
+ 0xC0, // End Collection
+};
+
+const size_t SLAGenericNESButtonCount = 11;
+const size_t SLAGenericNESAxisCount = 2;
+const size_t SLAGenericNESReportSize = 8;
+const char* SLAGenericNESName = "Virtual Sun Light Application Generic NES Controller";
+
+// Report layout:
+// |Axis1| |Axis2| |0| |b1-b8| |b9-bb| |0| |0| |0|
+static void publishReportCallback(Vector<float>& buttonValues, Vector<float>& axisValues, HIDUserDevice *userDevice)
+{
+ uint8_t reportData[SLAGenericNESReportSize];
+
+ for (size_t i = 0; i < SLAGenericNESAxisCount; ++i) {
+ if (axisValues[i] <= -0.5)
+ reportData[i] = 0x00;
+ else if (axisValues[i] >= 0.5)
+ reportData[i] = 0xff;
+ else
+ reportData[i] = 0x7f;
+ }
+
+ reportData[2] = 0x00;
+
+ // The next 2 bytes of the report have the following layout:
+ // |b8|b7|b6|b5|b4|b3|b2|b1| |0|0|0|0|0|bb|ba|b9|
+
+ uint8_t buttonByte = 0x00;
+ for (size_t i = 0; i < 8; ++i) {
+ long buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (i);
+ }
+ reportData[3] = buttonByte;
+
+ buttonByte = 0x00;
+ for (size_t i = 8; i < 11; ++i) {
+ long buttonBit = lround(buttonValues[i]);
+ buttonByte |= buttonBit << (i - 8);
+ }
+ reportData[4] = buttonByte;
+
+ reportData[5] = 0x00;
+ reportData[6] = 0x00;
+ reportData[7] = 0x00;
+
+ auto nsReportData = adoptNS([[NSData alloc] initWithBytes:reportData length:SLAGenericNESReportSize]);
+ [userDevice handleReport:nsReportData.get() error:nil];
+}
+
+GamepadMapping VirtualGamepad::sunLightApplicationGenericNESMapping()
+{
+ return {
+ SLAGenericNESDescriptor,
+ sizeof(SLAGenericNESDescriptor),
+ SLAGenericNESName,
+ HIDVendorID::SunLightApplication,
+ HIDProductID::GenericNES,
+ SLAGenericNESButtonCount,
+ SLAGenericNESAxisCount,
+ publishReportCallback
+ };
+}
+
+} // namespace TestWebKitAPI
+
+#endif // HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
Modified: trunk/Tools/TestWebKitAPI/mac/VirtualGamepad.h (264873 => 264874)
--- trunk/Tools/TestWebKitAPI/mac/VirtualGamepad.h 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Tools/TestWebKitAPI/mac/VirtualGamepad.h 2020-07-25 01:28:03 UTC (rev 264874)
@@ -23,6 +23,8 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
+#pragma once
+
#if HAVE(HID_FRAMEWORK) && USE(APPLE_INTERNAL_SDK)
#include <dispatch/dispatch.h>
@@ -31,7 +33,6 @@
#include <wtf/RetainPtr.h>
#include <wtf/Vector.h>
-
OBJC_CLASS HIDDevice;
OBJC_CLASS HIDUserDevice;
OBJC_CLASS NSString;
@@ -38,14 +39,54 @@
namespace TestWebKitAPI {
+enum class HIDVendorID : uint16_t {
+ Microsoft = 0x045e,
+ ShenzhenLongshengweiTechnology = 0x0079,
+ Sony = 0x054c,
+ SteelSeries1 = 0x0111,
+ SteelSeries2 = 0x1038,
+ SunLightApplication = 0x12bd,
+ Fake = 0xffff,
+};
+
+// Technically different products from different vendors can have the same product ID,
+// But in practice that probably won't happen.
+enum class HIDProductID : uint16_t {
+ StratusXL1 = 0x1418,
+ StratusXL2 = 0x1419,
+ Nimbus = 0x1420,
+ Gamepad = 0x0011,
+ GenericNES = 0xd015,
+ XboxOne1 = 0x02ea,
+ XboxOne2 = 0x02e0,
+ XboxOne3 = 0x02fd,
+ Dualshock4_1 = 0x05c4,
+ Dualshock4_2 = 0x09cc,
+};
+
+typedef void (*PublishReportCallback)(Vector<float>&, Vector<float>&, HIDUserDevice*);
+
+struct GamepadMapping {
+ const uint8_t* descriptorData;
+ size_t descriptorDataSize;
+ const char* name;
+ HIDVendorID vendorID;
+ HIDProductID productID;
+ size_t buttonCount;
+ size_t axisCount;
+ PublishReportCallback publishReportCallback;
+};
+
class VirtualGamepad {
WTF_MAKE_FAST_ALLOCATED;
public:
- static std::unique_ptr<VirtualGamepad> makeVirtualNimbus();
- enum class Layout {
- Nimbus,
- };
- VirtualGamepad(Layout);
+ static GamepadMapping microsoftXboxOneMapping();
+ static GamepadMapping shenzhenLongshengweiTechnologyGamepadMapping();
+ static GamepadMapping sonyDualshock4Mapping();
+ static GamepadMapping steelSeriesNimbusMapping();
+ static GamepadMapping sunLightApplicationGenericNESMapping();
+
+ VirtualGamepad(const GamepadMapping&);
~VirtualGamepad();
size_t buttonCount() const;
@@ -65,10 +106,10 @@
RetainPtr<HIDDevice> m_device;
OSObjectPtr<dispatch_queue_t> m_dispatchQueue;
- Layout m_layout;
Vector<float> m_buttonValues;
Vector<float> m_axisValues;
+ GamepadMapping m_gamepadMapping;
};
} // namespace TestWebKitAPI
Modified: trunk/Tools/TestWebKitAPI/mac/VirtualGamepad.mm (264873 => 264874)
--- trunk/Tools/TestWebKitAPI/mac/VirtualGamepad.mm 2020-07-25 00:25:32 UTC (rev 264873)
+++ trunk/Tools/TestWebKitAPI/mac/VirtualGamepad.mm 2020-07-25 01:28:03 UTC (rev 264874)
@@ -39,125 +39,25 @@
namespace TestWebKitAPI {
-// HID descriptors are an interesting language.
-// This is the descriptor dumped from an MFi Nimbus controller, which is fairly representative of a modern controller.
-// I got this formatting by dumping the raw descriptor from my device into https://eleccelerator.com/usbdescreqparser/
-// In addition to the raw HID language, I've added commentary along the way to further document how this works.
-const uint8_t NimbusDescriptor[] = {
- 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
- 0x09, 0x05, // Usage (Game Pad)
- 0xA1, 0x01, // Collection (Application)
- 0x09, 0x05, // Usage (Game Pad)
- 0xA1, 0x02, // Collection (Logical)
- 0x75, 0x08, // Report Size (8) - All reports past this point will be 1 byte per element until Report Size changes.
- 0x95, 0x04, // Report Count (4) - The upcoming report will have 4 elements, 1 byte each.
- 0x15, 0x00, // Logical Minimum (0)
- 0x26, 0xFF, 0x00, // Logical Maximum (255)
- 0x35, 0x00, // Physical Minimum (0)
- 0x46, 0xFF, 0x00, // Physical Maximum (255)
- 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
- 0x09, 0x90, // Usage (D-pad Up) - These 4 d-pad elements will each be a button.
- 0x09, 0x92, // Usage (D-pad Right)
- 0x09, 0x91, // Usage (D-pad Down)
- 0x09, 0x93, // Usage (D-pad Left)
- 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (4 bytes)
- 0x95, 0x08, // Report Count (8) - The next report will have 8 elements, 1 byte each (cascaded from above)
- 0x05, 0x09, // Usage Page (Button)
- 0x19, 0x01, // Usage Minimum (0x01) - And it will be buttons...
- 0x29, 0x08, // Usage Maximum (0x08) - 8 of them!
- 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (8 bytes)
- 0x15, 0x00, // Logical Minimum (0) - The next elements are pure digital instead of 'analog'
- 0x25, 0x01, // Logical Maximum (1)
- 0x35, 0x00, // Physical Minimum (0)
- 0x45, 0x01, // Physical Maximum (1)
- 0x75, 0x01, // Report Size (1) - The report size is 1 bit
- 0x95, 0x01, // Report Count (1) - And there is one such element
- 0x05, 0x0C, // Usage Page (Consumer)
- 0x0A, 0x23, 0x02, // Usage (AC Home) - And it is an "AC Home" element, which is basically a fancy button
- 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (1 bit)
- 0x95, 0x07, // Report Count (7) - The next report will have 7 elements (of 1 bit each)
- 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - And it's constant padding. Lock it in for (7 bits).
- 0x15, 0x00, // Logical Minimum (0)
- 0x25, 0x01, // Logical Maximum (1)
- 0x35, 0x00, // Physical Minimum (0)
- 0x45, 0x01, // Physical Maximum (1)
- 0x75, 0x01, // Report Size (1) - The next report will also be 1 bit each (like the AC Home button above)
- 0x95, 0x04, // Report Count (4) - But there will be 4 of them
- 0x05, 0x08, // Usage Page (LEDs) - And they are LEDs (This will probably be an output report)
- 0x1A, 0x00, 0xFF, // Usage Minimum (0xFF00) 0 ...
- 0x2A, 0x03, 0xFF, // Usage Maximum (0xFF03) ... to 3
- 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - Lock in the output report of (4 bits)
- 0x75, 0x04, // Report Size (4) - Also 4 elements in the next report
- 0x95, 0x01, // Report Count (1) - Also 1 bit each
- 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - Lock in the constant padding of (4 bits)
- 0x15, 0x81, // Logical Minimum (-127) - The next elements will have a neg-to-pos output range
- 0x25, 0x7F, // Logical Maximum (127)
- 0x35, 0x81, // Physical Minimum (-127)
- 0x45, 0x7F, // Physical Maximum (127)
- 0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
- 0x09, 0x01, // Usage (Pointer)
- 0xA1, 0x00, // Collection (Physical)
- 0x75, 0x08, // Report Size (8) - The report size is 1 byte per element (so that -127 to 127 maps to the 256 possible values)
- 0x95, 0x04, // Report Count (4) - And there will be 4 of them
- 0x09, 0x30, // Usage (X) - And they are the first 4 axis usages
- 0x09, 0x31, // Usage (Y)
- 0x09, 0x32, // Usage (Z)
- 0x09, 0x35, // Usage (Rz)
- 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - Lock in the above input report (4 bytes)
- 0xC0, // End Collection
- 0xC0, // End Collection
- 0xC0, // End Collection
-};
-
-// The total input report size from the above descriptor is:
-// 4 bytes + 8 bytes + (1 bit + 7 bits) + 4 bytes = 17 bytes total.
-// The total output report size from the above descriptor is:
-// (4 bits + 4 bits) == 1 byte total
-const size_t NimbusDescriptorSize = sizeof(NimbusDescriptor);
-const size_t NimbusButtonCount = 13;
-const size_t NimbusAxisCount = 4;
-const size_t NimbusInputReportSize = 17;
-const uint16_t NimbusVID = 0xFFFF; // The actual Vendor ID for the Nimbus is either 0x1038 or 0x0111. Let's use a fake one for testing.
-const uint16_t NimbusPID = 0x1420;
-const char* NimbusName = "Virtual Nimbus";
-
-std::unique_ptr<VirtualGamepad> VirtualGamepad::makeVirtualNimbus()
+VirtualGamepad::VirtualGamepad(const GamepadMapping& gamepadMapping)
+ : m_gamepadMapping(gamepadMapping)
{
- return makeUnique<VirtualGamepad>(Layout::Nimbus);
-}
-
-VirtualGamepad::VirtualGamepad(Layout layout)
- : m_layout(layout)
-{
m_dispatchQueue = adoptOSObject(dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL));
m_uniqueID = NSUUID.UUID.UUIDString;
- const uint8_t* descriptorData;
- size_t descriptorSize;
- uint16_t vid, pid;
- const char* name;
+ m_buttonValues = Vector<float>(m_gamepadMapping.buttonCount, 0.0);
+ m_axisValues = Vector<float>(m_gamepadMapping.axisCount, 0.0);
- switch (m_layout) {
- case Layout::Nimbus:
- descriptorData = NimbusDescriptor;
- descriptorSize = NimbusDescriptorSize;
- vid = NimbusVID;
- pid = NimbusPID;
- name = NimbusName;
- m_buttonValues = Vector<float>(NimbusButtonCount, 0.0);
- m_axisValues = Vector<float>(NimbusAxisCount, 0.0);
- }
-
- auto descriptor = adoptNS([[NSData alloc] initWithBytes:descriptorData length:descriptorSize]);
+ auto descriptor = adoptNS([[NSData alloc] initWithBytes:m_gamepadMapping.descriptorData length:m_gamepadMapping.descriptorDataSize]);
auto properties = adoptNS([[NSMutableDictionary alloc] init]);
properties.get()[@kIOHIDReportDescriptorKey] = descriptor.get();
- properties.get()[@kIOHIDVendorIDKey] = @(vid);
- properties.get()[@kIOHIDProductIDKey] = @(pid);
+ properties.get()[@kIOHIDVendorIDKey] = @((uint16_t)m_gamepadMapping.vendorID);
+ properties.get()[@kIOHIDProductIDKey] = @((uint16_t)m_gamepadMapping.productID);
properties.get()[@kIOHIDPhysicalDeviceUniqueIDKey] = m_uniqueID.get();
properties.get()[@kIOHIDTransportKey] = @"Bluetooth";
properties.get()[@"isVirtual"] = @(YES);
- properties.get()[@(kIOHIDProductKey)] = @(name);
+ properties.get()[@(kIOHIDProductKey)] = @(m_gamepadMapping.name);
m_userDevice = adoptNS([[HIDUserDevice alloc] initWithProperties:properties.get()]);
[m_userDevice setDispatchQueue:m_dispatchQueue.get()];
@@ -192,24 +92,7 @@
void VirtualGamepad::publishReport()
{
- uint8_t reportData[1024];
- size_t reportLength;
-
- switch (m_layout) {
- case Layout::Nimbus:
- reportLength = NimbusInputReportSize;
- for (size_t i = 0; i < 12; ++i)
- reportData[i] = (uint8_t)(m_buttonValues[i] * 255);
- reportData[12] = m_buttonValues[12] == 0.0 ? 0x01 : 0x00;
- for (size_t i = 13; i < 17; ++i)
- reportData[i] = (uint8_t)((int8_t)(m_axisValues[i - 13] * 127));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
-
- auto nsReportData = adoptNS([[NSData alloc] initWithBytes:reportData length:reportLength]);
- [m_userDevice handleReport:nsReportData.get() error:nil];
+ m_gamepadMapping.publishReportCallback(m_buttonValues, m_axisValues, m_userDevice.get());
}
} // namespace TestWebKitAPI