Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Joystick problems on the Mac  (Read 7114 times)

0 Members and 1 Guest are viewing this topic.

AshleyF

  • Newbie
  • *
  • Posts: 30
    • View Profile
Joystick problems on the Mac
« on: June 13, 2017, 09:49:19 am »
Dear Laurent and Associates,

In JoystickImpl.cpp on the Mac, I have discovered an error.

The function:

bool JoystickImpl::open(unsigned int index)

contains the following code, starting at line 243, that isn't quite complete:

    // Go through all connected elements.
    for (int i = 0; i < elementsCount; ++i)
    {
        IOHIDElementRef element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
        switch (IOHIDElementGetType(element))
        {
            case kIOHIDElementTypeInput_Misc:
                switch (IOHIDElementGetUsage(element))
                {
                    case kHIDUsage_GD_X:  m_axis[Joystick::X] = element; break;
                    case kHIDUsage_GD_Y:  m_axis[Joystick::Y] = element; break;
                    case kHIDUsage_GD_Z:  m_axis[Joystick::Z] = element; break;
                    case kHIDUsage_GD_Rx: m_axis[Joystick::U] = element; break;
                    case kHIDUsage_GD_Ry: m_axis[Joystick::V] = element; break;
                    case kHIDUsage_GD_Rz: m_axis[Joystick::R] = element; break;
                    default: break;
                    // kHIDUsage_GD_Vx, kHIDUsage_GD_Vy, kHIDUsage_GD_Vz are ignored.
                }
                break;

and there's a comment in the source code that is as follows from line 274:

    // Ensure that the buttons will be indexed in the same order as their
    // HID Usage (assigned by manufacturer and/or a driver).
    std::sort(m_buttons.begin(), m_buttons.end(), JoystickButtonSortPredicate);

    // Note: Joy::AxisPovX/Y are not supported (yet).
    // Maybe kIOHIDElementTypeInput_Axis is the corresponding type but I can't test.

Well, as I do have the necessary joysticks to test this, please let me know how to rewrite the source code in accordance with SFML standards and I'll see if I can do it for you.

Kind regards,

AshleyF

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Joystick problems on the Mac
« Reply #1 on: June 14, 2017, 04:01:41 pm »
Could you add to the switch something like this:

switch (IOHIDElementGetType(element))  {
case kIOHIDElementTypeInput_Axis:
  sf::err() << "Found something interesting: 0x" << std::hex << IOHIDElementGetUsage(element) << std::dec << std::endl;
  break;
 

You'll need to include <ios> too.

Then, compile SFML, install it and for each joystick you have, plug it in (only one at a time) and run one of your program. Maybe there are some axis reported in this category...
SFML / OS X developer

AshleyF

  • Newbie
  • *
  • Posts: 30
    • View Profile
Re: Joystick problems on the Mac
« Reply #2 on: June 15, 2017, 04:47:29 pm »
Dear Hiura,

After having tried the suggestion, unfortunately I discovered that it didn't work :(

Do you have any further ideas please?

Kind regards,

AshleyF

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Joystick problems on the Mac
« Reply #3 on: June 16, 2017, 09:12:06 am »
If you've got some time, try to see if you can find which USB page/usage the undetected elements are using.
SFML / OS X developer

AshleyF

  • Newbie
  • *
  • Posts: 30
    • View Profile
Re: Joystick problems on the Mac
« Reply #4 on: June 17, 2017, 01:04:01 am »
Dear Hiura,

Please let me know how to do that, and I'll be glad to help track down this bug!

AshleyF

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Joystick problems on the Mac
« Reply #5 on: June 20, 2017, 12:03:24 pm »
Unfortunately I don't have time to build a tutorial on USB. But this shouldn't be an issue as it's a standardised protocol. You can already find a several hints in SFML source code or around the web. This is also very useful -- don't worry, there's no need to read everything!

Good luck, and let me know if you find anything.
SFML / OS X developer

AshleyF

  • Newbie
  • *
  • Posts: 30
    • View Profile
Re: Joystick problems on the Mac
« Reply #6 on: June 29, 2017, 08:18:00 pm »
Hiura,

Sorry for the delay in replying to your kind post.

Would you consider page 146 to be the best place to start?

AshleyF

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Joystick problems on the Mac
« Reply #7 on: June 29, 2017, 11:58:31 pm »
Appendix A is a list of examples, giving a wide idea of the structure of such device, so why not.

I've managed to dig a bit into it. Could you try this code (replace the corresponding function) and post the outputs. Try running your SFML application several time with a different position for the missing axis to get a good idea of the possible recorded values.

bool JoystickImpl::open(unsigned int index)
{
    m_index = index;
    Location deviceLoc = m_locationIDs[index]; // The device we need to load

    // Get all devices
    CFSetRef devices = HIDJoystickManager::getInstance().copyJoysticks();
    if (devices == NULL)
        return false;

    // Get a usable copy of the joysticks devices.
    CFIndex joysticksCount = CFSetGetCount(devices);
    CFTypeRef devicesArray[joysticksCount];
    CFSetGetValues(devices, devicesArray);

    // Get the desired joystick.
    IOHIDDeviceRef self = 0;
    for (CFIndex i(0); self == 0 && i < joysticksCount; ++i)
    {
        IOHIDDeviceRef d = (IOHIDDeviceRef)devicesArray[i];
        if (deviceLoc == HIDInputManager::getLocationID(d))
            self = d;
    }

    if (self == 0)
    {
        // This shouldn't happen!
        CFRelease(devices);
        return false;
    }

    m_identification.name      = getDeviceString(self, CFSTR(kIOHIDProductKey), m_index);
    m_identification.vendorId  = getDeviceUint(self, CFSTR(kIOHIDVendorIDKey), m_index);
    m_identification.productId = getDeviceUint(self, CFSTR(kIOHIDProductIDKey), m_index);
    sf::err()
      << "Name: " << m_identification.name.toAnsiString()
      << ", Vendor ID: 0x" << std::hex << m_identification.vendorId
      << ", Product ID: 0x" << m_identification.productId
      << std::dec << std::endl;

    // Get a list of all elements attached to the device.
    CFArrayRef elements = IOHIDDeviceCopyMatchingElements(self, NULL, kIOHIDOptionsTypeNone);

    if (elements == NULL)
    {
        CFRelease(devices);
        return false;
    }

    // Go through all connected elements.
    CFIndex elementsCount = CFArrayGetCount(elements);
    for (int i = 0; i < elementsCount; ++i)
    {
        IOHIDElementRef element = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i);
        switch (IOHIDElementGetUsagePage(element))
        {
            case kHIDPage_GenericDesktop:
                switch (IOHIDElementGetUsage(element))
                {
                    case kHIDUsage_GD_X:  m_axis[Joystick::X] = element; break;
                    case kHIDUsage_GD_Y:  m_axis[Joystick::Y] = element; break;
                    case kHIDUsage_GD_Z:  m_axis[Joystick::Z] = element; break;
                    case kHIDUsage_GD_Rx: m_axis[Joystick::U] = element; break;
                    case kHIDUsage_GD_Ry: m_axis[Joystick::V] = element; break;
                    case kHIDUsage_GD_Rz: m_axis[Joystick::R] = element; break;
                    case kHIDUsage_GD_Hatswitch: {
                        sf::err() << "hat?" << std::endl;
                        sf::err()
                          << std::boolalpha
                          << "Null State? " << (bool)IOHIDElementHasNullState(element) << "\n"
                          << "Preferred State? " << (bool)IOHIDElementHasPreferredState(element) << "\n"
                          << "Is Linear? " << !(bool)IOHIDElementIsNonLinear(element) << "\n"
                          << "Is Wrapping? " << (bool)IOHIDElementIsWrapping(element) << "\n"
                          << "Is Relative? " << (bool)IOHIDElementIsRelative(element) << "\n"
                          << "Is Array? " << (bool)IOHIDElementIsArray(element) << "\n"
                          << "Is Virtual? " << (bool)IOHIDElementIsVirtual(element) << "\n"
                          << "Collection Type: " << IOHIDElementGetCollectionType(element) << " (based on IOHIDElementCollectionType)\n"
                          << "Logical Range: [" << IOHIDElementGetLogicalMin(element) << ", " << IOHIDElementGetLogicalMax(element) << "]\n"
                          << "Physical Range: [" << IOHIDElementGetPhysicalMin(element) << ", " << IOHIDElementGetPhysicalMax(element) << "]\n";

                        IOHIDValueRef value = 0;
                        IOHIDDeviceGetValue(IOHIDElementGetDevice(element), element, &value);
                        if (value) {
                          double x = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical);
                          CFIndex y = IOHIDValueGetIntegerValue(value);
                          sf::err() << "Value: " << x << "/" << x << std::endl;
                          sf::err() << "Raw value: size -> " << IOHIDValueGetLength(value) << " data -> " << (int)*(IOHIDValueGetBytePtr(value)) << std::endl;
                        } else {
                          sf::err() << "No Value." << std::endl;
                        }

                        break;
                                                 }

                    default:
                        sf::err() << "Unexpected usage for element of Page Generic Desktop: 0x" << std::hex << IOHIDElementGetUsage(element) << std::dec << std::endl;
                        break;
                }
                break;

            case kHIDPage_Button:
                if (m_buttons.size() < Joystick::ButtonCount) // If we have free slot...
                    m_buttons.push_back(element); // ...we add this element to the list
                // Else: too many buttons. We ignore this one.
                break;

            default: /* No other page is expected because of the mask applied by the HID manager. */ break;
        }
    }

    // Ensure that the buttons will be indexed in the same order as their
    // HID Usage (assigned by manufacturer and/or a driver).
    std::sort(m_buttons.begin(), m_buttons.end(), JoystickButtonSortPredicate);

    // Retain all these objects for personal use
    for (ButtonsVector::iterator it(m_buttons.begin()); it != m_buttons.end(); ++it)
        CFRetain(*it);
    for (AxisMap::iterator it(m_axis.begin()); it != m_axis.end(); ++it)
        CFRetain(it->second);

    // Note: we didn't retain element in the switch because we might have multiple
    // Axis X (for example) and we want to keep only the last one. To prevent
    // leaking we retain objects 'only' now.

    CFRelease(devices);
    CFRelease(elements);

    return true;
}
 
SFML / OS X developer

AshleyF

  • Newbie
  • *
  • Posts: 30
    • View Profile
Re: Joystick problems on the Mac
« Reply #8 on: June 30, 2017, 02:44:18 am »
Dear Hiura,

I have obtained the following outputs:

Name: Wireless Controller, Vendor ID: 0x54c, Product ID: 0x5c4
Unexpected usage for element of Page Generic Desktop: 0x5
hat?
Null State? true
Preferred State? true
Is Linear? true
Is Wrapping? false
Is Relative? false
Is Array? false
Is Virtual? false
Collection Type: 0 (based on IOHIDElementCollectionType)
Logical Range: [0, 7]
Physical Range: [0, 315]
Value: 360/360
Raw value: size -> 1 data -> 8

Irrespective of how the joystick is being used, these results are constant.

Hope this helps!

AshleyF

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Joystick problems on the Mac
« Reply #9 on: July 01, 2017, 01:24:13 pm »
Irrespective of how the joystick is being used, these results are constant.

This is strange because when I test this I have different values with also a Dualshock 4 (finally got my hand on one).

Could you test this PR? https://github.com/SFML/SFML/pull/1248
SFML / OS X developer

AshleyF

  • Newbie
  • *
  • Posts: 30
    • View Profile
Re: Joystick problems on the Mac
« Reply #10 on: July 01, 2017, 03:20:39 pm »
Dear Hiura,

Fantastic news! Other than one exception, all now seems to be working fine!  ;)

