Hi,
I am trying to achieve rumble / vibration / force feedback functionality from any type of USB gamepad / controller as we need such feedback for upcoming user tests. I was happy to see that at least the PS4 controller works well with recent versions of GLFW; however, patching in rumble feedback has proven difficult.
Now, it is fair to say that I am out of my element when it comes to ioctl and drivers, but leveraging (lifting) from mainly the GLFW source and this article from Linux Journal I have managed to produce a debug script (see below), which for a given controller returns its supported functions.
Running this with either a PS4 controller, a PS3 (dualshock) controller, or a Switch Pro Controller, returns that Synch events, Buttons, Absolute Axes, as well as Miscellaneous are available. None are showing rumble (Force feedback).
Does anyone have any ideas of what is wrong? Or better yet, a way to make any controller rumble? (I am willing to purchase any other kind of controller that is known to work).
Thanks!
#include <linux/input.h>
#include <regex.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
/*From glfw*/
const char* dirname = "/dev/input";
regex_t regex;
if (regcomp(®ex, "^event[0-9]\\+$", 0) != 0)
{
printf("Linux: Failed to compile regex");
}
DIR* dir = opendir(dirname);
printf("Try these as path: \n");
if (dir)
{
struct dirent* entry;
while ((entry = readdir(dir)))
{
regmatch_t match;
if (regexec(®ex, entry->d_name, 1, &match, 0) != 0)
continue;
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name);
printf("%s\n",path);
}
}
printf("\n");
/* *** *** */
int fd;
const char* path = "/dev/input/event2";
fd = open(path, O_RDWR | O_NONBLOCK);
if (fd == -1){
printf("Couldn't open file descriptor. Test other path or permission?\n");
return -1;
}
char name[256]= "Unknown";
if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) {
perror("evdev ioctl");
}
printf("The device says its name is %s\n",
name);
char phys[256]= "Unknown";
if(ioctl(fd, EVIOCGPHYS(sizeof(phys)), phys) < 0) {
perror("event ioctl");
}
printf("The device says its path is %s\n",
phys);
unsigned long evtype_bitmask[EV_MAX];
memset(evtype_bitmask, 0, sizeof(evtype_bitmask));
if (ioctl(fd, EVIOCGBIT(0, EV_MAX), evtype_bitmask) < 0) {
perror("evdev ioctl");
}
printf("Supported event types:\n");
printf(" Bitmask: %ld,",*evtype_bitmask);
printf(" 0x%02lx, ",*evtype_bitmask);
long binaryOut = *evtype_bitmask;
for (int i = EV_MAX-1; i >= 0; --i) {
if ((binaryOut >> i) & 1)
printf("1");
else
printf("0");
}
printf("\n");
#define TEST_BIT(bit, array) (array[bit / 8] & (1 << (bit % 8)))
for (int yalv = 0; yalv < EV_MAX; yalv++) {
if (TEST_BIT(yalv, evtype_bitmask)) {
printf(" Event type 0x%02x ", yalv);
switch ( yalv)
{
case EV_SYN :
printf(" (Synch Events)\n");
break;
case EV_KEY :
printf(" (Keys or Buttons)\n");
break;
case EV_REL :
printf(" (Relative Axes)\n");
break;
case EV_ABS :
printf(" (Absolute Axes)\n");
break;
case EV_MSC :
printf(" (Miscellaneous)\n");
break;
case EV_LED :
printf(" (LEDs)\n");
break;
case EV_SND :
printf(" (Sounds)\n");
break;
case EV_REP :
printf(" (Repeat)\n");
break;
case EV_FF :
printf(" (Force Feedback)\n");
break;
case EV_FF_STATUS:
printf(" (Force Feedback (Status))\n");
break;
case EV_PWR:
printf(" (Power Management)\n");
break;
default:
printf(" (Unknown: 0x%04hx)\n",
yalv);
}
}
}
return 0;
}