I am trying to figure out how to properly handle game controller inputs in an SDL2 program. I have written a program which is able to handle inputs from my game controller on my Mac, but not on my Linux machine. However, the game controller does work with other programs on the Linux machine. My program uses a library called Simple2D, which wraps SDL2 to provide an easier interface.
I'm trying to determine if the problem is with the controller input handling or some missing dependency/configuration on the Linux side. I have not found any examples of using SDL2 to handle game controller inputs to determine if the code is working correctly.
Here is the code that Simple2D uses for handling game controller inputs. Do you see anything that could cause my controller to not work on my Linux machine?
https://github.com/simple2d/simple2d/blob/master/src/simple2d.c#L642-L689
// Variables for controllers and joysticks
SDL_GameController *controller = NULL;
SDL_Joystick *joy = NULL;
// Enumerate joysticks
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
// Check to see if joystick supports SDL's game controller interface
if (SDL_IsGameController(i)) {
controller = SDL_GameControllerOpen(i);
if (controller) {
sprintf(S2D_msg, "Found a valid controller, named: %s\n",
SDL_GameControllerName(controller));
S2D_Log(S2D_msg, S2D_INFO);
break; // Break after first available controller
} else {
sprintf(S2D_msg, "Could not open game controller %i: %s\n", i, SDL_GetError());
S2D_Log(S2D_msg, S2D_ERROR);
}
// Controller interface not supported, try to open as joystick
} else {
sprintf(S2D_msg, "Joystick %i is not supported by the game controller interface", i);
S2D_Log(S2D_msg, S2D_WARN);
joy = SDL_JoystickOpen(i);
// Joystick is valid
if (joy) {
sprintf(S2D_msg,
"Opened Joystick %i\n"
"Name: %s\n"
"Axes: %d\n"
"Buttons: %d\n"
"Balls: %d\n",
i, SDL_JoystickName(joy), SDL_JoystickNumAxes(joy),
SDL_JoystickNumButtons(joy), SDL_JoystickNumBalls(joy)
);
S2D_Log(S2D_msg, S2D_INFO);
// Joystick not valid
} else {
sprintf(S2D_msg, "Could not open Joystick %i", i);
S2D_Log(S2D_msg, S2D_ERROR);
}
break; // Break after first available joystick
}
}
https://github.com/simple2d/simple2d/blob/master/src/simple2d.c#L782-L806
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_JOYAXISMOTION:
if (window->on_controller)
window->on_controller(true, e.jaxis.axis, e.jaxis.value, false, 0);
break;
case SDL_JOYBUTTONDOWN:
if (window->on_controller)
window->on_controller(false, 0, 0, true, e.jbutton.button);
break;
Update: Thanks to @genpfault, I found out that the SDL codebase comes with a test script. I am working on compiling and running this. After downloading the source code, on my Mac, I was able to successfully compile and run the test script like this:
$ cd SDL2-2.0.4/test
$ gcc testgamecontroller.c `sdl2-config --cflags --libs`
$ ./a.out
However, when I tried the same thing on my Linux machine, it failed to compile:
$ gcc testgamecontroller.c `sdl2-config --cflags --libs`
In file included from /usr/include/SDL2/SDL.h:69:0,
from testgamecontroller.c:19:
testgamecontroller.c: In function 'main':
testgamecontroller.c:296:132: warning: comparison between pointer and integer
SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
^
/usr/include/SDL2/SDL_assert.h:143:19: note: in definition of macro 'SDL_enabled_assert'
while ( !(condition) ) { \
^
testgamecontroller.c:296:17: note: in expansion of macro 'SDL_assert'
SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
^
testgamecontroller.c:325:144: warning: comparison between pointer and integer
SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
^
/usr/include/SDL2/SDL_assert.h:143:19: note: in definition of macro 'SDL_enabled_assert'
while ( !(condition) ) { \
^
testgamecontroller.c:325:29: note: in expansion of macro 'SDL_assert'
SDL_assert(SDL_GameControllerFromInstanceID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) == gamecontroller);
^
/tmp/ccipRq1s.o: In function `main':
testgamecontroller.c:(.text+0x8ea): undefined reference to `SDL_GameControllerFromInstanceID'
testgamecontroller.c:(.text+0xa0e): undefined reference to `SDL_GameControllerFromInstanceID'
collect2: error: ld returned 1 exit status
So then I tried to run ./configure && make
instead:
$ ./configure
checking build system type... armv7l-unknown-linux-gnueabihf
checking host system type... armv7l-unknown-linux-gnueabihf
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking for an ANSI C-conforming const... yes
checking for pkg-config... /usr/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for SDL... yes
checking how to run the C preprocessor... gcc -E
checking for X... libraries , headers
checking for OpenGL support... yes
checking for OpenGL ES support... no
checking for OpenGL ES2 support... yes
checking for TTF_Init in -lSDL2_ttf... yes
configure: creating ./config.status
config.status: creating Makefile
$ make
gcc -o checkkeys checkkeys.c -g -O2 -D_REENTRANT -I/usr/include/SDL2 -DHAVE_OPENGLES2 -DHAVE_OPENGL -DHAVE_SDL_TTF -g -lSDL2_test -lSDL2
gcc -o loopwave loopwave.c -g -O2 -D_REENTRANT -I/usr/include/SDL2 -DHAVE_OPENGLES2 -DHAVE_OPENGL -DHAVE_SDL_TTF -g -lSDL2_test -lSDL2
gcc -o loopwavequeue loopwavequeue.c -g -O2 -D_REENTRANT -I/usr/include/SDL2 -DHAVE_OPENGLES2 -DHAVE_OPENGL -DHAVE_SDL_TTF -g -lSDL2_test -lSDL2
/tmp/ccdYXqs4.o: In function `loop':
/home/chip/stash/SDL2-2.0.4/test/loopwavequeue.c:63: undefined reference to `SDL_GetQueuedAudioSize'
/home/chip/stash/SDL2-2.0.4/test/loopwavequeue.c:66: undefined reference to `SDL_QueueAudio'
collect2: error: ld returned 1 exit status
Makefile:78: recipe for target 'loopwavequeue' failed
make: *** [loopwavequeue] Error 1
I'm not sure why it is failing to compile and how to move past these errors.
Update: Thanks again to @genpfault, installing the latest version of SDL (2.0.4) from source, instead of using version 2.0.2 that was available as a package, fixed my compiling issues.
Now when I run the controller test script, on my Linux machine, I see this:
2016-06-29 12:59:05.588 a.out[64297:8754739] INFO: There are 0 game controller(s) attached (0 joystick(s))
However, when I run it on my Mac I see this:
2016-06-29 12:59:25.688 a.out[64303:8755040] INFO: Joystick 0: USB,2-axis 8-button gamepad (guid 83050000000000006020000000000000)
2016-06-29 12:59:25.690 a.out[64303:8755040] INFO: There are 0 game controller(s) attached (1 joystick(s))
What could be different about the Linux environment? Again, the same controller works in other programs on this same Linux machine.
Update: I found that the device is detected:
$ cat /proc/bus/input/devices
...
I: Bus=0003 Vendor=0583 Product=2060 Version=0110
N: Name="USB,2-axis 8-button gamepad "
P: Phys=usb-1c14400.usb-1/input0
S: Sysfs=/devices/platform/soc@01c00000/1c14400.usb/usb2/2-1/2-1:1.0/0003:0583:2060.0003/input/input5
U: Uniq=
H: Handlers=js0 event2
B: PROP=0
B: EV=1b
B: KEY=ff 0 0 0 0 0 0 0 0 0
B: ABS=3
B: MSC=10
However, I'm not sure why SDL is not detecting it.