Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411275 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 04:23:20 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Simple cross-platform gamepad library
Pages: [1] 2 3 ... 6
Print
Author Topic: Simple cross-platform gamepad library  (Read 52743 times)
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« on: January 25, 2010, 05:38:23 AM »

UPDATE: libstem_gamepad is now hosted on github: https://github.com/ThemsAllTook/libstem_gamepad
Latest binaries for all platforms: http://ludobloom.com/files/libstem_gamepad_1.4.2.zip

Hello all,

I've been working on a library for Mac OS X, Windows, and Linux that provides a clean, easy-to-use API for reading input from USB game controllers, to be released as an open source project under the zlib/libpng license. I've built a simple test harness that shows all connected gamepads and the state of their controls. It looks like this:



Here's what the API looks like:

Code:
struct Gamepad_device {
// Unique device identifier for application session, starting at 0 for the first device attached and
// incrementing by 1 for each additional device. If a device is removed and subsequently reattached
// during the same application session, it will have a new deviceID.
unsigned int deviceID;

// Human-readable device name
const char * description;

// USB vendor/product IDs as returned by the driver. Can be used to determine the particular model of device represented.
int vendorID;
int productID;

// Number of axis elements belonging to the device
unsigned int numAxes;

// Number of button elements belonging to the device
unsigned int numButtons;

// Array[numAxes] of values representing the current state of each axis, in the range [-1..1]
float * axisStates;

// Array[numButtons] of values representing the current state of each button
bool * buttonStates;

// Platform-specific device data storage. Don't touch unless you know what you're doing and don't
// mind your code breaking in future versions of this library.
void * privateData;
};

/* Initializes gamepad library and detects initial devices. Call this before any other Gamepad_*()
   function, other than callback registration functions. If you want to receive deviceAttachFunc
   callbacks from devices detected in Gamepad_init(), you must call Gamepad_deviceAttachFunc()
   before calling Gamepad_init().
   
   This function must be called from the same thread that will be calling Gamepad_processEvents()
   and Gamepad_detectDevices(). */
void Gamepad_init();

/* Tears down all data structures created by the gamepad library and releases any memory that was
   allocated. It is not necessary to call this function at application termination, but it's
   provided in case you want to free memory associated with gamepads at some earlier time. */
void Gamepad_shutdown();

/* Returns the number of currently attached gamepad devices. */
unsigned int Gamepad_numDevices();

/* Returns the specified Gamepad_device struct, or NULL if deviceIndex is out of bounds. */
struct Gamepad_device * Gamepad_deviceAtIndex(unsigned int deviceIndex);

/* Polls for any devices that have been attached since the last call to Gamepad_detectDevices() or
   Gamepad_init(). If any new devices are found, the callback registered with
   Gamepad_deviceAttachFunc() (if any) will be called once per newly detected device.
   
   Note that depending on implementation, you may receive button and axis event callbacks for
   devices that have not yet been detected with Gamepad_detectDevices(). You can safely ignore
   these events, but be aware that your callbacks might receive a device ID that hasn't been seen
   by your deviceAttachFunc. */
void Gamepad_detectDevices();

/* Reads pending input from all attached devices and calls the appropriate input callbacks, if any
   have been registered. */
void Gamepad_processEvents();

/* Registers a function to be called whenever a device is attached. The specified function will be
   called only during calls to Gamepad_init() and Gamepad_detectDevices(), in the thread from
   which those functions were called. Calling this function with a NULL argument will stop any
   previously registered callback from being called subsequently. */
void Gamepad_deviceAttachFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context);

/* Registers a function to be called whenever a device is detached. The specified function can be
   called at any time, and will not necessarily be called from the main thread. Calling this
   function with a NULL argument will stop any previously registered callback from being called
   subsequently. */
void Gamepad_deviceRemoveFunc(void (* callback)(struct Gamepad_device * device, void * context), void * context);

/* Registers a function to be called whenever a button on any attached device is pressed. The
   specified function will be called only during calls to Gamepad_processEvents(), in the
   thread from which Gamepad_processEvents() was called. Calling this function with a NULL
   argument will stop any previously registered callback from being called subsequently.  */
void Gamepad_buttonDownFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context);

/* Registers a function to be called whenever a button on any attached device is released. The
   specified function will be called only during calls to Gamepad_processEvents(), in the
   thread from which Gamepad_processEvents() was called. Calling this function with a NULL
   argument will stop any previously registered callback from being called subsequently.  */
void Gamepad_buttonUpFunc(void (* callback)(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context), void * context);

/* Registers a function to be called whenever an axis on any attached device is moved. The
   specified function will be called only during calls to Gamepad_processEvents(), in the
   thread from which Gamepad_processEvents() was called. Calling this function with a NULL
   argument will stop any previously registered callback from being called subsequently.  */
void Gamepad_axisMoveFunc(void (* callback)(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context), void * context);
« Last Edit: June 30, 2015, 03:24:53 PM by ThemsAllTook » Logged

deadeye
First Manbaby Home
Level 10
*



View Profile
« Reply #1 on: January 25, 2010, 05:58:57 AM »

