dirkwhoffmann / vAmiga

vAmiga is a user-friendly Amiga 500, 1000, 2000 emulator for macOS
https://dirkwhoffmann.github.io/vAmiga
Other
305 stars 25 forks source link

Xbox One Wireless Controller doesn't work. #442

Closed kyods closed 3 years ago

kyods commented 3 years ago

Hi Dirk,

I can't get my Xbox One Wireless Controller to work in vAmiga. The odd thing is that it works fine in Virtual C64 (at least the analogue stick, the D-Pad behaves like if the "up" button was being constantly pressed). Thanks in advance for looking into this.

dirkwhoffmann commented 3 years ago

Are you using an XBox-S-Controller? I am asking because macOS completely ignores my own Xbox controller (even if connected by cable).

kyods commented 3 years ago

I use exactly this model: https://www.xbox.com/en-US/accessories/controllers/phantom-white - and it works perfectly in macOS, both Catalina and Big Sur.

dirkwhoffmann commented 3 years ago

I've added the controller to the list of yet unsupported controllers, so it won't be forgotten:

https://www.amazon.de/hz/wishlist/ls/35K6X4B0FIEOF/ref=nav_wishlist_lists_2?_encoding=UTF8&type=wishlist

dirkwhoffmann commented 3 years ago

@kyods: Thank you very much for your kind donation, which arrived just in time for Christmas. I have already analyzed the USB messages from the controller and made the appropriate adjustments in the GameController class. In v0.9.16 the controller is recognized and both the two analog sticks and the hatswitch should work. Please let me know if there is any issue left with the support of this controller.

BTW, the controller should be displayed as „XBox Carbon Black“. If not, it is not recognized correctly (due to a different product ID of your model and mine):

Bildschirmfoto 2020-12-21 um 17 06 00
kyods commented 3 years ago

Hi Dirk,

I'm glad I could help - and I was quite amazed (pun intended) about Amazon delivering so quickly. The bad news, however, is that I'm still having issues with my controller not being recognised. vAmiga doesn't recognise the DualShock 4 controller I borrowed from a friend either, which makes me think there might be something happening on my side - although both controllers work in VirtualC64. To make things even weirder, vAmiga is showing the generic "mouse" USB device available even when there's nothing connected neither via Bluetooth nor USB. Odd.

dirkwhoffmann commented 3 years ago

Amazon delivering so quickly

Sadly, this is one of the rare things still working here 🥴.

To tackle the remaining issues, I think it's best to come up with a more general solution. Since most controllers seem to map events based on one of a limited number of schemes, I'll try to make these schemes configurable in the configuration panel. I'll also display the important USB device information such as the vendor ID and product ID. Doing so will enable us to debug these things without buying each and every controller.

dirkwhoffmann commented 3 years ago

In the next version, external HID devices will be customizable by selecting predefined mapping schemes for the left stick, right stick, and hat switch.

Bildschirmfoto 2020-12-23 um 15 24 54

It's not fully functional, yet. I'll follow up on that after the Christmas break.

dirkwhoffmann commented 3 years ago

The panel is (supposed to be) fully functional in v0.9.16.1.

Can you connect the unrecognised DualShock 4 controller and make a screenshot? I can then compare the values with the ones in the database.

kyods commented 3 years ago

Here's the weird thing: vAmiga doesn't recognise any controllers I connect via Bluetooth, neither the DualShock 4, nor the Xbox One controller. The only device it recognises is the built-in trackpad, which doesn't seem right:

Screenshot 2020-12-27 at 14 11 59 Screenshot 2020-12-27 at 14 04 01 Screenshot 2020-12-27 at 14 04 12

I do have an external Magic Trackpad which I use when I connect my Macbook to the monitor, and I had a feeling that this is what might be confusing vAmiga, or interfering with the device detection. There might be a hint of truth in that, because when I switched the trackpad on (I keep it off when detached from the monitor), the device selection drop-down list went bonkers:

Screenshot 2020-12-27 at 14 07 48

