SDL controller mapping ignored without error

I have a logitech f710, where the mapping is basically a mess.
this is wrong:
dpad is seen as joystickleft (should be dpad)
lefttrigger is seen as rightx
back is seen as guide, etc.

So I made my own mapping with the help of this website you find if you google “gamepad online test”.

Here is the mapping:
char* mapping = “030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a6,lefty:a7,rightx:a3,righty:a4,dpadx:a0,dpady:a1”;

Here is the function that handles controllers:

void initControllers(){
	char* mapping = "030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a6,lefty:a7,rightx:a3,righty:a4,dpx:a0,dpy:a1";
	SDL_GameController *ctrl;
	SDL_Joystick *joy;
	int i;

	int mapped = SDL_GameControllerAddMapping(mapping);
	if (mapped==-1){
		printf( "SDL could not map! SDL Error: %s\n", SDL_GetError() );
	}

	printf("mapped: %d\n", mapped);

	for(i = 0; i < SDL_NumJoysticks(); ++i) {
	    if (SDL_IsGameController(i)) {
	    	char *mapping;
	        printf("Index \'%i\' is a compatible controller, named \'%s\'\n", i, SDL_GameControllerNameForIndex(i));
	        ctrl = SDL_GameControllerOpen(i);
	        joy = SDL_GameControllerGetJoystick(ctrl);
	        mapping = SDL_GameControllerMapping(ctrl);

	        printf("mapping:%s\n", mapping);
	        SDL_free(mapping);

	        char string[512] = {0};
	        SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(i);
	        SDL_JoystickGetGUIDString(guid,string,512);
	        printf("%s\n", string);
	    } else {
	        printf("Index \'%i\' is not a compatible controller.\n", i);
	    }
	}
}

The program prints:

mapped: 0
Index '0' is a compatible controller, named 'Logitech F710 Gamepad (XInput)'
mapping:030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a6,lefty:a7,rightx:a3,righty:a4,dpadx:a0,dpady:a1
030000006d0400001fc2000005030000

So that means it updated the mapping successfully, right?

So then why is the mapping still wrong when I actually press some buttons on the controller? For fun I tried swapping the mapping for the A and B buttons, but this did nothing, while I would have expected it to swap a and b.

So something somewhere is preventing my custom map from being applied even though sdl claims all is well.

To reiterate:

I supplied my own mapping
SDL straight up ignores it silently

Any ideas? Here is the whole code:

[spoiler]

#include <SDL2/SDL.h>

void initControllers(){
	char* mapping = "030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a6,lefty:a7,rightx:a3,righty:a4,dpadx:a0,dpady:a1";
	SDL_GameController *ctrl;
	SDL_Joystick *joy;
	int i;

	int mapped = SDL_GameControllerAddMapping(mapping);
	if (mapped==-1){
		printf( "SDL could not map! SDL Error: %s\n", SDL_GetError() );
	}

	printf("mapped: %d\n", mapped);

	for(i = 0; i < SDL_NumJoysticks(); ++i) {
	    if (SDL_IsGameController(i)) {
	    	char *mapping;
	        printf("Index \'%i\' is a compatible controller, named \'%s\'\n", i, SDL_GameControllerNameForIndex(i));
	        ctrl = SDL_GameControllerOpen(i);
	        joy = SDL_GameControllerGetJoystick(ctrl);
	        mapping = SDL_GameControllerMapping(ctrl);

	        printf("mapping:%s\n", mapping);
	        SDL_free(mapping);

	        char string[512] = {0};
	        SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(i);
	        SDL_JoystickGetGUIDString(guid,string,512);
	        printf("%s\n", string);
	    } else {
	        printf("Index \'%i\' is not a compatible controller.\n", i);
	    }
	}
}