Tried the windows version with my Logitech Dual Action.  Works fine, all buttons and axis...es... light up normally.  Your buttons are numbered 0 - 11, and the ones on my controller are numbered 1 - 12, but I'm sure that's of no consequence.

Don't know what kind of hardware info you need, but here:
Windows XP SP2,  Athlon 64 Dual Core 4200+ 2.21GHz, 2G Ram
Logged

tweet tweet @j_younger
Draknek
Level 6
*


"Alan Hazelden" for short


View Profile WWW
« Reply #2 on: January 25, 2010, 06:42:24 AM »

Tried the Linux version with an Xbox 360 controller and it worked. The axis ordering is maybe a bit weird (left x, left y, left trigger, right x, right y, right trigger), but that has nothing to do with your library.
Logged

Ivan
Owl Country
Level 10
*


alright, let's see what we can see


View Profile
« Reply #3 on: January 25, 2010, 10:14:12 AM »

Wow, this looks great!! I'm gonna see if i can roll it up into my engine. I'll let you know how it works with my gamepads
Logged

http://polycode.org/ - Free, cross-platform, open-source engine.
Alex May
...is probably drunk right now.
Level 10
*


hen hao wan


View Profile WWW
« Reply #4 on: January 25, 2010, 10:27:50 AM »

Great idea.

One thing you could do is detect certain types of commonly-used controllers and have defines or enumerations for their buttons, so that apps could use those in a switch against the controller type.
Logged

ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #5 on: January 25, 2010, 10:43:28 AM »

Cool, glad to hear it's working so far!

Great idea.

One thing you could do is detect certain types of commonly-used controllers and have defines or enumerations for their buttons, so that apps could use those in a switch against the controller type.

Yep, I'm definitely thinking about ways to do this nicely... Once I get the low-level parts of this library working well enough (which looks like it might be already), I'd like to build a higher-level device-aware API on top of it. The hope is that the high-level API would be able to consistently tell you which button is which, tell you their names, know the physical layout for displaying configuration screens, etc. This sounds like it might require a large data collection effort to build up a reasonable database of device behaviors, so I'm still thinking about the logistics of that... If I can get it going, though, I feel like this database could be a great asset to the game development community.
Logged

iamwhosiam
Level 0
***


View Profile WWW
« Reply #6 on: January 25, 2010, 05:05:03 PM »

don't know if your still wondering but the 360 controller works on windows!

perfectly!

I was just wondering how I would do this a couple days ago. Can't wait for this one!
Logged
mcc
Level 10
*****


glitch


View Profile WWW
« Reply #7 on: January 25, 2010, 09:36:51 PM »

What would be the difference between this and SDL joystick input?
Logged

My projects:<br />Games: Jumpman Retro-futuristic platforming iJumpman iPhone version Drumcircle PC+smartphone music toy<br />More: RUN HELLO
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #8 on: January 26, 2010, 07:01:34 AM »

What would be the difference between this and SDL joystick input?

The two main reasons I wrote this instead of using SDL_joystick:
  • SDL is licensed under GPL, which I find too restrictive. I prefer permissive licenses like zlib/libpng.
  • SDL_joystick doesn't look like it can be used standalone. You have to pull in all of SDL to use it.

If you're already using SDL or if the above two things don't bug you, it'll probably work just as well. I actually used SDL joystick code as a reference/sanity check for a lot of what I did here, so it ends up being reasonably similar.
Logged

ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #9 on: January 28, 2010, 06:54:33 PM »

I think I've finally managed to get this into a releasable state.

Source
Pre-built binaries

Have fun! Please let me know if I've overlooked anything, or if you find bugs, have suggestions for improvements, etc.
Logged

d3sphil
Level 0
**


View Profile
« Reply #10 on: February 06, 2010, 09:22:35 PM »

this looks sweet =).
I typically use SDL, but the SDL_joystick doesn't support any type of attach/detach stuff.
From running your example and looking at the code it seems this library can do that stuff,
so I will most likely use this over anything else. Detecting when a gamepad is connected or disconnected
is like super important in my opinion =).

Thanks!
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #11 on: February 07, 2010, 05:58:29 AM »

The two main reasons I wrote this instead of using SDL_joystick:
  • SDL is licensed under GPL, which I find too restrictive. I prefer permissive licenses like zlib/libpng.
  • SDL_joystick doesn't look like it can be used standalone. You have to pull in all of SDL to use it.

I agree with your second point, the less external dependencies I have to include the happier I am! But SDL is not GPL, it is LGPL.

Still, I really love your effort and I will absolutely try your library!  Kiss
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #12 on: February 08, 2010, 10:06:01 AM »

