SDL_NumJoysticks() - Cross Platform Behaviour

Hi.

First time poster here, please be gentle! :smiley:

Iā€™m currently working on an OSX application Iā€™m porting across from Windows. Itā€™s written in SDL 2.0.3.
A key feature is Joystick hotpluggingā€¦ To make the feature work on Windows, I infrequently execute the SDL_NumJoysticks() method to get a running count of the number of devices plugged into the host computer.

Between polling intervals, I store the SDL_NumJoysticks() result in a var, and compare it to the latest reading to determine when to attempt to open new Joysticks. Itā€™s probably not an optimal technique, but it works well enough. The problem comes with working on OSXā€¦ When I attempt to run the same code on a Mac, it breaks, because all successive calls to SDL_NumJoysticks() after initialisation return the same value as the initial call.

I canā€™t imagine that being expected behaviourā€¦

For the record, I tend not to use the eventing system, and would much rather avoid doing so (as Iā€™m binding to a dynamic runtime, and am trying to keep the application logic in script).

Is the SDL_NumJoysticks() method deprecated, or should I expect the method to work differently across different operating systems?
And if the method is deprecated, do you have any advice on how I should effect hotplugging support on OSX?

So far, my research has led me to the following links:

http://stackoverflow.com/questions/21224023/why-does-sdl-numjoysticks-never-update#comment35466354_21224023 - Which appears to be a similar issueā€¦
http://forums.libsdl.org/viewtopic.php?t=9592&sid=f1468617007f482a33de58d62234405f - Which appears to be somewhat related, and explores alternative hotplugging schemesā€¦

Is there anyone out there who can shed a little light on this?

Thank you.

Have you tried using the SDL_JOYDEVICEADDED and SDL_JOYDEVICEREMOVED event types (corresponding to SDL_JoyDeviceEvent / event.jdevice) instead?On Apr 22, 2014, at 9:07 AM, raskie <l_r_charles at hotmail.com> wrote:

Hi.

First time poster here, please be gentle!

Iā€™m currently working on an OSX application Iā€™m porting across from Windows. Itā€™s written in SDL 2.0.3.
A key feature is Joystick hotpluggingā€¦ To make the feature work on Windows, I infrequently execute the SDL_NumJoysticks() method to get a running count of the number of devices plugged into the host computer.

Between polling intervals, I store the SDL_NumJoysticks() result in a var, and compare it to the latest reading to determine when to attempt to open new Joysticks. Itā€™s probably not an optimal technique, but it works well enough. The problem comes with working on OSXā€¦ When I attempt to run the same code on a Mac, it breaks, because all successive calls to SDL_NumJoysticks() after initialisation return the same value as the initial call.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Message-ID: <1398168431.m2f.43167 at forums.libsdl.org>
Content-Type: text/plain; charset=ā€œiso-8859-1ā€

Hi.

First time poster here, please be gentle! :smiley:

Iā€™m currently working on an OSX application Iā€™m porting across from Windows.
Itā€™s written in SDL 2.0.3.
A key feature is Joystick hotpluggingā€¦ To make the feature work on Windows,
I infrequently execute the SDL_NumJoysticks() method to get a running count
of the number of devices plugged into the host computer.

Between polling intervals, I store the SDL_NumJoysticks() result in a var,
and compare it to the latest reading to determine when to attempt to open
new Joysticks. Itā€™s probably not an optimal technique, but it works well
enough. The problem comes with working on OSXā€¦ When I attempt to run the
same code on a Mac, it breaks, because all successive calls to
SDL_NumJoysticks() after initialisation return the same value as the initial
call.

I canā€™t imagine that being expected behaviourā€¦

For the record, I tend not to use the eventing system, and would much rather
avoid doing so (as Iā€™m binding to a dynamic runtime, and am trying to keep
the application logic in script).

Sorry to rain on your parade, but events simply ARE the correct way to
go about it. However, it should be fully possible to do this in
basically the same manner as you do the _NumJoysticks() polling,
because the callback portion of the events system is OPTIONAL (even on
e.g. iOS, I think), and was actually added to the already existing
event system part way through SDL 2ā€™s initial development.

As for howā€¦> Date: Tue, 22 Apr 2014 12:07:11 +0000

From: ā€œraskieā€ <l_r_charles at hotmail.com>
To: sdl at lists.libsdl.org
Subject: [SDL] SDL_NumJoysticks() - Cross Platform Behaviour.

