Force Feedback

From FreeGameDevWiki
Jump to: navigation, search

Linux supports both simple rumble as well as full blown force feedback on a number of hardware devices. In addition Linux's userspace input system is fully force feedback capable. High level support for force feedback has however been lacking, as SDL 1.2 doesn't support force feedback, SDL 1.3 will fix this, see SDL_haptic.h, the interface is pretty similar to what is shown here.

Force feedback in Linux is provided, just like regular input support, via the /dev/input/eventX devices, but instead of opening the device for reading it has to be opened for writing. What then follows is a two step process, first one has to upload a effect description to the device and then in a second step one has to start the previously uploaded effect by id.

Opening/closing the device

 int fd = open("/dev/input/event0", O_RDWR);
 ... 
 close(fd);

Querying device capabilites

 #define BITS_PER_LONG (sizeof(long) * 8)
 #define OFF(x)  ((x)%BITS_PER_LONG)
 #define BIT(x)  (1UL<<OFF(x))
 #define LONG(x) ((x)/BITS_PER_LONG)
 #define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
 
 unsigned long features[4];
 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) == -1) {
   perror("Ioctl query");
   exit(1);
 }
 
 if (test_bit(FF_CONSTANT, features)) printf("Constant ");
 if (test_bit(FF_PERIODIC, features)) printf("Periodic ");
 if (test_bit(FF_SPRING, features))   printf("Spring ");
 if (test_bit(FF_FRICTION, features)) printf("Friction ");
 if (test_bit(FF_RUMBLE, features))   printf("Rumble ");
 
 int n_effects; /* Number of effects the device can play at the same time */
 if (ioctl(fd, EVIOCGEFFECTS, &n_effects) == -1) {
   perror("Ioctl query");
   exit(1);
 }

Uploading an Effect

The effect is uploaded via an ioctl(). After the ioctl() call the effect.id variable will contain the effects id which must be used when playing or stopping the effect.

 struct ff_effect effect;
 ...
 /* Set member variables in effect struct, see below */
 ...
 if (ioctl(fd, EVIOCSFF, &effect) == -1) {
   perror("upload effect");
   exit(1);
 }

It is also possible to reupload the same effect, with the same effect.id later on with new parameters, this allows to updating a running effect, without first stopping it, deleteing it and then reuploading it and restarting.

An effect can be deleted via:

 if (ioctl(fd, EVIOCRMFF, effect.id) == -1) {
   perror("upload effect");
   exit(1);
 }

Gamepads are not full force feedback devices and only support the rumble effect in hardware, the force feedback driver will however try to emulate other effects via rumble.

FF_RUMBLE

The rumble effect is the most basic effect, it lets the gamepad vibrate. The API contains support for two motors, a strong one and a weak one, which can be controlled independently.

 effect.type = FF_RUMBLE;
 effect.id = -1;
 effect.u.rumble.strong_magnitude = 0;
 effect.u.rumble.weak_magnitude   = 0xc000;
 effect.replay.length = 5000;
 effect.replay.delay  = 0;

FF_PERIODIC

 effect.type = FF_PERIODIC;
 effect.id = -1;
 effect.u.periodic.waveform = FF_SINE;
 effect.u.periodic.period = 0.1*0x100;     /* 0.1 second */
 effect.u.periodic.magnitude = 0x4000;     /* 0.5 * Maximum magnitude */
 effect.u.periodic.offset = 0;
 effect.u.periodic.phase = 0;
 effect.direction = 0x4000;        /* Along X axis */
 effect.u.periodic.envelope.attack_length = 0x100;
 effect.u.periodic.envelope.attack_level  = 0;
 effect.u.periodic.envelope.fade_length = 0x100;
 effect.u.periodic.envelope.fade_level  = 0;
 effect.trigger.button = 0;
 effect.trigger.interval = 0;
 effect.replay.length = 20000;  /* 20 seconds */
 effect.replay.delay = 0;

File:FF envelope.png

FF_CONSTANT

 effect.type = FF_CONSTANT;
 effect.id = -1;
 effect.u.constant.level = 0x2000; /* Strength : 25 % */
 effect.direction = 0x6000;        /* 135 degrees */
 effect.u.constant.envelope.attack_length = 0x100;
 effect.u.constant.envelope.attack_level = 0;
 effect.u.constant.envelope.fade_length = 0x100;
 effect.u.constant.envelope.fade_level = 0;
 effect.trigger.button = 0;
 effect.trigger.interval = 0;
 effect.replay.length = 20000;  /* 20 seconds */
 effect.replay.delay = 0;

FF_SPRING

 effect.type = FF_SPRING;
 effect.id = -1;
 effect.u.condition[0].right_saturation = 0x7fff;
 effect.u.condition[0].left_saturation = 0x7fff;
 effect.u.condition[0].right_coeff = 0x2000;
 effect.u.condition[0].left_coeff = 0x2000;
 effect.u.condition[0].deadband = 0x0;
 effect.u.condition[0].center = 0x0;
 effect.u.condition[1] = effect.u.condition[0];
 effect.trigger.button = 0;
 effect.trigger.interval = 0;
 effect.replay.length = 20000;  /* 20 seconds */
 effect.replay.delay = 0;

File:FF condition.png

FF_DAMPER

 effect.type = FF_DAMPER;
 effect.id = -1;
 effect.u.condition[0].right_saturation = 0x7fff;
 effect.u.condition[0].left_saturation = 0x7fff;
 effect.u.condition[0].right_coeff = 0x2000;
 effect.u.condition[0].left_coeff = 0x2000;
 effect.u.condition[0].deadband = 0x0;
 effect.u.condition[0].center = 0x0;
 effect.u.condition[1] = effect.u.condition[0];
 effect.trigger.button = 0;
 effect.trigger.interval = 0;
 effect.replay.length = 20000;  /* 20 seconds */
 effect.replay.delay = 0;

Playing/Stopping an Effect

After the effect has been uploaded one needs send an input event of type EV_FF to the device:

   struct input_event play;
   
   play.type = EV_FF;
   play.code =  effect.id; /* the id we got when uploading the effect */
   play.value = 1; /* play: 1, stop: 0 */
   
   if (write(fd, (const void*) &play, sizeof(play)) == -1) {
     perror("sending event");
     exit(1);
   }

Examples

Todo: Insert some examples on how to achieve gun fire, explosions, car crashes and other common game effects.

Issues

  • xboxdrv ends up as non-interruptable dead process when it is killed in the wrong moment, seems to be a limitation in the userspace force feedback kernel API
  • Wine crashes in some games when a force feedback capable driver is used
  • documentation on the min/max values and of how the effect should 'feel' are very vague
  • documentation on how effect emulation of not supported effects should work is non existant

Supported Devices

  • Xbox360 gamepad (both with xpad kernel driver as well as the xboxdrv)
  • ThrustMaster Firestorm Dual Power 3 (only via xboxdrv, not via the kernel driver)
  • I-Force devices capable devices

The protocol of unsupported devices that work in Windows can easily be figured out with a USB protocol analyser such as USBlyzer.

Official Documentation

Third Party non-Linux Documentation

Software with Fore Feedback support