DualShock 4 gyro recognized by evdev but not SDL

Hello!

I have a DualShock 4 controller that I’m interested in using to test the new sensor features in SDL 2.0.14. The controller is connected via Bluetooth to a PC running Arch Linux, which recognizes it as three separate evdev devices: the touchpad, the motion sensors, and the main controller. If I use evtest to monitor the motion sensors, the output appears correct (it is reporting ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, and ABS_RZ.)

However, SDL doesn’t recognize it - neither as a joystick, nor as additional axes or sensors on the main controller joystick, nor as a standalone motion sensor. I’ve tried the suggestion in https://wiki.gentoo.org/wiki/Sony_DualShock to use the joystick driver for the gyro, but that just causes it to control the mouse (and also seems to do so incorrectly, creating button mappings that don’t exist.)

Reading some of the source, it looked like SDL specifically excludes motion sensors to prevent unwanted interference with the main joystick. If that is the case, how should I actually access the motion sensors in SDL? Is something wrong with my controller configuration?

1 Like

Hi Nolbin! How are you setting up and reading from the controller?

Mine looks something like this:

SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR);

for (int i = 0; i < SDL_NumJoysticks(); i++)
{
	if (!SDL_IsGameController(i))
	{
		continue;
	}
	SDL_GameController* g = SDL_GameControllerOpen(i);
	gamepadId[numGamepads++] = g;
	if (SDL_GameControllerSetSensorEnabled(g, SDL_SENSOR_GYRO, SDL_TRUE) < 0)
	{
		printf("Could not enable gyro for controller %d\n", numGamepads - 1);
	}
}

// main loop
while (1)
{
	for (int i = 0; i < numGamepads; i++)
	{
		SDL_GameController* g = gamepadId[i];
		if (g == nullptr || !SDL_GameControllerGetAttached(g))
		{
			continue;
		}

		bool up = SDL_GameControllerGetButton(g, SDL_CONTROLLER_BUTTON_DPAD_UP);
		... etc
		
		float gyro[3] = { 0.f };
		SDL_GameControllerGetSensorData(g, SDL_SENSOR_GYRO, gyro, 3);
	}

	SDL_Delay(16);
	SDL_GameControllerUpdate();
}

Thanks for your reply! I had a similar setup to yours. I tried running your code directly, and the first portion of the code reports only the message “Could not enable gyro for controller 0”.

If I call SDL_GetError() at that point, the returned string is “That operation is not supported”. Similarly, SDL_GameControllerHasSensor(g, SDL_SENSOR_GYRO) and SDL_GameControllerHasSensor(g, SDL_SENSOR_ACCEL) both return false.

My guess is that this happens because the Linux evdev drivers for the DualShock 4 report it as three separate devices, so the joystick it sees doesn’t include any data from the motion sensors or the joypad. I’m not sure how or if SDL attempts to re-bundle the motion sensors back with the joystick…

First you need to enable enhanced mode to turn on rumble and sensors:

SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");

Then, on Linux, SDL uses hidraw to read the device so you need to set the read right with an udev rule. The easy way to do that is to install steam-devices package.

If your distribution does not provide this package, you can add the rule yourself in /usr/lib/udev/rules.d/. Here the rules for Dualshock controller from the steam-devices package:

# DualShock 4 over USB hidraw
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="05c4", MODE="0660", TAG+="uaccess"

# DualShock 4 wireless adapter over USB hidraw
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0ba0", MODE="0660", TAG+="uaccess"

# DualShock 4 Slim over USB hidraw
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="09cc", MODE="0660", TAG+="uaccess"

# DualShock 4 over bluetooth hidraw
KERNEL=="hidraw*", KERNELS=="*054C:05C4*", MODE="0660", TAG+="uaccess"

# DualShock 4 Slim over bluetooth hidraw
KERNEL=="hidraw*", KERNELS=="*054C:09CC*", MODE="0660", TAG+="uaccess"

Thank you for your reply - and for correctly recognizing that Arch Linux doesn’t seem to have the steam-devices package available yet. I added the rule you provided as /usr/lib/udev/rules.d/71-dualshock.rules - and it worked! I can now get both gyro and accel data.

The only remaining oddity is that now, when I call SDL_JoystickOpen(0), the following line appears:

ioctl (GFEATURE): Input/output error

But it seems to be working correctly despite the error, as far as I can tell.