For the testing purposes I removed the trackpad from my Bluetooth devices, rebooted and started vAmiga again, but that didn't make the situation any different.

Let me know please if there's anything else I can do on my end to assist.

dirkwhoffmann commented 3 years ago

The only device it recognises is the built-in trackpad, which doesn't seem right:

The HID manager presents the internal trackpad to vAmiga every time. Normally, vAmiga filters the trackpad out as it does with every other device that is marked as "build-in". Maybe the device we see here is the external trackpad which itself identifies as internal trackpad. Apple does something similar with mice. It's extremely hard to distinguish the internal mouse from an external one (simply because Apple doesn't want us to distinguish them).

the device selection drop-down list went bonkers:

😳 Uh, this shouldn't be possible. For some reason, the device slots get overwritten. However, the emulator is not supposed to modify the first three slots at all (or four if "None" is considered a device slot).

Edit: VirtualC64 does not recognize the trackpad, because it filters out all HID devices of type "mouse". vAmiga is able to support external mice, VirtualC64 is not (because mice aren't as important on the C64 as they are on the Amiga).

kyods commented 3 years ago

Is it possible that the device filtering might be somehow getting in the way? I can't really imagine why that would be happening only in my setup, though. Anyway, I can see both controllers just fine in VirtualC64 (although the Xbox controller uses the generic name):

Screenshot 2020-12-27 at 16 12 11
dirkwhoffmann commented 3 years ago

Hmm, VirtualC64 (3.4) gets it right (the new version will likely fail, too, because it uses the same codes as vAmiga).

It's hard to debug remotely. Are you familiar with Xode? If it's possible for you to compile and run vAmiga inside Xcode, the following function would be the entry point (in GamePadManager.swift):

    //
    // HID support
    //

    // Matching callback (invoked when a matching HID device is plugged in)
    func hidDeviceAdded(context: UnsafeMutableRawPointer?,
                        result: IOReturn,
                        sender: UnsafeMutableRawPointer?,
                        device: IOHIDDevice) {

        track()

        // Ignore internal devices
        if device.isBuiltIn { return }

        // Find a free slot for the new device
        guard let slot = findFreeSlot() else { return }

        // Add device
        addDevice(slot: slot, device: device)

        // Reconnect devices (assignments trigger side effects)
        parent.config.gameDevice1 = parent.config.gameDevice1
        parent.config.gameDevice2 = parent.config.gameDevice2

        // Inform about the changed configuration
        parent.toolbar.validateVisibleItems()
        parent.myAppDelegate.deviceAdded()

        listDevices()
    }

Debug messages can be produced via track(). Here, it would be interesting to see if the function is called at all, and if yes, if it executes all down the way until listDevices()is called (which produces more debug messages in the Xcode console window).

Filtering is done in init(parent: ) with the following list:

       // Prepare to accept HID devices
        let deviceCriteria = [
            [
                kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
                kIOHIDDeviceUsageKey: kHIDUsage_GD_Joystick
            ],
            [
                kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
                kIOHIDDeviceUsageKey: kHIDUsage_GD_GamePad
            ],
            [
                kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
                kIOHIDDeviceUsageKey: kHIDUsage_GD_MultiAxisController
            ],
            [
                kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop,
                kIOHIDDeviceUsageKey: kHIDUsage_GD_Mouse
            ]
        ]

vAmiga accepts more devices than VirtualC64. Hence, it's unlikely that your controller is filtered out here.Nevertheless, it would be interesting to see how the emulator reacts if kHIDUsage_GD_Mouse devices are removed from that list. Your trackpad should no longer show up in the emulator in this case.

kyods commented 3 years ago

My only experience with Xcode so far was a failed attempt at compiling Madd's fork of Boxer https://github.com/MaddTheSane/Boxer. I am willing to reinstall Xcode and give it a try, though, but you would need to guide me step by step, and since I am a total layman, our mileage may vary.

dirkwhoffmann commented 3 years ago

I am willing to reinstall Xcode and give it a try

Great. You can compile it just as VirtualC64 which was discussed in an older thread here. If everything works as expected, it's basically downloading the project, launching Xcode and hitting "Run":

https://github.com/dirkwhoffmann/virtualc64/issues/566

If compilation should go wrong, I am sure we can easily rule out the issues.

kyods commented 3 years ago

Sounds like an adventure. I'll install XCode tonight and try to compile vAmiga tomorrow.

kyods commented 3 years ago

Well, mission accomplished. I have managed to built and run vAmiga in Xcode. Here's what I see in the console log upon startup with the DualShock 4 connected via Bluetooth:

Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
MyController.301::addListener()
MyController.321::createTimer()
MyAppDelegate.53::applicationDidFinishLaunching(_:)
MyControllerTouchBar.44::makeTouchBar()
GamePadManager.169::hidDeviceAdded(context:result:sender:device:)
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [-] v: 1452 p: 631 l: 16777216 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
MyAppDelegate.176::deviceAdded()
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
GamePadManager.169::hidDeviceAdded(context:result:sender:device:)
MyDocument.380::loadScreenshots(): Seeking screenshots for disk with id 0
MyDocument.389::loadScreenshots(): 0 screenshots loaded
MyController.446::processMessage(_:): Registered to message queue
Animation.197::zoomIn(steps:): Zooming in...
2020-12-28 16:35:29.254553+0100 vAmiga[35757:1563221] [aqme] AQMEIO.cpp:182:AwaitIOCycle: timed out after 0.012s (16 17); suspension count=0 (IOSuspensions: )
dirkwhoffmann commented 3 years ago

Well, mission accomplished

Very good. I can spot two calls to hidDeviceAdded in your log. The first one is for your internal / external trackpad. The second one is for the unrecognised DualShock 4. In the second call the function is exited earlier, because I can't see the result of listDevices() in the log. Now, we need to find out where the functions exists. To find out, simply place track() statements in each blank line in the following function and run again (File GamePadManager.swift). The log will show where the function exits.

    func hidDeviceAdded(context: UnsafeMutableRawPointer?,
                        result: IOReturn,
                        sender: UnsafeMutableRawPointer?,
                        device: IOHIDDevice) {

        track()

        // Ignore internal devices
        if device.isBuiltIn { return }

        // Find a free slot for the new device
        guard let slot = findFreeSlot() else { return }

        // Add device
        addDevice(slot: slot, device: device)

        // Reconnect devices (assignments trigger side effects)
        parent.config.gameDevice1 = parent.config.gameDevice1
        parent.config.gameDevice2 = parent.config.gameDevice2

        // Inform about the changed configuration
        parent.toolbar.validateVisibleItems()
        parent.myAppDelegate.deviceAdded()

        listDevices()
    }
kyods commented 3 years ago

Not sure if I did it right, but here we go:

Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
MyController.301::addListener()
MyController.321::createTimer()
MyControllerTouchBar.44::makeTouchBar()
GamePadManager.169::hidDeviceAdded(context:result:sender:device:)
GamePadManager.169::hidDeviceAdded(context:result:sender:device:)
GamePadManager.173::hidDeviceAdded(context:result:sender:device:)
GamePadManager.176::hidDeviceAdded(context:result:sender:device:)
GamePadManager.179::hidDeviceAdded(context:result:sender:device:)
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [-] v: 1452 p: 631 l: 16777216 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
GamePadManager.183::hidDeviceAdded(context:result:sender:device:)
MyAppDelegate.176::deviceAdded()
GamePadManager.187::hidDeviceAdded(context:result:sender:device:)
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
MyDocument.380::loadScreenshots(): Seeking screenshots for disk with id 0
MyDocument.389::loadScreenshots(): 0 screenshots loaded
MyController.446::processMessage(_:): Registered to message queue
Animation.197::zoomIn(steps:): Zooming in...
2020-12-28 17:20:59.717732+0100 vAmiga[36134:1590803] [aqme] AQMEIO.cpp:182:AwaitIOCycle: timed out after 0.012s (16 17); suspension count=0 (IOSuspensions: )
dirkwhoffmann commented 3 years ago
GamePadManager.169::hidDeviceAdded(context:result:sender:device:)
GamePadManager.169::hidDeviceAdded(context:result:sender:device:)

What we see in the log here is a strong indication of a multi-threading issue. In the next release, I'll add a lock to synchronise calls. With some luck, this is going to fix the issue.

dirkwhoffmann commented 3 years ago

I've checked in a potential fix on the main branch. Could you download the latest code and compile it in Xcode as you did before?

What I did is to add a NSLock to the GamePadManager:

var lock = NSLock()

This lock is utilised in function hidDeviceAdded as follows:

    func hidDeviceAdded(context: UnsafeMutableRawPointer?,
                        result: IOReturn,
                        sender: UnsafeMutableRawPointer?,
                        device: IOHIDDevice) {

        lock.lock(); defer { lock.unlock() }
        ...

Now, all devices should be processed one after another.

kyods commented 3 years ago

Unfortunately still no go.

Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
MyController.301::addListener()
MyController.321::createTimer()
MyControllerTouchBar.44::makeTouchBar()
GamePadManager.168::hidDeviceAdded(context:result:sender:device:)
GamePadManager.172::hidDeviceAdded(context:result:sender:device:)
GamePadManager.175::hidDeviceAdded(context:result:sender:device:)
GamePadManager.178::hidDeviceAdded(context:result:sender:device:)
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [-] v: 1452 p: 631 l: 16777216 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
GamePadManager.182::hidDeviceAdded(context:result:sender:device:)
MyAppDelegate.176::deviceAdded()
GamePadManager.186::hidDeviceAdded(context:result:sender:device:)
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
GamePadManager.168::hidDeviceAdded(context:result:sender:device:)
MyDocument.380::loadScreenshots(): Seeking screenshots for disk with id 0
MyDocument.389::loadScreenshots(): 0 screenshots loaded
MyController.446::processMessage(_:): Registered to message queue
Animation.197::zoomIn(steps:): Zooming in...
dirkwhoffmann commented 3 years ago

Although it's not working as expected yet, we can see in the log that the lock is doing what it is supposed to do. Let's try to produce more debug output. Could you replace function hidDeviceAdded by the following code and run again? The new code will print out all property keys for all devices that are accepted by the HID handler.

    func hidDeviceAdded(context: UnsafeMutableRawPointer?,
                        result: IOReturn,
                        sender: UnsafeMutableRawPointer?,
                        device: IOHIDDevice) {

        lock.lock(); defer { lock.unlock() }
        track("Processing device:")
        device.listProperties()

        // Ignore internal devices
        if device.isBuiltIn {
            track("Skipping this device")
            return
        }
        track("External device")

        // Find a free slot for the new device
        guard let slot = findFreeSlot() else { return }
        track("slot =\(slot)")

        // Add device
        addDevice(slot: slot, device: device)
        track("Added")

        // Reconnect devices (assignments trigger side effects)
        parent.config.gameDevice1 = parent.config.gameDevice1
        parent.config.gameDevice2 = parent.config.gameDevice2
        track("Reconnected")

        // Inform about the changed configuration
        parent.toolbar.validateVisibleItems()
        parent.myAppDelegate.deviceAdded()

        listDevices()
        track("Done")
    }
kyods commented 3 years ago

Done. Somewhere down there I see my DualShock 4, and unless I'm confusing things, vAmiga identifies is as a built-in device?

Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
MyController.301::addListener()
MyController.321::createTimer()
MyControllerTouchBar.44::makeTouchBar()
GamePadManager.168::hidDeviceAdded(context:result:sender:device:): Processing device:
    Transport: SPI
    VendorID: 1452
    ProductID: 631
    VersionNumber: 2198
    Product: Apple Internal Keyboard / Trackpad
    CountryCode: 0
    LocationID: 16777216
    DeviceUsagePairs: (
        {
        DeviceUsage = 2;
        DeviceUsagePage = 1;
    },
        {
        DeviceUsage = 1;
        DeviceUsagePage = 1;
    },
        {
        DeviceUsage = 5;
        DeviceUsagePage = 13;
    },
        {
        DeviceUsage = 12;
        DeviceUsagePage = 65280;
    }
)
    PrimaryUsage: 2
    PrimaryUsagePage: 1
    MaxInputReportSize: 1368
    MaxOutputReportSize: 1
    MaxFeatureReportSize: 1
    ReportInterval: 8000
    ReportDescriptor: {length = 110, bytes = 0x05010902 a1010901 a1000509 19012903 ... 75089657 058100c0 }
    UniqueID: 4295071364
GamePadManager.176::hidDeviceAdded(context:result:sender:device:): External device
GamePadManager.180::hidDeviceAdded(context:result:sender:device:): slot =3
GamePadManager.184::hidDeviceAdded(context:result:sender:device:): Added
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [-] v: 1452 p: 631 l: 16777216 48 49 53 50 0
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
GamePadManager.189::hidDeviceAdded(context:result:sender:device:): Reconnected
MyAppDelegate.176::deviceAdded()
Input devices:
Mouse (Mouse) [1] 48 49 53 50 0
Joystick Keyset 1 [-] 48 49 53 50 0
Joystick Keyset 2 [-] 48 49 53 50 0
Apple Internal Keyboard / Trackpad (Mouse) [2] v: 1452 p: 631 l: 16777216 48 49 53 50 0
GamePadManager.196::hidDeviceAdded(context:result:sender:device:): Done
GamePadManager.168::hidDeviceAdded(context:result:sender:device:): Processing device:
    Transport: Bluetooth
    VendorID: 1356
    VendorIDSource: 2
    ProductID: 2508
    VersionNumber: 256
    Manufacturer: Unknown
    Product: DUALSHOCK 4 Wireless Controller
    SerialNumber: a4-ae-12-e7-fd-33
    CountryCode: 0
    LocationID: 317193523
    DeviceUsagePairs: (
        {
        DeviceUsage = 5;
        DeviceUsagePage = 1;
    }
)
    PrimaryUsage: 5
    PrimaryUsagePage: 1
    MaxInputReportSize: 547
    MaxOutputReportSize: 547
    MaxFeatureReportSize: 64
    ReportInterval: 11250
    ReportDescriptor: {length = 442, bytes = 0x05010905 a1018501 09300931 09320935 ... 0285d409 44b102c0 }
    Built-In: 1
    UniqueID: 4295072039
GamePadManager.173::hidDeviceAdded(context:result:sender:device:): Skipping this device
MyDocument.380::loadScreenshots(): Seeking screenshots for disk with id 0
MyDocument.389::loadScreenshots(): 0 screenshots loaded
MyController.446::processMessage(_:): Registered to message queue
Animation.197::zoomIn(steps:): Zooming in...
2020-12-29 15:54:37.346969+0100 vAmiga[39139:1743375] [aqme] AQMEIO.cpp:182:AwaitIOCycle: timed out after 0.012s (16 17); suspension count=0 (IOSuspensions: )
dirkwhoffmann commented 3 years ago

OK, now we know what the problem is:

Built-In: 1

The HID manager presents the DualShock 4 to the emulator as a built-in device on your machine. I have no idea how this is possible. Either I misinterpret the semantics of a "built-in" device or it's a macOS bug. As a workaround, we could consider all Bluetooth devices as external and ignore the built-in flag in that case. 🤔

kyods commented 3 years ago

I think that's a good strategy, and we can safely assume that something connecting via Bluetooth cannot at the same time be built-in. :)

dirkwhoffmann commented 3 years ago

Your DualShock controller should be detected in v0.9.16.2 (hopefully).

kyods commented 3 years ago

Works beautifully.

Screenshot 2020-12-30 at 13 31 18

Now that we know what the issue is, I hope that fixing the Xbox controller wouldn't prove too much of a hassle?

EDIT: Scratch that. The Xbox controller works like a charm too - although not at the same time with the DualShock. Excellent work, thanks a lot, Dirk!

Screenshot 2020-12-30 at 13 35 20