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.