int main(int argc, char ** argv)
{
	// variables
 
	int quit = 0;
	SDL_Event event;
	int x = 288;
	int y = 208;

	// Initialize SDL
    if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0 )
    {
        printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
    }

    initControllers();

	SDL_Window * window = SDL_CreateWindow("SDL2 Keyboard/Mouse events",
		SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);
	SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);

	SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
	SDL_RenderPresent(renderer);

	// handle events

	while (!quit)
	{
		SDL_WaitEvent(&event);

		switch (event.type)
		{
			case SDL_QUIT:{
				quit = 1;
				break;
			}

			case SDL_KEYDOWN:{
				const char* a = SDL_GetKeyName(event.key.keysym.sym);
				printf("Pressed down: %s\n", a);
				break;
			}

			case SDL_KEYUP:{
				const char* a = SDL_GetKeyName(event.key.keysym.sym);
				printf("Released: %s\n", a);
				break;
			}

			case SDL_MOUSEBUTTONUP:
			case SDL_MOUSEBUTTONDOWN:
			{
				switch (event.button.button)
				{
					case SDL_BUTTON_LEFT:{
						printf("LMB");
						break;
					}

					case SDL_BUTTON_RIGHT:{
						printf("RMB");
						break;
					}

					case SDL_BUTTON_MIDDLE:{
						printf("MMB");
						break;
					}

					case SDL_BUTTON_X1:{
						printf("X1");
						break;
					}

					case SDL_BUTTON_X2:{
						printf("X2");
						break;
					}
				}

				if (event.type == SDL_MOUSEBUTTONDOWN){
					printf(" was clicked!\n");
				} else {
					printf(" was released!\n");
				}
				
				break;
			}

			case SDL_MOUSEMOTION:{
				printf("Mouse moved to (%d, %d)\n", event.motion.x, event.motion.y);
				break;
			}

			case SDL_MOUSEWHEEL:{
				printf("Scrolled ");

				if(event.wheel.y > 0){
					printf("up\n");
				}else if(event.wheel.y < 0){
					printf("down\n");
				}

				if(event.wheel.x > 0){
					printf("left\n");
				}else if(event.wheel.x < 0){
					printf("right\n");
				}

				break;
			}

			case SDL_JOYAXISMOTION:{
				const char* axisname = SDL_GameControllerGetStringForAxis(event.jaxis.axis);
				SDL_Log("Joystick axis %s value: %d\n", axisname, event.jaxis.value);
				break;
			}

			case SDL_JOYHATMOTION:{
				SDL_Log("Joystick %d hat %d value:",
				event.jhat.which, event.jhat.hat);
				if (event.jhat.value == SDL_HAT_CENTERED)
				SDL_Log(" centered");
				if (event.jhat.value & SDL_HAT_UP)
				SDL_Log(" up");
				if (event.jhat.value & SDL_HAT_RIGHT)
				SDL_Log(" right");
				if (event.jhat.value & SDL_HAT_DOWN)
				SDL_Log(" down");
				if (event.jhat.value & SDL_HAT_LEFT)
				SDL_Log(" left");
				SDL_Log("\n");
				break;
			}

			case SDL_JOYBALLMOTION:{
				SDL_Log("Joystick %d ball %d delta: (%d,%d)\n",
				event.jball.which,
				event.jball.ball, event.jball.xrel, event.jball.yrel);
				break;
			}

			case SDL_JOYBUTTONDOWN:{
				const char* btnname = SDL_GameControllerGetStringForButton(event.jbutton.button);
				SDL_Log("Joystick button %s down\n", btnname);
				break;
			}

			case SDL_JOYBUTTONUP:{
				const char* btnname = SDL_GameControllerGetStringForButton(event.jbutton.button);
				SDL_Log("Joystick button %s up\n", btnname);
				break;
			}

			default:{
				break;
				printf("Unsupported input method (%d): ", event.type);
				switch (event.type){
					case SDL_QUIT: printf("SDL_QUIT\n");break;
					case SDL_APP_TERMINATING: printf("SDL_APP_TERMINATING\n");break;
					case SDL_APP_LOWMEMORY: printf("SDL_APP_LOWMEMORY\n");break;
					case SDL_APP_WILLENTERBACKGROUND: printf("SDL_APP_WILLENTERBACKGROUND\n");break;
					case SDL_APP_DIDENTERBACKGROUND: printf("SDL_APP_DIDENTERBACKGROUND\n");break;
					case SDL_APP_WILLENTERFOREGROUND: printf("SDL_APP_WILLENTERFOREGROUND\n");break;
					case SDL_APP_DIDENTERFOREGROUND: printf("SDL_APP_DIDENTERFOREGROUND\n");break;
					case SDL_WINDOWEVENT: printf("SDL_WINDOWEVENT\n");break;
					case SDL_SYSWMEVENT: printf("SDL_SYSWMEVENT\n");break;
					case SDL_KEYDOWN: printf("SDL_KEYDOWN\n");break;
					case SDL_KEYUP: printf("SDL_KEYUP\n");break;
					case SDL_TEXTEDITING: printf("SDL_TEXTEDITING\n");break;
					case SDL_TEXTINPUT: printf("SDL_TEXTINPUT\n");break;
					case SDL_KEYMAPCHANGED: printf("SDL_KEYMAPCHANGED\n");break;
					case SDL_MOUSEMOTION: printf("SDL_MOUSEMOTION\n");break;
					case SDL_MOUSEBUTTONDOWN: printf("SDL_MOUSEBUTTONDOWN\n");break;
					case SDL_MOUSEBUTTONUP: printf("SDL_MOUSEBUTTONUP\n");break;
					case SDL_MOUSEWHEEL: printf("SDL_MOUSEWHEEL\n");break;
					case SDL_JOYAXISMOTION: printf("SDL_JOYAXISMOTION\n");break;
					case SDL_JOYBALLMOTION: printf("SDL_JOYBALLMOTION\n");break;
					case SDL_JOYHATMOTION: printf("SDL_JOYHATMOTION\n");break;
					case SDL_JOYBUTTONDOWN: printf("SDL_JOYBUTTONDOWN\n");break;
					case SDL_JOYBUTTONUP: printf("SDL_JOYBUTTONUP\n");break;
					case SDL_JOYDEVICEADDED: printf("SDL_JOYDEVICEADDED\n");break;
					case SDL_JOYDEVICEREMOVED: printf("SDL_JOYDEVICEREMOVED\n");break;
					case SDL_CONTROLLERAXISMOTION: printf("SDL_CONTROLLERAXISMOTION\n");break;
					case SDL_CONTROLLERBUTTONDOWN: printf("SDL_CONTROLLERBUTTONDOWN\n");break;
					case SDL_CONTROLLERBUTTONUP: printf("SDL_CONTROLLERBUTTONUP\n");break;
					case SDL_CONTROLLERDEVICEADDED: printf("SDL_CONTROLLERDEVICEADDED\n");break;
					case SDL_CONTROLLERDEVICEREMOVED: printf("SDL_CONTROLLERDEVICEREMOVED\n");break;
					case SDL_CONTROLLERDEVICEREMAPPED: printf("SDL_CONTROLLERDEVICEREMAPPED\n");break;
					case SDL_FINGERDOWN: printf("SDL_FINGERDOWN\n");break;
					case SDL_FINGERUP: printf("SDL_FINGERUP\n");break;
					case SDL_FINGERMOTION: printf("SDL_FINGERMOTION\n");break;
					case SDL_DOLLARGESTURE: printf("SDL_DOLLARGESTURE\n");break;
					case SDL_DOLLARRECORD: printf("SDL_DOLLARRECORD\n");break;
					case SDL_MULTIGESTURE: printf("SDL_MULTIGESTURE\n");break;
					case SDL_CLIPBOARDUPDATE: printf("SDL_CLIPBOARDUPDATE\n");break;
					case SDL_DROPFILE: printf("SDL_DROPFILE\n");break;
					case SDL_DROPTEXT: printf("SDL_DROPTEXT\n");break;
					case SDL_DROPBEGIN: printf("SDL_DROPBEGIN\n");break;
					case SDL_DROPCOMPLETE: printf("SDL_DROPCOMPLETE\n");break;
					case SDL_AUDIODEVICEADDED: printf("SDL_AUDIODEVICEADDED\n");break;
					case SDL_AUDIODEVICEREMOVED: printf("SDL_AUDIODEVICEREMOVED\n");break;
					case SDL_RENDER_TARGETS_RESET: printf("SDL_RENDER_TARGETS_RESET\n");break;
					case SDL_RENDER_DEVICE_RESET: printf("SDL_RENDER_DEVICE_RESET\n");break;
					case SDL_USEREVENT: printf("SDL_USEREVENT\n");break;
					case SDL_LASTEVENT: printf("SDL_LASTEVENT\n");break;
					default: printf("Not a valid event\n");
				}
			}
		}
	}

	// cleanup SDL

	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();

	return 0;
}