Cool, thanks guys. I've done a test integration of this library into one of my own games to experience any integration pains myself. Here's the stuff I'm working on fixing:

  • The 1.1.0 release posted above was accidentally built with some debug code left in. The effect this will has is that hat switches on some joysticks will give erroneous inputs on Mac OS X.
  • I'm tracking down a crash that occurs when I remove a device and plug it back in while running the game, which for some reason works correctly in the test harness. No idea yet what's different about the two cases.
  • Axis inputs are a huge pain to manage. The game I'm integrating this with is wired to read keyboard inputs for each of the four directions. In order to determine the equivalent of "key down" and "key up" for each direction, I have to keep the last value of each mapped axis around and compare it with new values that are coming in, and do some fudging and thresholding to make sure it feels stable. I'd like to add an abstracted API that will ease this somewhat (while still allowing you to read raw axis inputs if desired, of course).
  • Mapping buttons/axes to game controls and saving those settings between game sessions is a big can of worms, to say the least... There's a lot of complex logic I still need to work out conceptually. Thinking about having multiple gamepads of the same type attached (indistinguishable to my library other than the order in which they were attached), remembering mappings for each gamepad type, what to do when more than one gamepad that has mappings is plugged in... It's a bit of a nightmare, really. Hopefully I'll be able to come up with a nice solution that the API can support. A lot of this will be on the individual application, but I want to consolidate the logic in the library as much as makes sense.

All of the above is still ignoring the additional layer I was talking about that would be aware of physical device layouts and characteristics. Lots to still do here!
Logged

Matt Thorson
Level 7
**

c'est la vie


View Profile WWW
« Reply #13 on: February 09, 2010, 08:33:52 PM »

I'm on Windows 7 with an XB360 controller and it works perfectly Smiley
Logged

ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #14 on: July 18, 2013, 07:14:06 PM »

Digging this topic up again because there's been a fair bit of interest in this library - lots of people have e-mailed me to ask if what I posted here is the most up-to-date. First post updated with the latest version of everything. Here are some tidbits from the changelog between 1.1.0 and 1.2.0 (latest):

  • Removed some dependencies on the larger Stem framework. Library is now completely standalone, though the test harness still requires a few things from it to compile.
  • Gamepad API no longer uses EventDispatcher, instead providing GLUT-style callback registration for all previous event types
  • Added 64-bit Windows and Linux support
  • Gamepad_detectDevices() significantly sped up on Linux
  • Gamepad_processEvents() will now do nothing if called from within itself
  • Fixed crashes from retrieving gamepad description strings
  • Fixed a potential thread deadlock on Linux

I used this library for gamepad support in my most recent game Convergence, so it's a bit more battle-tested now. I'm currently working on adding some kind of force feedback API, though it looks like that might be complicated and take a while - 1.2.0 is in pretty good shape as it is for anyone who wants to use it with the current feature set. One thing to note is that on Windows, it's made to be used with MinGW, and probably won't work as-is with Visual Studio. If there's interest, I can see about getting it to be compatible. Assistance with this would also be appreciated, since I know little to nothing about VS. Smiley

Hope some new people can find this useful! Sorry for the long period of inactivity.
Logged

Dr. Cooldude
Guest
« Reply #15 on: July 18, 2013, 08:18:17 PM »

Nice, might play around with this in SFML soon. Thanks for sharing! Beer!

Does this work with the Xbox 360 controller? (wired USB)
Logged
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #16 on: July 18, 2013, 08:50:55 PM »

Does this work with the Xbox 360 controller? (wired USB)

Yes, though there are a few minor issues with it... For some reason, the left and right triggers are reported as a single axis, where one trigger is negative and the other is positive. That means there's no way to tell between both pressed, and both released. I'm going to be changing APIs on Windows soon to resolve this (there are three different gamepad APIs to use, for some reason; all with different tradeoffs), but for now we're stuck with it. The center button (whatever it's called) also won't register presses on Windows. Other than those two things, it works perfectly as far as I know. Hope to get them solved soon.
Logged

randomshade
Level 1
*

Fastzelda


View Profile
« Reply #17 on: July 18, 2013, 09:04:16 PM »

This looks like exactly what I was going to write myself, which makes you awesome.
Logged
Klaim
Level 10
*****



View Profile WWW
« Reply #18 on: July 19, 2013, 04:44:10 AM »

I don't use controllers for my current games but the subject interest me. May I ask if you evaluated OIS and if yes, what are the differences with your library? I mean, other than focusing only on the controllers.
Logged

NoobsArePeople2
Level 0
**



View Profile WWW
« Reply #19 on: July 19, 2013, 08:59:55 AM »

Yes, though there are a few minor issues with it... For some reason, the left and right triggers are reported as a single axis, where one trigger is negative and the other is positive. That means there's no way to tell between both pressed, and both released. I'm going to be changing APIs on Windows soon to resolve this (there are three different gamepad APIs to use, for some reason; all with different tradeoffs), but for now we're stuck with it. The center button (whatever it's called) also won't register presses on Windows. Other than those two things, it works perfectly as far as I know. Hope to get them solved soon.

The 360 pad only works 100% correctly on Windows if you use XInput for some reason that's probably political rather than technical. Most of the gamepad libs I've looked at use DirectInput which works fine aside from the issue with the triggers that you mentioned. I believe the SDL2 joystick/gamepad code on Windows handles XInput controllers by first looking for XInput and then DirectInput when initializing a controller.
Logged
Pages: [1] 2 3 ... 6
Print
Jump to:  

Theme orange-lt created by panic