Date: Tue, 22 Apr 2014 18:48:14 -0300
From: Alex Szpakowski
To: sdl at lists.libsdl.org
Subject: Re: [SDL] SDL_NumJoysticks() - Cross Platform Behaviour.
Message-ID: <5EADC85D-145D-4161-B4E0-D6502B2FAE06 at gmail.com>
Content-Type: text/plain; charset=ā€œiso-8859-1ā€

Have you tried using the SDL_JOYDEVICEADDED and SDL_JOYDEVICEREMOVED event types (corresponding to SDL_JoyDeviceEvent / event.jdevice) instead?

Those are the joystick hotplug events. You call _PollEvent() with the
pointer to your event structure, check the type, ignore it if you
donā€™t care about it, otherwise you do something about it. Because
_PollEvent() is called (and to the best of my knowledge doesnā€™t block)
you can use it to replace _NumJoysticks() (_PumpEvents() with
_PeepEvents() might be a better fit to your particular engine model).
After that you would need to decide exactly how you want things to
work: if you automatically open joysticks then youā€™re basically set,
if the script does it then youā€™ll need to store the hotplugging data
in some format, maybe by maintaining a tree of available joysticks.
Thanks to the magic of Turing Completeness you can do the script-side
stuff however you feel like, but events are the correct way to do this
with SDL2 (consider: what happens if a joysticks gets plugged in, and
another gets unplugged? Your technique can miss this, resulting in the
erroneous belief that joystick A isnā€™t available but joystick B is.).

For the record, I tend not to use the eventing system, and would much
rather avoid doing so (as Iā€™m binding to a dynamic runtime, and am
trying to keep the application logic in script).

Is there anyone out there who can shed a little light on this?

The joystick count updates when we get hotplug events from Cocoa (even
if you donā€™t personally want events, SDL still needs them from the OS).