[/spoiler]

I can imagine that this onlinetool doesn’t “see” the gamepad in the same way SDL does and thus button numbers or whatever don’t match.

I’d try https://generalarcade.com/gamepadtool/ instead

Generally it’d also be interesting to know what operating system you’re using.

By the way, the equivalent of [spoiler] in Discourse is:

[details="Summary"]
This text will be hidden
[/details]
1 Like

Oh that’s wacky, that tool gives the exact correct mapping but also the same default mapping that causes my program to report wrong results.

So we can say it’s probably my program. But why? What did I do wrong?

I would love to see their source code so that I can adjust my input handling to whatever they’re doing.

EDIT:

I just made a custom mapping using that tool and lo and behold it now works on my end as well! Thanks for letting me know about that tool. I still don’t understand why the default mapping showed up in their program correctly but that’s water under the bridge.

Looks like their tool is not open source - but SDL2 includes a mapping utility in their source distribution (controllermap.c in the test/ subdir).

However, looking at your source more closely, I think the problem is that you’re handling joystick events instead of controller events - SDL_JOY* gives you unmapped joystick events, you want the SDL_CONTROLLER* events, see “Controller events” on https://wiki.libsdl.org/SDL_EventType

1 Like

I added the following before the joystick stuff, but it doesn’t seem to work. Anything else I need to set up to use controller events?

			case SDL_CONTROLLERAXISMOTION:
			case SDL_CONTROLLERBUTTONDOWN:
			case SDL_CONTROLLERBUTTONUP:
			case SDL_CONTROLLERDEVICEADDED:
			case SDL_CONTROLLERDEVICEREMOVED:
			case SDL_CONTROLLERDEVICEREMAPPED:
			{
				printf("controller thing\n");
				break;
			}