The exception is the Thrustmaster Dual Analog 4 V.1.

For some strange reason, the right thumb controller only works horizontally, and not vertically.

The error in the output window is as follows:

Unexpected usage for element of Page Generic Desktop: 0x1
Unexpected usage for element of Page Generic Desktop: 0x36
Unexpected usage for element of Page Generic Desktop: 0xbb
Unexpected usage for element of Page Generic Desktop: 0xbb
Unexpected usage for element of Page Generic Desktop: 0xbb
Unexpected usage for element of Page Generic Desktop: 0xbb
Unexpected usage for element of Page Generic Desktop: 0xbb

By way of information, there are the occasional 'Unexpected usage ...' error messages with various other joysticks even though they work with the new code.

Please let me know what you'd like me to try next.

Thanks very much for the excellent code update!

Kind regards,

AshleyF

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Joystick problems on the Mac
« Reply #11 on: July 12, 2017, 11:49:48 am »
Good to know it works in most cases! :-)

Dunno when I'll have time to work on this.. so here are a few things to take into account if you want to dig into it.

It is well possible that some elements of a joysticks are reported several times under different categories. Usage 0x01 is a "pointer", 0x36 is a "slider" and 0xbb should be ignored because it's in the reserved page of usage code. But it's not obvious to which axis a pointer of a slider should be bind. You could try to print more information for those usage, in a similar fashion I did above.
SFML / OS X developer