If you absolutely never ever want SDL events, you should still do this
in your program, somewhere that happens with some frequencyā€¦

 { SDL_PollEvent e; while (SDL_PollEvent(&e) {} }

ā€¦because SDL needs to talk to the OS, and does so in this function.
The loop makes sure SDLā€™s internal event queue doesnā€™t fill up, so we
throw it all away here. If you care about other events, youā€™ll need to
check them here.

(SDL_JoystickUpdate() isnā€™t enough for hotplug on Mac OS X, since it
doesnā€™t run the Cocoa runloop, it just sorts out data we got from the
runloop last time it ran.)

Lots of things will break in subtle ways if you donā€™t pump the SDL event
queue, this is one of them.

ā€“ryan.

Thanks for the suggestions.

Iā€™ll test out the event queue stuff over the week/weekend, and will let you know if the changes work. :smiley:

If you know youā€™re not using SDL for anything besides joysticks, you can
call SDL_JoystickUpdate() instead of running the event loop, and that
should take care of any periodic work that needs to be done by the joystick
system. If you do this, you should always call it before you check the
state of joysticks or gamepads.On Tue, Apr 22, 2014 at 5:07 AM, raskie <l_r_charles at hotmail.com> wrote:

Hi.

First time poster here, please be gentle! [image: Very Happy]

Iā€™m currently working on an OSX application Iā€™m porting across from
Windows. Itā€™s written in SDL 2.0.3.
A key feature is Joystick hotpluggingā€¦ To make the feature work on
Windows, I infrequently execute the SDL_NumJoysticks() method to get a
running count of the number of devices plugged into the host computer.

Between polling intervals, I store the SDL_NumJoysticks() result in a var,
and compare it to the latest reading to determine when to attempt to open
new Joysticks. Itā€™s probably not an optimal technique, but it works well
enough. The problem comes with working on OSXā€¦ When I attempt to run the
same code on a Mac, it breaks, because all successive calls to
SDL_NumJoysticks() after initialisation return the same value as the
initial call.

I canā€™t imagine that being expected behaviourā€¦

For the record, I tend not to use the eventing system, and would much
rather avoid doing so (as Iā€™m binding to a dynamic runtime, and am trying
to keep the application logic in script).

Is the SDL_NumJoysticks() method deprecated, or should I expect the method
to work differently across different operating systems?
And if the method is deprecated, do you have any advice on how I should
effect hotplugging support on OSX?

So far, my research has led me to the following links:

http://stackoverflow.com/questions/21224023/why-does-sdl-numjoysticks-never-update#comment35466354_21224023- Which appears to be a similar issueā€¦

http://forums.libsdl.org/viewtopic.php?t=9592&sid=f1468617007f482a33de58d62234405f- Which appears to be somewhat related, and explores alternative
hotplugging schemesā€¦

Is there anyone out there who can shed a little light on this?

Thank you.


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

[quote=ā€œSam Lantingaā€]If you know youā€™re not using SDL for anything besides joysticks, you can call??SDL_JoystickUpdate() instead of running the event loop, and that should take care of any periodic work that needs to be done by the joystick system. ??If you do this, you should always call it before you check the state of joysticks or gamepads.

[quote]

Heh!..

Hi, Sam!

The SDL_JoystickUpdate() method appears to work fine on OSX.
This, for example, works as expected:

Code:
SDL_JoystickUpdate();
SDL_JoystickGetButton(device pointer here); // Returns an array of button states

But this code excerpt, in a manner very similar to the SDL_NumJoystick() issue will always return ā€œSDL_TRUEā€ on OSX after pulling the joystick out of the USB socket:

Code:
SDL_JoystickUpdate();
SDL_JoystickGetAttached(device pointer here); // Evaluates to SDL_TRUE

On Windows however, SDL_JoystickGetAttached() works as expected.
I think Iā€™ll try out the event queue stuff tonight, if not, before the end of the weekā€¦

It shouldnā€™t be too painful to rewrite my code to take advantage of it, but SDL_NumJoystick() behaviour on OSX is quite confusing.

Btw, Iā€™ve only just realized that you have to initialise the SDL video subsystem to access the device IO event queue - https://wiki.libsdl.org/CategoryEvents

As Sam rightly pointed out, Iā€™m keen to avoid that as I donā€™t need a graphics window. Just the joystick/gamepad subsystem.

Okā€¦

So Iā€™ll have to retest and make sure, but Ryan C. Gordon suggestion works as described on OSX, but for anyone looking at the issue, just bear in mind that this:

Should be this:

Code:
SDL_Event e;
while (SDL_PollEvent(&e) {};

And before applying the ā€˜hackā€™, youā€™ll need to initialise the SDL_VIDEO subsystem, which isnā€™t ideal for me, but itā€™s nice to have the feature working.
Itā€™s certainly a bug, but I suppose itā€™s a minor one.

Thanks for the help. :smiley:

Okā€¦

So Iā€™ll have to retest and make sure, but Ryan C. Gordonā€™s suggestion works as described on OSX.
A note for anyone looking at the issueā€¦ just bear in mind that this:

Should be this:

Code:
SDL_Event e;
while (SDL_PollEvent(&e) {};

And before applying the ā€˜hackā€™, youā€™ll need to initialise the SDL_VIDEO subsystem, which isnā€™t ideal for me, but itā€™s nice to have the feature working.
Itā€™s certainly a bug, but I suppose itā€™s a minor one.

Thanks for the help. :smiley:

{ SDL_PollEvent e; while (SDL_PollEvent(&e) {} }

Doh, yes, that should be ā€œSDL_Event e;ā€ ā€¦ I mistyped. Sorry for the
confusion!

And before applying the ā€˜hackā€™, youā€™ll need to initialise the SDL_VIDEO
subsystem, which isnā€™t ideal for me, but itā€™s nice to have the feature
working.
Itā€™s certainly a bug, but I suppose itā€™s a minor one.

For initial detection, we run joystick events in a temporary runloop,
and then move it over to the main runloop, so the events land with
everything else. Maybe we should leave these in the separate runloop,
and run it in SDL_JoystickUpdate()?

This will still work as usual, as SDL_PollEvent() calls SDL_PumpEvents()
which calls SDL_JoystickUpdate(), but it also lets SDL_JoystickUpdate()
run separately and collect joystick events from the OS.

ā€“ryan.

For initial detection, we run joystick events in a temporary runloop,
and then move it over to the main runloop, so the events land with
everything else. Maybe we should leave these in the separate runloop,
and run it in SDL_JoystickUpdate()?

(by ā€œrunloopā€ I mean ā€œrunloop mode,ā€ fwiw.)

Attached is a patch that implements what I described here. Iā€™m away from
the office and donā€™t have any joysticks with me, so Iā€™ll push this when
I get home and test, but if anyone wants to test in the meantime, this
should fix the problem.

ā€“ryan.

-------------- next part --------------

HG changeset patch

User Ryan C. Gordon <@icculus>

Date 1398396288 14400

Thu Apr 24 23:24:48 2014 -0400

Node ID 09d8e949731014ba3226aecf73050e35a54a5cdd

Parent 94af945dbb573f9de82036c3fa0759f495ac9a0a

Mac OS X: Look for joystick hotplug in its own CFRunLoop.

This allows the joystick hotplug to function without the main event loop
(specifically: without SDL_INIT_VIDEO), and moves explicit polling for
joysticks where it belongs at the low-level: in SDL_SYS_JoystickDetect().

This lets apps call SDL_JoystickUpdate() to get hotplug events and keep
SDL_NumJoysticks() correct, as expected. As SDL_PumpEvents() (and
SDL_PollEvents, etc) calls SDL_JoystickUpdate(), existing apps will function
as before.

Thanks to ā€œraskieā€ on the forums for pointing this out!

diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c
ā€” a/src/joystick/darwin/SDL_sysjoystick.c
+++ b/src/joystick/darwin/SDL_sysjoystick.c
@@ -38,6 +38,8 @@
#include ā€œā€¦/ā€¦/events/SDL_events_c.hā€
#endif

+#define SDL_JOYSTICK_RUNLOOP_MODE CFSTR(ā€œSDLJoystickā€)+
/* The base object of the HID Manager API */
static IOHIDManagerRef hidman = NULL;

@@ -67,6 +69,11 @@
{
recDevice *pDeviceNext = NULL;
if (removeDevice) {

  •    if (removeDevice->deviceRef) {
    
  •        IOHIDDeviceUnscheduleFromRunLoop(removeDevice->deviceRef, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
    
  •        removeDevice->deviceRef = NULL;
    
  •    }
    
  •    /* save next device prior to disposing of this device */
       pDeviceNext = removeDevice->pNext;
    

@@ -378,7 +385,7 @@

 /* Get notified when this device is disconnected. */
 IOHIDDeviceRegisterRemovalCallback(ioHIDDeviceObject, JoystickDeviceWasRemovedCallback, device);
  • IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  • IOHIDDeviceScheduleWithRunLoop(ioHIDDeviceObject, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);

    /* Allocate an instance ID for this device */
    device->instance_id = ++s_joystick_instance_id;
    @@ -420,25 +427,19 @@
    {
    CFRunLoopRef runloop = CFRunLoopGetCurrent();

  • /* Run in a custom RunLoop mode just while initializing,

  •   so we can detect sticks without messing with everything else. */
    
  • CFStringRef tempRunLoopMode = CFSTR(ā€œSDLJoystickInitā€);

  • if (IOHIDManagerOpen(hidman, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
    return SDL_FALSE;
    }

    IOHIDManagerRegisterDeviceMatchingCallback(hidman, JoystickDeviceWasAddedCallback, NULL);

  • IOHIDManagerScheduleWithRunLoop(hidman, runloop, tempRunLoopMode);

  • IOHIDManagerScheduleWithRunLoop(hidman, runloop, SDL_JOYSTICK_RUNLOOP_MODE);
    IOHIDManagerSetDeviceMatchingMultiple(hidman, matchingArray);
  • while (CFRunLoopRunInMode(tempRunLoopMode,0,TRUE)==kCFRunLoopRunHandledSource) {
  • while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {
    /* no-op. Callback fires once per existing device. */
    }
  • /* Put this in the normal RunLoop mode now, for future hotplug events. */
  • IOHIDManagerUnscheduleFromRunLoop(hidman, runloop, tempRunLoopMode);
  • IOHIDManagerScheduleWithRunLoop(hidman, runloop, kCFRunLoopDefaultMode);
  • /* future hotplug events will come through SDL_JOYSTICK_RUNLOOP_MODE now. */

    return SDL_TRUE; /* good to go. */
    }
    @@ -544,6 +545,10 @@
    void
    SDL_SYS_JoystickDetect()
    {

  • while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE,0,TRUE) == kCFRunLoopRunHandledSource) {

  •    /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
    
  • }

  • if (s_bDeviceAdded || s_bDeviceRemoved) {
    recDevice *device = gpDeviceList;
    s_bDeviceAdded = SDL_FALSE;
    @@ -793,6 +798,7 @@
    }

    if (hidman) {

  •    IOHIDManagerUnscheduleFromRunLoop(hidman, CFRunLoopGetCurrent(), SDL_JOYSTICK_RUNLOOP_MODE);
       IOHIDManagerClose(hidman, kIOHIDOptionsTypeNone);
       CFRelease(hidman);
       hidman = NULL;

Attached is a patch that implements what I described here. Iā€™m away from
the office and donā€™t have any joysticks with me, so Iā€™ll push this when
I get home and test, but if anyone wants to test in the meantime, this
should fix the problem.

Tested and pushed, enjoy!

ā€“ryan.