Update:

Controller map is still broken.

I thought the generated map was ok because the dpad was matching up. However I learned that the f710 has a “mode” switch, that switches the dpad and left joystick (facepalm).

So, sdl is still using the default mapping, despite me overriding it with addMapping (which again returns OK).

Consider the map:

030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,

Now, knowing that lefttrigger and rightx are swapped, we can just swap them in the map, right?

"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:a3,righttrigger:a5,platform:Linux,"

BUT it doesn’t work!!! What the hell!

Does anybody know what kind of black magic is going on here?

I think SDL_GameControllerOpen() should suffice to get controller events

if you added it to the second switch/case under default: of the first switch/case, it won’t show anything because you break; before printing and before the second switch(event.type) (unless you changed that code since)

Ah yeah it turns out I hadn’t converted my code correctly to handle controllers instead of joysticks.

Now it also handles the mapping properly. In hindsight obvious that the controller mapping does not work on a joystick (not controller)

For those wondering here is the code now:

SDL_Init( SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC )

Handling events:

case SDL_CONTROLLERAXISMOTION:
{
	printf("Controller Axis: %s\n",SDL_GameControllerGetStringForAxis(event.caxis.axis));
	break;
}

case SDL_CONTROLLERBUTTONDOWN:
{
	printf("Controller Btn: %s pressed\n",SDL_GameControllerGetStringForButton(event.cbutton.button));
	
	break;
}

case SDL_CONTROLLERBUTTONUP:
{
	printf("Controller Btn: %s released\n",SDL_GameControllerGetStringForButton(event.cbutton.button));
	
	break;
}

case SDL_CONTROLLERDEVICEADDED:
case SDL_CONTROLLERDEVICEREMOVED:
case SDL_CONTROLLERDEVICEREMAPPED:
{
	printf("controller changed\n");
	initControllers();
	break;
}

initControllers()

void initControllers(){
	char* mapping = "030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,";
	SDL_GameController *ctrl;
	SDL_Joystick *joy;
	int i;

	for(i = 0; i < SDL_NumJoysticks(); ++i)
	{
		if (SDL_IsGameController(i))
		{
			int mapped = SDL_GameControllerAddMapping(mapping);
			if (mapped==-1){
				printf( "SDL could not map! SDL Error: %s\n", SDL_GetError() );
			}
			printf("mapped: %d\n", mapped);

			char *reportedmapping;
			printf("Index \'%i\' is a compatible controller, named \'%s\'\n", i, SDL_GameControllerNameForIndex(i));

			ctrl = SDL_GameControllerOpen(i);
			//joy = SDL_GameControllerGetJoystick(ctrl);

			reportedmapping = SDL_GameControllerMapping(ctrl);

			printf("mapping:%s\n", reportedmapping);
			SDL_free(reportedmapping);
		}
		else
		{
			printf("Index \'%i\' is not a compatible controller.\n", i);
		}
	}
}