Title: [264874] trunk
Revision
264874
Author
[email protected]
Date
2020-07-24 18:28:03 -0700 (Fri, 24 Jul 2020)

Log Message

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.

Source/WebCore:

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.

Source/WebKit:

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:

Tools:

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.

Modified Paths

Added Paths

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, &registryID) != 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, &registryID) != 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
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to